【问题标题】:Pointer size on x86 and weird characters outputx86 上的指针大小和奇怪的字符输出
【发布时间】:2018-04-13 15:33:37
【问题描述】:
#include <iostream>
#define SIZE 2

int main(int argc, char** argv)
{
    char w = 'a';
    char *array[SIZE];
    array[0] = &w;
    std::cout << *array << std::endl;
    system("PAUSE");

    return(0);
}

程序的输出是:a╠╠╠╠┴Kôè∞≈5«,┌

Char 是一个字节长,所以输出应该是:a

或者它输出垃圾是因为 x86 的指针大小是 4 个字节(对于 int、double、float、char)而所有垃圾都是这 3 个字节?

【问题讨论】:

  • 你为什么不只使用字符串?
  • @PasserBy 不,那是关于不期望打印 C 字符串。 Tracy 确实预料到了这一点,但没有提供有效的 C 字符串(因为它指向未使用 1 初始化的内存,并溢出到不允许访问的不相关内存中)。
  • 你为什么要跟我说话?为什么不阅读其他人给出的非常明确的答案? cout 想要一个以NUL 结尾的字符串,而您在内存的数据区域中给它一个从未声明为此类字符串的位置。没有什么可推理的;你只是做错了。
  • 操作系统与它无关。语言定义了当你传递一个有效参数时必须发生的事情,而你没有这样做(所以代码被破坏了,发生的事情非常无趣)

标签: c++ string pointers character


【解决方案1】:
char *array[SIZE];

您正在声明一个 char 指针数组,该数组的每个元素都是一个 char*, 那么

char[0] = &w;

您将第 0 个指针分配给变量 w 的地址。

std::cout<<*array<<std::endl;

相同
std::cout<<array[0]<<std::endl;

您正在尝试打印第 0 个指针,该指针设置为非空终止字符串,当您从内存中读取随机内容时,任何事情都可能发生。

【讨论】:

  • 所以 *array 并不意味着“取消引用第一个元素”? Spec 说数组被隐式转换为指向数组第一个元素的指针。您说我正在尝试输出地址,但实际上我取消了对地址的引用。还是我弄错了?我正在取消引用指向指针的指针,所以我需要做 **array?
  • @Tracy - 是的,因为第一个元素也是一个指针,你必须取消引用 array 两次才能到达字符。
【解决方案2】:

这与指针大小或内置类型的大小无关。

代码将字符的地址存储在array[0] 中,然后将该地址发送到std::cout。当您将char* 插入std::cout 时,插入器假定指针指向一个以空结尾的字符数组。这里没有 nul 终止符,所以它会继续写东西直到它达到零。形式上,行为是未定义的。

使用char* array = &amp;w; std::cout &lt;&lt; array &lt;&lt; '\n';,您会得到同样的结果,而且更清楚。或者,更简单,std::cout &lt;&lt; &amp;w &lt;&lt; '\n';

要写入字符的值,请使用std::cout &lt;&lt; w &lt;&lt; '\n';,或者,如果您需要使用该数组,请写出数组开头的字符。那是array[0][0],或者,如果你喜欢直接取消引用,**array

【讨论】:

    【解决方案3】:

    Char 是一个字节长,所以输出应该是:a

    那个长度的“一个字节”没有存储在你编译的程序的任何地方。

    指向一个字节的指针,如char *,与指向许多字节的指针没有什么不同。指针不知道。

    当您向std::cout 发送字符指针 时,该流被设计为输出连续字符,直到遇到空字符。这就是确定应该输出多少个字符的方式。指针不知道,所以指向的数据应该标记它的结束。

    【讨论】:

    • 由MSVC编译器确定char是一个字节。
    • sizeof(char) 是 1 个字节。这与 C 风格的 NUL 终止字符串的长度无关。
    • @Tracy 字节在您的示例中无关紧要。
    • 在我的示例中,我有长度为 2 的数组。我正在尝试打印第一个元素而不是整个数组。该数组是一个字符指针数组,其中每个指针都可以指向每一个。就我而言,它指向值为“w”的字符。我试图通过打印 *array 来打印 w,这是我拥有的任何数组大小的第一个元素。如果我打印了整个数组,但它只有一个字符,我会理解。
    • @Tracy -- 您在示例中设置的字节无关紧要。当std::cout 被赋予char * 时,它将输出字符直到达到空字符。你在哪里设置了一个空字节,以便输出在你的单个字符上终止?
    【解决方案4】:

    表达式中使用的数组(极少数例外)被转换为指向其第一个元素的指针。

    来自 C++ 标准(4.2 数组到指针的转换)

    1 类型为“N T 数组”或“未知数组”的左值或右值 T 的边界”可以转换为“指向 T 的指针”类型的纯右值。这 result 是指向数组第一个元素的指针。

    所以在这个声明中

    std::cout << *array << std::endl;
    

    子表达式array 的类型为char **,因为数组的元素的类型为char *

    因此,表达式*array 的类型为char *。并且您正试图输出一个由指针*array 指向的字符串,该字符串不是以零结尾的。所以程序有未定义的行为。它输出字符'a' 之后的所有字符,直到在内存中遇到零字符。

    你需要写

    std::cout << **array << std::endl;
    

    只输出一个字符。

    这是一个演示程序

    #include <iostream>
    
    int main() 
    {
        const size_t SIZE = 2;
        char w = 'a';
        char *array[SIZE];
        array[0] = &w;
    
        std::cout << **array << std::endl;  
    
        return 0;
    }
    

    它的输出是

    a
    

    此声明

    std::cout << *array << std::endl;
    

    如果在你的对象中的字符“a”之后会有一个零字符,即如果语句将处理字符串,则可以正常工作。

    例如

    #include <iostream>
    
    int main() 
    {
        const size_t SIZE = 2;
        char w[] = { 'a', '\0' };
        char *array[SIZE];
        array[0] = &w[0];
    
        std::cout << *array << std::endl;   
    
        return 0;
    }
    

    程序输出和上面的shwon一样。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-06-20
      • 1970-01-01
      • 1970-01-01
      • 2012-12-27
      • 2021-04-23
      • 1970-01-01
      相关资源
      最近更新 更多