在 C 中无符号字符大于 127 的 %s 格式说明符



我编写了以下示例程序,但它们的输出不是我预期的。
在我的第一个程序中,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%sn", 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("%sn", s);                                                                                                               
    return 0;
}

输出也让我感到惊讶,它们是:

taobn

为了检查这是否是glibc的一个奇怪功能,我编写了第三个程序,它绕过glibc的I/O缓冲区,并通过系统调用直接将s写入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 openn");
        return -1;
    }
    write(fd, s, sizeof(s));
    close(fd);
    return 0;
} 

输出仍然是:

[cobblau@baba test]$ cat a.out
taobn

谁能解释一下? 这是怎么回事?
谢谢。

使用变量调用printf("n%sn", s) s不指向以 null 结尾的字符串会产生未定义的行为。简单来说,数组中的最后一个字符应该是 0(又名 (。

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

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


以下是最终打印"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将尝试打印以下以 null 结尾的字符串:

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("%s",s),看看你得到了什么......

除了其他人指出的当前未以 null 结尾的字符串(这可能导致未定义的行为(的问题之外,代码高于 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 }表示

目前,当您尝试在控制台中打印其代码未知的字符时,您可以得到一个 ? ,一个正方形或什么都没有,具体取决于系统。

打印单个字符不同于打印不以空终止符终止的字符数组

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

或者,您可以自己提供尺寸

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

来自 printf(( 的文档:

。数

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

最新更新