> 文档中心 > 【C语言】 - 指针进阶 - 指针和数组大小详解

【C语言】 - 指针进阶 - 指针和数组大小详解

目录

前言

一、数组

二、补充

三、经典笔试题(求大小)

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;}

在x86环境下:

在x64的环境下:

  综上小结:

  指针在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. 除此之外所有的数组名都表示首元素的地址