类型二
废话不多说上代码:
#include<stdio.h> int main() { char arr[] = "abcdef"; //看清楚是 sizeof printf("%d\n", sizeof(arr)); printf("%d\n", sizeof(arr + 0)); printf("%d\n", sizeof(*arr)); printf("%d\n", sizeof(arr[1])); printf("%d\n", sizeof(&arr)); printf("%d\n", sizeof(&arr + 1)); printf("%d\n", sizeof(&arr[0] + 1)); return 0; }
输出:
相信大家应该挺疑惑的吧,为什么会是 7 不是 6 呢?
首先我们得知道,"abcdef"里面存了啥?
其实在这个字符串最后面有一个 \0 没有写出来
写成大括号的形式就是{‘a’,‘b’,‘c’,‘d’,‘e’,‘f’,‘\0’}
我们再看 sizeof 的用法:strlen 求字符串长度的,计算的是字符串中 \0 之前出现的字符的个数,统计到 \0 为止,如果没有看到 \0,会继续往后找
如此想必大家都明白了吧。
分析:
char arr[] = "abcdef"; printf("%d\n", sizeof(arr)); // 7 printf("%d\n", sizeof(arr + 0)); // 4/8 //首元素地址 printf("%d\n", sizeof(*arr)); // 1 //arr[0]的大小 printf("%d\n", sizeof(arr[1])); // 1 printf("%d\n", sizeof(&arr)); // 4/8 不多解释了 printf("%d\n", sizeof(&arr + 1)); // 4/8 printf("%d\n", sizeof(&arr[0] + 1)); // 4/8
再来看关于 strlen 的:
#include<stdio.h> #include<string.h> int main() { char arr[] = "abcdef"; printf("%d\n", strlen(arr)); printf("%d\n", strlen(arr + 0)); //printf("%d\n", strlen(*arr)); //这两行代码会导致非法访问,屏蔽 //printf("%d\n", strlen(arr[1])); printf("%d\n", strlen(&arr)); printf("%d\n", strlen(&arr + 1)); printf("%d\n", strlen(&arr[0] + 1)); return 0; }
看输出:
分析:
printf("%d\n", strlen(arr)); // 6 arr是整个数组地址 printf("%d\n", strlen(arr + 0)); // 6 arr+0是首元素地址 printf("%d\n", strlen(&arr)); // 6 &arr是整个数组地址 printf("%d\n", strlen(&arr + 1)); //随机值 &arr+1是跳过整个数组后的地址 printf("%d\n", strlen(&arr[0] + 1)); //5 &arr[0] + 1是第二个元素的地址
类型三
#include<stdio.h> int main() { const char* p = "abcdef"; //p存放的是字符串首元素 —‘a’的地址,可以通过该地址找到后面的元素 printf("%d\n", sizeof(p)); printf("%d\n", sizeof(p + 1)); printf("%d\n", sizeof(*p)); printf("%d\n", sizeof(p[0])); printf("%d\n", sizeof(&p)); printf("%d\n", sizeof(&p + 1)); printf("%d\n", sizeof(&p[0] + 1)); return 0; }
输出:
分析:
printf("%d\n", sizeof(p)); // 4/8 //p存放的是字符串首元素 —‘a’的地址,地址大小就是 4/8 printf("%d\n", sizeof(p + 1)); // 4/8 ‘b’的地址 printf("%d\n", sizeof(*p)); // 1 首元素‘a’的大小为一字节 printf("%d\n", sizeof(p[0])); // 1 首元素‘a’的大小为一字节 printf("%d\n", sizeof(&p)); // 4/8 取出首元素地址 printf("%d\n", sizeof(&p + 1)); // 4/8 ‘b’的地址 printf("%d\n", sizeof(&p[0] + 1)); // 4/8 ‘b’的地址
再上strlen:
#include<stdio.h> #include<string.h> int main() { const char* p = "abcdef"; printf("%d\n", strlen(p)); printf("%d\n", strlen(p + 1)); //printf("%d\n", strlen(*p)); //老规矩,非法访问,先屏蔽 //printf("%d\n", strlen(p[0])); printf("%d\n", strlen(&p)); printf("%d\n", strlen(&p + 1)); printf("%d\n", strlen(&p[0] + 1)); return 0; }
输出:
是不是没有想到呢,我们来分析一下:
const char* p = "abcdef"; printf("%d\n", strlen(p)); //6 首元素地址 printf("%d\n", strlen(p + 1)); //5 第二个元素地址 printf("%d\n", strlen(&p)); //随机值 //p是首元素地址,那么&p就是把 p 的地址取出来,strlen从 p 的地址处开始找\0,见下图 printf("%d\n", strlen(&p + 1)); //随机值 printf("%d\n", strlen(&p[0] + 1)); //5 第二个元素地址
假设‘a’的地址是 0x0012ff40 ,那么p里存放的就是该地址,如下图:
strlen(&p)就是把p的地址放进去,然后从p的地址处开始找 \0 ,如图
图中标出&p的位置,从该处往后找 \0 ,地址是占4个或8个字节,我们这里用x86,占4个字节,那里面的数是怎么样的呢?(假设我们是小端存储,如有不懂请看往期)
我们可以看到第四个字节位置是00,也就是 \0 ,strlen应该是读到这里为止,那么strlen(&p)应该为3,这是在‘a’的地址确定的情况下,问题是我们不知道‘a’的地址是多少,在每次测试时都有可能不同,所以就是随机值了。strlen(&p + 1)也是同样的道理。
二维数组
首先来补充一点:(假设有个二维数组 int arr[3][4])
arr 也可以看做另类的一维数组,我们可以把每行当成一个元素,这样第一行就可以用 a[0]来表示,第二行用 a[1]来表示,以此类推(a[0]、a[1]若是单独放在sizeof里面,或者前面加了个&,则a[0]、a[1]的类型才是int(*)[4],才能代表一行元素的地址,否则只能代表一行的首元素地址。如有疑惑看往期—指针进阶),那么每一行里的具体元素怎么表示呢?我们可以再加个括号即可,如:第一行第二个元素,我们可以写成:a[0][1]也可以写成 *(a[0]+1) 还可以写成 * ( *(a+0)+1)来表示,为什么呢?
我们知道 *(a+1)==a[1] ,那么二维数组第一行 a[0] 也可以写成 *(a+0) ,同样的 a[0][1]也就可以写成 *(a[0]+1)
下面我们来练练二维数组:
#include<stdio.h> int main() { int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a[0][0])); printf("%d\n", sizeof(a[0])); printf("%d\n", sizeof(a[0] + 1)); printf("%d\n", sizeof(*(a[0] + 1))); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(*(a + 1))); printf("%d\n", sizeof(&a[0] + 1)); printf("%d\n", sizeof(*(&a[0] + 1))); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a[3])); return 0; }
输出:
不知道大家准确率咋样,我们直接开始分析了:
int a[3][4] = { 0 }; printf("%d\n", sizeof(a)); // 48 //a是二维数组的数组名,数组名单独放在sizeof内部,计算的是数组的总大小,单位是字节 printf("%d\n", sizeof(a[0][0])); // 4 //a[0][0]是一个整型元素,大小是4个字节 printf("%d\n", sizeof(a[0])); // 16 //a[0]是第一行的数组名,第一行的数组名单独放在sizeof内部,计算的是第一行的总大小,单位是字节 - 16 printf("%d\n", sizeof(a[0] + 1)); // 4/8 //a[0]虽然是第一行的数组名,但是并非单独放在sizeof内部 //a[0]作为第一行的数组名并非表示整个第一行这个数组,a[0]就是第一行首元素的地址,a[0]--> &a[0][0] - int* //a[0]+1,跳过一个int,是a[0][1]的地址 4/8字节 printf("%d\n", sizeof(*(a[0] + 1))); // 4 //a[0]+1是第一行第二个元素的地址,所以*(a[0]+1)就是a[0][1],大小是4个字节 printf("%d\n", sizeof(a + 1)); // 4/8 //a是二维数组的数组名,没单独放在sizeof内部,也没有&,所以a就是数组首元素的地址 //二维数组,我们把它想象成一维数组,它的第一个元素就是二维数组的第一行 //a就是第一行的地址,a+1 是第二行的地址,是地址,大小就是 4/8 个字节 printf("%d\n", sizeof(*(a + 1))); // 16 //a+1是第二行的地址,*(a+1) 找到的就是第二行,sizeof(*(a + 1))也就是sizeof(a[1])计算的就是第二行的大小 printf("%d\n", sizeof(&a[0] + 1)); // 4/8 //&a[0]是第一行的地址,&a[0]+1就是第二行的地址,sizeof(&a[0] + 1)计算的第二行地址大小 printf("%d\n", sizeof(*(&a[0] + 1))); // 16 //&a[0] + 1是第二行的地址,*(&a[0] + 1)拿到的就是第二行,大小就是16个字节 printf("%d\n", sizeof(*a)); // 16 //a表示首元素的地址,就是第一行的地址 - &a[0] printf("%d\n", sizeof(a[3])); //16 //a[3]是二维数组的第4行,虽然没有第四行,但是类型能够确定,大小就是确定的。大小就是一行的大小,单位是字节 - 16
总结
做这类题就是要抓住核心要点,明白 strlen、sizeof 的用法,再多加练习就能做到秒杀,相信大家看完本期一定收获满满吧,以后碰到就不怕了。