Visual C 中的重大更改

2022-03-22 21:18:41 字數 4602 閱讀 8651

當你公升級到 visual c++ 編譯器的新版本後,可能會在之前編譯並正常執行的**中遇到編譯和/或執行時錯誤。 新版本中會引起這類問題的更改稱為重大更改,通常,修改 c++ 語言標準、函式簽名或記憶體中的物件布局時需要進行這種更改。

若要避免難以檢測和診斷的執行時錯誤,我們建議你永遠不靜態鏈結到使用不同編譯器版本編譯的二進位制檔案。 此外,當你公升級 exe 或 dll 專案時,請確保公升級它所鏈結的庫。 如果使用 crt(c 執行時庫)或 stl(標準模板庫)型別,請勿在使用不同編譯器版本編譯的二進位制檔案(包括 dll)之間傳遞這些型別。 有關詳細資訊,請參閱 跨 dll 邊界傳遞 crt 物件時可能的錯誤。

我們進一步建議,你在編寫**時永遠不依賴除 com 介面或 pod 物件以外的特定物件布局。 如果確實要編寫此類**,則必須在公升級後確保其正常執行。 有關詳細資訊,請參閱 abi 邊界處的可移植性(現代 c++)。

本文的其餘部分介紹了 visual studio 2015 中的 visual c++ 中具體的重大更改,並且在本文中,術語「新行為」或「現在」均指該版本。 術語「舊行為」和「之前」指 visual studio 2013 和早期版本。

編譯器的重大更改

b38385a9-a483-4de9-99a6-797488bc5110#bk_crt

b38385a9-a483-4de9-99a6-797488bc5110#bk_stl

b38385a9-a483-4de9-99a6-797488bc5110#bk_mfc

visual c++ 編譯器

c 執行庫 (crt)

常規更改

和 和

