Why Does Pointer to Int Convert to Void* But Pointer to Function Convert to Bool

Why does pointer to int convert to void* but pointer to function convert to bool?

Based on the above, it is perfectly OK to convert a function pointer or a pointer to an int to a void* as well as bool.

The quotation states that a pointer to an object can be converted to cv void *. Functions are not objects, and this disqualifies the conversion to cv void *, leaving only bool.


However, given the choice of both, which one should a pointer convert to?

It should convert to const void * over bool. Why? Well, prepare for a journey that starts in Overload Resolution (§13.3 [over.match]/2). Emphasis mine, of course.

But, once the candidate functions and argument lists have been identified, the selection of the best function is the same in all cases:

— First, a subset of the candidate functions (those that have the proper number of arguments and meet
certain other conditions) is selected to form a set of viable functions (13.3.2).

— Then the best viable function is selected based on the implicit conversion sequences (13.3.3.1) needed to match each argument to the corresponding parameter of each viable function.

So what about these implicit conversion sequences?

Let's jump over to §13.3.3.1 [over.best.ics]/3 and see just what an implicit conversion sequence is:

A well-formed implicit conversion sequence is one of the following forms:

— a standard conversion sequence (13.3.3.1.1),

— a user-defined conversion sequence (13.3.3.1.2), or

— an ellipsis conversion sequence (13.3.3.1.3).

We're interested in standard conversions sequences. Let's pop over to Standard Conversion Sequences (§13.3.3.1.1 [over.ics.scs]):

1 Table 12 summarizes the conversions defined in Clause 4 and partitions them into four disjoint categories: Lvalue Transformation, Qualification Adjustment, Promotion, and Conversion. [ Note: These categories are orthogonal with respect to value category, cv-qualification, and data representation: the Lvalue Transformations do not change the cv-qualification or data representation of the type; the Qualification Adjustments do not change the value category or data representation of the type; and the Promotions and Conversions do not change the value category or cv-qualification of the type. — end note ]

2 [ Note: As described in Clause 4, a standard conversion sequence is either the Identity conversion by itself (that is, no conversion) or consists of one to three conversions from the other four categories.

The important part is in /2. A standard conversion sequence is allowed to be a single standard conversion. These standard conversions are listed in Table 12, shown below. Notice that both your Pointer Conversions and Boolean Conversions are in there.

Table of standard conversions and their categories and ranks

From here, we learn something important: Pointer conversions and boolean conversions have the same rank. Remember that as we head to Ranking Implicit Conversion Sequences (§13.3.3.2 [over.ics.rank]).

Looking at /4, we see:

Standard conversion sequences are ordered by their ranks: an Exact Match is a better conversion than a Promotion, which is a better conversion than a Conversion. Two conversion sequences with the same rank are indistinguishable unless one of the following rules applies:

— A conversion that does not convert a pointer, a pointer to member, or std::nullptr_t to bool is
better than one that does.

We've found our answer in the form of a very explicit statement. Hooray!

C++, does bool conversion always fall back to implicit conversion to void*?

If the compiler cannot convert a user-defined type to bool directly, then it tries to do it indirectly, i.e. convert (via an user-defined conversion) to a type that can be converted to bool without involving another user-defined conversion. The list of such types includes (and seems to be limited to) the following types:

  • an integer arithmetic type (char, int, etc)
  • a floating point arithmetic type (float, double, long double)
  • a pointer type (void* belongs here, but it could as well be const std::vector<Something>*)
  • a pointer to function (including a pointer to a member function)
  • a reference type to any of the above

Note however that only one such indirect conversion must exist. If two or more conversions from the above list are possible, then the compiler will face an ambiguity and will report an error.

Bug casting from bool* to void* to int*

static_cast<int*>(ptr)[0] casts ptr to int* and reads the first element. Since the original array is only 2 bytes, you're reading outside it (because you're reading a 4-byte int) and invokes undefined behavior, unless int is a 2-byte type on your system. You're also violating the strict aliasing rule by accessing a type using a different pointer type which also invokes UB. Besides you'll get UB if the bool array isn't properly aligned. On x86 it doesn't cause any problems because x86 allows unaligned access by default but you'll get a segfault on most other architectures

static_cast<int>(test[0]) OTOH converts test[0] (which is a bool) to int and is a completely valid value conversion.


Update:

The type int* refers to a pointer whose object is 4-bytes long, whereas bool* refers to a pointer whose object is 2-bytes long

