[英]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
此警告是針對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
然而,如果這是有意的,那么:
為什么Microsoft文檔表明在C中產生此警告的代碼在C ++中產生錯誤?
除了忽視或抑制該警告,沒有任何合理的替代方法,當一個人必須free
一個非const
指針非const
指針const
數據(如在我的現實世界的情況下)? 如果在C ++中發生類似這樣的事情,我可以將在變量參數列表中傳遞的字符串存儲在某個高級STL容器中而不是數組中。 對於無法訪問C ++ STL且不使用高級集合的C程序,這種事情不是一個合理的選擇。
一些程序員在公司/組織政策下工作,將警告視為錯誤。 即使使用/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.