這些字串的任何一種都可能已採用符號作為字首並且格式設定也可能略有不同,具體取決於字段寬度和精度(有時會起到不尋常的作用,例如 printf("%.2f\n", infinity) 可以列印 1.#j,因為 #inf 會「四捨五入」為 2 位數的精度)。 c99 引入了有關如何設定無窮大和 nan 格式的新要求。 現在,visual c++ 實現符合這些要求。 新字串如下所示:

可能以符號作為其中任何一種字串的字首。 如果使用了大寫格式說明符(%f 而不是 %f),則字串將按要求以大寫字母形式(inf 而不是 inf)列印。

已修改 scanf 函式以便分析這些新的字串,因此這些字串會通過 printf 和 scanf 往返。

浮點格式設定和分析

引入了新浮點格式設定和分析演算法以提高正確性。 此更改會影響 printf 和 scanf 系列函式,以及像 strtod 這樣的函式。

舊的格式設定演算法將僅生成有限數量的數字,然後將用零填充其餘的小數字數。 這是通常足以生成將往返回原始浮點值的字串,但如果你想要精確值(或最接近十進位制的表示),則不夠完美。 新的格式設定演算法會盡可能多地生成數字來表示值(或填充指定的精度)。 作為改進的乙個例子;列印兩個中指數較大的乙個時,請考慮結果:

printf("%.0f\n", pow(2.0, 80))

舊版本:1208925819614629200000000

新版本:1208925819614629174706176

舊版本分析演算法考慮的輸入字串中有效位數僅達 17,並將丟棄其餘數字。 這不足以生成由字串表示的近似值,結果通常是非常接近正確捨入的結果。 新版本的實現會考慮所有存在的數字,並生成所有輸入(長度多達 768 位)的正確捨入的結果。 此外,這些函式現在遵循捨入模式(可通過 fesetround 控制)。  這可能是重大的行為更改,因為這些函式可能會輸出不同的結果。 新版本的結果始終比舊版本的結果更準備。

十六進製制和無窮大/nan 浮點分析

浮點分析演算法現在將分析十六進製制浮點字串(例如,那些由 %a 和 %a printf 格式說明符生成的字串)和由 printf 函式生成的所有無窮大和 nan 字串(如上文所述)。 

%a 和 %a 零填充

%a 和 %a 格式說明符將浮點數轉化為十六進製制的尾數和二進位制指數。 在早期版本中,printf 函式可能會錯誤地用零填充字串。 例如,printf ("%07.0a\n", 1.0) 可能會列印 00x1p+0,而它本應列印 0x01p+0。 已解決此問題。

%a 和 %a 精度

在早期版本的庫中,%a 和 %a 格式說明符的預設精度是 6。 為了符合 c 標準,現在預設精度為 13。

這是使用帶 %a 或 %a 的格式字串的任一函式輸出中的執行時行為更改。 在舊版本行為中,使用 %a 說明符的輸出可能是「1.1a2b3cp+111」。 現在相同值的輸出是「1.1a2b3c4d5e6f7p+111」。 若要獲取舊版本行為,則可以指定精度(例如,%.6a)。 請參閱 精度規範。

%f 說明符

現在支援 %f 格式/轉換說明符。 它在功能上等效於 %f 格式說明符,但使用大寫字母形式進行格式設定的無窮大和 nan 除外。

在早期版本中,實現過去通常將 f 和 n 分析為長度修飾符。 此行為追溯到分段位址空間的時代:這些長度修飾符分別用於指示或近或遠的指標(如 %fp 或 %ns 中所示)。 此行為已被刪除。 如果遇到 %f,現在則將其視為 %f 格式說明符;如果遇到 %n,現在則將其視為無效的引數。

指數格式設定

%e 和 %e 格式說明符將浮點數轉化為十進位制的尾數和指數。 %g 和 %g 格式說明符在某些情況下也以此形式設定格式位數。 在早期版本中,crt 會始終生成具有三個數字指數的字串。 例如,printf ("%e\n", 1.0) 可能會列印 1.000000e+000。 這是錯誤的:根據 c 要求,如果可使用乙個或兩個數字表示指數,則僅列印兩個數字。

visual studio 2005 中新增了全域性一致性切換: _set_output_format。 程式可以呼叫引數為 _two_digit_exponent 的此函式,以啟用符合標準的指數列印。 已將預設行為更改為符合標準的指數列印模式。

格式字串驗證

在早期版本中,printf 和 scanf 函式以靜默方式接受許多無效格式字串,有時會起到不尋常的作用。 例如,%hlhlhld 將被視為 %d。 現在所有無效格式字串都被視為無效的引數。

fopen 模式字串驗證

在早期版本中,fopen 系列函式以靜默方式接受某些無效的模式字串(例如 r+b+)。 現在可檢測無效的模式字串並將其視為無效的引數。

_o_u8text 模式

_setmode 函式現在可以準確報告在 in_o_u8text 模式中開啟的流模式。 在早期版本的庫中,它將報告正在 _o_wtext 中開啟的此類流。

如果你的**解釋其中編碼為 utf-8 的流的 _o_wtext 模式,這則是一項重大更改。 如果你的應用程式不支援 utf_8,請考慮為此越來越常見的編碼新增支援。

snprintf 和 vsnprintf

現在已實現 snprintf 和 vsnprintf 函式。 較舊的**通常為巨集版本的這些函式提供定義,因為它們未由 crt 庫實現,但在較新版本中則不再需要這些。 如果將 snprintf 或vsnprintf 在包括 之前定義為巨集,則現在編譯將因出錯而失敗,該錯誤指示定義了巨集的位置。

通常情況下,解決此問題的方法是刪除使用者**中 snprintf 或 vsnprintf 的任何宣告。

tmpnam 生成可用檔名

在早期版本中,tmpnam 和 tmpnam_s 函式在驅動器根目錄(如 \sd3c)中生成檔名。 這些函式現在在臨時目錄中生成可用的檔名路徑。

檔案封裝

在早期版本中,完全在 中定義檔案型別,因此使用者**可以進入檔案並修改其內部結構。 已對 stdio 庫進行了更改以隱藏實現細節。 作為此操作的一部分,中所定義的檔案現在是不透明型別且無法從 crt 自身外部訪問其成員。

_outp 和 _inp

已刪除函式 _outp、 _outpw、 _outpd、 _inp、 _inpw 和 _inpd。

、和 標準模板庫

為了實現新的優化和除錯檢查,c++ 標準庫的 visual studio 實現特意破壞了連續兩個版本之間的二進位制相容性。 因此,在使用 c++ 標準庫時,使用不同版本編譯的物件檔案和靜態庫不能混合在同一二進位制檔案(exe 或 dll)中,並且不能在使用不同版本編譯的二進位制檔案之間傳遞 c++ 標準庫物件。 這樣混合會發出關於 _msc_ver 不匹配的鏈結器錯誤。(_msc_ver 是包含編譯器主版本的巨集,例如,visual studio 2013 的 1800。) 此檢查無法檢測 dll 混合,也無法檢測涉及 visual c++ 2008 或早期版本的混合。

mfc 和 atl

併發執行時

與 concurrency::context::yield 衝突的 windows.h 中的 yield 巨集

併發執行時之前使用 #undef 來取消定義 yield 巨集,以避免 windows.h h 中定義的 yield 巨集和 concurrency::context::yield 函式之間的衝突。 已刪除此 #undef,並新增了新的非衝突等效 api 呼叫 concurrency::context::yieldexecution。 若要解決與 yield 的衝突,可以改為更新**以呼叫 yieldexecution 函式,或在呼叫站點用括號將 yield 函式名括起來,如下例所示:

(concurrency::context::yield)();

請參閱getting started with visual c++ in visual studio 2015

visual c++ 2013 中的重大更改

Visual C 中的斷言

斷言語句指定在程式的某些特定點應為真的條件。如果該條件不為真,則斷言失敗,中斷程式的執行,並顯示 斷言失敗 對話方塊。visual c 支援基於下列構造的斷言語句 mfc 程式的 mfc 斷言。使用 atl 的程式的 atlassert。使用 c 執行時庫的程式的 crt 斷言。其他 c c 程式的...

Visual C 中的ODBC程式設計

一 mfc提供的odbc資料庫類 visual c 的mfc基類庫定義了幾個資料庫類。在利用odbc程式設計時,經常要使用到 cdatabase 資料庫類 crecordset 記錄集類 和crecordview 可視記錄集類 cdatabase類物件提供了對資料來源的連線,通過它可以對資料來源進行...

Visual C 中的資料繫結

我們知道在由於visual c 自身沒有類庫,和其他的.net開發語言一樣,visual c 呼叫的類庫是.net框架中的乙個共有的類庫 net framework sdk。ado.net是.net framework sdk提供給.net開發語言進行資料庫開發的乙個系列類庫的集合。在ado.net...