ฟังก์ชั่น“ ยืนยัน” คืออะไร?


262

ฉันกำลังศึกษาบทเรียนของ OpenCV และพบกับassertฟังก์ชั่น; มันทำอะไร?


20
สังเกตุข้อความจาก man assert: "assert () ถูกนำมาใช้เป็นมาโครถ้านิพจน์ที่ทดสอบมีผลข้างเคียงพฤติกรรมของโปรแกรมจะแตกต่างกันขึ้นอยู่กับว่ากำหนด NDEBUG ไว้หรือไม่ซึ่งอาจสร้าง Heisenbugs ซึ่งหายไปเมื่อเปิดการดีบัก ."
Johannes Schaub - litb


35
@ S.Lott ตอนนี้ผู้ที่ค้นหา google จะพบว่าหน้านี้เป็นหนึ่งในผลการค้นหาอันดับต้น ๆ ให้คำตอบที่ดีสำหรับเพื่อนของพวกเขาและให้การสนับสนุน Stack Overflow ในเวลาเดียวกันดังนั้นจึงเป็น +1 จากฉัน!
Matt Grum

6
มันคือ -1 จากฉัน ผลการค้นหาอันดับต้น ๆ ควรเป็นเอกสารที่ OP ควรพิจารณาในตอนแรก
Lightness Races ในวงโคจร

9
@LightnessRacesinOrbit โทรหาฉันขี้เกียจ แต่ฉันชอบที่จะใช้การย่อสรุปและอธิบายการอ่านเอกสารที่ผู้ใช้ที่มีความรู้อย่างที่คุณต้องการ ขอบคุณจากใจจริง ๆ :)
Tyson Hilmer

คำตอบ:


298

assertจะยุติโปรแกรม (โดยปกติจะมีข้อความที่อ้างถึงคำสั่ง assert) หากการโต้แย้งของมันกลายเป็นเท็จ โดยทั่วไปจะใช้ในระหว่างการดีบักเพื่อให้โปรแกรมล้มเหลวอย่างชัดเจนยิ่งขึ้นหากเกิดเหตุการณ์ที่ไม่คาดคิด

ตัวอย่างเช่น:

assert(length >= 0);  // die if length is negative.

คุณยังสามารถเพิ่มข้อความที่ให้ข้อมูลเพิ่มเติมเพื่อแสดงถ้ามันล้มเหลวเช่น:

assert(length >= 0 && "Whoops, length can't possibly be negative! (didn't we just check 10 lines ago?) Tell jsmith");

หรืออย่างอื่นเช่นนี้:

assert(("Length can't possibly be negative! Tell jsmith", length >= 0));

เมื่อคุณทำบิลด์ (ไม่ใช่ debug) คุณสามารถลบโอเวอร์เฮดของการประเมินผลassertข้อความโดยกำหนดNDEBUGแมโครโดยปกติแล้วจะมีคอมไพเลอร์สวิทช์ ข้อสรุปของสิ่งนี้คือโปรแกรมของคุณไม่ควรเชื่อถือการทำงานของแมโครยืนยัน

// BAD
assert(x++);

// GOOD
assert(x);    
x++;

// Watch out! Depends on the function:
assert(foo());

// Here's a safer way:
int ret = foo();
assert(ret);

จากการรวมกันของโปรแกรมเรียกยกเลิก () และไม่ได้รับประกันว่าจะทำอะไรยืนยันควรจะใช้เพื่อทดสอบสิ่งที่นักพัฒนาได้สันนิษฐานมากกว่าตัวอย่างเช่นผู้ใช้ป้อนตัวเลขมากกว่าตัวอักษร (ซึ่งควรจะเป็น จัดการโดยวิธีอื่น)


49
" assertมักจะทำให้เกิดข้อยกเว้น" - ใน C ++ มันจะไม่เพิ่มขึ้น "ยกเว้น" มันเรียกว่ายกเลิก ... มันแตกต่างกันเล็กน้อย
Artyom