No. When dereferencing a variable var, an amount of memory of length sizeof(var) will be read from memory starting from that address and treat as the value of that variable. So *bool_ptr will read 1 byte and *int_ptr will read 4 bytes from memory (if bool and int are 1 and 4-byte types respectively)

In your case the bool array contains 2 bytes, so when 4 bytes is read from static_cast<int*>(ptr), 2 byte inside the array and 2 bytes outside the array are read. If you declared bool test[4] = {}; (or more elements) you'll see that the int* dereferencing completes successfully because it reads all 4 bools that belong to you, but you still suffer from the unalignment issue

Now try changing the bool values to nonzero and see

bool test[4] = { true, false, true, false };

You'll quickly realize that casting a pointer to a different pointer type isn't a simple read in the old type and convert to the new type like a simple value conversion (i.e. a cast) but a different "memory treatment". This is essentially just a reinterpret_cast which you can read to understand more about this problem

I don't understand what you are saying about char*. You're saying casting from any type to char* is valid?

Casting from any other pointer types to char* is valid. Read the question about strict aliasing rule above:

You can use char* for aliasing instead of your system's word. The rules allow an exception for char* (including signed char and unsigned char). It's always assumed that char* aliases other types.

It's used for things like memcpy where you copy the bytes representing a type to a different destination

bool test[4] = { true, true, true, true };
int v;
memcpy((char*)&test, (char*)&v, sizeof v);

Technically mempcy receives void*, the cast to char* is just used for demonstration

See also

  • Strict aliasing rule and 'char *' pointers
  • https://en.wikipedia.org/wiki/Pointer_aliasing

Why can I cast int and BOOL to void*, but not float?

BOOL is not a C++ type. It's probably typedef or defined somewhere, and in these cases, it would be the same as int. Windows, for example, has this in Windef.h:

    typedef int                 BOOL;

so your question reduces to, why can you typecast int to void*, but not float to void*?

int to void* is ok but generally not recommended (and some compilers will warn about it) because they are inherently the same in representation. A pointer is basically an integer that points to an address in memory.

float to void* is not ok because the interpretation of the float value and the actual bits representing it are different. For example, if you do:

   float x = 1.0;

what it does is it sets the 32 bit memory to 00 00 80 3f (the actual representation of the float value 1.0 in IEEE single precision). When you cast a float to a void*, the interpretation is ambiguous. Do you mean the pointer that points to location 1 in memory? or do you mean the pointer that points to location 3f800000 (assuming little endian) in memory?

Of course, if you are sure which of the two cases you want, there is always a way to get around the problem. For example:

  void* u = (void*)((int)x);        // first case
void* u = (void*)(((unsigned short*)(&x))[0] | (((unsigned int)((unsigned short*)(&x))[1]) << 16)); // second case

Why function pointer address is printing in bool type in c++?

Function pointers aren't convertible to data pointers. You'd get a compiler error if you were to try and assign one to a void* variable. But they are implicitly convertible to bool!

That is why the bool overload for operator<< is chosen over the const void* one.

To force the overload you want, you'd need to use a very strong C++ cast, that will almost completely ignore the static type information.

#include<iostream>
using namespace std;
int add(int x, int y)
{
int z;
z = x+y;
cout<<"Ans:"<<z<<endl;
}

int main()
{
int a=10, b= 10;
int (*func_ptr) (int,int);
func_ptr = &add;
cout<<"The address of function add()is :"<< reinterpret_cast<void*>(func_ptr) <<endl;
(*func_ptr) (a,b);
}

Note that casting and treating function pointers as data pointers is only conditionally supported (from the C++ standard standpoint). Using it for anything other than casting back to the same function pointer will have an implementation specific outcome, that can very greatly among compilers.

Is failure to implicitly convert a pointer to _Bool a compiler deficiency?

Yes, this is a compiler bug. While C has no implicit conversions in either direction between integer types and pointer types, nor implicit conversions between pointer types except in special cases like to/from pointer-to-void or from pointer-to-unqualified to pointer-to-qualified, it does define an implicit conversion to _Bool. (Traditionally many compilers supported such implicit conversions in other places, but doing so was harmful and was not part of the C language.)

The language about implicit conversion is under 6.5.16.1 Simple assignment:

One of the following shall hold:

  • the left operand has atomic, qualified, or unqualified arithmetic type, and the right has arithmetic type;
  • the left operand has an atomic, qualified, or unqualified version of a structure or union type compatible with the type of the right;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) 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;
  • the left operand has atomic, qualified, or unqualified pointer type, and (considering the type the left operand would have after lvalue conversion) one operand is a pointer to an object 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 an atomic, qualified, or unqualified pointer, and the right is a null pointer constant; or
  • the left operand has type atomic, qualified, or unqualified _Bool, and the right is a pointer.

