簡體   English   中英

為什么Visual C ++會在C中警告從const void **到void *的隱式轉換,而不是在C ++中?

[英]Why does Visual C++ warn on implicit cast from const void ** to void * in C, but not in C++?

摘要

當C程序試圖將指向const數據(如const void **const char ** )的指針轉換為void *時,Microsoft Visual Studio中的C / C ++編譯器會發出警告C4090 (即使這樣的類型實際上不是指向const的指針。 更奇怪的是,同一個編譯器默默接受編譯為C ++的相同代碼。

這種不一致的原因是什么,為什么Visual Studio(與其他編譯器不同)有一個問題,即將指向const的指針隱式轉換為void *

細節

我有一個C程序,其中在變量參數列表中傳遞的C字符串被讀入一個數組(通過調用va_arg的循環)。 由於C字符串的類型為const char * ,因此跟蹤它們的數組的類型為const char ** 這個帶有const內容的字符串指針數組本身是動態分配的(使用calloc ),我在函數返回之前free它(在處理C字符串之后)。

當我用cl.exe (在Microsoft Visual C ++中)編譯此代碼時,即使警告級別較低, free調用也會觸發警告C4090 由於free取一個void * ,這告訴我編譯器不喜歡我將const char **轉換為void * 我創建了一個簡單的例子來證實這一點,我嘗試將const void **轉換為void *

/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}

然后我按如下方式編譯它,確認這是觸發警告的原因:

>cl cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
cast.c(7) : warning C4090: '=' : different 'const' qualifiers
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

微軟關於C4090警告文檔說:

此警告是針對C程序發出的。 在C ++程序中,編譯器發出錯誤:C2440。

這是有道理的,因為C ++是一種比C更強類型的語言,並且在C ++中不允許使用C中允許的潛在危險的隱式轉換。 微軟的文檔似乎警告C2440在C中觸發相同的代碼或代碼的子集,這將觸發C ++中的錯誤C2440

或者我想,直到我嘗試將我的測試程序編譯為C ++( /TP標志執行此操作):

>cl /TP cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

當相同的代碼編譯為C ++時,不會發生錯誤或警告。 可以肯定的是,我重建了,告訴編譯器盡可能積極地警告:

>cl /TP /Wall cast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

cast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:cast.exe
cast.obj

它默默地成功。

這些版本是在Windows 7計算機上使用Microsoft Visual C ++ 2010 Express Edition的cl.exe ,但在Windows XP計算機上,在Visual Studio .NET 2003的cl.exe和Visual C ++ 2005 Express Edition的cl.exe中都會出現相同的錯誤。 所以這似乎發生在所有版本上(盡管我沒有在每個可能的版本上進行測試)並且在我的機器上設置Visual Studio的方式不是問題。

gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 11.10系統(版本字符串gcc (Ubuntu/Linaro 4.6.1-9ubuntu3) 4.6.1 )上,相同的代碼在GCC 4.6.1中編譯沒有問題,設置為盡可能積極地警告,如C89,C99和C ++:

$ gcc -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]

$ gcc -std=c99 -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘main’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]

$ g++ -x c++ -ansi -pedantic -Wall -Wextra -o cast cast.c
cast.c: In function ‘int main()’:
cast.c:6:11: warning: variable ‘q’ set but not used [-Wunused-but-set-variable]

它確實警告q在被分配之后永遠不會從中讀取,但是該警告是有意義的並且是無關的。

除了在GCC中沒有啟用所有警告的情況下觸發警告,並且在GCC或MSVC中沒有在C ++中觸發警告之外,在我看來,從指針到指針轉換為const到void *不應該被認為是一個問題,因為void *是指向非const的指針,指向const的指針也是指向非const的指針。

在我的真實世界代碼(不是示例)中,我可以使用#pragma指令或顯式強制轉換,或者編譯為C ++(嘿嘿)來沉默,或者我可以忽略它。 但我寧願不做任何這些事情,至少在我明白為什么會發生這種情況之前。 (為什么它不會在C ++中發生!)

一個可能的,部分解釋發生在我身上:與C ++不同,C允許從void *隱式轉換為任何指向數據的指針類型。 所以我可以將一個指針從const char **隱式轉換為void * ,然后從void *隱式轉換為char ** ,從而可以修改指向指針的常量數據,而無需強制轉換。 那會很糟糕。 但我不知道這比C的弱類型安全所允許的各種其他事情更糟糕。

我想也許這個警告是有意義的,因為當非void指針類型被轉換為void *時選擇不警告:

/* cast.c - Can a const void** be cast implicitly to void* ? */

int main(void)
{
    const void **p = 0;
    void *q;
    q = p;

    return 0;
}
>cl /Wall voidcast.c
Microsoft (R) 32-bit C/C++ Optimizing Compiler Version 16.00.40219.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

voidcast.c
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:voidcast.exe
voidcast.obj

然而,如果這是有意的,那么:

  1. 為什么Microsoft文檔表明在C中產生此警告的代碼在C ++中產生錯誤?

  2. 除了忽視或抑制該警告,沒有任何合理的替代方法,當一個人必須free一個非const指針非const指針const數據(如在我的現實世界的情況下)? 如果在C ++中發生類似這樣的事情,我可以將在變量參數列表中傳遞的字符串存儲在某個高級STL容器中而不是數組中。 對於無法訪問C ++ STL且不使用高級集合的C程序,這種事情不是一個合理的選擇。

  3. 一些程序員在公司/組織政策下工作,將警告視為錯誤。 即使使用/W1也啟用C4090 人們一定以前遇到過這種情況。 那些程序員做了什么?

顯然這只是VC ++中的一個錯誤。

如果你聲明const char **x; 結果是一個指向chars的“只讀”指針的指針,它本身不是一個“只讀”指針(我使用術語“只讀”,因為const -ness術語推出了錯誤的概念,即該字符是指向是常量而一般來說這是假的...帶引用和指針的const是引用或指針的屬性,並且不會告訴指向或引用數據的const )。

任何讀/寫指針都可以轉換為void *並且在編譯該代碼時VC ++沒有真正的理由發出警告,無論是在C還是在C++模式下。

請注意,這不是正式問題,因為標准沒有強制要求或不應該發出哪些警告,因此編譯器可以自由地發出警告,以保證完全有效的代碼仍然符合要求。 VC ++實際上發出了大量有效C ++代碼的警告......

像6502說這似乎是編譯器中的一個錯誤。 但是,你也問你應該怎么做。

我的答案是你應該為免費電話添加一個明確的演員,然后是一個解釋為什么需要它的評論。 編譯器中的錯誤確實發生,使用最簡單的解決方法並添加一個注釋,以便在以后解決該錯誤時可以對其進行測試。

還向編譯器供應商報告錯誤的額外點。

至於1.它似乎是指將一個const T *隱式地轉換為void * ,它應該是C中的警告和C ++中的錯誤。

暫無
暫無

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

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