数组名涵义
- 数组名有两个含义:
- 第一含义是:整个数组
- 第二含义是:首元素地址
- 当出现以下情形时,那么数组名就代表整个数组:
- 在数组定义中
- 在 sizeof 运算表达式中
- 在取址符&中
- 其他任何情形下,那么数组名就代表首元素地址。即:此时数组名就是一个指向首元素的指针。
- 示例:
int a[3] = {1,2,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];
char *p;
};
// 给结构体额外分配 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]);