簡體   English   中英

如何在該函數中核心地使用assert?

[英]How to use assert corectly in this function?

我有以下功能,並且在兩個地方都使用了assert 我想知道我在哪里以錯誤的方式使用assert以及為什么。 第一個是錯誤的,因為我們不能在用戶輸入上使用assert。 第二個:我們可以使用assert來檢查malloc成功嗎? 其余的我仍然不知道為什么。 你能幫忙嗎? 我想簡短地解釋在給定的位置上assert

#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <malloc.h>

#define num 10
int hh(char *);

您可以定義對與錯,這取決於您,但是作為一般規則,您不希望在斷言中使用程序邏輯(因此,檢查輸入字符串的長度是一種不好的用法)。

請記住:assert僅在調試模式下處於活動狀態,因此,如果您依靠它進行錯誤檢查(幾乎像所有示例一樣),則在發布模式下會發生奇怪的事情。

即assert通常是一個定義,例如:

/* assert() only does something if NDEBUG is NOT defined */
#ifdef NDEBUG
#else
#define assert(x) _assert(x)
#endif

請參閱-DNDEBUG通常來自哪里? 有關NDEBUG的更多信息

您絕對不想使用斷言來更改流程,例如

assert(some_function()); /* some_function only called if NDEBUG is not defined */

使用assert來檢查malloc的返回:如果未定義NDEBUG,則檢查不會完成,因此從理論上講,您可以退出並訪問NULL指針。 我會說這不安全。 請參閱使用斷言處理錯誤檢查更多討論。

讓我們通過簡單的“僅在調試中”過濾器一一看待您的斷言:

assert(argc==2);
/*
 * If NDEBUG is defined and argc == 1 you get through,
 * so anything that uses argc[1] will give undefined behavior.
 * = BAD USE
 */
 char *s = malloc(N);
 /* 2 */
 assert(s!=NULL);
 scanf("%s", s
 /*
  * If NDEBUG is defined and malloc fails you keep going and read into 
  * NULL = undefined behaviour = BAD
  */

 assert(strlen(s)<N);
 /*
  * If NDEBUG is defined keeps going even in strlen >= N.
  * How bad that is depends on the code - if you are checking the size
  * before putting it in a buffer, then it's bad.
  * To me it is logic in the assert and so I say BAD
  */
 /* 4 */
 assert(!*(s+strlen(s)));
 /*
  * First of that's just silly code.
  * If that comes to my code review you'll be removing or rewriting
  * it - strlen relies on the NUL, so how can it fail?
  * It is logic in the assert - If NDEBUG is set you'll keep going
  * even if the condition is not met. How bad that is?
  */
 /* 5 */
 assert(atol(s));
 /*
  * Whoa. If NDEBUG is set and s = "Hello" we still keep going...
  * What is atol("Hello") ?
  */
 printf("%ld\n", 100000000/atol(s));
 free(s);
 return 0;
}
int f(char *s)
{
 /* 6 */
 assert(s!=NULL);
 /*
  * Again if NDEBUG is defined the check disappears and so if s = NULL 
  * then you dereference a NULL pointer which is undefined behavior
  */
 return !*s;

我希望此答案通過努力查看斷言示例的有意義用法來提供您的斷言的替代視圖。 否則,我同意譴責大多數問題的答案。

斷言用於驗證假設,即斷言它們是正確的。
認為一個斷言是

  • 一種自我記錄的方法,說明
    “在下面的代碼中,我盲目地假設以下表達式始終為真。如果不是這種情況,則我控制范圍之外的某些事情是非常錯誤的,並且此代碼無法執行應做的事情。”
    無論斷言是否處於活動狀態,此自我文檔始終有效。
  • 一種技術實現,如果處於活動狀態,則將在運行時驗證表達式是否為真,或者在失敗的情況下會發出警報;
    即它將使用某種適當的方法來引起某人的注意
    (這種引起注意的方法可能相差很大;在深度嵌入式平台上,該方法可能會變得非常有趣...)
    注意,斷言通常以可以停用的方式實現。 這樣做的目的是消除它們的時間消耗,代碼大小的消耗以及可能產生的混亂輸出。 因此,有時會在開發/測試結束時在交付軟件之前取消激活它們。 引起注意的方法可能不僅令人不快,而且實際上很危險。 想一想飛機上的軟件,在空中遇到了失敗的假設……某個地方的軟件肯定是一個錯誤。 但是,您不希望在空中方便地顯示有用的調試信息。 您只希望飛機將其吸起並以某種方式盡快恢復正常運行。
    因此,技術實施部分僅對系統發生的問題有用,即可以假設在正常使用中可能發生的情況也會在測試/開發情況下發生。 對於大多數種類的規范,都是這種情況。 暫時性問題或罕見問題(即僅在不可預測的情況下發生的問題)不是這種情況。

單獨查看斷言示例:

  1. 盲目假設輸入參數的數量為2似乎有點大膽。
    但是,如果程序的環境定義得非常嚴格,這是可以想象的。 您的程序可能是管道設置的一部分,或者只能從Makefile中調用。 對輸入自變量數量的斷言將反映體系結構的更大范圍,並且將支持由多個開發人員維護的系統中的錯誤檢測。
    但是,可以通過檢入程序代碼並在未成功終止之前給出有用的錯誤消息來更好地實現該目的。
  2. 盲目假設malloc不返回零是一個普遍存在的錯誤。 使用斷言來進行明確的聲明實際上至少是在不檢查的情況下使用malloc的一種改進,它至少可以檢測並發出問題的信號。 由系統內存在異常情況下耗盡導致的malloc故障是無法通過斷言(即不是“系統地”,不是“針對規范的濫用”)有效檢測到的問題的示例。
    同樣,以合適的錯誤消息和不成功的術語檢入程序代碼會更好。
  3. 盲目假設最大輸入長度是大膽的。 什么應該使人類不遵守該規則? 特別是如果沒有輸出禁止更長的輸入。
    像1一樣,這在嚴格定義的更大的體系結構中是可以想象的,在這種體系結構中,人類不會遇到障礙。
    同樣,即使使用make工具作為用戶,使用錯誤消息檢入代碼也會更好。
  4. 我同意約翰的觀點,我簡直不知道這個斷言怎么會失敗。
    斷言應描述實際上可能因C語法和語義而失敗的假設。 相比之下,我看assert(false)次數比我想象的要多。 這至少意味着“我盲目地認為運行時執行永遠不會在這里結束”。 但是非常奇怪,因此需要大量的(通常是缺失的)注釋說明。
  5. 另一個“人不在乎”的假設,在make的上下文中可能是有道理的。
    在運行時進行更好的檢查和錯誤報告。
  6. 這是在函數開始時的典型斷言,該函數使用指針並依賴於“承諾”,即僅使用非NULL指針進行調用。 因此,在規范中的某處應該有一些“只能使用非NULL指針調用此函數”。
    在運行時使用錯誤消息和不成功的終止進行檢查仍然是明智的,以便在停用斷言時也能針對NULL指針進行魯棒處理。 但是,這可能會浪費時間和代碼大小。 與對代碼質量,規范遵守和測試深度的信心相比,兩者都越昂貴。

暫無
暫無

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

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