簡體   English   中英

為什么 iostream 需要在使用 vcvarsall.bat 編譯“Hello World”時調用異常處理程序?

[英]Why does iostream require the exception handler to be called while using vcvarsall.bat to compile 'Hello World'?

嘗試在命令行中使用 vcvarsall.bat 編譯以下代碼會引發警告,指出代碼中需要異常處理程序,但在使用/EHsc之前未調用。

代碼:

#include <iostream>

int main()
{
    std::cout << "hello world" << std::endl;
    
    return 0;
}

批處理文件:

@echo off

cl C:\Development\..\basicmath.cpp

警告:

C:\...\ostream(746): warning C4530: C++ exception handler used, but unwind semantics are not enabled. Specify /EHsc
C:...\basicmath.cpp(10): note: see reference to function template instantiation 'std::basic_ostream<char,std::char_traits<char>> &std::operator <<<std::char_traits<char>>(std::basic_ostream<char,std::char_traits<char>> &,const char *)' being compiled

ostream 第 746 行的第 743 - 754 行(來自錯誤)是 _TRY:

if (!_Ok) {
        _State |= ios_base::badbit;
    } else { // state okay, insert
        _TRY_IO_BEGIN
        if ((_Ostr.flags() & ios_base::adjustfield) != ios_base::left) {
            for (; 0 < _Pad; --_Pad) { // pad on left
                if (_Traits::eq_int_type(_Traits::eof(), _Ostr.rdbuf()->sputc(_Ostr.fill()))) {
                    _State |= ios_base::badbit; // insertion failed, quit
                    break;
                }
            }
        }

將 /EHsc 添加到我的批處理文件將允許它運行,但我想知道這是為什么。 為什么 output 文件中的這段代碼需要調用 EHsc?

MSDOCS 說 EHsc 用於清理以防止 memory 泄漏,是什么導致了泄漏,為什么他們需要外部程序來修復泄漏而不是在同一個文件中修復它(這聽起來可能很粗魯,但它只是無知)?

編輯:感謝您指出它是警告而不是錯誤。

簡短的回答:

按照文檔的建議,將/EHs /EHsc添加到您的編譯選項中。 如果您需要在 Unix 機器上執行相同的代碼,它是關於異常處理的最便攜的選項。


長答案:

這個問題有兩個部分。 首先是iostream中出現警告的原因,其次是警告的含義。

為什么iostream中有異常?

C++ 中流的默認行為是無異常的——任何失敗都通過設置一個內部失敗位來表示,可以通過eof()fail()bad()函數訪問。 但是,您可以通過在 stream 上使用exceptions()方法將此行為更改為在失敗時引發異常。 您可以選擇哪些失敗位觸發異常,但要點是代碼必須按標准存在。 警告似乎只分析了這一點 - 它注意到發生throw的可能路徑並報告警告。

警告是什么意思?

微軟文檔(強調我的):

默認情況下(即,如果未指定/EHsc/EHs EHs 或/EHa選項),編譯器在本機 C++ catch(...)子句中支持 SEH 處理程序。 但是,它也會生成僅部分支持 C++ 異常的代碼 默認的異常展開代碼不會因為異常而破壞 go 超出 scope 的 try 塊之外的自動 C++ 對象。

問題是(出於某種原因)MSVC 編譯器默認生成的程序集根據標准是錯誤的。 拋出異常時不會執行堆棧展開,這可能會導致 memory 泄漏和其他意外行為。

一個正確的示例 C++ 代碼,在默認設置下具有 memory 泄漏:

void foo()
{
    std::string str = "This is a very long string. It definitely doesn't use Small String Optimization and it must be allocated on the heap."
    std::cout << str;
    throw std::runtime_error{"Oh no, something went wrong"};
}

int main()
{
    try
    {
        foo();
    }
    catch (std::exception&)
    {
        // str in foo() was possibly not released, because it wasn't deleted when exception was thrown!
    }
}

所以最終的答案是:

  • 如果您打算使用結構化異常(例如被零除或無效的 memory 訪問錯誤)或使用使用它們的庫,請使用/EHa
  • 如果不需要抓 SE,選擇/EHs EHs 以兼容 C++ 標准和可移植性
  • 永遠不要保留默認值,始終將/EH設置為一種或另一種,否則在使用異常時您將不得不處理奇怪的行為。

這是一個警告,因此您當前的程序可以正常編譯。 但是這樣的程序會出現問題:

#include <exception>
#include <iostream>

struct A{
    A(int x):x(x) {
        std::cout<<"Contructed A::"<<x<<'\n';
    }
    ~A() {
        std::cout<<"Destructed A::"<<x<<'\n';
    }
private:
    int x;
};


void foo() {
    A a{2};
    throw std::bad_exception{};
}

int main()
{
    A a {1};
    try {
        foo();
    } catch(const std::bad_exception& ex) {
        std::cout<<ex.what()<<'\n';
    }
    
    return 0;
}

使用cl test.cpp產生 output:

Contructed A::1
Contructed A::2
bad exception
Destructed A::1

使用cl test.cpp /EHsc產生:

Contructed A::1
Contructed A::2
Destructed A::2
bad exception
Destructed A::1

警告C4530的文檔解釋了此行為:

當 /EHsc 選項未啟用時,在拋出的 function 和捕獲異常的 function 之間的堆棧幀中的自動存儲對象不會被破壞。 只有在 try 或 catch 塊中創建的自動存儲對象會被破壞,這可能導致嚴重的資源泄漏和其他意外行為。

這解釋了當程序未使用/EHsc編譯時a {2}未被破壞。

而且當然,

如果在您的可執行文件中不可能拋出異常,您可以放心地忽略此警告。

所以,對於像這樣的程序

#include <cstdio>

int main()
{
    std::printf("hello world\n");
    
    return 0;
}

cl.exe安靜地編譯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM