深入解析sizeof运算符:透视C语言中数组大小的细节

深入解析sizeof运算符:透视C语言中数组大小的细节

前言

当我们在编程中使用一维数组、二维数组和指针时 , 了解数组的大小和指针的操作非常重要

这篇博客将介绍了一些一维数组、二维数组和指针相关的内容

相信通过这篇文章你将会对sizeof运算符在不同情景下的应用能有更深入的了解 , 以及指针与数组之间的关系

这些知识对于正确操作数组和指针非常重要

受于目前经验有限 , 如有错误和不足之处还望见谅、指正

本文所求字节数均是在32位环境下

先前知识简要介绍

sizeof:

sizeof是C语言中的一个运算符,用于获取数据类型或变量的大小(以字节为单位)

它的语法形式为sizeof(expression),其中expression可以是数据类型、变量、数组、指针等

sizeof运算符的结果是一个整数常量,表示给定表达式的大小。这个大小是在编译时确定的,并且与具体的机器和操作系统相关

数组名的意义:

  • sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小
  • &数组名,这里的数组名表示整个数组,取出的是整个数组的地址(注意这里取出的是地址)
  • 除此之外所有的数组名都表示首元素的地址

1.一维数组

//一维数组
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));
  •   printf("%d\n",sizeof(a));    //1
    

    4

    数组名单独放在sizeof()内部 , 数组名表示整个数组 , 计算的是整个数组的大小

  •   printf("%d\n",sizeof(a+0));  //2
    

    4

    数组名没有单独放在sizeof内部 , 也没有& , 所以数组名a是数组首元素地址 , a + 0还是首元素地址

  •   printf("%d\n",sizeof(*a));   //3
    

    4

    a并非单独放在sizeof()内部 , 也没有& , 所以数组名a是首元素的地址 , *a就是首元素

  •   printf("%d\n",sizeof(a+1));   //4
    

    4

    a并非单独放在sizeof内部 , 也没有& , 所以数组名a是首元素地址 , a + 1就是第二个元素的地址 , a + 1 == &a[1]是第二个元素的地址

  •   printf("%d\n",sizeof(a[1]));  //5
    

    4

    a[1]就是数组的第二个元素 , 这里计算的就是第二个元素的大小

  •   printf("%d\n",sizeof(&a));    //6
    

    4

    取出的是数组的地址 , 但是数组的地址也是地址 , 是地址就是4个字节

    数组的地址和数组首元素的地址的本质区别就是类型的区别 , 大小并没有区别

  •   printf("%d\n",sizeof(*&a));
    

    16

    对数组指针解引用访问一个数组的大小

    sizeof(*&a) --> sizeof(a)

  •   printf("%d\n",sizeof(&a+1);  //7
    

    4

    &a是数组的地址 , &a + 1还是地址 , 是地址就是4个字节

  •   printf("%d\n",sizeof(&a[0])); //8
    

    4

    &a[0]首元素的地址 , 计算的是地址的大小

  •   printf("%d\n",sizeof(&a[0]+1));//9
    

    4

    &a[0]是首元素的地址 , &a[0] + 1就是第二个元素的地址

2. 字符数组

第一种

//字符数组
char arr[] = {'a','b','c','d','e','f'};
//不是字符串, 系统不会自动在末尾添加一个null终止符
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));

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));
  •   printf("%d\n", sizeof(arr));    //1
    

    6

    数组名arr单独放在sizeof内部 , 计算是整个数组的大小

  •   printf("%d\n", sizeof(arr+0));    //2
    

    4

    数组首元素地址 , 是地址就是4/8个字节

    指针变量的大小与类型无关 , 不管什么类型的指针变量 , 都是4/8个字节

  •   printf("%d\n", sizeof(*arr));     //3
    

    1

    arr是首元素地址 , *arr就是首元素 , 大小是一个字节

  •   printf("%d\n", sizeof(arr[1]));    //4
    

    1

    arr[1] == *(arr + 1)

  •   printf("%d\n", sizeof(&arr));      //5
    

    4

    &arr是数组的地址 , 是地址就是4/8个字节

  •   printf("%d\n", sizeof(&arr+1));    //6
    

    4

    &arr + 1是跳过数组后的地址 , 是地址就是4/8个字节

  •   printf("%d\n", sizeof(&arr[0]+1));  //7
    

    4

    第二个元素的地址 , 是地址就是4/8个字节

  •   printf("%d\n", strlen(arr));       //8
    

    strlen 求字符串长度

    统计的是在字符串中\0之前出现的字符的个数

    随机值 , arr是首元素的地址

  •   printf("%d\n", strlen(arr+0));    //9
    

    随机值

    arr是首元素的地址 , arr + 0还是首元素的地址

  •   printf("%d\n", strlen(*arr));     //10
    

    ERR

    arr是首元素的地址 , *arr就是首元素

    站在strlen的角度 , 认为传参进去的是‘a’ - 97就是地址 , 97作为地址 , 直接进行访问 , 就是非法访问

  •   printf("%d\n", strlen(arr[1]));    //11
    

    ERR

    同样传入‘b’ - 98非法访问

  •   printf("%d\n", strlen(&arr));      //12
    

    随机值

    在使用strlen函数时,传入的参数应该是一个以null结尾的字符串,而不是一个字符数组的地址

    在这种情况下,strlen函数会继续计算内存中的字符直到遇到null终止符。但是,由于arr是一个字符数组的地址,它可能不是以null结尾的字符串,所以strlen函数会继续计算内存中的字符直到遇到null终止符,这可能导致访问到未初始化的内存,从而得到随机值

  •   printf("%d\n", strlen(&arr+1));   //13
    

    随机值

    传入的数组地址的下一个地址 , 因为是地址, 而不是一个以\0结尾的字符

  •   printf("%d\n", strlen(&arr[0]+1));  //14
    

    随机值

    传入的是第二个元素的地址

