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<<
将其解释为 C 字符串,并尝试打印字符序列而不是其地址。
尝试
cout << "address of char :" << (void *) &b << endl
代替。
[编辑] 就像 Tomek 评论的那样,在这种情况下更合适的转换是
static_cast
,这是一个更安全的选择。这是一个使用它而不是 C 风格转换的版本:
cout << "address of char :" << static_cast<void *>(&b) << endl;
有2个问题:
打印指针将打印
int*
和 string*
的地址,但不会打印 char*
的内容,因为 operator<<
中有一个特殊的重载。如果您想要地址,请使用:static_cast<const void *>(&c);
int
和string
之间的地址差异是8
在你的平台上
sizeof(int)
是 4
和 sizeof(char)
是 1
所以你真的应该问为什么 8
而不是 5
。原因是字符串在 4 字节边界上对齐。机器使用单词而不是字节工作,如果单词不因此“拆分”这里几个字节和那里几个字节,那么机器工作得更快。这叫做对齐
您的系统可能与 4 字节边界对齐。如果你有一个 64 位系统和 64 位整数,那么差异就是 16.
(注意:64 位系统一般指的是指针的大小,而不是 int。因此具有 4 字节 int 的 64 位系统仍然会有 8 的差异,因为 4+1 = 5 但四舍五入为8. 如果 sizeof(int) 为 8,则 8+1 = 9 但四舍五入为 16)
当您将 char 的地址流式传输到 ostream 时,它会将其解释为 ASCIIZ“C 风格”字符串的第一个字符的地址,并尝试打印假定的字符串。您没有 NUL 终止符,因此输出将继续尝试从内存中读取,直到恰好找到一个或操作系统将其关闭以尝试从无效地址读取。它扫描的所有垃圾都将发送到您的输出。
您可能可以通过转换它来显示您想要的地址,如
(void*)&b
.
关于结构中的偏移量:您观察到字符串位于偏移量 8 处。这可能是因为您有 32 位整数,然后是 8 位字符,然后编译器选择再插入 3 个 8 位字符,以便字符串对象将在 32 位字边界处对齐。许多 CPU/内存架构需要指针、整数等位于字大小边界上以对其执行高效操作,否则在能够使用这些值之前必须执行更多操作来从内存中读取和组合多个值在一次手术中。根据您的系统,可能每个类对象都需要从单词边界开始,或者可能是
std::string
特别是从 size_t、指针或其他需要这种对齐的类型开始。
因为当您将
char*
传递给 std::ostream
时,它将打印它指向的 C 样式(即:char 数组,char*
)字符串。
记住
"hello"
是char*
.
char 的地址被视为以 nul 结尾的字符串,并显示该地址的内容,该地址可能未定义,但在本例中为空字符串。如果你把指针指向
void *
,你会得到你想要的结果。
something2 和 something 8 之间的区别是由于对齐和编译器自行决定在堆栈中声明变量的位置的能力。
对于第二个问题——编译器默认会填充结构成员。默认填充到
sizeof(int)
,4字节(在大多数架构上)。这就是为什么 int
后跟一个 char
将在结构中占用 8 个字节,因此 string
成员位于偏移量 8.
要禁用填充,请使用
#pragma pack(x)
,其中 x 是以字节为单位的填充大小。
你的语法应该是
cout << (void*) &b
hrnt 关于空白的原因是正确的:
&b
的类型为 char*
,因此在第一个零字节之前作为字符串打印。大概 b
是 0。如果您将 b
设置为“A”,那么您应该期望打印输出是一个以“A”开头并继续垃圾直到下一个零字节的字符串。使用 static_cast<void*>(&b)
将其打印为地址。
对于你的第二个问题,
&c - &i
是8,因为int的大小是4,char是1,字符串从下一个8字节的边界开始(你可能是64位系统)。每种类型都有特定的对齐方式,C++ 根据它对齐结构中的字段,适当地添加填充。 (经验法则是,大小为 N 的原始字段与 N 的倍数对齐。)特别是,您可以在 char
之后再添加 3 个 b
字段,而不会影响地址 &c
.