为什么 %c 格式说明符在以下代码中不起作用?
Why does %c format specifier not work in the following code?
我刚刚开始阅读有关 C 的内容,目前正在研究格式说明符。
这是代码示例:
#include <stdio.h>
int main(void) {
char code = 'a' - 'A';
printf("\n>>>%c (%d)", code, code);
printf("\n>>>%c", 32);
char incode;
printf("\n\nGive me some char: ");
scanf("%c", &incode);
printf("\n>>>%c (%d)", incode, incode);
return 0;
}
输出:
PS C:\ex> ./print
>>> (32)
>>>
Give me some char: A
>>>A (65)
那么,为什么%c
在最后printf
起作用而在开始却不起作用?
我在 Windows 和 Linux 上测试了这个示例,两者的行为相同。
- Windows 编译器:
gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
- Linux 编译器:
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
ASCII 32 是 ' '
(space)。印的还行,就是看不出来
如果将格式字符串更改为 "\n>>>%c<< (%d)"
,您将在(双关语)否定 space.
中看到它
也可以通过od -a
输出来确认。
假设你的字符编码是ASCII。那么a
编码为97(十进制),A
编码为65(十进制)。它们的区别是 32,它编码
space 字符。
所以char code = 'a' - 'A';
等同于char code = 32;
也就是声明char code = ' ';
另请参阅 this C reference 网站。
如果允许,使用所有警告和调试信息进行编译,因此(使用 GCC)如 gcc -Wall -Wextra -g
您可以使用 gcc -Wall -O -fverbose-asm minerals.c -S -o minerals.s
编译您的 C 源代码 minerals.c
并查看生成的汇编代码 foo.s
。在装有 GCC 10.2 的 Debian 计算机上,我收到以下警告:
minerals.c: In function ‘main’:
minerals.c:11:5: warning: ignoring return value of ‘scanf’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
11 | scanf("%c", &incode);
| ^~~~~~~~~~~~~~~~~~~~
生成的汇编代码有:
# /usr/include/x86_64-linux-gnu/bits/stdio2.h:107: return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
movl , %ecx #,
movl , %edx #,
leaq .LC0(%rip), %rsi #,
movl , %edi #,
call __printf_chk@PLT #
movl , %edx #, // 32 is the space
leaq .LC1(%rip), %rsi #,
movl , %edi #,
movl [=11=], %eax #,
call __printf_chk@PLT #
leaq .LC2(%rip), %rsi #,
movl , %edi #,
movl [=11=], %eax #,
call __printf_chk@PLT #
实际上,在 2021 年,UTF-8 is used everywhere (and complicates matter). Look into GNU libunistring。
我刚刚开始阅读有关 C 的内容,目前正在研究格式说明符。 这是代码示例:
#include <stdio.h>
int main(void) {
char code = 'a' - 'A';
printf("\n>>>%c (%d)", code, code);
printf("\n>>>%c", 32);
char incode;
printf("\n\nGive me some char: ");
scanf("%c", &incode);
printf("\n>>>%c (%d)", incode, incode);
return 0;
}
输出:
PS C:\ex> ./print
>>> (32)
>>>
Give me some char: A
>>>A (65)
那么,为什么%c
在最后printf
起作用而在开始却不起作用?
我在 Windows 和 Linux 上测试了这个示例,两者的行为相同。
- Windows 编译器:
gcc.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
- Linux 编译器:
clang version 6.0.0-1ubuntu2 (tags/RELEASE_600/final)
ASCII 32 是 ' '
(space)。印的还行,就是看不出来
如果将格式字符串更改为 "\n>>>%c<< (%d)"
,您将在(双关语)否定 space.
中看到它
也可以通过od -a
输出来确认。
假设你的字符编码是ASCII。那么a
编码为97(十进制),A
编码为65(十进制)。它们的区别是 32,它编码
space 字符。
所以char code = 'a' - 'A';
等同于char code = 32;
也就是声明char code = ' ';
另请参阅 this C reference 网站。
如果允许,使用所有警告和调试信息进行编译,因此(使用 GCC)如 gcc -Wall -Wextra -g
您可以使用 gcc -Wall -O -fverbose-asm minerals.c -S -o minerals.s
编译您的 C 源代码 minerals.c
并查看生成的汇编代码 foo.s
。在装有 GCC 10.2 的 Debian 计算机上,我收到以下警告:
minerals.c: In function ‘main’:
minerals.c:11:5: warning: ignoring return value of ‘scanf’ declared with attribute ‘warn_unused_result’ [-Wunused-result]
11 | scanf("%c", &incode);
| ^~~~~~~~~~~~~~~~~~~~
生成的汇编代码有:
# /usr/include/x86_64-linux-gnu/bits/stdio2.h:107: return __printf_chk (__USE_FORTIFY_LEVEL - 1, __fmt, __va_arg_pack ());
movl , %ecx #,
movl , %edx #,
leaq .LC0(%rip), %rsi #,
movl , %edi #,
call __printf_chk@PLT #
movl , %edx #, // 32 is the space
leaq .LC1(%rip), %rsi #,
movl , %edi #,
movl [=11=], %eax #,
call __printf_chk@PLT #
leaq .LC2(%rip), %rsi #,
movl , %edi #,
movl [=11=], %eax #,
call __printf_chk@PLT #
实际上,在 2021 年,UTF-8 is used everywhere (and complicates matter). Look into GNU libunistring。