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

C中的%s格式说明符的无符号字符大于127,c,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) / 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%s\n", s);                                                                                                               
    return 0;
}
然后我对第一个程序做了一些小改动,这是我的第二个程序:

#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;
}
为了检查这是否是
glibc
的奇怪功能,我编写了第三个程序,它绕过
glibc
的I/O缓冲区,并通过
write
系统调用将
s
直接写入文件

#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;
} 
有人能解释一下吗?这是怎么回事?
谢谢。

使用变量
s
调用
printf(“\n%s\n”,s)
不指向以null结尾的字符串会产生未定义的行为。简单地说,数组中的最后一个字符应该是0(也称为
\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)
,看看您得到了什么…

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

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);
发件人:

.号码

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


正如其他人所指出的,除了字符串当前以NOTNULL结尾(这可能导致未定义的behaviout)之外,代码高于127的字符的输出取决于当前控制台字符集

您可以使用单字节字符集,如ISO-8859-1(又名Latin1),或其微小变化的Windows 1252、CP850或CP437,每个窗口都有自己的高位字符表示,但其中一个字节在一侧是一个字符,而多字节字符集,如UTF8在另一侧

例如,字符串
è
在ISO-8859-1中由
{0xe9,0xe8,0}
表示,在CP850中由
{0x82,0x8a,0}
表示,在UTF8中由
{0xc3,0xa9,0xc3,0xa8,0}
表示


当前,当您尝试在控制台中打印代码未知的字符时,根据系统的不同,您可以得到一个
、一个正方形或什么都没有。

这是正确的。但是字符字符串在0x63之前包含
0xE1 0x6F
。最后一个(0x63)在作为单个字符发送时生成“c”,但在作为字符串发送时会被删除。这不应该依赖于终止零。呃。。。以null结尾的可能不是点。实际上,如果我将
s
定义更改为
unsigned char s[]={0x74,0x61,0x6f,0x62,0xe1,0x6f,0x63,0x64,0x6e,'\0'}
,然后调用
printf(“%s\n”,s)
,输出仍然是
taobn
。's'数组需要以null结尾才能使用,就像在printf()调用中一样。此外,您的控制台驱动程序可能会在其“当前选定的代码页值”中将0xe1视为多字节字符的一部分。您的命令
cat a.out
不会执行程序,而是打印其内容。这就是重点。顺便说一句,你能告诉我如何指出我的计算机使用的是哪个控制台字符集,这个字符集下每个字符的含义是什么?它严重依赖于系统和配置。。。我会在Windows上尝试
chcp
echo$LANG
,或者询问系统管理员
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};
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);