I can't find a warning for the following in Visual Studio. I turned on /Wall but still get nothing:
const char * pointer = '\0';
gcc won't compile it for C++11, C++14, or C++17:
[x86-64 gcc 7.2 #1] error: invalid conversion from 'char' to 'const char*' [-fpermissive]
gcc will compile with the above as a warning if I pass -fpermissive:
[x86-64 gcc 7.2 #1] warning: invalid conversion from 'char' to 'const char*' [-fpermissive]
clang won't compile for C++11, C++14, or C++17:
[x86-64 clang 5.0.0 #1] error: cannot initialize a variable of type 'const char *' with an rvalue of type 'char'
I'm asking because of the below code that ended up in our codebase, apparently with no warnings:
std::ofstream file;
//...
file.write('\0', 20);
Is there a way to turn on a warning for this in Visual Studio?
Visual Studio 2015 only allows this conversion with const values of '\0'. Examples:
char c = '\0';
const char cconst = '\0';
const char * p1 = c; //error (not even warning)
const char * p2 = cconst; //ok
const char * p3 = '\0'; //ok
const char * p4 = '\1'; //error (not even warning)
The specific error is:
Error: a value of type "char" cannot be used to initialize an entity of type "const char *"
Apple LLVM 8.1.0 (clang-802.0.41) gives a warning with C++03 but an error with C++11 and later. The behavior was changed sometime between the Feb. 28, 2011 (draft N3242 and May 15, 2013 (draft N3690). I can't find the exact point.
In earlier drafts of C++, such as n1905, the OP code is defined as a valid conversion:
A null pointer constant is an integral constant expression (5.19) rvalue of integer type that evaluates to zero. A null
pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable
from every other value of pointer to object or pointer to function type. Two null pointer values of the same type shall
compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not
the sequence of a pointer conversion followed by a qualification conversion (4.4).
Section 3.9.1.2 defines the signed integer types:
There are five signed integer types: “signed char”, “short int”, “int”, “long int”, and “long long int”.
This was changed in later drafts. In draft N3690 from 2013, section 4.10 says:
A null pointer constant is an integer literal (2.14.2) with value zero or a prvalue of type std::nullptr_t.
A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type
and is distinguishable from every other value of object pointer or function pointer type. Such a conversion
is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The
conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the
sequence of a pointer conversion followed by a qualification conversion (4.4).
character-literal is a defined as a literal in section 2.14.1, but it does not appear in section 2.14.2. Instead it gets its own section - 2.14.3.
C++17 draft n4659 has the exact same verbiage but in different sections.
I don't see a way to give a warning for this in VS 2015. This would be another reason to run static analysis tools / other compilers to catch extra warnings.
Thanks to #EricPostpischil for the help.
Related
In C one can have string literals in the form of
char *string = "string here";
integer literals:
uint8_t num = 5;
long literals:
long long bigNum = 90322L;
floating point literals:
float decimal = 6.3f;
Is the a way to have a pointer literal? That is a literal address to a memory space. I am doing some work on an embedded project and need to hard code a value for a DMA access. I am doing something similar to the following:
uint32_t *source = 0x08000000;
While this compiles and works correctly I get the following compiler error (I'm using a variant of GCC):
cc0144: {D} warning: a value of type "int" cannot be used to initialize an entity of type "uint32_t *"
cc0152: {D} warning: conversion of nonzero integer to pointer
Is there a correct way to do this or do I just need to accept this as a fact of C? I know I can do:
uint32_t *source = (uint32_t *)0x08000000;
But that just seems very unnecessary. What is the industry way of doing this? I am also wondering if this feature exists in C++.
In both C and C++ the only pointer literal or constant is zero. We can go to the draft C99 standard section 6.3.2.3 Pointers:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55)
and:
An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be
correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.56)
the correct way to deal with non-zero integer constant is to use a cast.
The equivalent section from the draft C++ standard would probably be section 5.2.10 Reinterpret cast which says:
A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined. [ Note:
Except as described in 3.7.4.3, the result of such a conversion will
not be a safely-derived pointer value. —end note ]
You need to see section 3.7.4.3 for all the details.
For the pointer literal reference you need section 2.14.7 Pointer literals which says:
The pointer literal is the keyword nullptr. It is a prvalue of type
std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is
neither a pointer type nor a pointer to member type; rather, a prvalue
of this type is a null pointer constant and can be converted to a null
pointer value or null member pointer value. See 4.10 and 4.11. —end
note ]
No, it's not. That is because literals are valid values, and the only valid pointers are addresses of objects, i.e. the result of address-of operations or of pointer arithmetic on valid pointers.
You could argue that the nullptr keyword furnishes a kind of "pointer literal"; the C++ standard calls it that. It is however the only pointer literal, and ironically it is not of pointer type.
In C++ could you do something like the following?
uint32_t * source = reinterpret_cast<uint32_t*>(0x08000000);
In C one can have string literals in the form of
char *string = "string here";
integer literals:
uint8_t num = 5;
long literals:
long long bigNum = 90322L;
floating point literals:
float decimal = 6.3f;
Is the a way to have a pointer literal? That is a literal address to a memory space. I am doing some work on an embedded project and need to hard code a value for a DMA access. I am doing something similar to the following:
uint32_t *source = 0x08000000;
While this compiles and works correctly I get the following compiler error (I'm using a variant of GCC):
cc0144: {D} warning: a value of type "int" cannot be used to initialize an entity of type "uint32_t *"
cc0152: {D} warning: conversion of nonzero integer to pointer
Is there a correct way to do this or do I just need to accept this as a fact of C? I know I can do:
uint32_t *source = (uint32_t *)0x08000000;
But that just seems very unnecessary. What is the industry way of doing this? I am also wondering if this feature exists in C++.
In both C and C++ the only pointer literal or constant is zero. We can go to the draft C99 standard section 6.3.2.3 Pointers:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55)
and:
An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be
correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.56)
the correct way to deal with non-zero integer constant is to use a cast.
The equivalent section from the draft C++ standard would probably be section 5.2.10 Reinterpret cast which says:
A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined. [ Note:
Except as described in 3.7.4.3, the result of such a conversion will
not be a safely-derived pointer value. —end note ]
You need to see section 3.7.4.3 for all the details.
For the pointer literal reference you need section 2.14.7 Pointer literals which says:
The pointer literal is the keyword nullptr. It is a prvalue of type
std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is
neither a pointer type nor a pointer to member type; rather, a prvalue
of this type is a null pointer constant and can be converted to a null
pointer value or null member pointer value. See 4.10 and 4.11. —end
note ]
No, it's not. That is because literals are valid values, and the only valid pointers are addresses of objects, i.e. the result of address-of operations or of pointer arithmetic on valid pointers.
You could argue that the nullptr keyword furnishes a kind of "pointer literal"; the C++ standard calls it that. It is however the only pointer literal, and ironically it is not of pointer type.
In C++ could you do something like the following?
uint32_t * source = reinterpret_cast<uint32_t*>(0x08000000);
Is comparing a pointer to '\0' legal?
On the trunk version of clang++ (25836be2c)
const char *a = "foo";
if(a == '\0')
gives an error: comparison between pointer and integer ('const char *' and 'int')
whereas
if(a == 0)
does not give any error as expected.
Isn't the null character equivalent to the null pointer for comparisons with pointer? Is this a compiler bug?
Another point is that this error does not show up with "-std=c++03" flag but shows up with "-std=c++11" flag. However, I don't get the error in both standards when I use g++ (v4.8.5)
This was a change from C++03 to C++14. In C++03, [conv.ptr]p1 says:
A null pointer constant is an integral constant expression rvalue of integer type that evaluates to zero.
A character literal is an integral constant expression.
In C++14, [conv.ptr]p1 says:
A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t.
A character literal is not an integer literal, nor of type std::nullptr_t.
The originally published version of C++11 didn't contain this change; however, it was introduced due to defect report DR903 and incorporated into the standard sometime after January 2013 (the date of the last comment on that DR).
Because the change is the result of a DR, compilers treat it as a bugfix to the existing standard, not part of the next one, and so Clang and GCC both made the behavior change when -std=c++11, not just when -std=c++14. However, apparently this change wasn't implemented in GCC until after version 4.8. (Specifically, it seems to have only been implemented in GCC 7 and up.)
From [conv.ptr]§1:
A null pointer constant is an integer literal with value zero or a prvalue of type std::nullptr_t. [...]
'\0' is not an integer literal, it's a character literal, thus the conversion does not apply.
In C one can have string literals in the form of
char *string = "string here";
integer literals:
uint8_t num = 5;
long literals:
long long bigNum = 90322L;
floating point literals:
float decimal = 6.3f;
Is the a way to have a pointer literal? That is a literal address to a memory space. I am doing some work on an embedded project and need to hard code a value for a DMA access. I am doing something similar to the following:
uint32_t *source = 0x08000000;
While this compiles and works correctly I get the following compiler error (I'm using a variant of GCC):
cc0144: {D} warning: a value of type "int" cannot be used to initialize an entity of type "uint32_t *"
cc0152: {D} warning: conversion of nonzero integer to pointer
Is there a correct way to do this or do I just need to accept this as a fact of C? I know I can do:
uint32_t *source = (uint32_t *)0x08000000;
But that just seems very unnecessary. What is the industry way of doing this? I am also wondering if this feature exists in C++.
In both C and C++ the only pointer literal or constant is zero. We can go to the draft C99 standard section 6.3.2.3 Pointers:
An integer constant expression with the value 0, or such an expression
cast to type void *, is called a null pointer constant.55)
and:
An integer may be converted to any pointer type. Except as previously
specified, the result is implementation-defined, might not be
correctly aligned, might not point to an entity of the referenced
type, and might be a trap representation.56)
the correct way to deal with non-zero integer constant is to use a cast.
The equivalent section from the draft C++ standard would probably be section 5.2.10 Reinterpret cast which says:
A value of integral type or enumeration type can be explicitly
converted to a pointer. A pointer converted to an integer of
sufficient size (if any such exists on the implementation) and back to
the same pointer type will have its original value; mappings between
pointers and integers are otherwise implementation-defined. [ Note:
Except as described in 3.7.4.3, the result of such a conversion will
not be a safely-derived pointer value. —end note ]
You need to see section 3.7.4.3 for all the details.
For the pointer literal reference you need section 2.14.7 Pointer literals which says:
The pointer literal is the keyword nullptr. It is a prvalue of type
std::nullptr_t. [ Note: std::nullptr_t is a distinct type that is
neither a pointer type nor a pointer to member type; rather, a prvalue
of this type is a null pointer constant and can be converted to a null
pointer value or null member pointer value. See 4.10 and 4.11. —end
note ]
No, it's not. That is because literals are valid values, and the only valid pointers are addresses of objects, i.e. the result of address-of operations or of pointer arithmetic on valid pointers.
You could argue that the nullptr keyword furnishes a kind of "pointer literal"; the C++ standard calls it that. It is however the only pointer literal, and ironically it is not of pointer type.
In C++ could you do something like the following?
uint32_t * source = reinterpret_cast<uint32_t*>(0x08000000);
Here is a code -
1 int main(int argc, char *argv[])
2 {
3 signed char S, *psc;
4 unsigned char U, *pusc;
5 char C, *pc;
6
7 C = S;
8 C = U;
9
10 pc = psc;
11 pc = pusc;
12
13 return 0;
14 }
$ gcc test.cpp -o a
test.cpp: In function ‘int main(int, char**)’:
test.cpp:10:7: error: invalid conversion from ‘signed char*’ to ‘char*’ [-fpermissive]
test.cpp:11:7: error: invalid conversion from ‘unsigned char*’ to ‘char*’ [-fpermissive]
This is compiled on gcc version 4.6.3 on Ubuntu 12.10 on an Intel 32-bit machine.
Considering that char type is unsigned char on x86. -
If assignments on line 7 and 8 for non-pointer types are Ok, why errors are thrown for pointer types on lines 10 and 11 ?
Also, should C = U succeeds without requiring a cast?
First of all, it is important to stress the fact that char, signed char, and unsigned char are all different types. Section 4.10 of the C++11 Standard defines the three possible standard pointer conversions between pointers of different types:
1 . A null pointer constant is an integral constant expression (5.19) prvalue of integer type that evaluates to zero or a prvalue of type std::nullptr_t. A null pointer constant can be converted to a pointer type; the result is the null pointer value of that type and is distinguishable from every other value of object pointer or function pointer type. Such a conversion is called a null pointer conversion. Two null pointer values of the same type shall compare equal. The conversion of a null pointer constant to a pointer to cv-qualified type is a single conversion, and not the sequence of a pointer conversion followed by a qualification conversion (4.4). A null pointer constant of integral type can be converted to a prvalue of type std::nullptr_t. [ Note: The resulting prvalue is not a null pointer value. —end note ]
This is not relevant, since we don't have null pointers of type nulltptr_t here.
2 . 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 result of converting a “pointer to cv T” to a “pointer to cv void” points to the start of the storage location where the object of type T resides, as if the object is a most derived object (1.8) of type T (that is, not a base class subobject). The null pointer value is converted to the null pointer value of the
destination type.
This cannot apply, since the destination type is not void. Finally,
3 . A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object. The null pointer value is converted to the
null pointer value of the destination type.
signed char is not a base class of char, so not even this applies.
Therefore, an implicit, standard pointer conversion from signed char to char cannot be performed.
On the other hand, conversions between values of integral types are permitted according to what specified in Paragraph 4.7.
C++ don't have automatic pointer conversion, it does not matter what are the pointer types on each side of the assignment, if they are different, you need a cast.
char is a distinct type from unsigned char and signed char. It is only guaranteed to have equivalent value representation to one of them, but it is still a distinct type. You therefore cannot convert from either unsigned char* or signed char* to char* (that is, unless you use a reinterpret_cast). C++ just doesn't allow pointer conversions between distinct types like this, because then one type could masquerade as another.
However, a conversion from either unsigned char or signed char to char is perfectly fine because it just involves a conversion of its value.
Consider it this way: you can convert an int to a float, but you can't convert an int* to a float*.
I could be wrong, but as said above, when you assigned "C = S; C = U;", C++ automatically converts it, kinda like if you do "char x = "h"; printf("%i", x);". However, pointers point to a specific location in memory, and that location has a size. So while converting sort of just looks at the values from a different angles, pointing to different values may involve changing the size of the value that is being pointed at.