Background
I was writing code that uses functions from ctype.h
to identify things in strings. I accidentally passed the string (char*
) to the function(s) which take and int
type, causing the program to segfault. It was easy enough to see that I forgot to dereference the string pointer, but GCC gave me no warnings even when compiling with the following arguments:
gcc -o main main.c -Wall -Wextra -Werror -pedantic -pedantic-errors -std=c99 -Wconversion
I am using Debian GNU/Linux bookworm 12.5 x86_64
and gcc (Debian 12.2.0-14) 12.2.0
which is all up to date. Here’s a example of the problem:
/* main.c */
#include <ctype.h>
#include <stdio.h>
int main(void)
{
char msg[] = "hello";
int res = isspace(msg); // char* gets cast to int without warning
// It should be `isspace(*msg)`
// This also segfaults
printf("%in", res);
return 0;
}
Questions
- What warnings can I turn on to get compile time errors for these pointer-to-integer conversions?
- Why does this even segfault in the first place?
2
Answers
You’re passing in a value that is outside the range of values the function expects. Doing so triggers undefined behavior, as per section 7.4p1 of the C standard regarding functions defined in ctype.h:
And since this is undefined behavior, crashing is one possible outcome.
As for why there’s no warning generated by the compiler, we need to look at the preprocessor output. The call to
isspace
gets converted to the following after the preprocessor:From this, we can see that
isspace
is implemented as a macro which uses a lookup table with the given argument as an index, and we can see that the argument is explicitly casted toint
. This explicit cast explains why there’s no warning.The above also explains the crash, since a pointer value will likely be far out of the bounds of this lookup table and therefore attempt to access memory it doesn’t have access to.
Likely, with your compiler,
isspace()
is implemented as a macro that includes a typecast of whatever argument it gets tochar
orint
.Obviously, when the compiler sees a cast, it will just assume, "well, he said so". Macros are not type-checked at all (well, you can’t specify a type, so how should the compiler check it).