In other places in the standard where implicit conversion appears, it's specified "as if by assignment", referring to the above. For example, in 6.5.2.2 Function calls:

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, ...

Note that your question is actually about initialization, which is not assignment in C but something different. However, 6.7.9 Initialization ¶11 covers it:

The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.

How can converting a pointer to `void*` twice be invalid?

The thing you're asking about was a contrived example, meant to illustrate a corner case of the rules of C, and the thing that's invalid isn't the double cast, it's the assignment.

If you have any pointer-to-function type, such as

typedef void (*fp)(void);

you can initialize variables of this type with any valid null pointer constant, e.g.

fp a = 0;          // canonical null pointer constant in C
fp b = (void *)0; // another common choice
fp c = '\0'; // yes, really, this is a null pointer constant
fp d = (1-1); // and so is this

But you can't initialize them with a pointer to any specific type other than fp itself, even if that pointer is a null pointer, and even if the specific type in question is void *.

char *x = 0;
void *y = 0;
fp e = x; // invalid: no assignment conversion from `char *` to `fp`
fp f = y; // invalid: no assignment conversion from `void *` to `fp`

The line from my old answer that you were confused by,

fp g = (void *)(void *)0;

is essentially the same as fp f = y above. The right-hand side of both assignments is a null pointer with type void *, but not a null pointer constant, so the assignment is invalid.

You are probably now wondering why (void *)(void *)0 isn't a null pointer constant, even though (void *)0 is a null pointer constant. This is just the way the C standard happens to be written: a null pointer constant is defined as any integer constant expression with value 0, possibly with one cast to void * in front of it. Any extra casts and it's no longer a null pointer constant (but is still a constant expression). (Integer constant expressions cannot contain internal casts to pointer types.)

In your contrasting example

int a;
int *pa = (int *)&a;

no null pointers are involved, and no integer constant expressions either, and the cast really is 100% redundant. &a could be a constant expression (if and only if a has static storage duration), but it is not an integer constant expression.

Why does std::cout convert volatile pointers to bool?

ostream::operator<< has the following overloads, among others:

ostream& operator<< (bool val );
ostream& operator<< (const void* val );

When you pass in a volatile pointer, the second overload can't apply because volatile pointers cannot be converted to non-volatile without an explicit cast. However, any pointer can be converted to bool, so the first overload is chosen, and the result you see is 1 or 0.

So the real reason for this is not an intentional decision on behalf of the standards committe, but simply that the standard does not specify an overload that takes a volatile pointer.

Casting object pointer to double void pointer (pointer to pointer to void)

For all intents and purposes a void pointer can hold addresses of any object (data type), i.e. it can point to any object, and can be typecasted to any object, your code is valid, I would just use a more idiomatic cast:

Q->pop(reinterpret_cast<void**>(&buf), size, sn);

§7.3.12 Pointer conversions [conv.ptr]


  1. A prvalue of type “pointer to cv T”, where T is an object type, can be converted to a prvalue of type “pointer to cv void”. The pointer value (6.8.3) is unchanged by this conversion.

Example:

void example(void **packet){
std::cout << *packet << "\n"; // pointer value
std::cout << packet << "\n"; // pointer address
std::cout << **reinterpret_cast<int**>(packet); // value
}

int main()
{
int* x = new int(20);
std::cout << x << "\n"; // pointer value
std::cout << &x << "\n"; // pointer address
example(reinterpret_cast<void**>(&x));
delete x;
}

Output:

0xb83eb0
0x7ffc181ab2c8
0xb83eb0
0x7ffc181ab2c8
20

The explicit cast is even only needed because it's a pointer to pointer otherwise the conversion would be implicit, no cast would be needed.

bool vs void* casts on the same object

In an if statement, implicit and explicit conversion operators are considered. Because A has an operator bool, it chooses that one, as it is a better match than converting A to a void* and then converting that to bool.

But in every other statement, which are not conditions (if, while, ...), the explicit conversion operators do not participate in overload resolution, and the only valid operator then is operator void*, which can be used because there is an implicit conversion from pointers to bool.

If you want operator bool to be selected, you need make it non-explicit, or use a cast (because that's what marking it explicit means, making sure that one has to be explicit to use it):

f(static_cast<bool>(a));


Related Topics



Leave a reply



Submit