Q: char类型、int类型和long类型的长度由什么决定?
A: 在不同的系统或编译系统上, int有时是2字节, 有时是4字节. 编译器决定着int究竟是2字节还是4字节.
C语言标准没有规定int类型的准确长度, 因为它是标准, 不能太个性化。C语言只是粗略地规定char <= short <= int <= long.
从编译器的角度, 它解析到int类型, 它是当做2字节还是4字节完全随性, 如果真想个性,也可以是5字节。
Q: 如何证明char类型和int类型的长度。
A: 使用sizeof得到长度显得太幼稚,用汇编看看它们的长度。
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
char c = 'a';
int i = 0xFFFEFDFC;
printf("c: %p, i: %p\n", &c, &i);
return 0;
}
我们期望可以通过堆栈中紧挨着的char类型和int类型变量的地址来确认.
c: 0x7ffeed4d474f, i: 0x7ffeed4d4748
地址相差0x7: 变量是4字节对齐, char类型被填充3字节。
代码对应汇编展示了c和i的地址是如何被rbp偏移表达。
movb %0x61, (%rsi) 是单字节操作,对应char类型变量c.
movl $0xfffefdfc, (%rdx) 是四字节操作,对应int类型变量i.
Q: 为什么c的地址和i的地址不是相差4字节?
A: 平台是Intel架构, little-endian. 广为流传的确认是否是小端法的代码:
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int is_little_endian()
{
short a = 0x1;
return *(char *)&a == 0x1;
}
int main(int argc, char *argv[])
{
int little_endian;
little_endian = is_little_endian();
printf("%s little endian!\n", little_endian ? "is" : "isn't");
return 0;
}
即判断一个超过1字节变量的低地址对应字节和变量低字节是否一致。
Q: 如何让c和i之间紧挨着,没有"填充的数据"?
A: 我们试着在char和int之间加一些可以造成对齐的变量。
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
short h = 0xAABB;
char c0 = 'b';
char c = 'a';
int i = 0xFFFEFDFC;
printf("h: %p, c0: %p\n", &h, &c0);
printf("c: %p, i: %p\n", &c, &i);
return 0;
}
但事与愿违,编译器根本不了解我们的心意,它不知道h和c0的存在是为了"padding",不同变量都会做所谓对齐处理。
h: 0x7ffee215c73c, c0: 0x7ffee215c73e
c: 0x7ffee215c73f, i: 0x7ffee215c738
Q: 结构体是提示编译器把变量放在一起,我们来试试。
A:
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
typedef struct __attribute((packed))
{
char c;
int i;
} char_int;
int main(int argc, char *argv[])
{
char_int ci;
printf("c: %p, i: %p\n", &ci.c, &ci.i);
return 0;
}
__attribute((packed))是为了变量中间不做padding, 按原生大小摆放。
c: 0x7ffee5e5c738, i: 0x7ffee5e5c739
作者: 陈曦
环境: MacOS 10.14.5 (Intel i5)
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
Linux 3.16.83 (Ubuntu)
转载请注明出处