Co to jest funkcja „potwierdzająca”?

261

Studiowałem samouczki OpenCV i natknąłem się na tę assertfunkcję; co to robi?

eomer
źródło
20
Zwróć uwagę na notatkę man assert: „assert () jest zaimplementowany jako makro; jeśli testowane wyrażenie ma skutki uboczne, zachowanie programu będzie się różnić w zależności od tego, czy zdefiniowano NDEBUG. Może to spowodować Heisenbugs, które znikną po włączeniu debugowania . ”
Johannes Schaub - litb
35
@ S.Lott teraz ludzie szukający google znajdą tę stronę jako jeden z najlepszych wyników wyszukiwania, zapewniając dobrą, sprawdzoną odpowiedź na swoje pytanie i jednocześnie promując Przepełnienie stosu, więc jest to +1 ode mnie!
Matt Grum,
6
To -1 ode mnie. Najlepszym wynikiem wyszukiwania powinna być dokumentacja , z którą PO powinien był zapoznać się w pierwszej kolejności.
Wyścigi lekkości na orbicie
9
@LightnessRacesinOrbit. Nazywaj mnie leniwym, ale wolę korzystać z doskonałych, skondensowanych, podsumowanych i objaśnionych dokumentów, które dostarczają znawcy SO, tacy jak ty. Z całego serca dziękuję :)
Tyson Hilmer

Odpowiedzi:

297

assertzakończy program (zwykle z komunikatem cytującym instrukcję assert), jeśli jego argument okaże się fałszywy. Jest to powszechnie używane podczas debugowania, aby program w bardziej oczywisty sposób zawiódł, jeśli wystąpi nieoczekiwany warunek.

Na przykład:

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

Możesz także dodać bardziej pouczający komunikat, który będzie wyświetlany, jeśli tak się nie powiedzie:

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

Lub inaczej:

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

Podczas wykonywania kompilacji wydania (bez debugowania) można również usunąć narzut związany z ocenianiem assertinstrukcji, definiując NDEBUGmakro, zwykle za pomocą przełącznika kompilatora. Następstwem tego jest to, że twój program nigdy nie powinien polegać na działającym makrze assert.

// 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);

Z kombinacji programu wywołującego abort () i bez zagwarantowania, że ​​zrobi cokolwiek, twierdzenia powinny być używane tylko do testowania rzeczy, które programista założył, a nie na przykład przez wprowadzenie liczby zamiast litery (która powinna być obsługiwane w inny sposób).

bdonlan
źródło
49
assertzwykle zgłasza wyjątek” - w C ++ nie powstaje „wyjątek”, który nazywa przerwaniem… jest trochę inny.
Artyom,
5
Nie sądzę, że ta odpowiedź dotyczy tych samych języków, w których pytanie jest oznaczone (C i C ++). W C i C ++ assert nie zgłasza wyjątku, ma nawiasy wokół swojego argumentu, a #znak nie wprowadza komentarza.
Steve Jessop
Oto twoje opcje: Utwórz odpowiedź wiki-społeczności i edytuj swoje pytanie, usuwając stare elementy, prosząc innych o podanie poprawnych informacji. W ten sposób opinie negatywne nie wpłyną na twojego przedstawiciela, a ludzie mogą poprawić odpowiedź.
Johannes Schaub - litb
2
length> = 0 również okaże się false, jeśli długość wynosi NaN
Andreas
1
Obecnie w VS 2015 potwierdzenie z komunikatem o błędzie nie będzie działać, ponieważ nie działa. Powinno byćassert("error message", expression)
Dooskington,
102

Assert oświadczenie komputer jest analogiczna do rachunku Upewnij się w języku angielskim.

