【问题标题】:Confused by (char *) cast被 (char *) 强制转换混淆
【发布时间】:2013-11-19 10:18:25
【问题描述】:
struct myStruct
{
  int foo;
}

myStruct bar;
bar.foo = 123;
cout<<(char *) &bar<<endl;

cout 语句打印出'{'。 123 的 ASCII 实际上是 '{' (bar.foo = 123。改变这个值会打印出不同的字符)。我不明白的是

cout<<(char *) &bar<<endl;

技术上应该打印酒吧的地址。如何使用 (char *) 强制转换访问结构内的数据?使用 (int *) 仍然返回地址。我确实知道 (char *) 会导致数据以逐字节的方式排列,但这不应该只影响地址吗?

【问题讨论】:

  • std::coutchar * 视为以 null 结尾的字符串并按原样打印。 bar::foo 地址等于bar 地址本身。所以你打印bar::foo,就好像它是一个以空结尾的字符串。请注意,它将打印到\0,因此它可能会超过bar::foo,具体取决于它的值。
  • 你很幸运没有崩溃,因为它会读取内存直到第一个'\0'......在访问禁区之前不能保证你有一个。

标签: c++ casting ampersand


【解决方案1】:

&lt;&lt; 采用 const char * 存在重载,它将操作数解释为指向 C 样式以零结尾的字符串的指针。例如,这就是使用的重载

cout << "Hello\n";

如果像这里一样,你给它一个指向 C 风格字符串以外的东西的指针,你会得到未定义的行为。在实践中,它会逐字节打印内存中的内容,直到它到达一个零值字节或离开可读内存的末尾。

【讨论】:

  • 所以如果我要通过套接字发送演员表,比如 char *bytes = (char *) &bar;写(socketFD,字节,sizeof(myStruct));考虑到两端的字节顺序相同,它会起作用吗?
  • @AamirKhan:是的(如您所说,假设两端的内存布局兼容)。但是演员阵容毫无意义:write 采用 const void*,如果您这样做 &amp;bar 将隐式转换write(fd, &amp;bar, sizeof bar)
  • 正如@MikeSeymour 所说。这取决于内存布局。如果您打算将来支持不同的平台,最好以定义明确的格式序列化网络传输结构。整数在不同平台上的存储方式不同,即使跨编译器,结构成员也有不同的对齐规则。参见例如 htonl,htons 函数,用于在主机和网络 odred 之间转换值。
  • “如果像这里一样,你给它一个指向 C 风格字符串以外的东西的指针”,好吧,我认为在这种情况下它 被赋予了一个指向C 风格的字符串。但这取决于具有 123 值的 int 的实现定义的布局——在另一个实现或具有不同值的情况下,它不一定是 C 样式的字符串。
  • @SteveJessop:确实;我不想陷入迂腐的细节,但你说得对,如果内存中任何内容的实现定义的布局在你正在使用的实现中是一个有效的 C 字符串,那么它是明确定义的。不过,我会保持简单的回答 - 除非您知道自己在做什么,否则“未定义”是一个足够好的近似值。
【解决方案2】:

char* 上的&lt;&lt; 将其视为 C 字符串,而不是指针。该程序将myStruct 解释为sizeof(myStruct) 字节的缓冲区,并依赖字节序来确保它是NUL 终止的。

要打印地址,只需传递&amp;bar 而不进行强制转换。

【讨论】:

  • @Wolf:我的意思是它可能会被强制转换为 void* 以打印指针值,尽管这不是必需的。
  • 啊,我明白了,很抱歉大惊小怪。
【解决方案3】:

由于 bar 是 myStruct 结构的对象,它的起始地址和 myStruct 中第一个成员的起始地址,即 bar.foo 将相同。因此 &bar 将为您提供 bar 对象的起始地址,而 char* 将为您提供该地址位置中第一个字节的值。

【讨论】:

    【解决方案4】:

    int * 不同,char * 是一种特殊的指针,它们也用于指向 ASCII 字符串,例如:

    const char * s = "hello world";

    对于表达式(char *) &amp;bar,结果是一个char * 指针,因此cout 将打印一个字符串,因为它有一个用于输出字符串的char * 特殊运算符。对于int *,没有运算符,只有一个用于仅打印地址的指针的通用运算符。

    【讨论】:

      【解决方案5】:

      std::ostream 通常具有 operator &lt;&lt; 的重载,const char * 将绑定到 char *

      此重载将其参数视为以空字符结尾的 ASCII 字符串。

      【讨论】:

        【解决方案6】:

        如果您需要将 myStruct 的内容输出到输出流,您需要为您的结构定义 ostream

        std::ostream& operator <<(std::ostream& os, const myStruct& ms) 
        {
            os << "myStruct { foo: " << ms.foo << "\n}";
            return os; 
        }
        

        (char *) 不会重新排列任何内容。该结构在 64 位 CPU 上以 4 字节 = sizeof(int) 或 8 字节的形式存储在内存中,具体取决于小端或大端布局的处理器架构(请参阅http://en.wikipedia.org/wiki/Endianness)。

        您的代码可能在 x86 架构 CPU 上运行,因此最低有效字节存储在较低地址。例如 foo 中的数字 123 将存储为字节 7B 00 00 00。在内存中。您的 cast co char * 将告诉编译器对 char * 数组使用 ostream 运算符,该数组在 C 中用作字符串。每个字节被解释为一个字符,以 0 字节结尾。字节 123 对应于 ASCII 字符 '{',它以字节 00 结束,所以你的代码在这种情况下不会崩溃。其他值可能会使您的代码崩溃,或在结构存储之外的堆栈上打印字节(直到遇到零字节或崩溃)。

        希望这会有所帮助。

        【讨论】:

          【解决方案7】:

          实际上,您的代码正在打印结构变量 bar 的地址而不是 foo 的值。(char*)&bar 正在将结构类型的地址转换为 (char*) 表示 c 字符串类型。 一些 c,c++,function 参数确实将 struct 类型作为参数,因此我们需要根据函数参数将 struct 类型转换为 (char*) 或 (void*)。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2010-11-25
            • 2016-03-26
            • 2020-03-04
            • 1970-01-01
            • 1970-01-01
            • 2017-09-20
            • 2013-09-27
            相关资源
            最近更新 更多