如何避免 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 位编码)。