为什么char数据的地址不显示?
class Address { int i ; char b; string c; public: void showMap ( void ) ; }; void Address :: showMap ( void ) { cout << "address of int :" << &i << endl ; cout << "address of char :" << &b << endl ; cout << "address of string :" << &c << endl ; }
输出是:
address of int : something address of char : // nothing, blank area, that is nothing displayed address of string : something
为什么?
另一个有趣的事情:如果int,char,string是公开的,那么输出是
... int : something ... char : ... string : something_2
something_2 - something
总是等于8. 为什么? (不是9)
当你采取b的地址,你得到char *
。 operator<<
将其解释为Cstring,并尝试打印字符序列而不是地址。
请尝试cout << "address of char :" << (void *) &b << endl
。
[编辑]像Tomek评论说的,在这种情况下使用更合适的投射是static_cast
,这是一个更安全的select。 这是一个使用它的版本,而不是C风格的演员:
cout << "address of char :" << static_cast<void *>(&b) << endl;
有两个问题:
- 为什么不打印char的地址:
打印指针将打印int*
和string*
的地址,但会打印char*
的内容,因为operator<<
存在特殊的重载。 如果你想要的地址,然后使用: static_cast<const void *>(&c);
- 为什么
int
和string
之间的地址差异是8
在你的平台上, sizeof(int)
是4
, sizeof(char)
是1
所以你应该问为什么8
不是5
。 原因是string在一个4字节的边界上alignment。 机器使用的是单词而不是字节,如果单词不在这里“分裂”几个字节,而且在那里只有几个字节,则工作得更快。 这被称为alignment
您的系统可能alignment到4个字节的边界。 如果你有64位整数的64位系统,那么它们的差别就是16。
(注意:64位系统一般是指一个指针的大小,而不是一个int,所以一个具有4个字节的int的64位系统仍然会有一个8的差异,如4 + 1 = 5,但会舍入到8 。如果sizeof(int)是8,那么8 + 1 = 9,但是这个取整为16)
将string的地址stream式传输到ostream时,将其解释为ASCIIZ“C-style”string的第一个字符的地址,并尝试打印推测的string。 您没有NUL终止符,所以输出将不断尝试从内存中读取,直到碰巧find一个,或者操作系统将其closures以尝试从无效地址读取。 它扫描的所有垃圾将被发送到您的输出。
你可以通过强制转换来显示你想要的地址,例如(void*)&b
。
将偏移量复制到结构中:您观察到string被放置在偏移8处。这可能是因为您有32位整数,然后是8位字符,则编译器select插入3个8位字符,以便string对象将在32位字边界alignment。 许多CPU /存储器体系结构需要指针,整数等在字大小的边界上对其执行有效的操作,否则将不得不做更多的操作来读取和组合来自存储器的多个值,然后才能够使用值在操作中。 根据你的系统,可能每个类对象都需要从一个字边界开始,或者可能是std::string
特别是以一个size_t,指针或其他需要这种alignment的types开始。
因为当你传递一个char*
到std::ostream
它会打印它指向的C风格(即:char数组, char*
)string。
请记住, "hello"
是一个char*
。
char的地址被视为一个nul结尾的string,并显示该地址的内容,这可能是未定义的,但在这种情况下是一个空string。 如果你把指针指向void *
,你会得到你想要的结果。
something2和8之间的区别是由于编译器的alignment和能力决定自己在栈中声明variables的位置。
对于第二个问题 – 编译器默认会填充结构成员。 默认的pad是sizeof(int)
,4字节(在大多数体系结构上)。 这就是为什么一个int
后跟一个char
将需要8个字节的结构,所以string
成员在偏移8。
要禁用填充,请使用#pragma pack(x)
,其中x是以字节为单位的填充大小。
你的语法应该是
cout << (void*) &b
hrnt是正确的关于空白的原因: &b
有typeschar*
,所以被打印为一个string,直到第一个零字节。 假设b
是0.如果你将b
设置为'A',那么你应该期望打印输出是一个以'A'开头的string,并且继续下一个零字节。 使用static_cast<void*>(&b)
将其作为地址打印。
对于第二个问题, &c - &i
是8,因为int的大小是4,char是1,并且string从下一个8字节边界开始(可能是在64位系统上)。 每种types都有一个特定的alignment方式,C ++根据这个alignment结构中的字段,适当地添加填充。 (经验法则是大小为N的原始字段与N的倍数alignment。)特别是可以在b
之后再添加3个char
字段,而不会影响地址&c
。