为什么数组中的 char 在 gcc 中为 newlib 中的 isspace 给出下标警告

Why is does char from an array give subscript warning in gcc for isspace in newlib

我无法理解代码中的编译器警告。我们的代码与本例中给出警告 (-Wchar-subscripts) 的代码类似。我们使用 ARM gcc 9.2.1 embedded 和 newlib 作为标准库。

#include <ctype.h>

int main ()
{
  char str[]="Example string\n";

  int i = isspace(str[1]); // Warning

  char c=str[1];
  i = isspace(c); // No warning

  i = isspace((unsigned char)str[1]); // No warning
}

Example in godbolt

据我了解,isspace 的实现可以通过数组索引。因此警告。但在那种情况下,1 和 2 不应该都发出警告吗?为什么只有第一个发出警告?

我们通过添加强制转换解决了我们的代码,但在我理解它为什么有用之前我并不真正满意。

这似乎与此处 Bugzilla 中讨论的错误完全相同 Bug 95177 - error: array subscript has type char。基本上 gcc 的诊断不一致,这种行为只出现在以后的版本中。

正如该线程中所讨论的那样,将 char 传递给 ctype.h 函数理论上可能会出现问题,以防 char 包含任何未知内容。这些函数被定义为期望输入可以表示为 unsigned char,参见 C17 7.4:

In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF

因此 int i = isspace((unsigned char)str[1]); 使警告消失。

但正如您所见,警告是不一致的。这可能是因为当您使用 ctype.h 时,编译器有时会选择一个宏,有时会选择一个函数。

就把它禁用掉,当成误报。根据上面的 Bugzilla 日志,这个问题最近应该已经修复了。 gcc (trunk) 和 gcc >10.3 不再给出此警告。

isspace(str[1])isspace(c) 之间的警告消息差异是由 GCC 抑制系统 headers 警告消息的功能中的错误引起的。

考虑这段代码:

#include <ctype.h>

int foo(char c)
{
    return isspace(c);
}

int bar(char c)
{
    return 
          ((((_ctype_)+sizeof(""[
          c
          ]))[(int)(
          c
          )])&010);
}

bar中的代码是对foo中的代码进行宏替换的结果(用-E编译得到,以显示预处理结果)。所以这两个函数在宏替换后具有相同的代码,并且应该具有相同的语义。然而the second code gets a warning and the first code does not。因此,GCC 的报告并不仅仅基于代码的 C 语义。

GCC 具有抑制系统 header 文件中的警告的功能。当禁用此功能时(通过使用 -Wsystem-headers),两个函数都会收到一条警告消息。因此,isspace(str[1]) 而不是 isspace(c) 出现的警告是由于系统 headers 中抑制警告消息的功能失败。也就是说,它是 GCC 中的一个错误。

为了确认,将 -Wsystem-headers 与问题中的原始代码一起使用会产生 warning messages for both isspace(str[1]) and isspace(c)