第二种

char arr[] = "abcdef";
//当你声明一个字符数组并用字符串初始化它时,编译器会自动在字符串的末尾添加一个null终止符
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));

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));
  •   printf("%d\n", sizeof(arr));    //1
    

    7

    数组名单独放在sizeof内部 , 计算的是整个数组的大小, 其中还有终止符

  •   printf("%d\n", sizeof(arr+0));   //2
    

    4

    不是数组名单独放在sizeof内部 , 数组首元素地址

  •   printf("%d\n", sizeof(*arr));    //3
    

    1

    arr并非单独放在sizeof , arr是首元素地址 , *arr就是首元素

  •   printf("%d\n", sizeof(arr[1]));  //4
    

    1

    本质上是arr[1] = *(arr + 1)

  •   printf("%d\n", sizeof(&arr));    //5
    

    4

    &arr是数组的地址 , 只要是地址就是4个字节

  •   printf("%d\n", sizeof(&arr+1));  //6
    

    4

    表示的是跳过该数组地址的下一个地址

  •   printf("%d\n", sizeof(&arr[0]+1));//7
    

    4

    第二个元素的地址 , 是地址就是4个字节

  •   printf("%d\n", strlen(arr));      //8
    

    6

    统计的的是\0之前的

  •   printf("%d\n", strlen(arr+0));    //9
    

    6

    arr是首元素的地址 , arr + 0还是首元素的地址

  •   printf("%d\n", strlen(*arr));    //10
    

    ERR

    arr是首元素的地址 , *arr就是首元素

    站在strlen的角度 , 认为传参进去的是‘a’ - 97就是地址 , 97作为地址 , 直接进行访问 , 就是非法访问

  •   printf("%d\n", strlen(arr[1]));   //11
    

    ERR

    同样传入‘b’ - 98非法访问

  •   printf("%d\n", strlen(&arr));     //12
    

    随机值

    在使用strlen函数时,传入的参数应该是一个以null结尾的字符串,而不是一个字符数组的地址

  •   printf("%d\n", strlen(&arr+1));    //13
    

    随机值

    传入的是数组地址的下一个地址

  •   printf("%d\n", strlen(&arr[0]+1));  //14
    

    随机值

    传入的是第二个元素的地址

