简 述: 对 C / C++ 中的 sizeof() 运算符的一些深入思考;

[TOC]


本文初发于 “偕臧的小站“,同步转载于此。


背景:

昨晚看到一题,虽然可以轻易得到结果,但是突然深入思考一些,这是为什么?

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[])
{
    char str1[] = "Hello";
    str1[2] = 'a';
    printf("%s\n", str1);
    char str2[100]="Hello";
    char * p1 = "Hello";
    const char *p2 = p1;
    sizeof("Hello");
    sizeof(str1);
    sizeof(str2);
    sizeof(p1);
    sizeof(p2);
    printf("%d, %d, %d, %d, %d\n", sizeof("Hello"), sizeof(str1), sizeof(str2), sizeof(p1), sizeof(p2)) ;


    int a, b, c, d;
    a = sizeof("123456789"); //a为10
    b = sizeof("123456789"+1); //在 qt creator 和 VS 2015 结果不一致

    c = strlen("123456789"); //c为9
    d = strlen((const char *)("123456789"+1)); 

    printf("%d, %d, %d, %d, %d, %d\n", a, b, c, d, sizeof(unsigned int), sizeof(int));
    return 0;
}

思考:

这里的 sizeof("Hello"), sizeof(str1), sizeof(str2), sizeof(p1), sizeof(p2) 打印出来的值是多少?

下面一列表的 abcdsizeof(unsigned int)sizeof(int) 打印出来的值又是多少?


分析:

理解分析:

win 10 + VS2015x86 模式下,运行结果如下:

我的理解如下:

上一组的结果运行和分析如下:

  1. sizeof("Hello") 的值是 6, 等价于 "hello\0" 应在内存中占 6 个字节,且 "hello"char [6] 类型(由截图可知),但为什么不是 char *const char * 呢?那就不知道了,反正编译器的规则 就是这么定的;


  2. sizeof(str1) 的值是 6, 等价于 sizeof(char [6]) ,在内存中一共分配 1*6 个字节;下标里面的[] 中的 6 是根据赋值语句,编译器自己推算出来的;

  3. sizeof(str2) 的值是 100, 等价于 sizeof(char [100]) ,在内存中一共分配 1*100 个字节;

  4. sizeof(p1) 的值是 4,等价于 sizeof(char *),等价于 sizeof(指针)

  5. sizeof(p2) 的值是 4,等价于 sizeof(const char *),等价于 sizeof(指针)


下一组的结果运行和分析如下:

  1. a 的值是 10, 等价于 sizeof(char [10]) ,在内存中一共分配 1*10 个字节;

  2. b 的值在不同系统,不同编译器上,结果是不一样的(eg: mac10.14.6 + qtCretor5.12.8 && win10 + vs2015); 结果不一直的原因是 无法判定 "123456789"+1 到底应该是 char [10] + int 之后,应该是什么类型?只能够根据自己的编译器实际决定;且还有一中观点: 此行是在编译时刻就已经被替换为具体的数值了,然后这一行的代码也会被删除掉,更加不会被执行,所以其 sizeof 的副作用是不会实现的(如 sizeof(++i)其实只能够等价于 sizeof(对象(变量 i )的类型),其运行时刻在计算其值是不存在的)

    注意一点即可,有一个误区,以前的 类似于 int x1 = 3 + 'b' 能够顺利进行隐式转换,是因为知道最后应该转换为 等式 的左侧 的 int 类型;而我们这里系统没有 左值,故无法知道转换为什么类型,所以出现如下截图,此上下文不支持;


  3. c 的值是 9,没有什么好讲的,该函数的实现,是遇到第一个 '\0' 就停止;

  4. d 的值是 8 ?(两个不同环境都是 8,有点费解),这个不明所以;查看源码 strlen() 的实现如下 size_t strlen(const char *__s);,按照我的理解,应该是等价于 d = strlen((const char *)("123456789"+1)) 的,显示结果依旧为 8;这个结果为啥是 8, 有点费解,不过觉得没有探究的必要,也不是本文谈论的重点;此行多探究无意义,且这样写,有点 “无中生有”,“惹是生非”,“搞事情” 的感觉

  5. sizeof(unsigned int) 的值是 4,不用解释

  6. sizeof(int) 的值是 4,不用解释


下面是另外角度来思考此问题:

[角度 1]:

sizeof() 是 返回一个类型的在内存中说占的字节数。 是属于 运算符 , 同为 运算符 的还有 +-*/% 等。而这个是在编译时候就被执行,而非运行时候执行,一开始就已经是计算机被知道的。

C 标准和实际上的 编译器 一开始就定义好了,在 32 位电脑中,int 占据 4 字节, char 占据 1 字节;

对于内置的基本数据类型 来说,下面是很容易理解,觉得一开始就被编译机在内存里面分配了这么多的变量:

sizeof(int) --> 显示 4 字节
sizeof(char) --> 显示 1 字节

而 对于 复合数据类型 来求字节大小,其是需要根据 组合的基本数据类型 来给出实际的内存中所占的字节大小,比如自定义 类:

sizeof(自定类) --> 显示 字节对齐后的 字节
sizeof(数组) --> 显示 符合类型的数组 字节
sizeof(int [4]) --> 显示 4 * 4 字节  // 这样就容易理解了

[角度 2]:

[p101] 类似于都是指代 《C++ primer 中文版 第5 版》一书的页码

忽然发觉有点不简单, str1 是数组类型,属于复合类型 [p101],表达式右侧 "Hello" 是什么类型(是 char []char * 还是 const char * 呢?),其实也并不重要(虽然知道了是char [6]类型 );但是它一定是给 数组 str1 赋值的;且定义的赋值写法如此


[角度 3]:

有人自己使用如下两个宏,来实现了该 sizeof() 的实现;注意的是,这是网友自己使用宏来实现的,而非 编译器 就是这样实现的,但是对于我的理解有所帮助;

//适用于非数组
#define _sizeof(T) ((size_t)((T*)0 + 1))
//适用于数组
#define array_sizeof(T) ((size_t)(&T+1)-(size_t)(&T))

[角度 4]:

sizeof 运算符,实际上有两种使用方式,且书上明确有说 sizeof 实际上并不会去计算其运算符对象的值 [p139]sizeof 运算时候,也不会将数组成指针来处理;

sizeof(type)           // 返回             类型 在内存中所占的字节大小
sizeof expression      // 返回 表达式结果的 类型 在内存中所占的字节大小