2015-07-20 68 views
4

我試着用優化標誌-O1/-O2/-O3/-Og編譯一大段使用gcc-5.1.0的軟件。它給了我警告-Wmaybe-uninitialized-Wuninitialized並在運行時失敗。調試後,我發現導致它的代碼,但我無法理解爲什麼。我減少了代碼重現故障:程序員錯誤或gcc-5.1.0錯誤?

#include <cstdlib> 
#include <iostream> 

template<class T> 
struct foo { 
    template<class U> 
    char bar(const U &x) { 
     //return id(x)[0]; 
     const T &y = id(x); 
     return y[0]; 
    } 

    const T &id(const T &elem) { 
     return elem; 
    } 
}; 

int main(void) { 
    foo<const char *> f; 
    char *str = "hello world"; 
    //std::cout << f.bar((const char *)str) << std::endl; 
    std::cout << f.bar(str) << std::endl; 
    return 0; 
} 

GCC-5.1.0給出如下警告:

g++ -Og -Wall -Wextra -Wno-write-strings test.cpp -o test 
test.cpp: In function ‘int main()’: 
test.cpp:10:19: warning: ‘<anonymous>’ is used uninitialized in this function [-Wuninitialized] 
     return y[0]; 
       ^
test.cpp:9:24: note: ‘<anonymous>’ was declared here 
     const T &y = id(x); 
         ^

程序在運行時和崩潰臨危SIGSEGV。由於優化很難調試,但在玩代碼後,我認爲問題是const T &y = id(x);NULL指定爲y。 (通過將函數調用替換爲return y[0];,它返回y[0]。)我目前無法使用其他版本的gcc測試代碼,但是使用gcc-4.9.2進行編譯時,衍生該示例的代碼正常工作。當與clang-3.6.1搭配使用時,它也可以正確處理任何優化級別。 我的下一步是試圖弄清楚導致它的確切優化是什麼,所以我在makefile中放入了我能找到的所有gcc的優化標誌,但是它在沒有警告的情況下編譯並且不會在運行時崩潰。

我的問題是:

  • 它是正確的C++代碼?
  • 這是gcc-5.1.0中的已知/未知錯誤嗎?
  • 做我的理解是正確的,在上面T = [const char *]; U = [char *]的例子,當我寫const T &y = id(x);應該有xchar *隱皈依到const char *y是一個常數參考const char *
  • 如果模板錯誤,確切的錯誤是什麼?
  • 如何找出造成它的確切優化標誌是什麼?

注意:取消註釋任何註釋行可修復該程序。

$ g++ -v 
Using built-in specs. 
COLLECT_GCC=g++ 
COLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-unknown-linux-gnu/5.1.0/lto-wrapper 
Target: x86_64-unknown-linux-gnu 
Configured with: /build/gcc-multilib/src/gcc-5-20150623/configure --prefix=/usr --libdir=/usr/lib --libexecdir=/usr/lib --mandir=/usr/share/man --infodir=/usr/share/info --with-bugurl=https://bugs.archlinux.org/ --enable-languages=c,c++,ada,fortran,go,lto,objc,obj-c++ --enable-shared --enable-threads=posix --enable-libmpx --with-system-zlib --with-isl --enable-__cxa_atexit --disable-libunwind-exceptions --enable-clocale=gnu --disable-libstdcxx-pch --disable-libssp --enable-gnu-unique-object --enable-linker-build-id --enable-lto --enable-plugin --enable-install-libiberty --with-linker-hash-style=gnu --enable-gnu-indirect-function --enable-multilib --disable-werror --enable-checking=release --with-default-libstdcxx-abi=c++98 
Thread model: posix 
gcc version 5.1.0 (GCC) 

gcc的優化參數我想:

