如何避免 C 中的整数提升?
How to avoid integer promotion in C?
不清楚如何使用宽字符在 C 中编写可移植代码 API。考虑这个例子:
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
int main(void)
{
setlocale(LC_CTYPE, "C.UTF-8");
wchar_t wc = L'ÿ';
if (iswlower(wc)) return 0;
return 1;
}
使用 -Wconversion 选项使用 gcc-6.3.0 编译它会给出此警告:
test.c: In function 'main':
test.c:9:16: warning: conversion to 'wint_t {aka unsigned int}' from 'wchar_t {aka int}' may change the sign of the result [-Wsign-conversion]
if (iswlower(wc)) return 0;
^
为了消除这个警告,我们转换为 (wint_t)
,就像 iswlower((wint_t)wc)
,但这是不可移植的。
下面的例子演示了为什么它不可移植。
#include <stdio.h>
/* this is our hypothetical implementation */
typedef signed int wint_t;
typedef signed short wchar_t;
#define WEOF ((wint_t)0xffffffff)
void f(wint_t wc)
{
if (wc==WEOF)
printf("BUG. Valid character recognized as WEOF. This is due to integer promotion. How to avoid it?\n");
}
int main(void)
{
wchar_t wc = (wchar_t)0xffff;
f((wint_t)wc);
return 0;
}
我的问题是:如何使这个示例可移植,同时避免 gcc 警告。
为简单起见,我假设我正在讨论的 platform/implementation 具有以下特征:
- 二进制补码整数类型
int
是32位
short
是16位
我也将使用 C99 作为参考,因为它是我打开的。
标准规定以下关于这些 types/macros 必须为真:
wint_t
必须至少有一个值不对应于扩展字符集 (7.24.1/2) 的任何成员
WEOF
有一个值不对应于扩展字符集 (7.24.1/3) 的任何成员
wchar_t
可以表示最大扩展字符集(7.17/2) 的所有值
请记住,根据 C 标准对 "value" 的定义,(short int) 0xffff
的值与 (int) 0xffffffff
的值相同 ] - 也就是说它们都具有值 -1
(鉴于此答案开头所述的假设)。标准对整数提升 (6.3.1.1) 的描述清楚地表明了这一点:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
The integer promotions preserve value including sign.
我相信,当您组合这些元素时,似乎如果 WEOF
具有值 -1
,那么扩展字符集中的任何项目都不能具有值 -1
。我认为这意味着在您的实现示例中,wchar_t
必须是无符号的(如果它仍然是 16 位类型)或 (wchar_t) 0xffff
不能是有效字符。
但是我最初忘记的另一种选择(可能是您的示例实现的最佳解决方案)是标准在脚注中声明 "value of the macro WEOF
may differ from that of EOF
and need not be negative"。因此,例如,可以通过 WEOF == INT_MAX
来解决您的实施问题。这样它就不能具有与任何 wchar_t
.
相同的值
可能与有效字符值重叠的 WEOF
值是我认为在实际实现中可能会出现的值(即使标准似乎禁止这样做),并且它类似于已经提出的问题关于 EOF
可能与某些有效的有符号字符值具有相同的值。
对于大多数(所有?)可以 return WEOF
指示某种问题的函数,标准要求函数设置一些关于错误的附加指示可能是有趣的或条件(例如,将 errno
设置为特定值,或在流上设置文件结束指示符)。
另一件需要注意的事情是,据我所知,0xffff 是 UCS-2 或 UTF-16 中的非字符(不知道可能存在的任何其他 16 位编码)。
不清楚如何使用宽字符在 C 中编写可移植代码 API。考虑这个例子:
#include <locale.h>
#include <wchar.h>
#include <wctype.h>
int main(void)
{
setlocale(LC_CTYPE, "C.UTF-8");
wchar_t wc = L'ÿ';
if (iswlower(wc)) return 0;
return 1;
}
使用 -Wconversion 选项使用 gcc-6.3.0 编译它会给出此警告:
test.c: In function 'main':
test.c:9:16: warning: conversion to 'wint_t {aka unsigned int}' from 'wchar_t {aka int}' may change the sign of the result [-Wsign-conversion]
if (iswlower(wc)) return 0;
^
为了消除这个警告,我们转换为 (wint_t)
,就像 iswlower((wint_t)wc)
,但这是不可移植的。
下面的例子演示了为什么它不可移植。
#include <stdio.h>
/* this is our hypothetical implementation */
typedef signed int wint_t;
typedef signed short wchar_t;
#define WEOF ((wint_t)0xffffffff)
void f(wint_t wc)
{
if (wc==WEOF)
printf("BUG. Valid character recognized as WEOF. This is due to integer promotion. How to avoid it?\n");
}
int main(void)
{
wchar_t wc = (wchar_t)0xffff;
f((wint_t)wc);
return 0;
}
我的问题是:如何使这个示例可移植,同时避免 gcc 警告。
为简单起见,我假设我正在讨论的 platform/implementation 具有以下特征:
- 二进制补码整数类型
int
是32位short
是16位
我也将使用 C99 作为参考,因为它是我打开的。
标准规定以下关于这些 types/macros 必须为真:
wint_t
必须至少有一个值不对应于扩展字符集 (7.24.1/2) 的任何成员
WEOF
有一个值不对应于扩展字符集 (7.24.1/3) 的任何成员
wchar_t
可以表示最大扩展字符集(7.17/2) 的所有值
请记住,根据 C 标准对 "value" 的定义,(short int) 0xffff
的值与 (int) 0xffffffff
的值相同 ] - 也就是说它们都具有值 -1
(鉴于此答案开头所述的假设)。标准对整数提升 (6.3.1.1) 的描述清楚地表明了这一点:
If an int can represent all values of the original type, the value is converted to an int; otherwise, it is converted to an unsigned int. These are called the integer promotions. All other types are unchanged by the integer promotions.
The integer promotions preserve value including sign.
我相信,当您组合这些元素时,似乎如果 WEOF
具有值 -1
,那么扩展字符集中的任何项目都不能具有值 -1
。我认为这意味着在您的实现示例中,wchar_t
必须是无符号的(如果它仍然是 16 位类型)或 (wchar_t) 0xffff
不能是有效字符。
但是我最初忘记的另一种选择(可能是您的示例实现的最佳解决方案)是标准在脚注中声明 "value of the macro WEOF
may differ from that of EOF
and need not be negative"。因此,例如,可以通过 WEOF == INT_MAX
来解决您的实施问题。这样它就不能具有与任何 wchar_t
.
可能与有效字符值重叠的 WEOF
值是我认为在实际实现中可能会出现的值(即使标准似乎禁止这样做),并且它类似于已经提出的问题关于 EOF
可能与某些有效的有符号字符值具有相同的值。
对于大多数(所有?)可以 return WEOF
指示某种问题的函数,标准要求函数设置一些关于错误的附加指示可能是有趣的或条件(例如,将 errno
设置为特定值,或在流上设置文件结束指示符)。
另一件需要注意的事情是,据我所知,0xffff 是 UCS-2 或 UTF-16 中的非字符(不知道可能存在的任何其他 16 位编码)。