目录
前言
一、数组名
二、补充
三、经典笔试题(求大小)
1.一维数组
2.字符数组:
3. sizeof 和 strlen 的区别:
4.有关 strlen 求值的问题
5.字符串相关问题
6.二维数组相关问题
总结
前言
很多笔试题中有对指针和数组相关的深度考察,下面就来分各种情况探讨一下指针和数组的大小及相关问题。
一、数组名
一般情况下数组名是数组首元素的地址。
有两个例外:
1. sizeof(数组名),这里的数组名并不是首元素的地址
而是整个数组,计算的是整个数组的大小单位是字节Byte。
2. &数组名,这里的数组名也表示整个数组,取出的是数组的地址。
除上面两种情况外:
所有的数组名都是数组首元素的地址。
二、补充
指针在同一个平台下指针的大小都是相同的
在不同的操作平台下指针的大小有所差异:
int main() { printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(short*)); printf("%d\n", sizeof(int*)); printf("%d\n", sizeof(float*)); printf("%d\n", sizeof(double*)); return 0; }
综上小结:
指针在32位(x86)平台下是4个字节,在64位(x64)平台下是8个字节。
三、经典笔试题(求大小)
1.一维数组
int main() { int a[] = { 1,2,3,4 }; printf("%d\n", sizeof(a)); printf("%d\n", sizeof(a + 0)); printf("%d\n", sizeof(*a)); printf("%d\n", sizeof(a + 1)); printf("%d\n", sizeof(a[1])); printf("%d\n", sizeof(&a)); printf("%d\n", sizeof(*&a)); printf("%d\n", sizeof(&a + 1)); printf("%d\n", sizeof(&a[0])); printf("%d\n", sizeof(&a[0] + 1)); return 0; }
公布答案:
那么为什么会是这样呢:
详细解释如下:
printf("%d\n", sizeof(a));
//数组名单独放在sizeof()内部,计算的是整个数组的大小,单位是字节Byte - 16
printf("%d\n", sizeof(a + 0));
//a表示的是首元素的地址,a + 0 还是首元素地址,是地址大小4/8个字节
printf("%d\n", sizeof(*a));
//a表示的是首元素的地址,*a是对首元素地址的解引用,拿到的就是首元素,大小是4个字节
printf("%d\n", sizeof(a + 1));
//a + 1是第二个元素的地址,是地址,大小就是4/8个字节
printf("%d\n", sizeof(a[1]));
//a[1]是数组的第二个元素,大小就是4个字节
printf("%d\n", sizeof(&a));
//&a表示的是数组的地址,但也是地址,地址大小4/8个字节
printf("%d\n", sizeof(*&a));
//可以理解为*和&抵消,*&a相当于a,sizeof(*&a) == sizeof(a)是16
//&a -> int(*)[4]
//&a是数组的地址它的类型是int(*)[4]数组指针,如是果解引用访问的是4个int的数组大小是16个字节Byte
printf("%d\n", sizeof(&a + 1));
//&a是数组的地址,&a + 1就是跳过整个数组后的地址,是地址大小4/8个字节
printf("%d\n", sizeof(&a[0]));
//取出数组第一个元素的地址,是地址大小4/8个字节
printf("%d\n", sizeof(&a[0] + 1));
//&arr[0] - int*,第二个元素的地址,是地址大小4/8个字节
2.字符数组:
int main() { char arr[] = { 'a','b','c','d','e','f' }; 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; }
那么为什么会是这样呢:
详细解释如下:
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", sizeof(arr));
//arr作为数组名放在sizeof()内部,计算的整个数组的大小,单位是字节 - 6
printf("%d\n", sizeof(arr + 0));
//arr是首元素地址,arr + 0还是首元素的地址,地址大小就是4/8
printf("%d\n", sizeof(*arr));
//arr是首元素地址,*arr就是首元素,是一个字符,大小是一个字节 - 1
printf("%d\n", sizeof(arr[1]));
//arr[1]是数组的第二个元素,是一个字符大小是一个字节
printf("%d\n", sizeof(&arr));
//&arr取出的是数组的地址,数组的地址也是地址,地址就是4/8个字节
printf("%d\n", sizeof(&arr + 1));
//&arr取出的是数组的地址,&arr + 1跳过了整个数组,还是个地址,地址就是4/8个字节
printf("%d\n", sizeof(&arr[0] + 1));
//&arr[0]是第一个元素地址,&arr[0] + 1是第二个元素的地址,地址就是4/8个字节
3. sizeof 和 strlen 的区别:
(1)sizeof 的用法:
- sizeof只关注占用空间的大小,单位是字节
- sizeof不关注类型
- sizeof是操作符(关键字)
(2)strlen 的用法:
- strlen关注的字符串中到 ‘\0’ 为止的字符,计算的是 ‘\0’ 之前出现了多少个字符
- strlen指针对字符串
- strlen是函数,要包含头文件即:#include
4.有关 strlen 求值的问题
int main() { char arr[] = { 'a','b','c','d','e','f' }; 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; }
注意:
这里会有程序崩溃的情况就不再截图公布答案,可以在编译器上来验证。
详细解释如下:
char arr[] = { 'a','b','c','d','e','f' };
printf("%d\n", strlen(arr));
//arr是首元素地址,但是arr数组中没有\0,计算的时候不知道什么时候停止,结果是:随机值
printf("%d\n", strlen(arr + 0));
//arr是首元素地址,arr + 0还是首元素的地址,结果:是随机值
printf("%d\n", strlen(*arr));
//err,strlen需要的是一个地址,从这个地址开始向后找字符,直到\0统计字符的个数
//但是*arr表示的是数组首元素,也就是'a',这时传给strlen的是'a'的ascii码值97,strlen函数会把97作为起始地址
//统计字符串,会形成内存访问冲突
printf("%d\n", strlen(arr[1]));
//err 和上一个一样,内存访问冲突
printf("%d\n", strlen(&arr));
//&arr是arr数组的地址,虽然类型和strlen的参数类型有所差异,但是传参过去还是从第一个字符的位置
//向后数字符,结果还是随机值,因为没有 \0 ;
printf("%d\n", strlen(&arr + 1));
//随机值
printf("%d\n", strlen(&arr[0] + 1));
//随机值
int main() { 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; }
注意:
这里会有程序崩溃的情况就不再截图公布答案,可以在编译器上来验证。
详细解释如下:
char* p = "abcdef";
//p里面存的是a的地址
printf("%d\n", strlen(p));
//p中存放的是'a'的地址,strlen(p)就是从'a'的位置向后求字符串的长度,长度是6
printf("%d\n", strlen(p + 1));
//p + 1是字符'b'的地址,从b的位置
printf("%d\n", strlen(*p));
//'a' - 97
printf("%d\n", strlen(p[0]));
//err p[0] -> *(p + 0) -> *p
printf("%d\n", strlen(&p));
//随机值
printf("%d\n", strlen(&p + 1));
//随机值
printf("%d\n", strlen(&p[0] + 1));
//&p[0] -> *(p + 0) -> *p -> 'a',&p[0]就是首字符的地址
//&p[0] + 1就是第二个字符的地址,从第二个字符的位置向后数字符串,长度是5
5.字符串相关问题
int main() { char* p = "abcdef"; 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; }
公布答案:
那么为什么会是这样呢:
详细解释如下:
char* p = "abcdef";
//p里面存的是a的地址
printf("%d\n", sizeof(p));
//p是一个指针变量,sizeof(p)计算的就是指针变量的大小,4/8个字节
printf("%d\n", sizeof(p + 1));
//p是指针变量,是存放地址的,p + 1也是地址,地址大小就是4/8个字节
printf("%d\n", sizeof(*p));
//p是char*的指针,解引用访问一个字节,sizeof(*p)是1个字节
printf("%d\n", sizeof(p[0]));
//p[0] --> *(p + 0) -- *p还是1个字节
printf("%d\n", sizeof(&p));
//&p也是地址,是地址就是4/8个字节 &p是二级指针
printf("%d\n", sizeof(&p + 1));
//&p是char**类型的 &p + 1还是地址,是地址4/8个字节
printf("%d\n", sizeof(&p[0] + 1));
//&p[0]就是a,&p[0]就是a的地址,&p[0] + 1就是b的地址,是地址就是4/8个字节
6.二维数组相关问题
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));
//数组名单独放在sizeof内部,计算的是整个数组的大小 - 4*4*3 = 48
printf("%d\n", sizeof(a[0][0]));
//4
printf("%d\n", sizeof(a[0]));
//a[0]表示第一行的数组名,a[0]作为数组名单独放在sizeof内部,计算的是第一行大小 - 16
printf("%d\n", sizeof(a[0] + 1));
//a[0]作为第一行数组名,没有&,没有单独放在sizeof()内部,
//所以a[0]表示的就是首元素地址,即a[0][0]的地址,a[0] + 1就是第一行第二个元素的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(a[0] + 1)));
//第一行第二个元素 - 4
//二维数组的数组名是第一行数组的地址,类型是数组指针int(*)[]类型的
printf("%d\n", sizeof(a + 1));
//a是二维数组的数组名,没有&,没有单独放在sizeof()内部,a表示首元素地址
//即第一行地址,a + 1就是第二行的地址 a + 1 的类型是个数组指针int(*)[4],是地址就是4/8个字节
printf("%d\n", sizeof(*(a + 1)));
//*(a + 1)就是第二行,相当于第二行数组名,*(a + 1) -> a[1]就是第二行数组名
//sizeof(*(a + 1)) -> sizeof(a[1])计算的是第二行的大小 - 16
printf("%d\n", sizeof(&a[0] + 1));
// &a[0]是第一行的地址, &a[0] + 1就是第二行的地址,是地址就是4/8个字节
printf("%d\n", sizeof(*(&a[0] + 1)));
//*(&a[0] + 1)就相当于第二行,也就是第二行的数组名a[1],sizeof(a[1]) - 16个字节
printf("%d\n", sizeof(*a));
//a是二维数组的数组名,没有&,没有单独放在sizeof()内部,a表示首元素的地址。*a就是二维数组的首元素
//也就是第一行,*a -> *(a + 0) -> a[0]
printf("%d\n", sizeof(a[3]));
//感觉a[3]是越界了,但是没关系,sizeof只看类型不会去访问,会去推导sizeof()内的类型 - 16
总结:
数组名的意义:
1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
3. 除此之外所有的数组名都表示首元素的地址