为啥不显示char数据的地址?
Posted
技术标签:
【中文标题】为啥不显示char数据的地址?【英文标题】:Why is address of char data not displayed?为什么不显示char数据的地址? 【发布时间】:2011-02-01 09:19:26 【问题描述】: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 个)
【问题讨论】:
【参考方案1】:当你获取 b 的地址时,你会得到char *
。 operator<<
将其解释为 C 字符串,并尝试打印字符序列而不是其地址。
改用cout << "address of char :" << (void *) &b << endl
。
[编辑] 就像 Tomek 评论的那样,在这种情况下使用更合适的演员表是 static_cast
,这是一个更安全的选择。这是一个使用它而不是 C 样式转换的版本:
cout << "address of char :" << static_cast<void *>(&b) << endl;
【讨论】:
你能举例说明为什么 static_cast 在这里是一个更安全的选择吗?我不明白在这里使用 void * 有什么害处。 @VishalSharma 如果您知道b
是什么,这里真的没有。但是,当b
不是 你认为它是什么时,C++ 强制转换为你提供了额外的安全性; C 演员只会盲目地按照你说的去做,而不是真正关心,这是不好的。
这里的目标是否只是为了灌输良好的做法,因为即使我不知道 b 是什么,我仍然不明白 cout
@VishalSharma 是的,避免 C 样式转换是一个好习惯。在这种特殊情况下的行为将是相同的。在其他情况下,&
可能已经超载(所以你没有得到“地址”),或者你可能正在做一些 const
/volatile
-正确性很重要的事情,或者,或者,或者【参考方案2】:
有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)
【讨论】:
【参考方案3】:当您将 char 的地址流式传输到 ostream 时,它会将其解释为 ASCIIZ“C 样式”字符串的第一个字符的地址,并尝试打印假定的字符串。您没有 NUL 终止符,因此输出将继续尝试从内存中读取,直到它碰巧找到一个或操作系统将其关闭以尝试从无效地址读取。它扫描的所有垃圾都将发送到您的输出。
您可能可以通过投射它来显示您想要的地址,如(void*)&b
。
重新将偏移量放入结构中:您观察到字符串放置在偏移量 8 处。这可能是因为您有 32 位整数,然后是 8 位字符,然后编译器选择再插入 3 个 8 位字符以便字符串对象将在 32 位字边界处对齐。许多 CPU/内存架构需要指针、整数等位于字大小边界上才能对其执行有效操作,否则必须执行更多操作才能从内存中读取和组合多个值,然后才能使用这些值在一次手术中。根据您的系统,可能每个类对象都需要从字边界开始,或者可能是 std::string
特别以 size_t、指针或其他需要这种对齐的类型开始。
【讨论】:
【参考方案4】:因为当您将char*
传递给std::ostream
时,它将打印它指向的C 风格(即:char 数组,char*
)字符串。
记住"hello"
是char*
。
【讨论】:
"hello" 是const char[6]
。
@MSalters:没有。它是char[6]
,使用时衰减为char*
。
仅在 C 中为 char[6]
,但在 C++ 中为 const char[6]
。有趣的是,它仍然可以衰减到char *
(与 C 向后兼容)。
@hrnt:这在 C++03 中已被弃用,并在 C++11 中完全删除。【参考方案5】:
char 的地址被视为以 nul 结尾的字符串,并显示该地址的内容,这可能是未定义的,但在本例中是一个空字符串。如果你将指针指向void *
,你会得到你想要的结果。
something2 和 something 是 8 之间的区别是由于对齐和编译器能够自行决定在堆栈中声明变量的位置。
【讨论】:
既然没有构造函数,是不是自动创建了默认构造函数,它将设置b = 0
,因此自动null
终止?还有+1
@Muggen:上面的代码不完整,谁知道提供了什么构造函数。
@Muggen:不,生成的默认 ctor 不会零初始化 b。你必须明确地这样做;例如Address() (作为临时的), new Address() (对比新地址), Address var = Address(), (in 0x) Address var ; (我相信,需要仔细检查),或具有静态存储持续时间(函数/命名空间/全局静态)的 Address 对象。【参考方案6】:
对于第二个问题 - 默认情况下编译器将填充结构成员。默认填充到sizeof(int)
,4 字节(在大多数架构上)。这就是为什么 int
后跟 char
在结构中占用 8 个字节的原因,因此 string
成员的偏移量为 8。
要禁用填充,请使用 #pragma pack(x)
,其中 x 是填充大小(以字节为单位)。
【讨论】:
由于对齐要求,我怀疑打包是否会将字符串的地址放在五字节偏移处(在许多编译器上)。 不是数据对齐平台特定的吗?此外,AFAIKint
不是标准的 4 个字节。
@Muggen - 数据对齐确实是特定于平台的,但最常见的是 sizeof(int)
- 本机 CPU 大小。在 32 位 CPU 上,这是 4 个字节。
@Christopher - 偏移量不是 5 个字节,而是 3。int
是从地址 0 到 3。char
应该是从 4 到 5,而是从 4 到 7 . 最后string
从8开始。
@Eli:char
在字节 4 处。字节 5 到 7 是填充,不是 char
的一部分,根据定义,sizeof(char)==1
。我指的是相对于封闭对象开头的偏移量 5。【参考方案7】:
你的语法应该是
cout << (void*) &b
【讨论】:
【参考方案8】:hrnt 关于空白的原因是正确的:&b
的类型为 char*
,因此在第一个零字节之前作为字符串打印。大概b
是0。如果你将b
设置为'A',那么你应该期望打印输出是一个以'A' 开头的字符串,并继续使用垃圾直到下一个零字节。使用static_cast<void*>(&b)
将其打印为地址。
对于你的第二个问题,&c - &i
是 8,因为 int 的大小是 4,char 是 1,字符串从下一个 8 字节边界开始(你可能在 64 位系统上) .每种类型都有特定的对齐方式,C++ 会根据它对齐结构中的字段,并适当地添加填充。 (经验法则是大小为 N 的原始字段与 N 的倍数对齐。)特别是,您可以在 b
之后再添加 3 个 char
字段,而不会影响地址 &c
。
【讨论】:
以上是关于为啥不显示char数据的地址?的主要内容,如果未能解决你的问题,请参考以下文章
如果 char 数组是 Java 中的 Object,为啥打印它不显示其哈希码?
当我不创建任何 char[] 实例时,为啥探查器会显示大量实例?