Blake7
źródło
131
Cóż, niezupełnie. „Upewnij się, że światło jest wyłączone” oznacza „sprawdź światło i wyłącz je, jeśli jest włączone”, a nie „sprawdź, czy światło jest wyłączone, a jeśli nie, proszę eksplodować”. Powiedziałbym, że „podwójne sprawdzenie” jest dokładniejszym tłumaczeniem.
Luke Maurer
7
@Luke, który ilustruje cuda języka angielskiego, ponieważ język oferuje wiele sposobów na powiedzenie tego samego. Ale rozważ aser (długość> 0). Możemy więc przetłumaczyć to na „podwójne sprawdzenie, czy długość jest większa od zera” lub „upewnić się, że długość jest większa od zera” . Choć wyraźnie obie opcje działają, nadal wolę moją;)
Blake7
4
Cóż, ale inna interpretacja to „ spraw, aby długość była większa od zera”. Jest więc więcej niejasności.
Luke Maurer
29
Jest to bardziej analogiczne do angielskiego czasownika „asert”
Steve Carter
Rzeczywiście „upewnij się” można interpretować jako „sprawdź, a jeśli nie jest ok, zmień”.
Erik Bongers
14

Spojrzeć na

przykładowy program assert () w C ++

Wiele kompilatorów oferuje makro assert (). Makro assert () zwraca wartość PRAWDA, jeśli jego parametr ocenia wartość PRAWDA i podejmuje działanie, jeśli ocenia wartość FAŁSZ. Wiele kompilatorów przerwie program z powodu niepowodzenia assert (); inni zgłoszą wyjątek

Jedną z potężnych funkcji makra assert () jest to, że preprocesor nie zwija go w żaden kod, jeśli DEBUG nie jest zdefiniowany. Jest to wielka pomoc podczas programowania, a kiedy produkt końcowy jest wysyłany, nie ma ograniczenia wydajności ani wzrostu wielkości wykonywalnej wersji programu.

Na przykład

#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
Rahul
źródło
11
Nie powinno być „jeśli zdefiniowano NDEBUG”?
RichN
2
O co chodzi w tym „wielu kompilatorach”? Zarówno MSVC, jak i GCC są zgodne ze standardami w tym zakresie. Które niezgodne kompilatory: nie mają potwierdzenia; nie drukuj wiadomości i nie przerywaj, gdy asert nie powiedzie się; użyć DEBUG zamiast właściwego NDEBUG?
Steve Jessop
lmao, oczywiście, to jest strona o nazwie java-samples, która robi to tak źle.
underscore_d
6

Funkcja assert () może diagnozować błędy programu. W C jest on zdefiniowany w <assert.h>, aw C ++ jest zdefiniowany w <cassert>. Jego prototyp to

void assert(int expression);

Wyrażeniem argumentu może być wszystko, co chcesz przetestować - zmienna lub dowolne wyrażenie C. Jeśli wyrażenie ma wartość PRAWDA, assert () nic nie robi. Jeśli wyrażenie ma wartość FAŁSZ, assert () wyświetla komunikat o błędzie na stderr i przerywa wykonywanie programu.

Jak używasz assert ()? Najczęściej służy do śledzenia błędów programu (które różnią się od błędów kompilacji). Błąd nie uniemożliwia kompilacji programu, ale powoduje, że daje on niepoprawne wyniki lub działa nieprawidłowo (na przykład blokuje się). Na przykład program analizy finansowej, który piszesz, może czasami dawać nieprawidłowe odpowiedzi. Podejrzewasz, że problem jest spowodowany zmienną interest_rate przyjmującą wartość ujemną, co nigdy nie powinno się zdarzyć. Aby to sprawdzić, umieść instrukcję

assert (stopa_oprocentowania> = 0); w miejscach w programie, w których używana jest stopa_odsetek. Jeśli zmienna kiedykolwiek stanie się ujemna, makro assert () wyświetli ostrzeżenie. Następnie możesz sprawdzić odpowiedni kod, aby zlokalizować przyczynę problemu.

Aby zobaczyć, jak działa assert (), uruchom poniższy przykładowy program . Jeśli wprowadzisz wartość niezerową, program wyświetli tę wartość i zakończy się normalnie. Jeśli wpiszesz zero, makro assert () wymusza nieprawidłowe zakończenie programu. Dokładny komunikat o błędzie zależy od kompilatora, ale oto typowy przykład:

