数组和指针典型例题合集(一维数组、字符数组、二维数组)
1.一维数组
数组名的理解
数组名是数组首元素(第一个元素)的地址
但是有两个例外:
1.sizeof (数组名)—— 数组名表示整个数组,就算的是整个数组的大小,单位是字节。
2.&数组名 —— 数组名表示整个数组,取出的是整个数组的地址。
除此之外,所有的数组名表示的都是数组首元素(第一个元素)的地址。
对于数组名a
a —— 数组名
a —— 首元素(第一个元素)的地址,int*, a+1 跳过一个元素
&a —— 数组的地址,int(*)[4], &a+1 跳过y
辨析一下:
int a[] = { 1,2,3,4 };
// 数组有4个元素
printf("%zd\n", sizeof(a));
// 16
// sizeof(数组名)
printf("%zd\n", sizeof(a + 0));
// 4/8
// a是首元素的地址——类型是int*,a+0,还是首元素地址printf("%zd\n", sizeof(*a));
// 4
// a是首元素的地址,*a就是首元素,大小是4个字节
// *a == a[0] == *(a+0)printf("%zd\n", sizeof(a + 1));
// 4/8
// a是首元素的地址——类型是int*,a+1跳过一个整型,a+1就是第二个元素的地址printf("%zd\n", sizeof(a[1]));
// 4
// a[1]就是第二个元素,大小4个字节printf("%zd\n", sizeof(&a));
// 4/8
// &a —— 是数组的地址,数组的地址也是地址
// 而sizeof(&a)取出的是整个数组的地址,是地址就是 4/8 个字节printf("%zd\n", sizeof(*&a));
// 16
// * 和 & 相互抵消了
// &a 是数组的地址,类型是int(*)[4]
// 对数组指针解引用访问的是数组,计算的是数组的大小printf("%zd\n", sizeof(&a + 1));
// 4/8
// &a+1 跳过一个数组,数组的地址还是地址,地址就是 4/8 个字节printf("%zd\n", sizeof(&a[0]));
// 4/8
// 首元素的地址printf("%zd\n", sizeof(&a[0] + 1));
// 4/8
// 数组第二个元素的地址
运行代码验证一下:
//一维数组
int main()
{int a[] = { 1,2,3,4 };// 数组有4个元素printf("%zd\n", sizeof(a));// 16 // sizeof(数组名)printf("%zd\n", sizeof(a + 0));// 4/8 // a是首元素的地址——类型是int*,a+0,还是首元素地址printf("%zd\n", sizeof(*a));// 4 // a是首元素的地址,*a就是首元素,大小是4个字节// *a == a[0] == *(a+0)printf("%zd\n", sizeof(a + 1));// 4/8 // a是首元素的地址——类型是int*,a+1跳过一个整型,a+1就是第二个元素的地址printf("%zd\n", sizeof(a[1]));// 4// a[1]就是第二个元素,大小4个字节printf("%zd\n", sizeof(&a));// 4/8// &a —— 是数组的地址,数组的地址也是地址// 而sizeof(&a)取出的是整个数组的地址,是地址就是 4/8 个字节printf("%zd\n", sizeof(*&a));// 16// * 和 & 相互抵消了// &a 是数组的地址,类型是int(*)[4]// 对数组指针解引用访问的是数组,计算的是数组的大小printf("%zd\n", sizeof(&a + 1));// 4/8// &a+1 跳过一个数组,数组的地址还是地址,地址就是 4/8 个字节printf("%zd\n", sizeof(&a[0]));// 4/8// 首元素的地址printf("%zd\n", sizeof(&a[0] + 1));// 4/8 // 数组第二个元素的地址 return 0;
}
运行结果:
(注意:4/8是根据代码运行环境而定的,本次运行代码是在x64环境下的)
2.字符数组
对以下代码段进行辨析:
代码1:
char arr[] = { 'a','b','c','d','e','f' };
//一个字符一个字节printf("%d\n", sizeof(arr));
// 6 —— char
// 数组名单独放在sizeof内部了,计算的是数组大小,单位是字节printf("%d\n", sizeof(arr + 0));
// 4/8
// arr是数组名,表示首元素的地址,arr+0还是首元素的地址
// 是地址就是4/8printf("%d\n", sizeof(*arr));
// 1
// arr是首元素的地址,*arr就是首元素,大小是1个字节
// *arr —— arr[0] —— *(arr+0)printf("%d\n", sizeof(arr[1]));
// 1
// arr[1]是第二个元素,大小是1个字节printf("%d\n", sizeof(&arr));
// 4/8
// &arr 是数组地址,数组地址也是地址,是地址就是4/8
// &arr —— char(*)[6]printf("%d\n", sizeof(&arr + 1));
// 4/8
// 跳过整个数组,指向了这个数组后面的空间地址printf("%d\n", sizeof(&arr[0] + 1));
// 4/8
// 是第二个元素的地址,是地址就是4/8
代码运行验证一下(x64):
int main()
{char arr[] = { 'a','b','c','d','e','f' };//一个字符一个字节printf("%d\n", sizeof(arr));// 6 —— char// 数组名单独放在sizeof内部了,计算的是数组大小,单位是字节printf("%d\n", sizeof(arr + 0));// 4/8// arr是数组名,表示首元素的地址,arr+0还是首元素的地址// 是地址就是4/8printf("%d\n", sizeof(*arr));// 1// arr是首元素的地址,*arr就是首元素,大小是1个字节// *arr —— arr[0] —— *(arr+0)printf("%d\n", sizeof(arr[1]));// 1// arr[1]是第二个元素,大小是1个字节printf("%d\n", sizeof(&arr));// 4/8// &arr 是数组地址,数组地址也是地址,是地址就是4/8// &arr —— char(*)[6]printf("%d\n", sizeof(&arr + 1));// 4/8// 跳过整个数组,指向了这个数组后面的空间地址printf("%d\n", sizeof(&arr[0] + 1));// 4/8// 是第二个元素的地址,是地址就是4/8return 0;
}
运行结果:
代码2:
换为 strlen(#include<string.h>)
char arr[] = { 'a','b','c','d','e','f' };
//arr 中为 a b c d e fprintf("%d\n", strlen(arr));
// 随机的
// arr是首元素地址
// 数组中没有 \0 导致越界访问,结果是随机的printf("%d\n", strlen(arr + 0));
// 随机的
// arr + 0 是首元素地址
// 数组中没有 \0 导致越界访问,结果是随机的//printf("%d\n", strlen(*arr));
// err
// arr是首元素地址
// *arr是首元素,就是 a
// a的ASCII码值为97
// 而 strlen 会将97当成地址,但是97不是我们的地址
// 在内存中,有一大部分空间是不允许被占用的
// 程序会崩溃,得到的是野指针,代码有问题//printf("%d\n", strlen(arr[1]));
// err
// 同理,arr[1] 是 bprintf("%d\n", strlen(&arr));
// 随机值
// &arr 是数组的地址,起始位置是第一个元素printf("%d\n", strlen(&arr + 1));
// 随机值
// &arr + 1 是跳过了这个数组的地址printf("%d\n", strlen(&arr[0] + 1));
// 随机值
// 从第二个元素开始向后统计的
代码验证一下:
//换为strlen
int main()
{char arr[] = { 'a','b','c','d','e','f' };//arr 中为 a b c d e fprintf("%d\n", strlen(arr));// 随机的// arr是首元素地址// 数组中没有 \0 导致越界访问,结果是随机的printf("%d\n", strlen(arr + 0));// 随机的// arr + 0 是首元素地址// 数组中没有 \0 导致越界访问,结果是随机的//printf("%d\n", strlen(*arr));// err// arr是首元素地址// *arr是首元素,就是 a// a的ASCII码值为97// 而 strlen 会将97当成地址,但是97不是我们的地址// 在内存中,有一大部分空间是不允许被占用的// 程序会崩溃,得到的是野指针,代码有问题//printf("%d\n", strlen(arr[1]));// err// 同理,arr[1] 是 bprintf("%d\n", strlen(&arr));// 随机值// &arr 是数组的地址,起始位置是第一个元素printf("%d\n", strlen(&arr + 1));// 随机值// &arr + 1 是跳过了这个数组的地址printf("%d\n", strlen(&arr[0] + 1));// 随机值// 从第二个元素开始向后统计的return 0;
}
运行结果:
代码3:
printf("%d\n", sizeof(arr));
// 7
// arr是数组名,单独放在sizeof内部
// 计算的是数组总大小,是7个字节printf("%d\n", sizeof(arr + 0));
// 4/8
// arr表示数组首元素的地址,arr+0还是首元素的地址
// 是地址就是4/8printf("%d\n", sizeof(*arr));
// 1
// arr表示数组首元素的地址
// *arr是首元素,大小是1给个字节printf("%d\n", sizeof(arr[1]));
// 1
// 是第二个元素,大小1字节printf("%d\n", sizeof(&arr));
// 4/8
// &arr是数组地址
// 是地址就是4/8printf("%d\n", sizeof(&arr + 1));
// 4/8
// &arr是数组地址,+1是指向跳过整个数组的地址
// 是地址就是4/8printf("%d\n", sizeof(&arr[0] + 1));
// 4/8
// &arr[0] + 1是第二个元素的地址
// 是地址就是4/8
代码运行验证一下:
int main()
{char arr[] = "abcdef";printf("%d\n", sizeof(arr));// 7// arr是数组名,单独放在sizeof内部// 计算的是数组总大小,是7个字节printf("%d\n", sizeof(arr + 0));// 4/8// arr表示数组首元素的地址,arr+0还是首元素的地址// 是地址就是4/8printf("%d\n", sizeof(*arr));// 1// arr表示数组首元素的地址// *arr是首元素,大小是1给个字节printf("%d\n", sizeof(arr[1]));// 1// 是第二个元素,大小1字节printf("%d\n", sizeof(&arr));// 4/8// &arr是数组地址// 是地址就是4/8printf("%d\n", sizeof(&arr + 1));// 4/8// &arr是数组地址,+1是指向跳过整个数组的地址// 是地址就是4/8printf("%d\n", sizeof(&arr[0] + 1));// 4/8// &arr[0] + 1是第二个元素的地址// 是地址就是4/8return 0;
}
运行结果(x64):
代码4:
char arr[] = "abcdef";
// a b c d e f \0printf("%d\n", strlen(arr));
// 6printf("%d\n", strlen(arr + 0));
// 6
// arr + 0 表示从 arr 所指向的位置偏移 0 个元素
// 等同于arr,还是指向 arr 数组的首元素
// 向后数到 \0 前,有6个字符printf("%d\n", strlen(*arr));
// err
// 传给 stlen 的是第一个元素 a 的ASCII - 97
// 越界访问printf("%d\n", strlen(arr[1]));
// err
// 传给 stlen 的是第二个元素 b 的ASCII - 97
// 越界访问printf("%d\n", strlen(&arr));
// 6
// 是数组的地址,也是首元素的地址
// 从数组的首元素出发的直到 \0 前printf("%d\n", strlen(&arr + 1));
// 随机值
// 是跳过整个数组的下一个地址
// 在下一段中,不知道什么时候到 \0printf("%d\n", strlen(&arr[0] + 1));
// 5
// 是数组第二个元素的地址
代码验证一下:
int main()
{char arr[] = "abcdef";// a b c d e f \0printf("%d\n", strlen(arr));// 6printf("%d\n", strlen(arr + 0));// 6// arr + 0 表示从 arr 所指向的位置偏移 0 个元素// 等同于arr,还是指向 arr 数组的首元素// 向后数到 \0 前,有6个字符//printf("%d\n", strlen(*arr));// err// 传给 stlen 的是第一个元素 a 的ASCII - 97// 越界访问//printf("%d\n", strlen(arr[1]));// err// 传给 stlen 的是第二个元素 b 的ASCII - 97// 越界访问printf("%d\n", strlen(&arr));// 6// 是数组的地址,也是首元素的地址// 从数组的首元素出发的直到 \0 前printf("%d\n", strlen(&arr + 1));// 随机值// 是跳过整个数组的下一个地址// 在下一段中,不知道什么时候到 \0printf("%d\n", strlen(&arr[0] + 1));// 5// 是数组第二个元素的地址return 0;
}
运行结果(x64):
代码5:
const char* p = "abcdef";
// "abcdef"是常量字符串
// a b c d e f \0
// p指向a
// p是指针变量printf("%d\n", sizeof(p));
// 4/8
// 计算指针变量的大小,和地址大小一样
// 是地址就是4/8printf("%d\n", sizeof(p + 1));
// 4/8
// p + 1是 b 的地址printf("%d\n", sizeof(*p));
// 1
// p 的类型是 char*,*p 则是 char 类型
// char 类型是1个字节printf("%d\n", sizeof(p[0]));
// 1
// p[0] —— *(p+0) —— *p —— ‘a’
// 把常量字符串想象为数组
// p 可以理解为数组名
// p[0] 就是首元素
// p 的类型是 char*,*p 则是 char 类型
// char 类型是1个字节printf("%d\n", sizeof(&p));
// 4/8
// &p 是对 p 取地址
// 地址大小是4/8字节printf("%d\n", sizeof(&p + 1));
// 4/8
// p 的类型是 char*
// p 为一级指针,对一级指针取地址要用二级指针接收
// char* q = &p;
// &p + 1 == q + 1
// &p + 1 就是跳过 p 指针变量后的地址printf("%d\n", sizeof(&p[0] + 1));
// 4/8
// p[0] 就是首元素
// &p[0] 是首元素地址,&p[0] + 1是第二个元素地址
// 地址大小是4/8字节
代码验证一下:
int main()
{const char* p = "abcdef";// "abcdef"是常量字符串// a b c d e f \0// p指向a// p是指针变量printf("%d\n", sizeof(p));// 4/8// 计算指针变量的大小,和地址大小一样// 是地址就是4/8printf("%d\n", sizeof(p + 1));// 4/8// p + 1是 b 的地址printf("%d\n", sizeof(*p));// 1// p 的类型是 char*,*p 则是 char 类型// char 类型是1个字节printf("%d\n", sizeof(p[0]));// 1// p[0] —— *(p+0) —— *p —— ‘a’// 把常量字符串想象为数组// p 可以理解为数组名// p[0] 就是首元素// p 的类型是 char*,*p 则是 char 类型// char 类型是1个字节printf("%d\n", sizeof(&p));// 4/8// &p 是对 p 取地址// 地址大小是4/8字节printf("%d\n", sizeof(&p + 1));// 4/8// p 的类型是 char*// p 为一级指针,对一级指针取地址要用二级指针接收// char* q = &p;// &p + 1 == q + 1// &p + 1 就是跳过 p 指针变量后的地址printf("%d\n", sizeof(&p[0] + 1));// 4/8// p[0] 就是首元素// &p[0] 是首元素地址,&p[0] + 1是第二个元素地址// 地址大小是4/8字节return 0;
}
运行结果(x64):
代码6:
char* p = "abcdef";
printf("%d\n", strlen(p));
// 6
// 从‘a’开始读到 \0
printf("%d\n", strlen(p + 1));
// 5
// 从‘b’开始读到 \0//printf("%d\n", strlen(*p));
// err
// *p 就是‘a’
// 传给 stlen 的是第一个元素 a 的ASCII - 97
// 越界访问//printf("%d\n", strlen(p[0]));
// err
// p[0] —— *(p+0) —— *p —— ‘a’
// 传给 stlen 的是第一个元素 a 的ASCII - 97
// 越界访问
printf("%d\n", strlen(&p));
// 随机值
// &p 是从指针变量 p 的起始位置开始读,与常量字符串无关了
// 不知道什么时候到达\0
printf("%d\n", strlen(&p + 1));
// 随机值
// &p + 1 是从跳过指针变量 p 的位置开始读
// 不知道什么时候到达\0
printf("%d\n", strlen(&p[0] + 1));
// 5
// p[0] —— *(p+0) —— *p —— ‘a’
// &p[0]就是‘a’又取了个地址,+ 1 则是‘b’
// 就是从‘b’开始读到 \0
运行代码验证一下:
int main()
{char* p = "abcdef";printf("%d\n", strlen(p));// 6// 从‘a’开始读到 \0printf("%d\n", strlen(p + 1));// 5// 从‘b’开始读到 \0//printf("%d\n", strlen(*p));// err// *p 就是‘a’// 传给 stlen 的是第一个元素 a 的ASCII - 97// 越界访问//printf("%d\n", strlen(p[0]));// err// p[0] —— *(p+0) —— *p —— ‘a’// 传给 stlen 的是第一个元素 a 的ASCII - 97// 越界访问printf("%d\n", strlen(&p));// 随机值// &p 是从指针变量 p 的起始位置开始读,与常量字符串无关了// 不知道什么时候到达\0printf("%d\n", strlen(&p + 1));// 随机值// &p + 1 是从跳过指针变量 p 的位置开始读// 不知道什么时候到达\0printf("%d\n", strlen(&p[0] + 1));// 5// p[0] —— *(p+0) —— *p —— ‘a’// &p[0]就是‘a’又取了个地址,+ 1 则是‘b’// 就是从‘b’开始读到 \0return 0;
}
运行结果(x64):
3.二维数组
假设已知一个三行四列的数组,进行以下辨析:
printf("%d\n", sizeof(a));
// 48
// a是数组名,计算的是数组的大小
// 单位是字节,3*4*sizeof(int)=3*4*4=48
printf("%d\n", sizeof(a[0][0]));
// 4
// a[0][0]是第一行第一个元素的大小,是4个字节
printf("%d\n", sizeof(a[0]));
// 16
// a[0]是第一行的数组名
// 数组名单独在sizeof内,计算的是数组的总大小
// 第一行四个元素的大小 4*4=16
printf("%d\n", sizeof(a[0] + 1));
// 4/8
// a[0]并没有单独放在sizeof内
// a[0]是数组首元素的地址,就是a[0][0]的地址
// +1后是a[0][1]的地址
// 是地址就是4/8printf("%d\n", sizeof(*(a[0] + 1)));
// 4
// a[0] + 1后是a[0][1]的地址
// *(a[0] + 1)是第一行第二个元素
printf("%d\n", sizeof(a + 1));
// 4/8
// a 作为数组,并没有单独放在 sizeof 内
// a 表示数组首元素的地址,是二维数组首元素
// 第一行的地址,也就是a[1]的地址
// +1表示跳过一行的地址
// a + 1 是第二行的地址,是数组指针,是地址的大小
// 是地址就是4/8printf("%d\n", sizeof(*(a + 1)));
// 16
// a + 1 是第二行的地址
// *(a + 1) j就是第二行
// 计算的是第二行的大小3*4*4=16
// 另一个理解方法
// *(a + 1) = a[1]
// a[1]是第二行的数组名
// 相当于 sizeof(arr[1])
// 意思是将数组名单独放在 sizeof 内部了
// 同理,计算的是第二行的大小
printf("%d\n", sizeof(&a[0] + 1));
// 4/8
// a[0] 是第一行的数组
// &a[0] 取出的是数组的地址,是第一行的地址
// &a[0] + 1,是第二行的地址
// 是地址就是4/8printf("%d\n", sizeof(*(&a[0] + 1)));
// 16
// &a[0] + 1,是第二行的地址
// *(&a[0] + 1)是对第二行的地址解引用,就是第二行
// 计算的是第二行的大小 4*4=16
printf("%d\n", sizeof(*a));
// 16
// a 作为数组,并没有单独放在 sizeof 内
// a 表示数组首元素的地址,是二维数组首元素
// 第一行的地址,也就是a[1]的地址
// *a 解引用,就是第一行
// 计算的是第一行的大小 4*4=16
// 另一种理解方法*a == *(a + 0) == a[0]printf("%d\n", sizeof(a[3]));
// a[3]是第四行,单独放在 sizeof 内
// 其无需真实存在,只需要通过类型推导就可以计算出长度
// 推到得出,是类似于 int [4] 的一个整型数组
// 计算的是第四行的大小,4*4=16
return 0;
}
代码运行验证一下:
int main()
{int a[3][4] = { 0 };printf("%d\n", sizeof(a));// 48// a是数组名,计算的是数组的大小// 单位是字节,3*4*sizeof(int)=3*4*4=48printf("%d\n", sizeof(a[0][0]));// 4// a[0][0]是第一行第一个元素的大小,是4个字节printf("%d\n", sizeof(a[0]));// 16// a[0]是第一行的数组名// 数组名单独在sizeof内,计算的是数组的总大小// 第一行四个元素的大小 4*4=16printf("%d\n", sizeof(a[0] + 1));// 4/8// a[0]并没有单独放在sizeof内// a[0]是数组首元素的地址,就是a[0][0]的地址// +1后是a[0][1]的地址// 是地址就是4/8printf("%d\n", sizeof(*(a[0] + 1)));// 4// a[0] + 1后是a[0][1]的地址// *(a[0] + 1)是第一行第二个元素printf("%d\n", sizeof(a + 1));// 4/8// a 作为数组,并没有单独放在 sizeof 内// a 表示数组首元素的地址,是二维数组首元素// 第一行的地址,也就是a[1]的地址// +1表示跳过一行的地址// a + 1 是第二行的地址,是数组指针,是地址的大小// 是地址就是4/8printf("%d\n", sizeof(*(a + 1)));// 16// a + 1 是第二行的地址// *(a + 1) j就是第二行 // 计算的是第二行的大小3*4*4=16// 另一个理解方法// *(a + 1) = a[1]// a[1]是第二行的数组名// 相当于 sizeof(arr[1])// 意思是将数组名单独放在 sizeof 内部了// 同理,计算的是第二行的大小printf("%d\n", sizeof(&a[0] + 1));// 4/8// a[0] 是第一行的数组// &a[0] 取出的是数组的地址,是第一行的地址// &a[0] + 1,是第二行的地址// 是地址就是4/8printf("%d\n", sizeof(*(&a[0] + 1)));// 16// &a[0] + 1,是第二行的地址// *(&a[0] + 1)是对第二行的地址解引用,就是第二行// 计算的是第二行的大小 4*4=16printf("%d\n", sizeof(*a));// 16// a 作为数组,并没有单独放在 sizeof 内// a 表示数组首元素的地址,是二维数组首元素// 第一行的地址,也就是a[1]的地址// *a 解引用,就是第一行// 计算的是第一行的大小 4*4=16// 另一种理解方法*a == *(a + 0) == a[0]printf("%d\n", sizeof(a[3]));// a[3]是第四行,单独放在 sizeof 内// 其无需真实存在,只需要通过类型推导就可以计算出长度// 推到得出,是类似于 int [4] 的一个整型数组// 计算的是第四行的大小,4*4=16return 0;
}
运行结果(x64):
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址。