数组名涵义
数组名在不同情形会表现为不同的含义:第一含义是整个数组,第二含义是首元素地址。 触发第一含义的情形:
在数组定义中 在 sizeof 运算表达式中 在取址符&中
其他
int a[3]; // 此处,a 代表整个数组
printf("%d\n", sizeof(a)); // 此处,a 代表整个数组
printf("%p\n", &a); // 此处,a 代表整个数组,此处为整个数组的地址
int *p = a; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
p = a + 1; // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
function(a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
scanf("%d\n", a); // 此处,a 代表首元素 a[0] 的地址,等价于 &a[0]
C语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。
数组下标
数组下标实际上是编译系统的一种简写,其等价形式是:
a[i] = 100; 等价于 *(a+i) = 100;
a[i] = 100;
*(a+i) = 100;
*(i+a) = 100;
i[a] = 100;
数组运算,等价于指针运算(指针运算下一节教案会详细讲解 )。
字符串常量
字符串常量在内存中的存储,实质是一个匿名数组 匿名数组,同样满足数组两种涵义的规定 示例:
printf("%d\n", sizeof("abcd")); // 此处 "abcd" 代表整个数组
printf("%p\n", &"abcd"); // 此处 "abcd" 代表整个数组
printf("%c\n", "abcd"[1]); // 此处 "abcd" 代表匿名数组的首元素地址
char *p1 = "abcd"; // 此处 "abcd" 代表匿名数组的首元素地址
char *p2 = "abcd" + 1; // 此处 "abcd" 代表匿名数组的首元素地址
零长数组 (提前预习:结构体)
概念:长度为0的数组,比如 int data[0]; 用途:放在结构体的末尾,作为可变长度数据的入口 示例:
struct node
{
/* 结构体的其他成员 */
// 成员1
// 成员2
// ... ...
int len;
char *data[0];
};
// 给结构体额外分配 10 个字节的内存。
struct node *p = malloc(sizeof(struct node) + 10);
p->len = 10;
// 额外分配的内存可以通过 data 来使用
p->data[0] ~ p->data[9]
变长数组
概念:定义时,使用变量作为元素个数的数组。 要点:变长数组仅仅指元素个数在定义时是变量,而绝非指数组的长度可长可短。实际上,不管是普通数组还是所谓的变长数组,数组一旦定义完毕,其长度则不可改变。 示例:
int len = 5;
int a[len]; // 数组元素个数 len 是变量,因此数组 a 是变长数组
int x = 2;
int y = 3;
int b[x][y]; // 数组元素个数 x、y 是变量,因此数组 b 是变长数组
int b[2][y]; // 数组元素个数 y 是变量,因此数组 b 是变长数组
int b[x][3]; // 数组元素个数 x 是变量,因此数组 b 是变长数组
int len = 5;
int a[len] = {1,2,3,4,5}; // 数组 a 不可初始化