Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why do I not get a warning for casting a pointer to an int?

The following piece of code gives a warning for casting from a pointer to an int which makes a 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 warnings (regardless of the fact that it may 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 can probably see, the result of expanding the isalpha macro still explicitly casts the pointer baz to an int. However, this does not give any warnings when compiled. So apparently both pieces of code perform the same operation (i.e., casting a char * to an int), yet one gives a warning and the other does not. Why?

NOTE: compilation commands with the same options were used to compile the programs that contained these pieces of code.

Compiler version:

gcc (Ubuntu 4.8.2-19ubuntu1) 4.8.2
like image 718
kzidane Avatar asked Dec 31 '14 16:12

kzidane


People also ask

Can I cast a pointer to an integer?

An object pointer (including void* ) or function pointer can be converted to an integer type using reinterpret_cast . This will only compile if the destination type is long enough. The result is implementation-defined and typically yields the numeric address of the byte in memory that the pointer pointers to.

What happens when you cast a pointer?

A pointer is an arrow that points to an address in memory, with a label indicating the type of the value. The address indicates where to look and the type indicates what to take. Casting the pointer changes the label on the arrow but not where the arrow points.

Is it possible to cast a pointer to float as a pointer to integer?

int pointer can not be cast to float pointer unless you know what you are doing exactly. pointer is a address, and int float are stored in memory in different format. so you can not cast it.

Can I cast a pointer?

You can cast a pointer to another pointer of the same IBM® i pointer type. Note: If the ILE C compiler detects a type mismatch in an expression, a compile time error occurs. An open (void) pointer can hold a pointer of any type.


2 Answers

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

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

From gcc documentation (emphasize mine)

-Wsystem-headers

Print warning messages for constructs found in system header files. Warnings from system headers are normally suppressed, on the assumption that they usually do not indicate real problems and would only make the compiler output harder to read.

like image 74
ouah Avatar answered Sep 21 '22 20:09

ouah


The C standard permits any standard library function to be additionally implemented as a macro. The requirements for such a macro definition (N1570 section 7.1.4) are:

Any function declared in a header may be additionally implemented as a function-like macro defined in the header, so if a library function is declared explicitly when its header is included, one of the techniques shown below can be used to ensure the declaration is not affected by such a macro. Any macro definition of a function can be suppressed locally by enclosing the name of the function in parentheses, because the name is then not followed by the left parenthesis that indicates expansion of a macro function name. For the same syntactic reason, it is permitted to take the address of a library function even if it is also defined as a macro. The use of #undef to remove any macro definition will also ensure that an actual function is referred to. Any inv ocation of a library function that is implemented as a macro shall expand to code that evaluates each of its arguments exactly once, fully protected by parentheses where necessary, so it is generally safe to use arbitrary expressions as arguments. Likewise, those function-like macros described in the following subclauses may be invoked in an expression anywhere a function with a compatible return type could be called.

The macro definition for a library function such as isalpha() must work correctly for correct arguments, but it's not required to diagnose incorrect arguments. Since the macro definition is explicitly permitted by the C standard, if the implementation provides such a macro then there is no function call, and so the constraint that prohibits passing a char* argument to a function expecting an int does not apply.

If there's an actual function call, then passing a char* argument to an actual function expecting an int argument is a constraint violation, requiring a diagnostic. There is no implicit conversion from char* to int.

Here's a small program that illustrates the issue:

#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's the result of compiling it with gcc -std=c11 -pedantic (gcc 4.8.2):

c.c: In function ‘main’:
c.c: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 c.c:1:0:
/usr/include/ctype.h:111:1: note: expected ‘int’ but argument is of type ‘char *’
 __exctype (isalpha);
 ^
c.c: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 c.c:1:0:
/usr/include/ctype.h:111:1: note: expected ‘int’ but argument is of type ‘char *’
 __exctype (isalpha);
 ^

On line 5, the parentheses around isalpha prevent the macro expansion, exposing the function itself. On line 7, the actual function is exposed because the macro definition has been removed.

An actual function call performs an implicit conversion, not a cast; since there is no implicit conversion from char* to int, the compiler issues a diagnostic. (It could issue a fatal warning, but gcc is somewhat lax about implicit conversions, though a warning does satisfy the standard's requirements.) With the macro, the conversion is performed by an explicit cast operator, which the compiler doesn't warn about by default.

Note that, depending on the implementation, all three calls could be actual function calls. Macro definitions for standard library functions are optional. The GNU C library provides a macro definition for isalpha because the macro definition can be substantially more efficient than a function call (though an inline function might be equally efficient).

like image 44
Keith Thompson Avatar answered Sep 19 '22 20:09

Keith Thompson