%s格式说明符,C中的unsigned char大于127

时间:2014-11-05 14:09:07

标签: c

我写了下面的示例程序,但是他们的输出并不是我所期望的 在我的第一个程序中,s包含一些字符,但其中一个字符大于127(0xe1)。当我打印s时,输出不符合我的预期。

#include <stdio.h>

int main()
{
    int i, len;

    unsigned char s[] = {0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e};

    for (i = 0; i < sizeof(s) / sizeof(unsigned char); i++) {
        printf("%c ", s[i]);
    }

    printf("\n%s\n", s);                                                                                                               
    return 0;
}
猜猜是什么?产出是:

t a o b c d n 
taobn@

然后我对第一个程序进行了一些小改动,这是我的第二个程序:

#include <stdio.h>

int main()
{
    int i, len;

    unsigned char s[] = {0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e};
    // Iteratively output was deleted here

    printf("%s\n", s);                                                                                                               
    return 0;
}

输出也令我惊讶,他们是:

taobn

要检查这是否是glibc的一个奇怪特性,我写了第三个程序绕过glibc的I / O缓冲区并将s直接写入文件{{1系统调用。

write

产出仍然是:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{  
   int fd;                                                  
   unsigned char s[] = {0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e};

   if((fd = open("./a.out", O_WRONLY | O_CREAT)) < 0) {
        printf("error open\n");
        return -1;
    }

    write(fd, s, sizeof(s));
    close(fd);

    return 0;
} 

任何人都能解释一下吗?这是怎么回事?
感谢。

3 个答案:

答案 0 :(得分:7)

调用带有变量printf("\n%s\n", s)的{​​{1}}而不指向以null结尾的字符串会产生未定义的行为。简单来说,数组中的最后一个字符应为0(a.k.a。s)。

\0告诉%s打印位于输入参数指向的内存地址的字符,直到遇到0字符。

您传递的字符数组不包含0个字符,因此printf将继续从内存中读取字符,直到遇到0或执行非法内存访问。


以下是您最终打印printf的方法:

您的角色数组是:

"taobn@"

假设位于此数组后面的字符在内存中是:

unsigned char s[] = {0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e};

所以实质上,0x08, 0x08, 0x08, 0x08, 0x08, 0x6e, 0x40, 0x20, 0x20, 0x20, 0x08, 0x08, 0x08, 0x00 将尝试打印以下以空字符结尾的字符串:

printf

现在,尝试拨打unsigned char s[] = {0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e, 0x08, 0x08, 0x08, 0x08, 0x08, 0x6e, 0x40, 0x20, 0x20, 0x20, 0x08, 0x08, 0x08, 0x00}; ,看看你得到了什么......

答案 1 :(得分:5)

除了其他人注意到当前非空字符串终止(可能导致未定义的行为)的问题之外,代码大于127的字符输出取决于当前的控制台字符集。

你可以拥有像ISO-8859-1(AKA Latin1)这样的单字节字符集,或者它的微小变化Windows 1252,CP850或CP437,每个都有自己的高字符表示,但其中一个字节是一个字符,和另一边的UTF8等多字节字符集。

作为示例,字符串éè由ISO-8859-1中的{ 0xe9, 0xe8, 0 },CP850中的{ 0x82, 0x8a, 0 }和UTF8中的{ 0xc3, 0xa9, 0xc3, 0xa8, 0 }表示

目前,当您尝试在控制台中打印代码未知的字符时,您可以获得?,正方形或不存在,具体取决于系统。

答案 2 :(得分:1)

打印单个字符与打印字符数组不同,不会以空终结符终止

unsigned char s[] = { 0x74, 0x61, 0x6f, 0x62, 0xe1, 0x6f, 0x63, 0x64, 0x6e };
printf("\n%s\n", s); // Wrong, undefined behavior

或者你可以自己提供尺寸

printf("\n%.*s\n", (int)sizeof(s), s);

来自printf()'s documentation

  

。数

     

对于s:这是要打印的最大字符数。默认情况下,将打印所有字符,直到遇到结束的空字符。