如何避免C中的整数提升?

时间:2017-03-28 05:47:11

标签: c portability wchar-t widechar

目前尚不清楚如何使用宽字符API在C中编写可移植代码。考虑这个例子:

#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警告。

1 个答案:

答案 0 :(得分:1)

为了简单起见,我将假设我讨论的平台/实现具有以下特征:

  • 两个补码整数类型
  • int是32位
  • short是16位

我也会使用C99作为参考,因为它是我打开的。

标准规定对于这些类型/宏必须满足以下条件:

  • wint_t必须至少有一个与扩展字符集的任何成员不对应的值(7.24.1 / 2)
  • WEOF的值与扩展字符集的任何成员(7.24.1 / 3)不对应
  • wchar_t可以表示最大扩展字符集(7.17 / 2)的所有值

请注意,根据C标准对&#34;值&#34;的定义,(short int) 0xffff的值与<{>相同的值为{{ 1}} - 也就是说它们都具有值(int) 0xffffffff(给出本答案开头所述的假设)。标准对整数促销的描述(6.3.1.1)清楚地表明了这一点:

  

如果int可以表示原始类型的所有值,则该值将转换为int;否则,它将转换为unsigned int。这些被称为整数促销。所有其他类型都不会被整数促销更改。

     

整数促销保留包括符号在内的值。

我相信当你组合这些元素时,如果-1的值为WEOF,那么扩展字符集中的任何项都不能具有值-1。我认为这意味着在您的实现示例中,-1必须是无符号的(如果它仍然是16位类型)或wchar_t不能是有效字符。

但是我最初忘记的另一个替代方案(并且可能是您的示例实现的最佳解决方案)是标准在脚注中指出宏(wchar_t) 0xffff的值可能与WEOF的不同,不一定是否定的&#34;。因此,您可以通过制作EOF来解决您的实施问题。这样它就不能具有与任何WEOF == INT_MAX相同的值。

可能与有效字符值重叠的wchar_t值是我认为可能在实际实现中出现的值(即使标准似乎禁止它),并且它类似于已经存在的问题提出WEOF可能与某些有效的签名字符值具有相同的值。

对于可以返回EOF以指示某种问题的大多数(所有?)函数,可能会感兴趣,标准要求函数设置一些关于错误或条件的附加指示(例如,将WEOF设置为特定值,或在流上设置文件结束指示符。

另外需要注意的是,我的理解是0xffff是UCS-2或UTF-16中的非字符(不知道可能存在的任何其他16位编码)。