声明的局部变量数组,如果没有初始化,那么数组保存在栈上,数组的内容是不确定的。
局部变量不像全局变量,全局变量如果没初始化,那么默认是全 0。
将数组初始化为 0 的方式主要有以下 3 种,本人在开发过程中习惯于使用第一种,即使用 {0} 将数组初始化为全 0。
(1)数组声明的时候使用 {0} 初始化
#include
#include
#include
#include
int main() {
int a[10] = {0};
int b[10][10] = {0};
for (int i = 0; i < 10; i++) {
printf("a[%d] = %d\n", i, a[i]);
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
printf("b[%d][%d] = %d\n", i, j, b[i][j]);
}
}
return 0;
}
(2)memset()
使用 memset() 将数组设置成 0。
#include
#include
#include
#include
int main() {
int a[10] = {0};
int b[10][10] = {0};
for (int i = 0; i < 10; i++) {
printf("a[%d] = %d\n", i, a[i]);
}
for (int i = 0; i < 10; i++) {
for (int j = 0; j < 10; j++) {
printf("b[%d][%d] = %d\n", i, j, b[i][j]);
}
}
return 0;
}
(3)使用 for 循环将数组元组逐个设置为 0
指针的加减运算移动的单位是指针指向的数据的大小。
而不是把指针看做一个整型数,在这个数的基础上加减。
#include
#include
#include
#include
struct test {
int a;
char name[10];
long data;
};
int main() {
char c;
char *pc = &c;
int a = 0;
int *pa = &a;
struct test t;
struct test *pt = &t;
printf("pc: %p, pc + 1: %p\n", pc, pc + 1);
printf("pa: %p, pa + 1: %p\n", pa, pa + 1);
printf("pt: %p, pt + 1: %p\n", pt, pt + 1);
printf("sizeof(struct test): %d\n", sizeof(struct test));
printf("&t: %p, &t.a: %p, &t.name: %p, &t.data: %p\n", &t, &t.a, &t.name, &t.data);
return 0;
}
pc 指向的数据类型是 char 类型,所以 pc + 1,地址移动 1 个字节。
pa 指向的数据类型是 int 类型,所以 pa + 1,地址移动了 4 个字节。
pt 指向的数据类型是 struct test 类型,所以 pt + 1,地址移动了 24 个字节。
结构体的大小并不是所有结构体成员大小的和,因为结构体中的成员需要对齐。
怎么才算对齐 ? 成员地址 % 成员大小 等于 0
从上边的打印可以看出来,成员 a 的地址是 0x7ffd20d18350 对 4 可以整除;
成员 name 是一个数组,不是以数组为整体进行对齐,而是以数组中的元素进行对齐;
name 最后一个元素的地址是 0x7ffd20d1835e,但是 data 却不能保存到 0x7ffd20d1835f 处,因为这个地址对 data 的长度不能整除,所以 data 保存在了 0x7ffd20d18360 处。
指针的减法,得到的是两个指针之间相隔的元素的个数。
#include
#include
#include
int main() {
int a[10] = {0};
int *pa = a;
int *pa5 = a +5;
printf("pa5 - pa: %d\n", pa5 - pa);
return 0;
}
二级指针指向的数据类型是一级指针,所以二级指针加减运算的时候移动的单位是一个一级指针的大小。
#include
#include
#include
#include
struct test {
int a;
char name[17];
long data;
};
int main() {
char c;
char *pc = &c;
char **ppc = &pc;
int a = 0;
int *pa = &a;
int **ppa = &pa;
struct test t;
struct test *pt = &t;
struct test **ppt = &pt;
printf("ppc: %p, ppc + 1: %p\n", ppc, ppc + 1);
printf("ppa: %p, ppa + 1: %p\n", ppa, ppa + 1);
printf("ppt: %p, ppt + 1: %p\n", ppt, ppt + 1);
printf("sizeof(struct test): %d\n", sizeof(struct test));
printf("&t: %p, &t.a: %p, &t.name: %p, &t.data: %p\n", &t, &t.a, &t.name, &t.data);
return 0;
}
数组名其实是一个指针,一维数组的数组名是一个一级指针 int *,二维数组的数组名是一个二级指针 int **。但是在使用 sizeof() 计算数组的大小的时候,并不是返回的一个指针的大小,而是返回的数组占用的空间。
#include
#include
#include
#include
int main() {
int a[10] = {0};
int b[10][10] = {0};
int *pa = a;
int **pb = b;
printf("sizeof(a): %d, sizeof(&a): %d, sizeof(pa): %d\n", sizeof(a), sizeof(&a), sizeof(pa));
printf("sizeof(b): %d, sizeof(&b): %d, sizeof(pb): %d\n", sizeof(b), sizeof(&b), sizeof(pb));
return 0;
}
一维数组的数组名是一级指针 int *,使用数组名做加减运算,移动的单位是数组元素的长度。
一维数组的数组名取地址,然后做加减运算,移动的单位是整个数组的大小。
#include
#include
#include
#include
int main() {
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
printf("a: %p, a + 1: %p\n", a, a + 1);
printf("&a: %p, &a + 1: %p\n", &a, &a + 1);
printf("*a: %d\n", *a);
return 0;
}
使用数组名做运算,a 和 a + 1 相差 4 个字节,即一个数组元素的长度。
使用数组名取地址做运算, &a 和 &a + 1相差整个数组的大小,4 * 10 = 40,即 0x28。
数组的加减运算有自己的特殊之处,并不能完全按照指针的加减运算来使用。
如果按照指针的加减运算,那么 &a + 1 偏移的长度应该是一个 int * 的长度,在 64 为机器上是 8。
#include
#include
#include
#include
int main() {
int a[4][4] = {{1, 2, 3, 4}, {11, 12, 13, 14}, {21, 22, 23, 24}, {31, 32, 33, 34}};
printf("a: %p, a + 1: %p\n", a, a + 1);
printf("*a: %p, *a + 1: %p\n", *a, *a + 1);
printf("&a: %p, &a + 1: %p\n", &a, &a + 1);
return 0;
}
二维数组的数组名是一个二级指针 int **, 加减运算移动的单位是一行的大小,这个例子中行和列都是 4,所以移动单位是 16。
二维数组名使用 * 取值之后就是一级指针,指向某一行,而指向某一行的指针和一维数组的数组名是类似的,移动的单位是一个元素的长度。
二维数组的数组名取地址再做加减运算,和一维数组是类似的,移动的单位是整个数组的大小。
#include
#include
#include
int main() {
char *a = "hello";
char b[] = "hello";
printf("sizeof(a): %d, strlen(a): %d\n", sizeof(a), strlen(a));
printf("sizeof(b): %d, strlen(b): %d\n", sizeof(b), strlen(b));
printf("\n");
printf("a: %p, a + 1: %p\n", a, a + 1);
printf("b: %p, b + 1: %p\n", b, b + 1);
return 0;
}
a 是一个 char * 指针,b 是一个 char 数组的数组名,两者还是有区别的。
sizeof(a) 是一个指针的大小,在 64 位系统中,长度是 8。
sizeof(b) 是一个数组的大小,长度是 6,包括字符串最后的结束符 '\0'。
strlen(a) 和 strlen(b) 是相同的,均是字符串的长度。使用 strlen() 获得的字符串的长度是不包括最后的字符串结束符 '\0' 的。
指针数组只的是一个数组,数组中的元素都是指针。
数组指针,可以叫数组的指针,是指一个指针,指向一个数组。
如下代码中,pa 是一个指针数组,数组长度是 2,可以保存两个指针。
ap 是数组指针,指向了数组 b。
#include
#include
#include
int main() {
int a[4] = {1, 2, 3, 4};
int b[5] = {5, 6, 7, 8, 9};
int *pa[2] = {a, b};
int *ap = b;
for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++) {
printf("a[%d] = %d\n", i, pa[0][i]);
}
for (int i = 0; i < sizeof(b) / sizeof(b[0]); i++) {
printf("b[%d] = %d\n", i, ap[i]);
}
return 0;
}
如下代码需要深入理解指针数组与数组指针的概念,记录如下。
#include
#include
#include
int main() {
char *str[] = {"Welcome", "to", "Fortemedia", "Nanjing"};
for (int i = 0; i < sizeof(str) / sizeof(str[0]); i++) {
printf("str[%d] = %p\n", i, str[i]);
}
char **p = str + 1;
printf("1111, *p: %s\n", *p); // to
p++; // p 指向 Fortemedia
printf("2222, *p: %s\n", *p);
p--; // p 指向 to
str[0] = (*p++) + 2; // p++ --> p 指向 Fortemedia, + 2 --> 超出数组范围
printf("3333, *p: %s\n", *p);
str[1] = *(p + 1); // p + 1 --> p + 1 指向 Nanjing,p 不变
printf("4444, *p: %s\n", *p);
str[2] = p[1] + 3; // p[1] --> Nanjing + 3 --> jing,因为 *p 的值也保存在 str[2] 这个位置,所以 p 也改变了
printf("p[0]: %s, *p: %s, str[2]: %s, str[1]: %s\n", p[0], *p, str[2], str[1]);
str[3] = p[0] + (str[2] - str[1]); // p[0] --> Fortemedia, str[2] jing, str[1] Nanjing,str[2] - str[1] = 3,p[0] + 3 指向 Nanjing 的最后一个字符 g
printf("%s\n", str[0]);
printf("%s\n", str[1]);
printf("%s\n", str[2]);
printf("%s\n", str[3]);
return 0;
}