5
ฉันไม่คิดว่าคำตอบนี้เกี่ยวกับภาษาเดียวกันกับคำถามที่ติดแท็ก (C และ C ++) ใน C และ C ++ การยืนยันไม่เพิ่มข้อยกเว้นมันมีวงเล็บล้อมรอบอาร์กิวเมนต์ของมันและ#ตัวละครไม่แนะนำความคิดเห็น
Steve Jessop

นี่คือตัวเลือกของคุณ: สร้างคำตอบ community-wiki และแก้ไขคำถามของคุณเพื่อลบสิ่งเก่า ๆ ออกไปโดยให้ผู้อื่นใส่ข้อมูลที่ถูกต้อง ด้วยวิธีนี้การลงคะแนนจะไม่ส่งผลกระทบต่อตัวแทนของคุณและผู้คนสามารถปรับปรุงคำตอบได้
Johannes Schaub - litb

2
length> = 0 จะกลายเป็น false ถ้าความยาวเป็น NaN
Andreas

1
ขณะนี้ใน VS 2015 การยืนยันพร้อมข้อความแสดงข้อผิดพลาดจะไม่ทำงานเพราะไม่เป็นไปตามคำสั่ง มันควรจะเป็นassert("error message", expression)
Dooskington

102

ยืนยันคำสั่งคอมพิวเตอร์จะคล้ายคลึงกับคำสั่งให้แน่ใจว่าในภาษาอังกฤษ


131
ก็ไม่มาก “ ตรวจสอบให้แน่ใจว่าแสงปิดอยู่” หมายความว่า“ ตรวจสอบแสงและปิดถ้าเปิดอยู่” ไม่“ ดูว่าไฟดับหรือไม่โปรดระเบิด” ฉันจะบอกว่า "ตรวจสอบอีกครั้ง" เป็นการแปลที่ใกล้เคียงกัน
ลุคเมาเรอร์

7
@ ลุคที่แสดงให้เห็นถึงสิ่งมหัศจรรย์ของภาษาอังกฤษในภาษานั้นมีหลายวิธีที่จะพูดในสิ่งเดียวกัน แต่ให้พิจารณายืนยัน (ความยาว> 0) ดังนั้นเราจึงสามารถแปลว่า"ตรวจสอบความยาวเป็นจำนวนมากกว่าศูนย์"หรือ"ให้แน่ใจว่าระยะเวลาที่มีค่ามากกว่าศูนย์" ในขณะที่ตัวเลือกทั้งสองทำงานอย่างชัดเจนฉันยังคงชอบของฉัน;)
Blake7

4
แต่การตีความอื่น ๆ คือ“ ทำให้ความยาวมากกว่าศูนย์” ดังนั้นจึงมีความกำกวมมากกว่าเล็กน้อย
Luke Maurer

29
มันคล้ายกับคำกริยา "ยืนยัน" อย่างใกล้ชิดมากขึ้น
Steve Carter

อันที่จริง "ตรวจสอบให้แน่ใจ" สามารถตีความได้ว่า "ตรวจสอบและถ้าไม่ตกลงเปลี่ยน"
Erik Bongers

14

ลองดูที่

โปรแกรมตัวอย่าง assert () ใน C ++

คอมไพเลอร์จำนวนมากเสนอมาโคร assert () แมโคร assert () ส่งคืน TRUE หากพารามิเตอร์ประเมิน TRUE และดำเนินการบางอย่างถ้าประเมิน FALSE คอมไพเลอร์จำนวนมากจะยกเลิกโปรแกรมบน assert () ที่ล้มเหลว คนอื่นจะโยนข้อยกเว้น

คุณลักษณะที่มีประสิทธิภาพอย่างหนึ่งของแมโคร assert () คือตัวประมวลผลก่อนยุบลงในโค้ดที่ไม่มีเลยหากไม่ได้กำหนด DEBUG มันเป็นความช่วยเหลือที่ยอดเยี่ยมในระหว่างการพัฒนาและเมื่อผลิตภัณฑ์ขั้นสุดท้ายจัดส่งไม่มีการปรับประสิทธิภาพหรือเพิ่มขนาดของเวอร์ชันโปรแกรมที่เรียกใช้งานได้

