我有三种不同的机器,具有以下configuration:
他们的所有架构都是x86_64。 请注意,CentOS的软件版本比Debian的要早,但OpenSuSe软件的版本比Debian的版本要新。
我有以下示例代码:
#include <cstdio> #include <cstdlib> unsigned int cols=5; unsigned int rows=6; int main() { //allocating... double **mat=new double*[rows]; double *col=new double[rows]; for(unsigned int i=0;i<rows;++i) { mat[i]=new double[cols]; } //filling with something... for(unsigned int i=0;i<rows;++i) { for(unsigned int j=0;j<cols;++j) { mat[i][j]=i+j; } } //testing... unsigned long long sum,add; for(unsigned int i=0;i<cols;++i) { sum=0; for(unsigned int j=0;j<rows;++j) { col[j]=mat[j][i]; add=*((unsigned long long*) (&(col[j]))); sum+=add; } printf("%llu\n",sum); } return 0; }
如果我编译这个代码没有任何选项:
g++ code.cpp
它在所有机器上以相同的方式运行。
但是如果我使用-O2
编译,它在第一台和第三台机器上的performance相同,但是在第二台机器(Debian)上, -O2
优化会打破它。
这是第一台使用-O2
机器上的代码的输出:
4619567317775286272 9238008735643729920 9250393634618998784 9259400833873739776 9266719183268216832
这是在第二台机器与-O2
:
0 4619567317775286272 9238008735643729920 9250393634618998784 9259400833873739776
第二台机器的输出看起来像第一个输出,向下移动一行。
如果我用(&(mat[j][i]))
replace(&(col[j]))
(&(mat[j][i]))
,代码开始正常工作。
以下是使用-v
和-O2
选项的第二台机器上的编译器输出:
Using built-in specs. Target: x86_64-linux-gnu Configured with: ../src/configure -v --with-pkgversion='Debian 4.4.5-8' --with-bugurl=file:///usr/share/doc/gcc-4.4/README.Bugs --enable-languages=c,c++,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.4 --enable-shared --enable-multiarch --enable-linker-build-id --with-system-zlib --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.4 --libdir=/usr/lib --enable-nls --enable-clocale=gnu --enable-libstdcxx-debug --enable-objc-gc --with-arch-32=i586 --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnu Thread model: posix gcc version 4.4.5 (Debian 4.4.5-8) COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic' /usr/lib/gcc/x86_64-linux-gnu/4.4.5/cc1plus -quiet -v -D_GNU_SOURCE dmtest.cpp -quiet -dumpbase dmtest.cpp -mtune=generic -auxbase dmtest -O2 -version -o /tmp/cc6v7CNY.s ignoring nonexistent directory "/usr/local/include/x86_64-linux-gnu" ignoring nonexistent directory "/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../x86_64-linux-gnu/include" ignoring nonexistent directory "/usr/include/x86_64-linux-gnu" #include "..." search starts here: #include <...> search starts here: /usr/include/c++/4.4 /usr/include/c++/4.4/x86_64-linux-gnu /usr/include/c++/4.4/backward /usr/local/include /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include /usr/lib/gcc/x86_64-linux-gnu/4.4.5/include-fixed /usr/include End of search list. GNU C++ (Debian 4.4.5-8) version 4.4.5 (x86_64-linux-gnu) compiled by GNU C version 4.4.5, GMP version 4.3.2, MPFR version 3.0.0-p3. GGC heuristics: --param ggc-min-expand=100 --param ggc-min-heapsize=131072 Compiler executable checksum: 5a2e15051eaa06a84cf6320b754ba993 COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic' as -V -Qy -o /tmp/ccL37GHG.o /tmp/cc6v7CNY.s GNU assembler version 2.20.1 (x86_64-linux-gnu) using BFD version (GNU Binutils for Debian) 2.20.1-system.20100303 COMPILER_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/ LIBRARY_PATH=/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/:/lib/../lib/:/usr/lib/../lib/:/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../:/lib/:/usr/lib/ COLLECT_GCC_OPTIONS='-v' '-O2' '-shared-libgcc' '-mtune=generic' /usr/lib/gcc/x86_64-linux-gnu/4.4.5/collect2 --build-id --eh-frame-hdr -m elf_x86_64 --hash-style=both -dynamic-linker /lib64/ld-linux-x86-64.so.2 /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crt1.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crti.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtbegin.o -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5 -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib -L/lib/../lib -L/usr/lib/../lib -L/usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../.. /tmp/ccL37GHG.o -lstdc++ -lm -lgcc_s -lgcc -lc -lgcc_s -lgcc /usr/lib/gcc/x86_64-linux-gnu/4.4.5/crtend.o /usr/lib/gcc/x86_64-linux-gnu/4.4.5/../../../../lib/crtn.o
这种行为的原因是什么? 我的代码不正确,或者是gcc 4.4.5中的错误?
根据这个博客条目 ,GCC 4.4对-O2
执行严格的别名规则。 类型双击访问因此导致未定义的行为。 不幸的是,为了得到严格的别名规则的警告,你必须使用-Wstrict-aliasing
进行编译。
也许GCC的较新版本已经恢复到旧的行为,并且决定是否对用户启用严格的别名。
这是类型双关,因此是未定义的行为。 我很惊讶,你没有得到有关它的警告。
当然,它通常按照您期望的方式工作,但它仍然是未定义的行为。
为了解决未定义的行为部分,你可以将其转换为char *
并连续读取每个字节。 这是否解决了你所看到的行为?