数组名涵义
-
数组名有两个含义:
-
第一含义是:整个数组
-
第二含义是:首元素地址
-
-
当出现以下情形时,那么数组名就代表整个数组:
-
在数组定义中
-
在 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]
语言只有在第一含义的场合下表现为数组,其他大部分场合都表现为首元素的地址,当数组表现为首元素地址时,实际上就是一个指向其首元素的指针。数组运算实际上就是指针运算。
数组下标
-
数组下标实际上是编译系统的一种简写,其等价形式是:
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 = 15; 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 不可初始化
int len = 10; // 编译的时候不能确认len的值,并且申请空间的同时直接赋值,所以不能初始化 //int buf[len] = {10}; // 定义的时候没有使用此空间,所以不会报错 int buf[len]; buf[1] = 10; printf("%d\n",buf[1]);