When compiling the following code with gcc 4.7 (g++-mp-4.7 (GCC) 4.7.0 built with MacPorts on OS X) I get seemingly contradictory results.
The compiler does not complain when I try to reinterpret and dereference a section of an std::array as an uint32_t but it does when using a C-style array.
Example code:
#include <array>
#include <cstdint>
int main() {
std::array<uint8_t, 6> stdarr;
*reinterpret_cast<uint32_t*>(&stdarr[0]) = 0; // OK
uint8_t arr[6];
*reinterpret_cast<uint32_t*>(&arr[0]) = 0;
// ^ error: dereferencing type-punned pointer will break strict-aliasing rules [-Werror=strict-aliasing]
}
Compiler command is:
$ g++ -o test -std=c++0x -Wall -Wextra -Werror main.cpp
Why are they treated differently?
When taking the address of the std::array, the expression arr[0] is equivalent to the function call arr.operator[](0) which returns a reference, rather than the pointer arithmetic expression (arr + 0). Perhaps the compiler does not attempt to "see through" the operator[] function call when generating aliasing warnings.
Related
My problem seems somewhat similar to Conversion from null-integer to pointer in comma list
Here is a minimal example
#include <utility>
struct Object {
double foo;
};
std::pair<Object*,int> test_function() {
typedef std::pair<Object*,int> pair_t;
return pair_t(NULL, 2);
}
// test.cc
With gcc 4.4.7, g++ -std=c++0x -c test.cc -o test.o fails with
/usr/lib/gcc/x86_64-redhat-linux/4.4.7/../../../../include/c++/4.4.7/bits/stl_pair.h:90: error: invalid conversion from \u2018long int\u2019 to \u2018Object*
It does compile if I omit -std=c++0x. Also, if I use newer versions of gcc, the compilation is fine. Is there anything wrong with the code? Should one expect such differences in gcc versions?
A later edit: I can also return pair_t((Object*)NULL, 2)
I believe the answer to your question is here:
C++ can't initialize a pointer in a pair to NULL
The NULL is represented as a long by gcc and it cannot be converted to type Object* without the explicit cast.
I'm confused by the strict aliasing rules when it comes to casting a char array to other types. I know that it is permitted to cast any object to a char array, but I'm not sure what happens the other way around.
Take a look at this:
#include <type_traits>
using namespace std;
struct{
alignas (int) char buf[sizeof(int)]; //correct?
} buf1;
alignas(int) char buf2[sizeof(int)]; //incorrect?
struct{
float f; //obviously incorrect
} buf3;
typename std::aligned_storage<sizeof(int), alignof(int)>::type buf4; //obviously correct
int main()
{
reinterpret_cast<int&>(buf1) = 1;
*reinterpret_cast<int*>(buf2) = 1;
reinterpret_cast<int&>(buf3) = 1;
reinterpret_cast<int&>(buf4) = 1;
}
Compiling using g++-5.3.0 results in warnings only on the second and third line of main:
$ g++ -fsyntax-only -O3 -std=c++14 -Wall main.cpp
main.cpp: In function ‘int main()’:
main.cpp:25:30: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
*reinterpret_cast<int*>(buf2) = 1;
^
main.cpp:26:29: warning: dereferencing type-punned pointer will break strict-aliasing rules [-Wstrict-aliasing]
reinterpret_cast<int&>(buf3) = 1;
^
Is gcc correct in that lines 1 and 4 are correct, while lines 2 and 3 are not? I'm fairly sure line 4 is correct (that's what aligned_storage is for), but what are the rules at play here?
First of all, absence of warning is not a guarantee of correctness! gcc is getting better and better at spotting problematic code, but it is still not a static analyzing tool (and those are not perfect either!)
Second of all, yes, you are not allowed to access char array through a pointer to other type.
I seem to not be getting sign compare errors on my g++ gcc version 5.1.1 20150618 (Red Hat 5.1.1-4) (GCC).
When I compile the following with the options I don't get an error - but when I get rid of the const - it shows the warning.
Question: Is this expected behavior?
g++ typecompat.cxx -Wsign-compare -std=c++14
This is the code
#include <stddef.h>
#include <stdio.h>
#include <assert.h>
#include <vector>
int main() {
std::vector<int> v {1,2,3};
const int b = 2;
assert(v.size()>b);
return 0;
}
From a godbolt session of this code it looks like gcc is performing constant folding:
cmpq $2, %rax
So then it knows for sure the comparison is ok, any attempting to change a constant variable is undefined behavior and the compiler will assume no undefined behavior. If we look at the documents it does say:
Warn when a comparison between signed and unsigned values could produce an incorrect result when the signed value is converted to unsigned
This gcc bug report Bogus warning with -Wsign-compare looks like it covers this kind of false positive detection:
To be useful, these warnings are meant to be smart - not warning if it can
be proved that the signed value is nonnegative [...]
#include <string.h>
void test(char charArray [100])
{
strncpy(charArray, "some text", sizeof(charArray));
}
int main()
{
char charArray [100];
test(charArray);
// EDIT: According to comment from P0W I added this line - where is the difference?
strncpy(charArray, "some text", sizeof(charArray)); // compiles OK
}
Compiled with gcc 4.9.2 on SLES 11 SP2 with this command line g++ gcc-warning-bug-2.cpp -Wall -Wextra -c -Werror I get this warning. Due the -Werror flag I cannot compile the project:
gcc-warning-bug-2.cpp: In function ‘void test(char*)’:
gcc-warning-bug-2.cpp:5:40: error: argument to ‘sizeof’ in ‘char* strncpy(char*, const char*, size_t)’ call is the same expression as the destination; did you mean to provide an explicit length? [-Werror=sizeof-pointer-memaccess]
strncpy(charArray, "some text", sizeof(charArray));
^
cc1plus: all warnings being treated as errors
According to the actual gcc 4.9.2 documentation https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
-Wsizeof-pointer-memaccess
Warn for suspicious length parameters to certain string and memory built-in functions if the argument uses sizeof.
This warning warns e.g. about memset (ptr, 0, sizeof (ptr)); if ptr is not an array, but a pointer, and suggests a possible fix, or about memcpy (&foo, ptr, sizeof (&foo));. This warning is enabled by -Wall.
this should be compiled fine because charArray is an array!
Bug? Should I report it to GNU gcc developer team?
You fell straight into the trap.
In C, C++, Objective-C, Objective-C++, a parameter with a declaration that looks like "array of T" actually has type T*.
Your parameter charArray has a declaration that looks like "array of 100 chars", but the declaration is in fact "pointer to char".
Therefore, your third parameter to strncpy has a value of (most likely) 4 or 8, and not the 100 that you seem to expecct.
BTW. strncpy is highly dangerous the way you use it.
I have a line in some legacy's source code:
#define MAXMSG 1024
...
char m_recvBuf[MAXMSG];
unsigned int msgLength = ntohl(*((unsigned int *)m_recvBuf));
This yields the following warning:
x.cpp: In member function ‘bool xx::cccc(std::string&)’:
x.cpp:308: warning: dereferencing type-punned pointer will break strict-aliasing rules
How can I get rid of this warning ?
my compile line:
g++ -c -g -O2 -Wall -DDEBUG_ON -D_VERSION_=\"1.0.0\" `xml2-config --cflags` -I../src -I./common -I. -I../../test/ -o common/xx.o common/xx.cpp
$ g++ --version
g++ (GCC) 4.4.6 20110731 (Red Hat 4.4.6-3)
The problem with your code is that violates strict aliasing rules and thus it's potentially unsafe.
You can either hide the warning with -Wno-strict-aliasing (this won't solve your problem), modify your data structure or avoid the problem entirely by specifying position and length of your binary copy as Matt suggested (probably the best option):
unsigned int msgLength;
memcpy(&msgLength, m_recvBuf, sizeof(msgLength));
msgLength = ntohl(msgLength);
Notice: I've not been getting the error with clang 3.4 and gcc 4.8.2 in -O3, that means the compiler might have optimized the warning away. Anyway that doesn't assure you your code is safe.
As the previous responders have said you can alter the compile flags to make the warning go away. If you're open to a little light refactoring though, you can solve the issue more cleanly with a union like this:
#define MAXMSG 1024
union {
char buf[MAXMSG];
unsigned int length;
} recvbuf;
// [ read your message stream to recvbuf.buf ]
unsigned int msgLength = ntohl(recvbuf.length);