Windows 上 C `char **argv` 的实际类型是什么
What actually is the type of C `char **argv` on Windows
通过阅读 MSDN 或 n1256
委员会草案中的文档,我的印象是 char
始终正好是 <limits.h>
中定义的 CHAR_BIT
位].
如果 CHAR_BIT
设置为 8,则一个字节为 8 位长,char
.
也是如此
测试代码
给定以下 C 代码:
int main(int argc, char **argv) {
int length = 0;
while (argv[1][length] != '[=11=]') {
// print the character, its hexa value, and its size
printf("char %u: %c\tvalue: 0x%X\t sizeof char: %u\n",
length,
argv[1][length],
argv[1][length],
sizeof argv[1][length]);
length++;
}
printf("\nTotal length: %u\n", length);
printf("Actual char size: %u\n", CHAR_BIT);
return 0;
}
鉴于包含非 ASCII 字符的参数,例如 ç
和 à
.
,我不确定行为会是什么
这些字符应该是 UTF-8,所以每个都写成多个字节。我希望它们作为单独的字节进行处理,这意味着 ça
的长度为 3(如果计算 [=22=]
则为 4)并且在打印时,我会得到每个字节一行,所以3 行而不是 2 行(这将是实际的拉丁字符数)。
输出
$ gcc --std=c99 -o program.exe win32.c
$ program.exe test_çà
char 0: t value: 0x74 sizeof char: 1
char 1: e value: 0x65 sizeof char: 1
char 2: s value: 0x73 sizeof char: 1
char 3: t value: 0x74 sizeof char: 1
char 4: _ value: 0x5F sizeof char: 1
char 5: τ value: 0xFFFFFFE7 sizeof char: 1
char 6: α value: 0xFFFFFFE0 sizeof char: 1
Total length: 7
Actual char size: 8
问题
幕后可能发生的事情是 char **argv
变成了 int **argv
。这可以解释为什么第 5 行和第 6 行有一个写入 4 个字节的十六进制值。
- 真的是这样吗?
- 这是标准行为吗?
- 为什么字符 5 和 6 不是输入的内容?
CHAR_BIT == 8
和 sizeof(achar) == 1
和 somechar = 0xFFFFFFE7
。这似乎违反直觉。发生什么事了?
环境
- Windows 10
- 终端:Alacritty 和 Windows 默认 cmd(都试过以防万一)
- Mingw-w64 下的 GCC
从您的代码和系统的输出来看,似乎是:
- type
char
确实有8位。根据定义,它的大小为 1。 char **argv
是指向 C 字符串指针数组的指针,char
(8 位字节)的空终止数组。
char
类型已为您的编译器配置签名,因此对于超过 127 的值,输出 0xFFFFFFE7
和 0xFFFFFFE0
。char
值作为 int
到 printf
,它将 %X
转换的值解释为无符号。该行为在技术上未定义,但实际上负值在用作无符号时会偏移 232。您可以配置 gcc 使 char
类型默认无符号 -funsigned-char
,这是一个更安全的选择,也更符合 C 库行为。
- 2 个非 ASCII 字符
çà
被编码为单个字节 E7 和 E0,这对应于 Microsoft 的专有编码,它们的代码页 Windows-1252,而不是您假设的 UTF-8。
情况最终令人困惑:命令行参数被传递给使用 Windows-1252 代码页编码的程序,但终端使用旧的 MS/DOS code page 437 来与历史内容兼容。因此,您的程序输出它接收到的字节作为命令行参数,但终端显示来自 CP437 的相应字符,即 τ
和 α
.
Microsoft 就非 ASCII 字符的编码做出了历史性的决定,这些决定在今天的标准看来已经过时,令人遗憾的是,他们似乎坚持其他供应商出于充分理由而避开的繁琐选择。在这种环境下用C编程是一条坎坷的道路。
UTF-8 于 1992 年 9 月由 Unix 团队负责人 Kenneth Thomson 和 Rob Pike 发明。他们一夜之间在 plan-9 中实现了它,因为它具有许多与 C 语言字符串兼容的有趣属性。微软已经在他们自己的系统上投入了数百万美元,而忽略了这种更简单的方法,这种方法如今在网络上已经无处不在。
不,它不是作为 int
的数组接收的。
但这与事实相去不远:printf
确实收到 char
作为 int
。
当将小于 int
的整数类型传递给像 printf
这样的可变参数函数时,它会被提升为 int
。在您的系统上,char
是有符号类型。[1] 给定一个值为 -25 的 char
,一个值为 -25 的 int
的 -25 被传递给 printf
。 %u
需要一个 unsigned int
,因此它将值为 -25 的 int
视为 unsigned int
,打印 0xFFFFFFE7
.
一个简单的修复:
printf("%X\n", (unsigned char)c); // 74 65 73 74 5F E7 E0
但是为什么你一开始就得到了 E7 和 E0?
每个处理文本的 Windows 系统调用都有两个版本:
- 处理使用系统的活动代码页编码的文本的“ANSI”(
A
) 版本。[2] 对于 en-us 安装Windows,这是cp1252.
- 还有一个 Wide (
W
) 版本,它处理使用 UTF-16le 编码的文本。
正在使用 GetCommandLineA
从系统获取命令行,GetCommandLine
的 A
版本。您的系统使用 cp1252 作为其 ACP。使用cp1252编码,ç
为E7,à
为E0。
GetCommandLineW
will provide the command line as UTF-16le, and CommandLineToArgvW
将解析它。
最后,为什么E7和E0显示为τ
和α
?
终端编码与ACP不同!在你的机器上,它似乎是 437。(这可以更改。)使用 cp437 编码,τ
是 E7,α
是 E0。
发出 chcp 1252
会将终端的编码设置为 cp1252,与 ACP 匹配。 (UTF-8 为 65001。)
您可以使用GetConsoleCP
(for input) and GetConsoleOutputCP
查询终端的编码(用于输出)。是的,显然他们可以不同?我不知道这在实践中会如何发生。
char
是有符号类型还是无符号类型由编译器决定。
- 从 Windows 10,版本 1903(2019 年 5 月更新)开始,每个程序都可以 changed。
通过阅读 MSDN 或 n1256
委员会草案中的文档,我的印象是 char
始终正好是 <limits.h>
中定义的 CHAR_BIT
位].
如果 CHAR_BIT
设置为 8,则一个字节为 8 位长,char
.
测试代码
给定以下 C 代码:
int main(int argc, char **argv) {
int length = 0;
while (argv[1][length] != '[=11=]') {
// print the character, its hexa value, and its size
printf("char %u: %c\tvalue: 0x%X\t sizeof char: %u\n",
length,
argv[1][length],
argv[1][length],
sizeof argv[1][length]);
length++;
}
printf("\nTotal length: %u\n", length);
printf("Actual char size: %u\n", CHAR_BIT);
return 0;
}
鉴于包含非 ASCII 字符的参数,例如 ç
和 à
.
这些字符应该是 UTF-8,所以每个都写成多个字节。我希望它们作为单独的字节进行处理,这意味着 ça
的长度为 3(如果计算 [=22=]
则为 4)并且在打印时,我会得到每个字节一行,所以3 行而不是 2 行(这将是实际的拉丁字符数)。
输出
$ gcc --std=c99 -o program.exe win32.c
$ program.exe test_çà
char 0: t value: 0x74 sizeof char: 1
char 1: e value: 0x65 sizeof char: 1
char 2: s value: 0x73 sizeof char: 1
char 3: t value: 0x74 sizeof char: 1
char 4: _ value: 0x5F sizeof char: 1
char 5: τ value: 0xFFFFFFE7 sizeof char: 1
char 6: α value: 0xFFFFFFE0 sizeof char: 1
Total length: 7
Actual char size: 8
问题
幕后可能发生的事情是 char **argv
变成了 int **argv
。这可以解释为什么第 5 行和第 6 行有一个写入 4 个字节的十六进制值。
- 真的是这样吗?
- 这是标准行为吗?
- 为什么字符 5 和 6 不是输入的内容?
CHAR_BIT == 8
和sizeof(achar) == 1
和somechar = 0xFFFFFFE7
。这似乎违反直觉。发生什么事了?
环境
- Windows 10
- 终端:Alacritty 和 Windows 默认 cmd(都试过以防万一)
- Mingw-w64 下的 GCC
从您的代码和系统的输出来看,似乎是:
- type
char
确实有8位。根据定义,它的大小为 1。char **argv
是指向 C 字符串指针数组的指针,char
(8 位字节)的空终止数组。 char
类型已为您的编译器配置签名,因此对于超过 127 的值,输出0xFFFFFFE7
和0xFFFFFFE0
。char
值作为int
到printf
,它将%X
转换的值解释为无符号。该行为在技术上未定义,但实际上负值在用作无符号时会偏移 232。您可以配置 gcc 使char
类型默认无符号-funsigned-char
,这是一个更安全的选择,也更符合 C 库行为。- 2 个非 ASCII 字符
çà
被编码为单个字节 E7 和 E0,这对应于 Microsoft 的专有编码,它们的代码页 Windows-1252,而不是您假设的 UTF-8。
情况最终令人困惑:命令行参数被传递给使用 Windows-1252 代码页编码的程序,但终端使用旧的 MS/DOS code page 437 来与历史内容兼容。因此,您的程序输出它接收到的字节作为命令行参数,但终端显示来自 CP437 的相应字符,即 τ
和 α
.
Microsoft 就非 ASCII 字符的编码做出了历史性的决定,这些决定在今天的标准看来已经过时,令人遗憾的是,他们似乎坚持其他供应商出于充分理由而避开的繁琐选择。在这种环境下用C编程是一条坎坷的道路。
UTF-8 于 1992 年 9 月由 Unix 团队负责人 Kenneth Thomson 和 Rob Pike 发明。他们一夜之间在 plan-9 中实现了它,因为它具有许多与 C 语言字符串兼容的有趣属性。微软已经在他们自己的系统上投入了数百万美元,而忽略了这种更简单的方法,这种方法如今在网络上已经无处不在。
不,它不是作为 int
的数组接收的。
但这与事实相去不远:printf
确实收到 char
作为 int
。
当将小于 int
的整数类型传递给像 printf
这样的可变参数函数时,它会被提升为 int
。在您的系统上,char
是有符号类型。[1] 给定一个值为 -25 的 char
,一个值为 -25 的 int
的 -25 被传递给 printf
。 %u
需要一个 unsigned int
,因此它将值为 -25 的 int
视为 unsigned int
,打印 0xFFFFFFE7
.
一个简单的修复:
printf("%X\n", (unsigned char)c); // 74 65 73 74 5F E7 E0
但是为什么你一开始就得到了 E7 和 E0?
每个处理文本的 Windows 系统调用都有两个版本:
- 处理使用系统的活动代码页编码的文本的“ANSI”(
A
) 版本。[2] 对于 en-us 安装Windows,这是cp1252. - 还有一个 Wide (
W
) 版本,它处理使用 UTF-16le 编码的文本。
正在使用 GetCommandLineA
从系统获取命令行,GetCommandLine
的 A
版本。您的系统使用 cp1252 作为其 ACP。使用cp1252编码,ç
为E7,à
为E0。
GetCommandLineW
will provide the command line as UTF-16le, and CommandLineToArgvW
将解析它。
最后,为什么E7和E0显示为τ
和α
?
终端编码与ACP不同!在你的机器上,它似乎是 437。(这可以更改。)使用 cp437 编码,τ
是 E7,α
是 E0。
发出 chcp 1252
会将终端的编码设置为 cp1252,与 ACP 匹配。 (UTF-8 为 65001。)
您可以使用GetConsoleCP
(for input) and GetConsoleOutputCP
查询终端的编码(用于输出)。是的,显然他们可以不同?我不知道这在实践中会如何发生。
char
是有符号类型还是无符号类型由编译器决定。- 从 Windows 10,版本 1903(2019 年 5 月更新)开始,每个程序都可以 changed。