C 语言
extern 作用
extern 影响程序的链接过程,表示对变量或函数的声明,真正的定义在其他地方。
- 函数默认是外部链接(extern)
- 而变量而言,文件级别的定义为 extern,block 级别的为内部。
在 C 中声明全局变量的方式:
- 在头文件中使用 extern 声明(
int i这种形式称为 tentative definition ) - 在一个 C 文件中定义,
- 其他文件直接引用头文件即可
int i1 = 10; /* definition, external linkage */
static int i2 = 20; /* definition, internal linkage */
extern int i3 = 30; /* definition, external linkage */
int i4; /* tentative definition, external linkage */
static int i5; /* tentative definition, internal linkage */
int i1; /* valid tentative definition */
int i2; /* not legal, linkage disagreement with previous */
int i3; /* valid tentative definition */
int i4; /* valid tentative definition */
int i5; /* not legal, linkage disagreement with previous */
// 在 link 阶段,变量默认是 external 的,使用 static 可以变成 internal 的
// 任何文件级别的变量,都按照 static memory model 分配,不管有没有 extern/static 修饰
static int a = 1;
// 在 link 阶段,函数默认是外部链接,使用 static 可以变成内部链接
int add(int x, int y) {
// block 级别的变量在 link 阶段默认是 internal 的,可以使用 extern 改成外部链接,但是很少这么做
// 内存模型是 auto 的,使用 static 会改变内存模型为 static
int a = 10;
return x + y + a;
}const
解读声明的诀窍是从右向左阅读。具体如下:
int constA constant integerint const *A (variable) pointer to a constant integerint * constA constant pointer to a (variable) integerint * const *A pointer to a constant pointer to an integerint const * *A pointer to a pointer to a constant integerint const * const *A pointer to a constant pointer to a constant integer
可以看出, const 总是修饰它左边的内容,与 * 的规律是一致的。
#include<stdio.h>
extern int a;
// const 只能限定 b,foo 可以修改其指向的元素
void set_elmt(int *foo, int const *b) {
foo[0] = 3;
// error: read-only variable is not assignable
/* b[1] = 4; */
}
int main() {
printf("a = %d\n", a);
int foo[10] = {};
int const *b = foo;
set_elmt(foo, b);
printf("a = %d\n", *foo);
}局限性
下面演示说明:对于 const 的 struct,其内部的成员是可以被修改的
#include <assert.h>
#include <stdio.h>
#include <stdlib.h> //assert
typedef struct {
int *counter1, *counter2;
} counter_s;
void check_counter(int *ctr){ assert(*ctr !=0); }
double ratio(counter_s const *in){
check_counter(in->counter2);
return *in->counter1/(*in->counter2+0.0);
}
int main(){
counter_s cc = {
// Designated Initializers
.counter1=malloc(sizeof(int)),
.counter2=malloc(sizeof(int))
};
*cc.counter1 = *cc.counter2 = 1;
printf("ratio = %f\n",ratio(&cc));
}指针与数组的区别
数组名和指针之间存在一个重要区别:
- 指针是一个变量,因此像
pa=a和pa++这样的操作是合法的。 - 但数组名并不是一个变量,因此类似
a=pa和a++这样的用法是非法的。
int a[10];
int *pa = &a[0];
// 等价于
int *pa = a;
// valid
pa = a
pa ++
// invalid
a = pa
a++char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */- 可以通过 amessage 修改字符串内容,但是不能修改 amessage 的指向
- 可以修改 pmessage 的指向,但是不能修改字符串的内容

字符指针与数组的区别
如何解读 C 声明
变量声明的语法模仿了变量可能出现在表达式中的语法结构,从而使两者显得更加一致和自然。
The syntax of the declaration for a variable mimics the syntax of expressions in which the variable might appear.
double *dp, atof(char *);上面的声明可以解读为:
- 在表达式
*dp和atof(s)都会产生一个 double 类型的值。 atof的参数是一个指向 char 的指针
int *A[100];这意味着我们可以使用 *A[i] 表达式来获取一个整数值,所以 A :
- 必须是一个数组(因为它可以用下标访问)
- 数组的元素必须是指针(因为它可以被解引用)
所有 A 的类型是:数组(100 个元素),元素类型是指向整数的指针。
int (*A)[100];类似的,由于我们可以使用 (*A)[0] 表达式来获取一个整数值,所以 A:
- 必须是一个指针(因为它可以被解引用)
- 指针指向的内容必须是一个数组(因为它可以用下标访问)
所以 A 的类型是:指向一个包含 100 个整数的数组的指针。