Why does C ++ allow me to assign const char to const char * constant ?!

To my surprise, this compiles:

const char* c_str() { static const char nullchar = '\0'; return nullchar; } 

and he introduced an error in my code. Fortunately, I caught it.

Is this intentional C ++ or a compiler error? Is there a reason why the data type is actively ignored?
It worked in Visual C ++ 2010 and GCC , but I don't understand why it should work, given the obvious data type mismatch. ( static also not required.)

+70
c ++ type-safety implicit-conversion
Aug 19 2018-12-12T00:
source share
8 answers

As you defined it, nullchar is an integer constant expression with a value of 0.

The C ++ 03 standard defines the null pointer constant as: "The null pointer constant is an integral constant expression (5.19) of the rvalue of an integer type that evaluates to zero." In short, your nullchar is a null pointer constant, i.e. it can be implicitly converted and assigned to almost any pointer.

Please note that all of these elements are necessary for implicit conversion to work. For example, if you used '\1' instead of '\0' , or if you did not specify a const qualifier for nullchar , you would not receive an implicit conversion - your assignment would fail.

The inclusion of this conversion is intentional, but commonly known as undesirable. 0 as a constant of a null pointer, inherited from C. I am pretty sure that Bjarn and most of the other members of the C ++ standard committee (and most of the C ++ community as a whole) would very much like to remove this particular implicit conversion, but it would destroy compatibility with a lot of C code (possibly close to it).

+69
Aug 19 2018-12-12T00:
source share
β€” -

This is an old story: it goes back to C.

There is no null keyword in C. null pointer constant in C:

  • integral constant expression with value 0, for example 0 , 0L , '\0' (remember that char is an integral type), (2-4/2)
  • an expression other than void* , for example (void*)0 , (void*)0L , (void*)'\0' , (void*)(2-4/2)

Mask null (and not a keyword!) expands to such a null pointer constant.

In the first C ++ construct, only a constant constant expression was allowed as a null pointer constant. Recently, std::nullptr_t was added in C ++.