3. 指向字符串常量的指针

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));
  •   printf("%d\n", sizeof(p));    //1
    

    4

    返回指针变量p的大小 , 计算的是指针变量的大小(指针变量的大小在32位环境下不管什么类型都是4个字节)

  •   printf("%d\n", sizeof(p+1));  //2
    

    4

    指针变量p指向的是常量字符串首元素 , p + 1指向的是常量字符串的第二个元素, p + 1还是地址 , 是4个字节

  •   printf("%d\n", sizeof(*p));   //3
    

    1

    *p = 'a' p指向该常量字符串的首地址 , 解引用是该常量字符串的首元素 , 是一个字节

  •   printf("%d\n", sizeof(p[0]));  //4
    

    1

    本质上p[0] --> *(p + 0) --> *p =='a' , 因此是该常量字符串的首元素

  •   printf("%d\n", sizeof(&p));    //5
    

    4

    &p取出的该字符串的地址 , 只要是地址就是4个字节

  •   printf("%d\n", sizeof(&p+1));  //6
    

    4

    仍然是四个字节 , 只是跳过了整个该字符串 , 指向该字符串地址的下一个地址 , 只要是地址就是4个字节

  •   printf("%d\n", sizeof(&p[0]+1)); //7
    

    4

    取出的是第一个字符的地址的下一个元素的地址 , 只要是地址就是4个字节

4. 二维数组

//二维数组
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]));

二维数组其实是一维数组的数组

  •   printf("%d\n",sizeof(a));    //1
    

    48

    数组名单独放在了sizeof里面 , 表示的是整个数组(与整个数组的地址区分开) , sizeof(a) , 计算的是数组的大小 , 单位是字节

  •   printf("%d\n",sizeof(a[0][0])); //2
    

    4

    a[0][0]是数组的第一行第一个元素 , 这里计算的就是第一个元素的大小 , 单位是字节

  •   printf("%d\n",sizeof(a[0]));    //3
    

    16

    二维数组a[0]第一行这个一维数组的数组名 , 数组名单独放在了sizeof内部 , a[0]就表示整个第一行这个数组 , sizeof(a[0])计算的是整个第一行这个一维数组的大小 ,

    与一维数组的数组名单独放在sizeof里面类似只是 , 一维数组的数组名不是arr 而是a[0]

  •   printf("%d\n",sizeof(a[0]+1));  //4
    

    4

    a[0]并非单独放在sizeof内部 , 也没有& , 所以a[0]表示第一行这个一维数组首元素的地址, 也就是第一行第一个元素的地址

    与一维数组arr类似 , arr不是两种情况以外 , 全是表示的是首元素地址

  •   printf("%d\n",sizeof(*(a[0]+1))); //5
    

    4

    a[0]+ 1是第一行第二个元素的地址 , *(a[0] + 1)就是第一行第二个元素(与地址区分开)

  •   printf("%d\n",sizeof(a+1));      //6
    

    4

    a 作为二维数组的数组名 , 并没有单独放在sizeof()内部 , 也没有& , a 就是数组首元素地址(因为是二位数组的首元素 , 因此首元素是第一行) , 也就是第一行的地址(与第一行元素区分开 , 地址在32位下都是四个字节)

    在32位体系结构下,无论是char、int、short、long、float还是double类型的指针,它们的字节大小都是4个字节。

    a 的类型是int(*)[4](数组指针 , 因为是二维数组 , 因此指向的是一维数组 , 一维数组才是整形指针) ,

    a + 1就是第二行的地址 , 类型是: int(*)[4]

  •   printf("%zd\n", sizeof(*(a + 1)))  //7
    

    16

    a + 1是第二行的地址 , *(a + 1)就是第二行 , 计算的就是第二行的大小

    本质上*(a + 1) -- a[1]

    sizeof(a[1])-a[1]这个第二行的数组名 , 单独放在了sizeof内部 , 计算的是第二行的大小

  •   printf("%d\n",sizeof(&a[0]+1));    //8
    

    4

    a[0]是第一行的数组名 , &a[0]取出的是数组的地址 , 取出的是第一行这个一维数组的地址 , 类型是int(*)[4]

    &a[0] + 1就是第二行的地址 , 类型是int(*)[4]

  •   printf("%d\n",sizeof(*(&a[0]+1)));  //9
    

    16

    *(&a[0] + 1) 得到的就是第二行 , 计算的就是第二行的大小

  •   printf("%zd\n", sizeof(*a));        //10
    

    16

    a 表示数组首元素的地址 , 也就是第一行的地址 , *a就是第一行 , 也就相当于是第一行的数组名

    本质上 *a --> *(a + 0) --> a[0]

  •   printf("%zd\n", sizeof(a[3]));     //11
    

    16

    其实并不会越界访问 , sizeof()其实就是根据类型来算的 , 放入a[3]其实和放入a[0]没有任何区别 , 你在放入值的时候 , sizeof()就已经知道了你的类型

评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

繁星ベ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值