Why am I not getting a warning about casting an int pointer?

The following code snippet gives a warning for casting with a pointer to int, which makes perfect sense

int foo[] = {1, 2, 3}; #define bar(x) foo[(int)(x) % 3] char *baz = "quux"; bar(baz); 

On the other hand, the following code compiles without any warning (regardless of whether it can cause a runtime error)

 #include <ctype.h> // some code char *baz = "quux"; isalpha(baz); 

When I opened ctype.h to look at isalpha , I found that it is a macro that uses a couple of other macros

 # define _ISbit(bit) (1 << (bit)) enum { // some identifiers _ISalpha = _ISbit (2), // some identifiers }; extern const unsigned short int **__ctype_b_loc (void) __THROW __attribute__ ((__const__)); # define __isctype(c, type) \ ((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type) # define isalpha(c) __isctype((c), _ISalpha) 

As you probably see, the result of expanding the isalpha macro still explicitly maps the baz pointer to int . However, this does not give any warnings when compiling. Thus, it is obvious that both code fragments perform the same operation (i.e., drop a char * to int ), but one gives a warning and the other does not. Why?

NOTE. Compilation commands with the same parameters were used to compile programs containing these code fragments.

Compiler Version:

 gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 
+6
source share
2 answers

The first program does not warn with gcc 4.7 , but issues a warning with versions 4.8 .

The second program does not warn, because the macro definition is in the system header. Add -Wsystem-headers to get a warning with the second program.

From gcc documentation (underline mine)

-Wsystem headers

Print warning messages for constructs found in system header files. Warnings from system headers are usually suppressed , suggesting that they usually do not indicate real problems and only make the compiler's output more difficult to read.

+9
source

In the C standard, you can use any standard library function as a macro. The requirements for such a macro definition ( N1570 section 7.1.4):

Any function declared in the header can be further implemented as a functional macro defined in the header, so if the library function is explicitly declared when the header is enabled, one of the methods below can be used to ensure that such a macro is not affected by the declaration. Any macro function definition can be suppressed locally by including the function name in parentheses, because instead the name does not follow the left bracket, which indicates the extension of the macro function name. For the same syntactic reason, it is allowed to take the address of a library function, even if it is also defined as a macro. Using #undef to remove any macro definition will also provide a reference to the actual function. Any inclusion of a library function implemented as a macro expands to a code that accurately evaluates each of its arguments once, completely protected by brackets where necessary, so it is generally safe to use arbitrary expressions as arguments. Similarly, these functionally similar macros, described in the following sections, can be called in an expression anywhere with a function with a compatible return type.

A macro definition for a library function such as isalpha() should work correctly for valid arguments, but it is not necessary to diagnose invalid arguments. Since the definition of a macro is explicitly permitted by the C standard, if the implementation provides such a macro, there is no function call, so the restriction prohibiting the passing of the char* argument to a function that expects an int is not applied.

If there is an actual function call, passing the char* argument to a valid function that expects an int argument is a violation of the constraint that requires diagnostics. There is no implicit conversion from char* to int .

Here is a small program that illustrates the problem:

 #include <ctype.h> int main(void) { char *p = "hello"; isalpha(p); // line 4, possible macro invocation (isalpha)(p); // line 5, actual function call #undef isalpha isalpha(p); // line 7, actual function call } 

And here is the compilation result with gcc -std=c11 -pedantic (gcc 4.8.2):

 cc: In function 'main': cc:5:5: warning: passing argument 1 of 'isalpha' makes integer from pointer without a cast [enabled by default] (isalpha)(p); // line 5, actual function call ^ In file included from cc:1:0: /usr/include/ctype.h:111:1: note: expected 'int' but argument is of type 'char *' __exctype (isalpha); ^ cc:7:5: warning: passing argument 1 of 'isalpha' makes integer from pointer without a cast [enabled by default] isalpha(p); // line 7, actual function call ^ In file included from cc:1:0: /usr/include/ctype.h:111:1: note: expected 'int' but argument is of type 'char *' __exctype (isalpha); ^ 

In line 5, the brackets around isalpha prevent the macro from expanding, exposing itself to the function itself. Line 7 displays the actual function since the macro definition has been deleted.

The actual function call performs an implicit conversion, not a cast; since the implicit conversion from char* to int , the compiler will issue a diagnostic. (This can lead to a fatal warning, but gcc is somewhat weakened about implicit conversions, although the warning does meet the standard requirements.) With the macro, the conversion is performed by an explicit cast operator, which the compiler does not warn about by default.

Note that, depending on the implementation, all three calls may be valid function calls. Macro definitions for standard library functions are optional. The GNU C library provides a macro definition for isalpha , since a macro definition can be significantly more efficient than calling a function (although an inline function can be equally efficient).

+4
source

Source: https://habr.com/ru/post/980375/


All Articles