在C++中将十六进制值打印到控制台

6
#include <iostream>
#include <iomanip>

using namespace std;

int main()
{
    char array[10];

    for(int i = 0; i<10;i++)
    {
        array[i] = 'a' + i;
    }

    char* test = array;

    printf("%x\n", test);
    cout << hex << test << endl;

}

这的输出结果是:
bffff94e
abcdefghijN???

为什么它没有打印出相同的内容?

1
如果你想打印一个指针,应该在 printf() 中使用 %p,而 %x 是用于无符号十六进制整数的。类似于 %xstd::hex 对于指针不会按照你的期望工作,它旨在与整数值一起使用。 - Guillaume Papin
4个回答

7
cout << hex << test << endl; 

它打印的是字符串,而不是地址。这是因为有一个重载的 operator<<,它以 char const* 作为参数,并将该参数视为字符串。

如果你想打印地址,请将参数转换为 void*,这样就会调用 其他 重载的 operator<<,它会打印地址。

cout << hex << static_cast<void*>(test) << endl;

将以十六进制格式打印地址。

请注意,这里不需要使用hex流操作符,因为地址将以十六进制格式打印。所以:

cout << static_cast<void*>(test) << endl;

足够了。

&test 而不是 (void *) test 不也同样有效(对于一个[可能]较新的程序员来说看起来稍微不太吓人)吗?(如果我在这里错了,请原谅;我没有太多使用 C++。) - Zéychin
2
@Nawaz 错了。&test 打印出的是其他东西。你的例子使用的是数组,而不是指针,所以它可以工作,但 OP 使用的是指针。另外,请摆脱这些 C 风格的转换。我们有一个广泛的共识,认为它们不应该被使用,在教材中更加有害。 - Konrad Rudolph
@KonradRudolph:哦...你是对的。(现在请移除那个踩票 :D) - Nawaz
1
仅供记录,我对于这种情况并不反感 C 风格的强制类型转换(但一般而言,我将它们限制在数值上,并禁止它们用于指针——只有在输出语句中接受 (void*) 强制类型转换是个例外)。 - James Kanze
1
请注意,该响应在多个方面都是错误的。首先,而且最重要的是,您真的应该指出原始代码存在几种未定义行为。其次,不能保证输出指针将会以十六进制形式呈现。我曾经使用过一些机器,它们的输出是八进制的,而在几年前最广泛使用的机器之一上,它的格式为“XXXX:XXXX”(其中XXXX是十六进制)。并且“hex”修饰符对于“void *”没有任何影响;即使使用它,你仍然可能得到八进制的结果。 - James Kanze
显示剩余2条评论

6
因为您的程序有未定义的行为,因为您要求打印不同的内容。
您调用的printf是非法的,并导致未定义的行为(这是为什么您永远不应该使用printf的很好的例子)。您的格式说明符指定从参数列表中提取一个unsigned int,并以十六进制输出该值。传递除unsigned int之外的任何内容都是未定义的行为。发生这种情况时,由于可变参数通常的实现方式,如果您在一个unsigned和指针具有相同大小的机器上,则可能会输出指针的值,并将其位视为unsigned。然而,还可以有其他行为。(如果我没记错的话,g++会对此结构发出警告;在某些平台上,它也有可能崩溃。)
对于std::cout的情况,您将其传递为char *。根据定义,char*被视为'\0'字符串,而不是指针(当然也不是unsigned int)。再次,您具有未定义的行为,因为您的char *没有指向以'\0'结尾的字符串;您从未在末尾放置一个'\0'。(这可能解释了您在输出末尾看到“N???”的原因。但是,未定义的行为是未定义的。代码也可能很容易地崩溃。)
最后,您同时使用printf和std::cout;除非在两者之间刷新流,否则结果不会真正指定。(实际上,如果您正在输出到交互式设备,则应在输出'\n'字符时发生刷新。但是,如果将输出重定向到文件,则可能会得到不同的结果。)
不清楚您想要什么。如果您想输出array的地址,则应该是:
printf( "%p\n", test );
std::cout << static_cast<void*>( test ) << std::endl;

如果你想输出你生成的字符串,那么在其末尾添加一个 '\0'(不要溢出缓冲区),然后:
printf( "%s\n", test );
std::cout << test << std::endl;

我不确定您想要将什么内容转换为“十六进制”,因为字符串没有十六进制表示法,指针的表示法是由实现定义的,并且不必考虑iostream中的任何格式化参数。 (通常,在大多数现代机器上它将是十六进制的。但我曾在许多机器上工作过,其中它将是八进制的,并且至少有一个机器无论基数如何都不是一个数字。)如果您想要array的十六进制转储,则需要循环,输出每个值作为十六进制的unsigned
for ( int i = 0; i < 10; ++ i ) {
    printf( "%x", static_cast<unsigned char>( test[i] ) );
}
printf( "\n" );
std::cout.setf( std::ios_base::hex, std::ios::basefield );
for ( int i = 0; i < 10; ++ i ) {
    std::cout << static_cast<unsigned>( static_cast<unsigned char>( test[i] ) );
}
std::cout.setf( std::ios_base::dec, std::ios::basefield );
std::cout << std::endl;

最后,关于类型转换的问题:普通的char类型可以是有符号或无符号的;如果它是有符号的, 将其转换为int或unsigned可能会产生负值(int)或非常大的正值(unsigned)。因此,首先需要将其转换为unsigned char,以确保结果在范围 [0,UINT_MAX] 内。然后,我们当然需要将unsigned char转换为unsigned:
1.对于printf,因为否则我们会有未定义的行为,但是转换是隐式的,因为将unsigned char作为vararg传递会自动将其提升为unsigned。
2.对于std :: cout,规则是任何字符类型都应该被输出为字符,而不是数值(由于类型在此处用于函数重载解析,并且不被传递给vararg或unsigned,因此没有隐式转换)。

+1 是为了详细解释并指出原始代码中的所有问题。 - Nawaz

2
test本身是一个指针,即它存储一个地址。您的printf语句打印该地址的十六进制值。
然后cout <<语句打印整个字符串,因为std::hex操作符不影响字符串的打印方式。它只影响整数的打印方式。
您可以执行以下操作:
  1. 循环遍历数组的字符
  2. 将每个字符转换为整数并使用std::hex操作符进行打印
代码如下:
for (int i = 0 ; i < 10 ; ++i)
  std::cout << std::hex << static_cast<int>(array[i]) << '\n';

0

cout << HEX <<
不能用于 char* 打印十六进制字符字符串, 但可以用于 int、double、float 等。

另外,对于你的第二个打印,为什么字符串中有一些乱码是因为你没有给字符串一个 '\n',这意味着字符串的结尾。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接