我有這個簡單的 C 程式。
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
bool foo (unsigned int a) {
return (a > -2L);
}
bool bar (unsigned long a) {
return (a > -2L);
}
int main() {
printf("foo returned = %d\n", foo(99));
printf("bar returned = %d\n", bar(99));
return 0;
}
當我運行它時輸出 -
foo returned = 1
bar returned = 0
在godbolt重新這里
我的問題是為什么foo(99)回傳 true 但bar(99)回傳 false。
對我來說,bar回傳 false是有道理的。為簡單起見,假設 long 是 8 位,然后(使用二進制補碼作為有符號值):
99 == 0110 0011
-2 == unsigned 254 == 1111 1110
所以很明顯 CMP 指令會看到 1111 1110 更大并回傳 false。
但我不明白foo函式的幕后發生了什么。的程式集foo似乎硬編碼為總是 return mov eax,0x1。我本來希望foo做類似的事情bar。這里發生了什么?
uj5u.com熱心網友回復:
這包含在 C 類中,并在檔案中指定。以下是您如何使用檔案來解決這個問題。
在2018 C 標準中,您可以>在索引中查找或“關系運算式”以查看它們在第 68-69 頁上的討論。在第 68 頁,您將找到第 6.5.8 節,其中涵蓋了關系運算子,包括>. 閱讀它,第 3 段說:
如果兩個運算元都具有算術型別,則執行通常的算術轉換。
“常用算術轉換”列在第 39 頁定義的索引中。第 39 頁有第 6.3.1.8 節“常用算術轉換”。該子句解釋算術型別的運算元被轉換為通用型別,并給出了確定通用型別的規則。兩個整數型別不同的符號性的,如unsigned long與long int在bar(a和-2L),它說的是,如果無符號的型別的秩大于或等于所述其他型別的等級,有符號的型別被轉換為無符號型別。
“Rank” 不在索引中,但您可以搜索檔案以找到它在第 6.3.1.1 節中討論,它告訴您 的 ranklong int大于 的 rank int,并且任何無符號型別與 具有相同的 rank對應的型別。
現在,你可以考慮a > -2L在bar其中a的unsigned long。在這里,我們將 aunsigned long與 a進行比較long。它們具有相同的等級,因此-2L轉換為unsigned long。第 6.3.1.3 節討論了有符號整數到無符號整數的轉換。它表示該值是通過將其模ULONG_MAX 1包裝來轉換的,因此signed long-2 產生一個大整數。然后將a值為 99 的 與一個大整數進行比較,結果為>假,因此回傳零。
對于foo,我們繼續使用通常的算術轉換規則。當無符號型別的秩不大于或等于有符號型別的秩,但有符號型別可以表示無符號型別運算元的型別的所有值時,將無符號型別的運算元轉換為運算元簽名型別的。在foo,a是unsigned int和-2L是long int。大概在您的 C 實作中,long int是 64 位,因此它可以表示 32-bit 的所有值unsigned int。所以這條規則適用,并a轉換為long int. 這不會改變值。因此,將 的原始值a99 與 -2 與 進行比較,結果為>真,因此回傳 1。
uj5u.com熱心網友回復:
在第一個函式中
bool foo (unsigned int a) {
return (a > -2L);
}
運算式的兩個運算元a > -2L都具有型別long(long由于通常的算術轉換,第一個運算元被轉換為型別,因為型別long的等級大于型別的等級,unsigned int并且unsigned int使用的系統中的所有型別值都可以是由型別表示long)。很明顯,正值99L大于負值-2L。
第一功能可能產生的結果0提供了sizeof( long )等于sizeof( unsigned int )。在這種情況下,該型別long無法表示該型別的所有(正)值unsigned int。因此,由于通常的算術轉換,兩個運算元都將轉換為 type unsigned long。
例如,foo使用MS VS 2019where sizeof( long )is equal to運行函式4,sizeof( unsigned int )您將得到結果0。
這是一個用 C 撰寫的演示程式,直觀地展示了foo使用 MS VS 2019呼叫函式的結果可以等于0.
#include <iostream>
#include <iomanip>
#include <type_traits>
int main()
{
unsigned int x = 0;
long y = 0;
std::cout << "sizeof( unsigned int ) = " << sizeof( unsigned int ) << '\n';
std::cout << "sizeof( long ) = " << sizeof(long) << '\n';
std::cout << "std::is_same_v<decltype( x y ), unsigned long> is "
<< std::boolalpha
<< std::is_same_v<decltype( x y ), unsigned long>
<< '\n';
}
程式輸出為
sizeof( unsigned int ) = 4
sizeof( long ) = 4
std::is_same_v<decltype( x y ), unsigned long> is true
這通常是第一個函式的結果是實作定義的。
在第二個功能
bool bar (unsigned long a) {
return (a > -2L);
}
兩個運算元都具有型別unsigned long(同樣由于通常的算術轉換和型別的等級unsigned long并且signed long彼此相等,因此型別的物件signed long被轉換為型別unsigned long)并-2L解釋為unsigned long大于99。
uj5u.com熱心網友回復:
其原因與整數轉換的規則有關。
在第一種情況下,你比較的unsigned int一個long使用>運營商,并在第二種情況下您比較unsigned long有long。
這些運算元必須首先使用通常的算術轉換轉換為通用型別。這些在C 標準的第 6.3.1.8p1 節中有詳細說明,以下摘錄重點關注整數轉換:
如果兩個運算元具有相同的型別,則不需要進一步轉換。
否則,如果兩個運算元都具有有符號整數型別或都具有無符號整數型別,則具有較小整數轉換等級的型別的運算元將轉換為具有較高等級的運算元的型別。
否則,如果無符號整數型別的運算元的等級大于或等于另一個運算元型別的等級,則將有符號整數型別的運算元轉換為無符號整數型別的運算元的型別。
否則,如果有符號整數型別的運算元的型別可以表示無符號整數型別的運算元型別的所有值,則將無符號整數型別的運算元轉換為有符號整數型別的運算元的型別。
否則,兩個運算元都轉換為與帶符號整數型別的運算元型別對應的無符號整數型別。
在比較的情況下unsigned int與long所述第二加粗段適用。long具有更高的等級并且(假設long是 64 位和int32 位)可以保存所有值而不是unsigned intcan,因此unsigned int運算元a被轉換為 a long。由于所討論的值在 范圍內long,第 6.3.1.3p1 節規定了轉換是如何發生的:
當整數型別的值轉換為除 以外的其他整數型別時
_Bool,如果該值可以用新的型別表示,則保持不變
所以這個值被保留了,我們只剩下99 > -2哪個是真的了。
在比較 aunsigned long和 a的情況下long,第一個粗體段落適用。兩種型別的等級相同但符號不同,因此將long常數-2L轉換為unsigned long。-2 超出了unsigned longa的范圍,因此必須進行值轉換。此轉換在 6.3.1.3p2 節中指定:
否則,如果新型別是無符號的,則通過在新型別中可以表示的最大值的基礎上反復加減一,直到該值在新型別的范圍內。
所以long值 -2 將被轉換為unsigned long值 2 64 -2,假設unsigned long是 64 位。所以我們剩下 99 > 2 64 -2,這是錯誤的。
uj5u.com熱心網友回復:
我認為這里發生的是編譯器的隱式提升。當您對兩個不同的原語進行比較時,編譯器會將其中一個提升為與另一個相同的型別。我相信規則是使用具有較大可能值的型別作為標準。因此,在 foo() 中,您隱式地將您的引數提升為帶符號的 long 型別,并且比較按預期作業。在 bar() 中,您的引數是無符號長整數,其最大值大于有符號長整數。在這里,編譯器將 -2L 提升為 unsigned long,這變成了一個非常大的數字。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/407560.html
標籤:
上一篇:如何防止矩陣訪問不允許的值
下一篇:C中的陣列操作函式
