2016-03-18 84 views
6

我一直在試圖正確地將一個字符數組轉換爲長整數strtol,檢查是否有上溢或下溢,然後對長整型進行轉換。一路上,我發現有大量的代碼看起來像這樣爲什麼你不能檢查errno是否等於ERANGE?

if ((result == LONG_MAX || result == LONG_MIN) && errno == ERANGE) 
{ 
    // Handle the error 
} 

爲什麼你不能只說

if(errno == ERANGE) 
{ 
    // Handle the error 
} 

從我的理解,如果發生溢或溢出,errno設置以兩種情況下的ERANGE。前者是否真的有必要?單獨檢查ERANGE可能會產生問題嗎?

這是我的代碼看起來是截至目前

char *endPtr; 
errno = 0; 
long result = strtol(str, &endPtr, 10); 

if(errno == ERANGE) 
{ 
    // Handle Error 
} 
else if(result > INT_MAX || result < INT_MIN) 
{ 
    // Handle Error 
} 
else if(endPtr == str || *endPtr != '\0') 
{ 
    // Handle Error 
} 

num = (int)result; 
return num; 

如果是前者,請讓我知道一個道理。

+0

通過您給我們的有限背景很難說清楚。 –

+0

我從來沒有見過一個很好的解釋,爲什麼有必要檢查'LONG_MAX/LONG_MIN'和'ERANGE'。除了手冊頁顯示這一事實。我能想到的唯一明智的用例是區分溢出和下溢。我也有興趣知道是否有其他原因。 – kaylum

+0

@kaylum我不知道我覺得我的例子是正確的,主要是因爲我不打算區分是否發生溢出或下溢,並且在兩種情況下errno都設置爲ERANGE。如果其中任何一個發生,比結果無效。 –

回答

5

第一個代碼片段只是錯誤的,我會解釋爲什麼以後,但首先我們需要一些背景。

errno是一個thread-local變量。當系統調用或某些庫函數失敗時,它被設置爲非零值。系統調用成功時,它保持不變。所以它總是包含失敗的上次調用的錯誤號。

這意味着你有兩種選擇。在每次調用之前將errno設置爲0,或者使用errno的標準慣用語。下面是標準的成語

if (foo() == some_value_that_indicates_that_an_error_occurred) 
    then the value in errno applies to foo 
else 
    foo succeeded and the errno must be ignored because it could be anything 

大多數程序員將使用標準的成語僞代碼,因爲每個系統調用之前設置errno爲0是煩人的,重複的,重複的,煩人,惱人的重複。更何況你可能會忘記在實際上很重要的地方將errno設置爲0。


回到第一個代碼片段。這是錯誤的,因爲strtol沒有明確表示strtol失敗的返回值。如果strtol返回LONG_MAX,則可能是發生了錯誤,或者該字符串實際上包含數字LONG_MAX。沒有辦法知道strtol調用是成功還是失敗。這意味着標準成語(這是第一個代碼片段試圖實現的內容)不能與strtol一起使用。

要正確使用strtol,則需在通話之前設置errno爲0,這樣

errno = 0; 
result = strtol(buffer, &endptr, 10); 
if (errno == ERANGE) 
{ 
    // handle the error 
    // ERANGE is the only error mentioned in the C specification 
} 
else if (endptr == buffer) 
{ 
    // handle the error 
    // the conversion failed, i.e. the input string was empty, 
    // or only contained whitespace, or the first non-whitespace 
    // character was not valid 
} 

注意一些實現定義errno其他非零值。有關詳細信息,請參閱適用手冊頁。

+1

好的答案,但有一點:嚴格來說,'strtol'是一個庫函數,而不是系統調用。而且它操作'errno'的方式很不尋常。大多數(所有?)系統調用在失敗時設置「errno」,但很少有庫函數可以。 –

+0

感謝您的迴應。如果你不想問我,但是有可能'errno!= 0 && result == 0'?我已經看到了代碼,在那裏他們合併,並且如果((結果== LONG_MAX ||結果== LONG_MIN)&& errno == ERANGE)'就像你在調用Strol轉換A123之前將errno設置爲0一樣。您希望結果爲0,並且errno保持爲零。在那種情況下可能會導致errno不爲零? –

+0

@SteveSummit確實如此,但我不確定如何在不引入不必要的複雜性的情況下將其納入答案。我希望可以留下評論。 – user3386109

2

如果你打電話

result = strtol("-2147483648", NULL, 0); 

result = strtol("2147483647", NULL, 0); 
在32位機器上

,你會得到LONG_MINLONG_MAXresult,儘管還沒有一個錯誤。

正如user3386109解釋的那樣,從strtol檢測錯誤的一種方法是首先將errno設置爲0。另一種方法是讓它給你一個結束指針並看看它。有三種或四種情況:

char *endptr; 
long int result = strtol(str, &endptr, 10); 
if(*str == '\0') { 
    /* str was empty */ 
} else if(endptr == str) { 
    /* str was completely invalid */ 
} else if(*endptr != '\0') { 
    /* numeric result followed by trailing nonnumeric character(s) */ 
} else { 
    /* str was a completely valid number (perhaps with leading whitespace) */ 
} 

根據您的需要,前兩種或三種情況可能會一起摺疊。您可能需要擔心(a)「完全有效的數字」是否可表示(您可以使用errno進行測試),以及(b)是否有任何「尾隨的非數字字符」是無害的空格(唉,strtol不檢查你,所以如果你在意你必須檢查你自己)。

+0

所以說if(result> INT_MAX || result

+0

@LuisAverhoff對不起,我說'INT_MAX'的意思是'LONG_MAX'。 (現在修正了。)但總的來說,我會說沒有任何理由明確測試'strtol'的返回值與任何最小或最大值。 (如果要將值存儲到純int中,唯一的原因可能是與「INT_MIN」和「INT_MAX」進行比較。) –

+0

是的,我正在將該值存儲到純int中。 –

相關問題