Asercja nie powiodła się: x, plik list19_3.c, wiersz 13 Zauważ, że aby assert () działał, twój program musi być skompilowany w trybie debugowania. Informacje na temat włączania trybu debugowania znajdują się w dokumentacji kompilatora (jak wyjaśniono za chwilę). Podczas późniejszej kompilacji ostatecznej wersji w trybie wydania makra assert () są wyłączone.

 int x;

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

 assert(x >= 0);

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

Wprowadź wartość całkowitą: 10

Wpisałeś 10.

Wprowadź wartość całkowitą: -1

Komunikat o błędzie: Nieprawidłowe zakończenie programu

Twój komunikat o błędzie może się różnić w zależności od systemu i kompilatora, ale ogólny pomysł jest taki sam.

Abhishek Kaushik
źródło
Wyjaśniłeś, jak twój przykładowy program działa z asersem. Nie to, jak działa sam assert. Jak to nienormalnie kończy program? czy to rzuca jakiś wyjątek? Jaki rodzaj? Czy to tworzy specjalny sygnał? jaki sygnał? Czy twierdzenia mogą zostać „złapane” przez jakikolwiek mechanizm C ++ try-catch?
Motti Shneor,
4

Rzeczy takie jak „podnosi wyjątek” i „zatrzymuje wykonywanie” mogą być prawdziwe dla większości kompilatorów, ale nie dla wszystkich. (BTW, czy istnieją twierdzenia, które naprawdę zgłaszają wyjątki?)

Oto interesujące, nieco inne znaczenie asercji używane przez c6x i inne kompilatory TI: po obejrzeniu niektórych instrukcji aser, te kompilatory wykorzystują informacje zawarte w tej instrukcji do przeprowadzenia pewnych optymalizacji. Niegodziwy.

Przykład w 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;
}

To mówi de kompilatorowi, że tablice są wyrównane do granic 32-bitowych, więc kompilator może generować określone instrukcje wykonane dla tego rodzaju wyrównania.

stijn
źródło
1
Co więc robią, jeśli twierdzenie jest fałszywe, a NDEBUG nie jest ustawiony? Jeśli jest to wersja aser z <assert.h>, wówczas standard wymaga, aby wydrukował komunikat i przerwał. Oczywiście standard nie mówi, że nie wolno im optymalizować w oparciu o prawdziwość oświadczenia, ale aby zachować zgodność, nadal muszą przerwać, jeśli jest to nieprawda.
Steve Jessop
1
idą standardowe zachowanie
stijn
1

Wersja standardowa C ++ 11 N3337

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

19.3 Twierdzenia

1 Nagłówek <csert>, opisany w (Tabela 42), zawiera makro do dokumentowania asercji programu C ++ oraz mechanizm wyłączania kontroli asercji.

2 Zawartość jest taka sama jak nagłówek biblioteki Standard C <assert.h>.

Wersja standardowa C99 N1256

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

7.2 Diagnostyka <assert.h>

1 Nagłówek <assert.h>definiuje makro assert i odnosi się do innego makra, NDEBUGktóre nie jest zdefiniowane przez <assert.h>. Jeśli NDEBUGjest zdefiniowane jako nazwa makra w punkcie pliku źródłowego, w którym znajduje się <assert.h>, makro assert jest definiowane po prostu jako

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

Makro assert jest redefiniowane zgodnie z bieżącym stanem NDEBUG za każdym razem, gdy <assert.h>jest dołączane.

2. Makro potwierdzające jest realizowane jako makro, a nie jako rzeczywista funkcja. Jeśli definicja makra zostanie pominięta w celu uzyskania dostępu do faktycznej funkcji, zachowanie jest niezdefiniowane.

7.2.1 Diagnostyka programu

7.2.1.1 Makro asertywne

Streszczenie

1.

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

Opis