เช่น

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

void analyze (char *, int);

int main(void)
{
   char *string = "ABC";
   int length = 3;

   analyze(string, length);
   printf("The string %s is not null or empty, "
          "and has length %d \n", string, length);
}

void analyze(char *string, int length)
{
   assert(string != NULL);     /* cannot be NULL */
   assert(*string != '\0');    /* cannot be empty */
   assert(length > 0);         /* must be positive */
}

/****************  Output should be similar to  ******************
The string ABC is not null or empty, and has length 3

11
ไม่ควร "ถ้ากำหนด NDEBUG" หรือไม่
RichN

2
"คอมไพเลอร์มากมาย" เกี่ยวกับอะไร MSVC และ GCC เป็นไปตามมาตรฐานทั้งสองนี้ คอมไพเลอร์ที่ไม่สอดคล้องตามมาตรฐานใด: ไม่มีการยืนยัน อย่าพิมพ์ข้อความและยกเลิกเมื่อการยืนยันล้มเหลว ใช้ DEBUG แทน NDEBUG ที่เหมาะสมหรือไม่
Steve Jessop

lmao แน่นอนว่ามันเป็นเว็บไซต์ที่เรียกว่าตัวอย่างจาวาที่ทำให้สิ่งนี้ผิด
underscore_d

6

ฟังก์ชั่น assert () สามารถวินิจฉัยข้อบกพร่องของโปรแกรม ใน C มันถูกกำหนดไว้ใน<assert.h>และใน C ++ <cassert>มันถูกกำหนดไว้ใน ต้นแบบของมันคือ

void assert(int expression);

นิพจน์อาร์กิวเมนต์สามารถเป็นอะไรก็ได้ที่คุณต้องการทดสอบ - ตัวแปรหรือนิพจน์ C ใด ๆ ถ้า expression ประเมินค่าเป็น TRUE assert () จะไม่ทำอะไรเลย หาก expression ประเมินค่าเป็น FALSE assert () จะแสดงข้อความแสดงข้อผิดพลาดบน stderr และยกเลิกการทำงานของโปรแกรม

คุณใช้ assert () อย่างไร? มันถูกใช้บ่อยที่สุดในการติดตามข้อบกพร่องของโปรแกรม (ซึ่งแตกต่างจากข้อผิดพลาดในการรวบรวม) ข้อผิดพลาดไม่ได้ป้องกันโปรแกรมจากการรวบรวม แต่มันทำให้มันให้ผลลัพธ์ที่ไม่ถูกต้องหรือทำงานไม่ถูกต้อง (ล็อคเช่น) ตัวอย่างเช่นโปรแกรมวิเคราะห์ทางการเงินที่คุณเขียนอาจให้คำตอบที่ไม่ถูกต้อง คุณสงสัยว่าปัญหาเกิดจากตัวแปร interest_rate ที่รับค่าลบซึ่งไม่ควรเกิดขึ้น เพื่อตรวจสอบสิ่งนี้วางคำสั่ง

ยืนยัน (interest_rate> = 0); ที่สถานที่ในโปรแกรมที่ใช้ interest_rate หากตัวแปรกลายเป็นลบมาโคร assert () จะเตือนคุณ จากนั้นคุณสามารถตรวจสอบรหัสที่เกี่ยวข้องเพื่อค้นหาสาเหตุของปัญหา

เพื่อดูวิธีการยืนยัน () ทำงานเรียกใช้โปรแกรมตัวอย่างด้านล่าง หากคุณป้อนค่าที่ไม่ใช่ศูนย์โปรแกรมจะแสดงค่าและยกเลิกตามปกติ หากคุณป้อนศูนย์แมโครยืนยัน () จะบังคับให้ยุติโปรแกรมที่ผิดปกติ ข้อความแสดงข้อผิดพลาดที่แน่นอนที่คุณเห็นจะขึ้นอยู่กับคอมไพเลอร์ของคุณ แต่นี่เป็นตัวอย่างทั่วไป:

การยืนยันล้มเหลว: x, รายการไฟล์ 19_3.c, บรรทัดที่ 13 โปรดทราบว่าเพื่อให้ assert () ทำงานได้โปรแกรมของคุณจะต้องรวบรวมในโหมดการแก้ไขข้อบกพร่อง อ้างถึงเอกสารประกอบคอมไพเลอร์ของคุณสำหรับข้อมูลเกี่ยวกับการเปิดใช้งานโหมดดีบัก (ดังอธิบายในชั่วขณะ) เมื่อคุณคอมไพล์รุ่นสุดท้ายในโหมดนำออกใช้แมโคร assert () จะถูกปิดใช้งาน

 int x;

 printf("\nEnter an integer value: ");
 scanf("%d", &x);

 assert(x >= 0);

 printf("You entered %d.\n", x);
 return(0);

ป้อนค่าจำนวนเต็ม: 10

คุณป้อน 10

ป้อนค่าจำนวนเต็ม: -1

ข้อความแสดงข้อผิดพลาด: การยกเลิกโปรแกรมผิดปกติ

ข้อความแสดงข้อผิดพลาดของคุณอาจแตกต่างกันขึ้นอยู่กับระบบและคอมไพเลอร์ของคุณ แต่แนวคิดทั่วไปเหมือนกัน


คุณอธิบายว่าโปรแกรมตัวอย่างของคุณทำงานอย่างไรกับการยืนยัน ไม่ใช่วิธีที่ยืนยันตัวเองทำงาน มันจะจบโปรแกรมอย่างผิดปกติได้อย่างไร? มันมีข้อยกเว้นบางอย่าง? ชนิดไหน? มันสร้างสัญญาณพิเศษหรือไม่? สัญญาณอะไร การยืนยันสามารถ "ถูกจับ" โดยกลไกการทดลองแบบ C ++ ได้หรือไม่?
Motti Shneor

4

สิ่งที่ต้องการ 'ยกข้อยกเว้น' และ 'หยุดการทำงาน' อาจเป็นจริงสำหรับคอมไพเลอร์ส่วนใหญ่ แต่ไม่ใช่สำหรับทุกคน (BTW มีข้อความยืนยันที่ทำให้เกิดข้อยกเว้นจริง ๆ หรือไม่?)

นี่คือความหมายที่น่าสนใจและแตกต่างกันเล็กน้อยของการยืนยันที่ใช้โดย c6x และคอมไพเลอร์ TI อื่น ๆ : เมื่อเห็นข้อความยืนยันที่แน่นอนคอมไพเลอร์เหล่านี้จะใช้ข้อมูลในคำสั่งนั้นเพื่อทำการปรับให้เหมาะสม ชั่วร้าย

ตัวอย่างใน C:

int dot_product(short *x, short *y, short z)
{
  int sum = 0
  int i;

  assert( ( (int)(x) & 0x3 ) == 0 );
  assert( ( (int)(y) & 0x3 ) == 0 );

  for( i = 0 ; i < z ; ++i )
    sum += x[ i ] * y[ i ];
  return sum;
}

สิ่งนี้บอกให้คอมไพเลอร์อาร์เรย์ถูกจัดตำแหน่งบนขอบเขต 32- บิตดังนั้นคอมไพเลอร์สามารถสร้างคำสั่งเฉพาะสำหรับการจัดแนวแบบนั้นได้


1
ดังนั้นพวกเขาจะทำอย่างไรถ้าการยืนยันเป็นเท็จและไม่ได้ตั้งค่า NDEBUG หากนี่เป็นรุ่นของการยืนยันจาก <assert.h> ดังนั้นมาตรฐานกำหนดให้พิมพ์ข้อความและยกเลิก เห็นได้ชัดว่ามาตรฐานไม่ได้กล่าวว่าพวกเขาไม่ได้รับอนุญาตให้ปรับให้เหมาะสมตามความจริงของคำสั่ง แต่เพื่อให้เป็นไปตามมาตรฐานพวกเขายังคงต้องยกเลิกหากเป็นเท็จ
Steve Jessop

1
พวกเขาทำตามพฤติกรรมมาตรฐาน
stijn

1

ร่างมาตรฐาน C ++ 11 N3337

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2012/n3337.pdf

19.3 การยืนยัน

1 ส่วนหัว <cassert> ซึ่งอธิบายไว้ใน (ตารางที่ 42) จัดเตรียมแมโครสำหรับการทำเอกสารการยืนยันโปรแกรม C ++ และกลไกสำหรับปิดการใช้งานการตรวจสอบการยืนยัน

2 เนื้อหาเหมือนกันกับส่วนหัวของไลบรารีมาตรฐาน C <assert.h>

ร่างมาตรฐาน C99 N1256

http://www.open-std.org/JTC1/SC22/WG14/www/docs/n1256.pdf

7.2 การวินิจฉัย <assert.h>

1 ส่วนหัว<assert.h>กำหนดแมโครยืนยันและหมายถึงแมโครอื่นซึ่งไม่ได้ถูกกำหนดโดยNDEBUG <assert.h>หากNDEBUGถูกกำหนดให้เป็นชื่อแมโครที่จุดในไฟล์ต้นฉบับที่มี <assert.h> รวมอยู่แมโคร assert จะถูกกำหนดอย่างง่าย ๆ ดังนี้

 #define assert(ignore) ((void)0)

แมโครยืนยันจะถูกกำหนดใหม่ตามสถานะปัจจุบันของ NDEBUG ทุกครั้งที่ <assert.h>รวม

2. แมโครยืนยันจะต้องถูกนำมาใช้เป็นแมโครไม่ใช่ฟังก์ชันจริง หากคำจำกัดความของแมโครถูกระงับเพื่อให้สามารถเข้าถึงฟังก์ชันที่แท้จริงได้พฤติกรรมนั้นจะไม่ได้กำหนดไว้

7.2.1 การวินิจฉัยโปรแกรม

7.2.1.1 แมโครยืนยัน

สรุป

1

#include <assert.h>
void assert(scalar expression);

ลักษณะ

2 แมโคร assert ทำการทดสอบวินิจฉัยลงในโปรแกรม มันจะขยายเป็นนิพจน์ที่เป็นโมฆะ เมื่อมีการดำเนินการหากการแสดงออก (ซึ่งจะมีประเภทเซนต์คิตส์และเนวิส) เป็นเท็จ (นั่นคือเปรียบเทียบเท่ากับ 0) แมโครยืนยันยืนยันเขียนข้อมูลเกี่ยวกับการโทรเฉพาะที่ล้มเหลว (รวมถึงข้อความของอาร์กิวเมนต์ชื่อของ แฟ้มแหล่งที่มาจำนวนแหล่งบรรทัดและชื่อของฟังก์ชั่นปิดล้อม - หลังตามลำดับค่าของแมโครการ preprocessing __FILE__และ__LINE__และระบุ __func__) บนกระแสข้อผิดพลาดมาตรฐานในรูปแบบการดำเนินงานที่กำหนดไว้ 165) จากนั้นเรียกใช้ฟังก์ชันยกเลิก

ผลตอบแทน

3 แมโครยืนยันไม่คืนค่าใด ๆ


1

มีสามเหตุผลหลักสำหรับการใช้ฟังก์ชั่น assert () เหนือปกติถ้าอื่นและ printf

  1. ฟังก์ชั่น assert () ส่วนใหญ่จะใช้ในขั้นตอนการตรวจแก้จุดบกพร่องมันน่าเบื่อที่จะเขียนถ้ามีคำสั่ง printf ทุกครั้งที่คุณต้องการทดสอบเงื่อนไขที่อาจไม่ได้ทำในรหัสสุดท้าย

  2. ในการปรับใช้ซอฟต์แวร์ขนาดใหญ่ assert มีประโยชน์อย่างมากในการที่คุณสามารถทำให้คอมไพเลอร์เพิกเฉยต่อคำสั่ง assert โดยใช้มาโคร NDEBUG ที่กำหนดไว้ก่อนที่จะลิงก์ไฟล์ส่วนหัวสำหรับฟังก์ชั่น assert ()

  3. assert () มีประโยชน์เมื่อคุณกำลังออกแบบฟังก์ชั่นหรือโค้ดบางอย่างและต้องการที่จะรับความคิดเกี่ยวกับสิ่งที่ จำกัด รหัสที่จะและไม่ทำงาน


0

มันเป็นฟังก์ชั่นที่จะหยุดการทำงานของโปรแกรมหากค่าที่ประเมินไว้เป็นเท็จ โดยปกติจะล้อมรอบด้วยแมโครเพื่อไม่ให้คอมไพล์ลงในไบนารีผลลัพธ์เมื่อคอมไพล์ด้วยการตั้งค่ารีลีส

มันถูกออกแบบมาเพื่อใช้ในการทดสอบสมมติฐานที่คุณทำ ตัวอย่างเช่น:

void strcpy(char* dest, char* src){
    //pointers shouldn't be null
    assert(dest!=null);
    assert(src!=null);

    //copy string
    while(*dest++ = *src++);
}

อุดมคติที่คุณต้องการคือคุณสามารถทำให้เกิดข้อผิดพลาดในโปรแกรมของคุณเช่นการเรียกฟังก์ชันที่มีอาร์กิวเมนต์ที่ไม่ถูกต้องและคุณกดยืนยันก่อนที่จะ segfaults (หรือล้มเหลวในการทำงานตามที่คาดไว้)


ทำไมเราไม่ใช้เพียง & ถ้า & เพิ่มข้อมูลการบันทึก?
Asad Khan

1
เพราะคุณไม่ควรผ่านตัวชี้โมฆะเพื่อ strcpy มันเป็นหนึ่งในสิ่งเหล่านั้นที่ไม่ควรเกิดขึ้น หากตัวชี้โมฆะผ่านไปแล้วแสดงว่ามีบางสิ่งในระดับที่สูงกว่าเกิดความสับสน ที่ดีที่สุดคุณต้องล้มเหลวของโปรแกรมเพิ่มเติมจากปัญหาเนื่องจากค่าข้อมูลที่ไม่คาดคิด (ปลายทางไม่ถูกต้องหรือเป็นโมฆะ)
Yacoby

-5

นอกจากนี้คุณสามารถใช้เพื่อตรวจสอบว่าการจัดสรรแบบไดนามิกสำเร็จหรือไม่

ตัวอย่างรหัส:

int ** p;
p = new int * [5];      // Dynamic array (size 5) of pointers to int
for (int i = 0; i < 5; ++i) {
    p[i] = new int[3]; // Each i(ptr) is now pointing to a dynamic
                       // array (size 3) of actual int values
}

assert (p);            // Check the dynamic allocation.

คล้ายกับ:

if (p == NULL) {
    cout << "dynamic allocation failed" << endl;
    exit(1);
}

3
ไม่newส่งข้อยกเว้นเกี่ยวกับการจัดสรรล้มเหลวเว้นแต่คุณจะระบุnothrow(ซึ่งคุณไม่ได้อยู่ที่นี่) นอกจากนี้การจัดรูปแบบของคุณแปลกและexitชั่วร้าย
Lightness Races ในวงโคจร

ใช่คุณถูก. ฉันลืมที่จะไม่เพิ่ม ชอบที่จะดูว่าคุณจะใช้งานอย่างไรแทนที่จะออก
Sadikov

1
@Sadikov ทุกคนจะจัดการกับข้อผิดพลาดดังกล่าวโดยใช้รหัสที่ไม่ได้ถูกลบออกอย่างสมบูรณ์เมื่อสร้างในโหมดการปล่อยดังนั้นผู้ใช้ของคุณโดยไม่มีการป้องกันและความเมตตาของพฤติกรรมที่ไม่ได้กำหนดถ้าเงื่อนไขไม่เป็นที่พอใจและขอบคุณสิ่งนี้คือ ไม่เคยตรวจสอบและจับ assert()เป็นเพียงการแก้จุดบกพร่องและปั๊มออกสิ่งที่ไม่ควรเคยไม่เคยเกิดขึ้น - นานก่อนที่จะปล่อยสร้างทำ
underscore_d
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.