3

I´m having troubles understand a compiler warning that we have in our code. The code we have are similar to the one in this example that give the warning (-Wchar-subscripts). We are using ARM gcc 9.2.1 embedded and newlib as standard library.

#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

From what I have understood the implementation of isspace can be via array indexing. Hence the warning. But in that case shouldn't both 1 and 2 give the warning? Why is it only the first one that gives the warning?

We solved our code by adding a cast but I'm not really satisfied until I have understood why it helps.

lagget
  • 73
  • 7
  • 2
    Considering that you don't get that warning when building with GCC 8.5, nor with 9.3, I'd say it's a bug in GCC 9.2.1. – Some programmer dude Jan 26 '22 at 12:09
  • Add `-E` to the Godbolt compiler flags to see the preprocessor expansion (that shows that `isspace(x)` is approximately `_ctype[x] & 010`)... – AKX Jan 26 '22 at 12:10
  • @AKX: That omits the part that is key in this question, the part that triggers the error message, `""[x]`. – Eric Postpischil Jan 26 '22 at 12:21
  • @someprogrammerdude I should have specified that it was for embedded arm (none) and sure it's not in 8.3.1 (none) but it exists in 10.2.1 (none). But not in the Linux version which uses a different standard lib. So probably something with the compiler or the std lib then. – lagget Jan 26 '22 at 12:22

2 Answers2

4

This appears to be the very same bug as discussed in Bugzilla here Bug 95177 - error: array subscript has type char. Basically gcc is inconsistent in its diagnostics and this behavior only appeared in later versions.

As discussed in that thread, passing char to the ctype.h functions could in theory be a problem in case the char would contain anything unknown. These functions are defined as expecting the input to be something representable as unsigned char, see 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

Therefore int i = isspace((unsigned char)str[1]); makes the warning go away.

But the warning is as you can tell inconsistent. Which could possibly be explained by when you are using ctype.h, the compiler could sometimes be picking a macro and sometimes a function.

Just disable it and regard it as a false positive. According to the Bugzilla log above, this should now have been fixed very recently. gcc (trunk) and gcc >10.3 doesn't give this warning any longer.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • Re “the compiler could sometimes be picking a macro and sometimes a function”: No, it may not. The identifier is either defined as a preprocessor macro or it is not. If it is defined as a preprocessor macro, the compiler must replace the macro when it is invoked properly (followed by a `(`). It has no choice. The programmer could have a choice, by using `#undef isspace`, but the compiler does not. – Eric Postpischil Jan 26 '22 at 12:24
  • @EricPostpischil Which I mean is that it could be either implemented as a macro or a function, and not all ctype.h functions need to be implemented consistently as one or the other. To know for sure one has to dig up the library source. – Lundin Jan 26 '22 at 12:26
  • Many thanks! Now I get it. We will probably update out compiler soon anyway. – lagget Jan 26 '22 at 12:27
  • Bug 95177 is about about GCC using the `` facilities, not about inconsistent reporting of the `char` subscripts warning. This question asks about the inconsistency of the warning. – Eric Postpischil Jan 26 '22 at 12:29
  • @EricPostpischil Just read the comments in the log, for example [this](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=95177#c10). – Lundin Jan 26 '22 at 12:37
  • @Lundin: That comment speaks to why the warning occurs at all. It does not speak to why the warning appears some times that a `char` is the macro argument and not other times that a `char` is the macro argument. – Eric Postpischil Jan 26 '22 at 12:42
0

The difference in warning messages between isspace(str[1]) and isspace(c) is caused by a bug in GCC’s feature to suppress warning messages in system headers.

Consider this code:

#include <ctype.h>

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

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

The code in bar is the result of applying macro replacement to the code in foo (it was obtained by compiling with -E to show the result of preprocessing). So these two functions have identical code after macro replacement and should have identical semantics. Yet the second code gets a warning and the first code does not. Therefore, GCC’s reporting is not based solely on the C semantics of the code.

GCC has a feature to suppress warnings in system header files. When this feature is disabled (by using -Wsystem-headers), both functions get a warning message. Therefore, the warning that occurs with isspace(str[1]) but not with isspace(c) is due to a failure of the feature to suppress warning messages in system headers. That is, it is a bug in GCC.

For confirmation, using -Wsystem-headers with the original code in the question yields warning messages for both isspace(str[1]) and isspace(c).

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312