2 Makro assert umieszcza testy diagnostyczne w programach; rozwija się w pustkę. Gdy jest wykonywane, jeśli wyrażenie (które powinno mieć typ skalarny) jest fałszywe (to znaczy porównuje równe 0), makro potwierdzenia zapisuje informacje o konkretnym wywołaniu, które się nie powiodło (w tym tekst argumentu, nazwa plik źródłowy, numer linii źródłowej i nazwa funkcji zamykającej - te ostatnie to odpowiednio wartości makr przetwarzania wstępnego __FILE__i __LINE__identyfikatora __func__) w standardowym strumieniu błędów w formacie zdefiniowanym przez implementację. 165) Następnie wywołuje funkcję przerwania.

Zwroty

3 Makro aser nie zwraca żadnej wartości.

Ciro Santilli
źródło
1

Istnieją trzy główne powody używania funkcji assert () w porównaniu do normalnej metody if if i printf

  1. Funkcja assert () jest używana głównie w fazie debugowania, żmudne jest pisanie za pomocą instrukcji printf za każdym razem, gdy chcesz przetestować warunek, który może nawet nie dostać się do końcowego kodu.

  2. W dużych wdrożeniach oprogramowania funkcja assert jest bardzo przydatna, gdy kompilator może ignorować instrukcje assert przy użyciu zdefiniowanego makra NDEBUG przed połączeniem pliku nagłówka z funkcją assert ().

  3. assert () przydaje się, gdy projektujesz funkcję lub jakiś kod i chcesz dowiedzieć się, jakie ograniczenia będzie działał, a kod nie zadziała, a na końcu dołącz, jeśli jeszcze, do oceny go w zasadzie grając z założeniami.

Nnaik
źródło
0

Jest to funkcja, która zatrzyma wykonywanie programu, jeśli oceniona przez nią wartość jest fałszywa. Zazwyczaj jest on otoczony przez makro, dzięki czemu nie jest kompilowany w wynikowy plik binarny po skompilowaniu z ustawieniami wersji.

Jest przeznaczony do testowania przyjętych założeń. Na przykład:

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

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

Idealnym rozwiązaniem jest to, że możesz popełnić błąd w swoim programie, na przykład wywołać funkcję z niepoprawnymi argumentami, a trafisz na aser, zanim ulegnie on awarii (lub nie działa zgodnie z oczekiwaniami)

Yacoby
źródło
dlaczego nie używamy po prostu, jeśli & else i dodajemy jakieś dane logowania?
Asad Khan
1
Ponieważ nigdy nie powinieneś przekazywać wskaźnika pustego do strcpy. Jest to jedna z tych rzeczy, które nie powinny się zdarzyć. Jeśli wskaźnik zerowy został przekazany, oznacza to, że coś na wyższym poziomie się popsuło. W najlepszym razie musisz zakończyć awarię programu dalej od problemu z powodu nieoczekiwanych wartości danych (dest jest niepoprawny lub zerowy).
Yacoby
-5

Ponadto można go użyć do sprawdzenia, czy alokacja dynamiczna zakończyła się powodzeniem.

Przykład kodu:

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.

Podobny do:

if (p == NULL) {
    cout << "dynamic allocation failed" << endl;
    exit(1);
}
Sadikow
źródło
3
Nie. newZgłasza wyjątek w przypadku niepowodzenia alokacji, chyba że podasz nothrow(czego nie tutaj). Co więcej, twoje formatowanie jest dziwne i exitzłe.
Wyścigi lekkości na orbicie
Tak masz rację. Zapomniałem nie dodawać. Chciałbym zobaczyć, jak użyjesz zamiast wyjść
Sadikov
1
@Sadikov Ktoś inny poradziłby sobie z takim błędem za pomocą kodu, który nie jest całkowicie usuwany podczas budowania w trybie zwolnienia , pozostawiając tym samym użytkowników bez ochrony i na łaskę niezdefiniowanego zachowania, jeśli warunek wstępny nie jest spełniony, a dzięki temu jest nigdy nie sprawdzony i nie złapany . assert()służy tylko do debugowania i oznaczania rzeczy, które nigdy, nigdy, nigdy nie powinny się wydarzyć - na długo przed wydaniem kompilacji wydania.
underscore_d