In C ++, but not in C, a variable a const integral type, initialized by an integral constant expression, is an integral constant expression:

 const int c = 3; int i; switch(i) { case c: // valid C++ // but invalid C! } 

So, const char , initialized by the expression '\0' , is a null pointer constant:

 int zero() { return 0; } void foo() { const char k0 = '\0', k1 = 1, c = zero(); int *pi; pi = k0; // OK (constant expression, value 0) pi = k1; // error (value 1) pi = c; // error (not a constant expression) } 

And you think this is not a sound design?




Updated to include relevant parts of C99 ... According to Β§6.6.6 ...

An integer constant expression must be an integer type and must have only operands that are integer constants, enumeration constants, character constants, sizeof expressions, the results of which are integer constants and floating constants, which are the direct operands of castings. Translation operators in an integer constant expression must convert arithmetic types to integer types, except that part of the operand refers to the sizeof operator.

Some clarifications for C ++ are only programmers:

  • C uses the term "constant" for what C ++ programmers know as a "literal".
  • In C ++, sizeof always a compile-time constant; but C has variable length arrays, so sizeof sometimes not a compile-time constant.

Then we see that in Β§6.3.2.3.3 the states ...

An integer constant expression with a value of 0 or such an expression cast for type void * is called a null pointer constant. If the constant of the null pointer is converted to a type of pointer, the resulting pointer, called the null pointer, is guaranteed to compare unevenly with a pointer to any object or function.




To find out how old this functionality is, see identical mirror parts in the C99 standard ...

Β§6.6.6

An integer constant expression must be an integer type and must have only operands that are integer constants, enumeration constants, symbolic constants, sizeof expressions, the results of which are integer constants, and floating constants, which are the direct operands of castings. Cast statements in an integer constant expression must convert only arithmetic types to integer types, except that they are part of the operand for the sizeof operator.

Β§6.3.2.3.3

An integer constant expression with a value of 0 or such an expression cast for type void * is called a null pointer constant. If the constant of the null pointer is converted to a type of pointer, the resulting pointer, called the null pointer, is guaranteed to compare unevenly with a pointer to any object or function.

+26
Aug 19 2018-12-12T00:
source share

nullchar is a constant expression (compile-time-) with a value of 0. Thus, this is a fair game for implicit conversion to a null pointer.

In more detail: here I quote a draft of the project .

char is an integral type. nullchar is a constant, therefore it is an integral constant expression (compilation time) according to section 5.19.1:

5.19 Constant Expressions [expr.const]

1 In several places in C ++, expressions are needed that are evaluated using an interagral or enumeration constant ... An integral constant expression may include ... constant variables ...

In addition, nullchar evaluates to 0, allowing it to implicitly convert the pointer in accordance with section 4.10.1:

4.10 Pointer Conversions [conv.ptr]

1 The integral constant expression (expr.const) rvalue of an integer type that evaluates to zero (called the constant of a null pointer) can be the vertical type of a pointer.

Perhaps the intuitive reason why this can be allowed (only from my head) is because the width of the pointer is not specified, and therefore conversion from a constant integral expression of any size to a null pointer is allowed.




Updated with relevant parts of the (newer) C ++ 03 standard ... According to Β§5.19.1 ...

An integral constant expression can include only literals (2.13), counters, const variables or static data members of integral or enumerated types, initialized with constant expressions (8.5), asymmetric template parameters of integral or enumerated types, and sizeof .

Then we look at Β§4.10.1 ...

The null pointer constant is an integral constant expression (5.19) of the rvalue of an integer type that evaluates to zero. The null pointer constant can be converted to a pointer type; the result is a null value of a pointer of this type and is different from any other value of a pointer to an object or a pointer to a function type. Two null pointer values ​​of the same type are compared equal.

+14
Aug 19 2018-12-12T00:
source share

It compiles for the same reason as compilation

 const char *p = 0; // OK const int i = 0; double *q = i; // OK const short s = 0; long *r = s; // OK 

The expressions on the right are of type int and short , and the initialized object is a pointer. Does it surprise you?

In C ++ (as in C), integral constant expressions (ICE) with a value of 0 have a special status (although ICEs are defined differently in C and C ++). They qualify as null pointer constants. When used in pointer contexts, they are implicitly converted to null pointers of the appropriate type.

The char type is an integral type, not much different from int in this context, therefore the const char object initialized to 0 is also a null pointer constant in C ++ (but not in C).

BTW, the bool type in C ++ is also an integral type, which means that the const bool object initialized to false is also a null pointer constant

 const bool b = false; float *t = b; // OK 
+9
Aug 19 '12 at 7:05
source share

It does not ignore the data type. It's not a mistake. It takes advantage of the const you put in there, and assuming that its value is actually an integer of 0 (char is an integer type).

The integer 0 is a real (by definition) constant of a null pointer, which can be converted to a type of pointer (becomes a null pointer).

The reasons why you need a null pointer should have some pointer value that "points to nowhere" and can be checked (that is, you can compare the null pointer to the integer 0 and you will return instead).

If you release const, you will get an error message. If you put double there (as in many other non-integer types, I think that exceptions are only types that can be converted to const char * [via overloading conversion operators]), you will get an error (even w / o const ) And so on.

The thing is that in this case your implementation sees that you are returning the constant null ptr; which you can convert to a pointer type.

+6
Aug 19 2018-12-12T00:
source share

The answers to this question seem to have a lot of answer to this question. Summarizing:

  • The C ++ standard allows us to consider variables of the t20 type of the integral type as "integral constant expressions". What for? It is possible to get around the problem that C allows only macros and enumerations to hold the place of an integral constant expression.

  • Returning (at least) to C89, an integral constant expression with a value of 0 is implicitly converted to (any type) a null pointer. And this is often used in C code, where NULL pretty often (void*)0 like (void*)0 .

  • Returning to K & R, the literal value 0 used to represent null pointers. This convention is used universally with code such as:

     if ((ptr=malloc(...)) {...} else {/* error */} 
+5
Aug 19 2018-12-12T00:
source share

there is a drive. if you run this program well:

 #include <stdio.h> const char* c_str() { static const char nullchar = '\0'; return nullchar; } int main() { printf("%d" , sizeof(c_str())); return 0; } 

remote output should be 4 on my computer -> pointer size.

compiler auto select. notification, at least gcc gives a warning (I don't know about VS)

+2
Aug 19 2018-12-12T00:
source share

I think this may be the fact that the null character is common to types. What you do is set the null pointer when you return the null character. This will not work if any other character was used, because you are not passing the address of the character to the pointer, but the value of the character. Null is a valid pointer and character value, so a null character can be set as a pointer.

In short, null can be used by any type to set an empty value, regardless of whether it is an array, pointer, or variable.

+2
Aug 19 '12 at 6:40
source share



All Articles