-falign-functions -falign-jumps -falign-labels -falign-loops -fassociative-math 
-fauto-inc-dec -fbranch-count-reg -fbranch-probabilities -fbranch-target-load-optimize 
-fbranch-target-load-optimize2 -fbtr-bb-exclusive -fcaller-saves -fcheck-data-deps -fcheck-new 
-fcombine-stack-adjustments -fcompare-elim -fconserve-stack -fcprop-registers -fcrossjumping 
-fcse-follow-jumps -fcse-skip-blocks -fcx-fortran-rules -fcx-limited-range -fdata-sections 
-fdce -fdefer-pop -fdelete-null-pointer-checks -fdevirtualize -fdevirtualize-speculatively 
-fdse -fearly-inlining -fexpensive-optimizations -fext-numeric-literals -ffast-math 
-ffinite-math-only -ffloat-store -ffor-scope -fforward-propagate -ffriend-injection 
-ffunction-sections -fgcse -fgcse-after-reload -fgcse-las -fgcse-lm -fgcse-sm -fguess-branch-probability 
-fhoist-adjacent-loads -fif-conversion -fif-conversion2 -findirect-inlining -finline-functions 
-finline-functions-called-once -finline-small-functions -fipa-cp -fipa-cp-alignment -fipa-cp-clone 
-fipa-icf -fipa-matrix-reorg -fipa-profile -fipa-pta -fipa-pure-const -fipa-ra -fipa-reference 
-fipa-sra -fipa-struct-reorg -fisolate-erroneous-paths-dereference -fivopts -fkeep-inline-functions 
-fkeep-static-consts -floop-block -floop-interchange -floop-strip-mine -flra-remat -fmerge-all-constants 
-fmerge-constants -fmodulo-sched -fmodulo-sched-allow-regmoves -fmove-loop-invariants -fms-extensions 
-fnothrow-opt -fomit-frame-pointer -foptimize-register-move -foptimize-sibling-calls -foptimize-strlen 
-fpartial-inlining -fpeel-loops -fpeephole2 -fpermissive -fpredictive-commoning -fprefetch-loop-arrays 
-fprofile-correction -fprofile-generate -fprofile-use -fprofile-values -freciprocal-math -fregmove 
-frename-registers -freorder-blocks -freorder-blocks-and-partition -freorder-functions -frerun-cse-after-loop 
-freschedule-modulo-scheduled-loops -frounding-math -fsched-interblock -fsched-spec -fsched-spec-load 
-fsched-spec-load-dangerous -fsched-stalled-insns -fsched-stalled-insns-dep -fsched2-use-superblocks 
-fsched2-use-traces -fschedule-insns -fschedule-insns2 -fsee -fsel-sched-pipelining 
-fsel-sched-pipelining-outer-loops -fselective-scheduling -fselective-scheduling2 -fshrink-wrap 
-fsignaling-nans -fsingle-precision-constant -fsized-deallocation -fsplit-ivs-in-unroller 
-fsplit-wide-types -fssa-phiopt -fstack-protector -fstack-protector-all -fstrict-aliasing 
-fstrict-overflow -fthread-jumps -ftracer -ftree-bit-ccp -ftree-builtin-call-dce -ftree-ccp 
-ftree-ch -ftree-coalesce-vars -ftree-copy-prop -ftree-copyrename -ftree-dce -ftree-dominator-opts 
-ftree-dse -ftree-forwprop -ftree-fre -ftree-loop-distribute-patterns -ftree-loop-distribution 
-ftree-loop-im -ftree-loop-ivcanon -ftree-loop-linear -ftree-loop-optimize -ftree-loop-vectorize 
-ftree-partial-pre -ftree-phiprop -ftree-pre -ftree-pta -ftree-reassoc -ftree-sink -ftree-slp-vectorize 
-ftree-slsr -ftree-sra -ftree-switch-conversion -ftree-tail-merge -ftree-ter -ftree-vect-loop-version 
-ftree-vectorize -ftree-vrp -funit-at-a-time -funroll-all-loops -funroll-loops -funsafe-loop-optimizations 
-funsafe-math-optimizations -funswitch-loops -fuse-cxa-atexit -fvariable-expansion-in-unroller 
-fvect-cost-model -fvisibility-inlines-hidden -fvisibility-ms-compat -fvpt -fvtv-counts -fvtv-debug 
-fweb -fwhole-program 
+0

也許如果您沒有使用'-Wno-write-strings'禁用有用的警告,那麼您將自己發現問題。 –

+0

@JonathanWakely我能找到工作,但是,我無法理解爲什麼發生。我只使用'-Wno-write-strings'來縮短代碼。在原始代碼中,它的內容類似'f.bar(string())',其中'string()'返回'char *',我不認爲'-Wwrite-strings'在這種情況下顯示警告。 – zaquest

回答

11

你的字符串是(非法)綁定到char*

char *str = "hello world"; 

這是從C吃剩的轉換,這已自C++ 98(或C++ 03?)開始棄用,並已在C++ 11中刪除。 gcc仍然允許它作爲擴展,就像鏗鏘聲++。

當您撥打f.bar(str)時,它推導出U == char*,因此其函數參數類型變爲char* const&

using U = char*; 
template<> 
char bar(const U &x) { // char* const& x 
    const T &y = id(x); 
    return y[0]; 
} 

但是,你有實例f與模板參數char const*,因此函數參數類型的idchar const* const&

const T &id(const T &elem) { // char const* const& elem 
    return elem; 
} 

因此,在bar表達id(x)具有轉換從char*char const* ,這會創建一個臨時的。該臨時持續時間直到完整表達式const T& y = id(x);的末尾,因此創建了一個懸掛參考y


基本上,你的假設之一是不正確/不完整:

我是否正確地說明白它在上面的例子中T = [const char*]; U = [char *],當我寫const T &y = id(x);應該有x隱含皈依從char *const char *y是 對const char *的常量引用?

T const*T*的類型與參考不兼容。有關詳細信息,請參見[dcl.init.ref]。但是,T*可以轉換爲T const*,因此臨時創建並綁定到T const*&。這類似於:

int i = 42; 
double const& d = i; // creates a temporary double and binds it to d 

這是針對所有參考不兼容的類型的行爲,其中所述源是隱式轉換爲目標類型(除類類型的轉換函數/運算符)。

爲什麼T const*T*不參考兼容?與T const**T**相同的推理也適用,它們也是不兼容的。這可以防止一個微妙的錯誤,請參閱https://stackoverflow.com/a/2908332/

+0

我希望它會警告'char *'而不是'char const *'用於文字,會使它更容易被發現並且似乎通常很有用。 –

+1

@RaphaelMiedl它應該。使用'pedantic'或'-Wpedantic'。 – dyp

+0

噢,謝謝,我認爲它像C一樣工作,只是將'char *'強制轉換爲'const char *'。然而,其他問題仍然存在:爲什麼它在其他玩家和其他國旗沒有任何警告地工作?什麼是導致錯誤出現的確切優化標誌? – zaquest