简 述: 对 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)
打印出来的值是多少?
下面一列表的 a
, b
, c
, d
, sizeof(unsigned int)
, sizeof(int)
打印出来的值又是多少?
分析:
理解分析:
在 win 10 + VS2015
的 x86
模式下,运行结果如下:
我的理解如下:
上一组的结果运行和分析如下:
sizeof("Hello")
的值是 6, 等价于"hello\0"
应在内存中占 6 个字节,且"hello"
是char [6]
类型(由截图可知),但为什么不是char *
或const char *
呢?那就不知道了,反正编译器的规则 就是这么定的;
sizeof(str1)
的值是 6, 等价于sizeof(char [6])
,在内存中一共分配 1*6 个字节;下标里面的[]
中的 6 是根据赋值语句,编译器自己推算出来的;sizeof(str2)
的值是 100, 等价于sizeof(char [100])
,在内存中一共分配 1*100 个字节;sizeof(p1)
的值是 4,等价于sizeof(char *)
,等价于sizeof(指针)
sizeof(p2)
的值是 4,等价于sizeof(const char *)
,等价于sizeof(指针)
下一组的结果运行和分析如下:
a
的值是 10, 等价于sizeof(char [10])
,在内存中一共分配 1*10 个字节;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 类型;而我们这里系统没有 左值,故无法知道转换为什么类型,所以出现如下截图,此上下文不支持;
c
的值是 9,没有什么好讲的,该函数的实现,是遇到第一个'\0'
就停止;d
的值是 8 ?(两个不同环境都是 8,有点费解),这个不明所以;查看源码 strlen() 的实现如下size_t strlen(const char *__s);
,按照我的理解,应该是等价于d = strlen((const char *)("123456789"+1))
的,显示结果依旧为 8;这个结果为啥是 8, 有点费解,不过觉得没有探究的必要,也不是本文谈论的重点;此行多探究无意义,且这样写,有点 “无中生有”,“惹是生非”,“搞事情” 的感觉sizeof(unsigned int)
的值是 4,不用解释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 // 返回 表达式结果的 类型 在内存中所占的字节大小