Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is this implicit conversion (between different pointer types) valid?

I found myself in the following situation:

#include <stdio.h>

typedef struct T1 { int id; } T1;  
typedef struct T2 { int id; } T2;

void f(T1 *ptr) { printf("f called\n"); }

int main(void) 
{
    T2 obj; 
    T2 *ptr = &obj; 
    f(ptr); // shouldn't this be a compilation error ? 
    return 0;
}

of course, this is invalid C++, but in C, the program prints "f called". How is this valid ?

EDIT

(Just in case it's unclear) The program would still compile and run if T2 was "structurally" different, eg

typedef struct T2 { double cc[23]; } T2;
like image 413
Nikos Athanasiou Avatar asked Nov 25 '14 13:11

Nikos Athanasiou


People also ask

What is an implicit conversion?

An implicit conversion sequence is the sequence of conversions required to convert an argument in a function call to the type of the corresponding parameter in a function declaration. The compiler tries to determine an implicit conversion sequence for each argument.

Which of the following types of conversions is implicit in C++?

Type Conversion in C++ Implicit Type Conversion Also known as 'automatic type conversion'. Done by the compiler on its own, without any external trigger from the user.

Can you cast pointers in C++?

reinterpret_cast is a type of casting operator used in C++. It is used to convert a pointer of some data type into a pointer of another data type, even if the data types before and after conversion are different. It does not check if the pointer type and data pointed by the pointer is same or not.

What is incompatible pointer type?

Resolving Incompatible Pointer Types. In ISO C, a pointer to void can be assigned to a pointer of any other type. You do not need to cast the pointer explicitly. C++ allows void pointers to be assigned only to other void pointers.

What is an implicit conversion in C++?

(C++11) Implicit conversions are performed whenever an expression of some type T1 is used in context that does not accept that type, but accepts some other type T2; in particular: when the expression is used as the argument when calling a function that is declared with T2 as parameter;

Can you convert a pointer to a void?

Such conversion (known as null pointer conversion) is allowed to convert to a cv-qualified type as a single conversion, that is, it's not considered a combination of numeric and qualifying conversions. A prvalue pointer to any (optionally cv-qualified) object type T can be converted to a prvalue pointer to (identically cv-qualified) void.

What is a null pointer conversion in C++?

A null pointer constant (see NULL ), can be converted to any pointer type, and the result is the null pointer value of that type. Such conversion (known as null pointer conversion) is allowed to convert to a cv-qualified type as a single conversion, that is, it's not considered a combination of numeric and qualifying conversions.

Can a null pointer be converted to a prvalue?

If the original pointer is a null pointer value, the result is a null pointer value of the destination type. A prvalue pointer to a (optionally cv-qualified) derived class type can be converted to a prvalue pointer to its (identically cv-qualified) base class.


2 Answers

This is not valid, if you want to force standard compliant code it is important to compile with the correct flags for example both gcc and clang the following flags:

-std=c99 -pedantic-errors

will generate an error for diagnostics required by the C99 standard and similarly you could use -std=c11 for C11. This will generate the following error from gcc (see it live):

error: passing argument 1 of 'f' from incompatible pointer type

Compilers have extensions and allow features like implicit int due to legacy code and it is important to know the difference. See gcc document: Language Standards Supported by GCC for more details.

The quick way to see that this is actually invalid is to go to the Rationale for International Standard—Programming Languages—C which tell us in section 6.3.2.3 Pointers which deals with conversions that:

It is invalid to convert a pointer to an object of any type to a pointer to an object of a different type without an explicit cast.

The slightly longer path requires we go to the draft C99 standard section 6.5.2.2 Function calls which says (emphasis mine going forward):

If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment,

and if we then go to section 6.5.16 Assignment operators which says:

One of the following shall hold

and for pointers we have:

  • both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • one operand is a pointer to an object or incomplete type and the other is a pointer to a qualified or unqualified version of void, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;
  • the left operand is a pointer and the right is a null pointer constant;

we see that none of these cases hold and therefore the conversion is invalid.

like image 97
Shafik Yaghmour Avatar answered Oct 19 '22 05:10

Shafik Yaghmour


When compiling, I get the following warning:

temp.c: In function ‘main’:

temp.c:20:5: warning: passing argument 1 of ‘f’ from incompatible pointer type [enabled by default]

temp.c:13:6: note: expected ‘struct T1 *’ but argument is of type ‘struct T2 *’

It's "valid" because they're both pointers, and hence can be converted, it's just not a good idea.

like image 21
slugonamission Avatar answered Oct 19 '22 05:10

slugonamission