Spotkałem wiele zastosowań makro uninitialized_var() zaprojektowany, aby pozbyć się ostrzeżeń, takich jak:

warning: ‘ptr’ is used uninitialized in this function [-Wuninitialized]

Dla GCC ( <linux/compiler-gcc.h> ) Jest zdefiniowany taki sposób:

/*
 * A trick to suppress uninitialized variable warning without generating any
 * code
 */
#define uninitialized_var(x) x = x

Ale odkryłem też, że <linux/compiler-clang.h> ma to samo makro zdefiniowane w inny sposób:

#define uninitialized_var(x) x = *(&(x))

Dlaczego mamy dwie różne definicje? Z jakiego powodu pierwszy sposób może być niewystarczający? Czy pierwsza droga jest wystarczająca tylko dla klga lub w innych przypadkach?


Przykład:

#define uninitialized_var(x) x = x

struct some {
     int a;
     char b;
};

int main(void) {
     struct some *ptr;
     struct some *uninitialized_var(ptr2);

     if (1)
         printf("%d %d\n", ptr->a, ptr2->a); // warning about ptr, not ptr2
}
8
red0ct 6 marzec 2020, 15:45

2 odpowiedzi

Najlepsza odpowiedź

Kompilatory są uznawane za rozpoznawanie pewnych konstruktów jako wskazówki, że autor zamierzał coś celowo, gdy kompilator inaczej ostrzega o tym. Na przykład, biorąc pod uwagę if (b = a), GCC i Clang ostrzegają, że przydział jest używany jako warunkowy, ale nie ostrzegają o if ((b = a)), nawet jeśli jest równoważny pod względem standardu C. Ten konkretny konstrukt z dodatkowymi nawiasami po prostu został ustawiony jako sposób, aby powiedzieć kompilatorowi autor, który naprawdę zamierza ten kod.

Podobnie, x = x został ustawiony jako sposób, aby powiedzieć GCC, aby nie ostrzec o x jest niezainicjowany. Są chwile, w których funkcja może pojawić się , aby mieć ścieżkę kodu, w której obiekt jest używany bez zainicjowania, ale autor wie, że funkcja jest przeznaczona, aby nie być używana z parametrami, która kiedykolwiek spowodowałaby to szczególne Ścieżka kodu do wykonywania i ze względu na wydajność, chcą uciszyć ostrzeżenie kompilatora, a nie dodać inicjalizację, która nie jest właściwie niezbędna do poprawności programu.

Blang był prawdopodobnie zaprojektowany, aby nie rozpoznawać idiomu GCC dla tego i potrzebował innej metody.

5
Eric Postpischil 6 marzec 2020, 13:25

Dlaczego mamy dwie różne definicje?

Niejasny, ale spekuluję, że to dlatego, że Clanang nadal wytwarza ostrzeżenie dla x = x, gdy x jest niezainicjowany, ale nie dla x = *(&(x)). W prawie każdej okolicznościach * , w którym jedna z tych wyrażeń ma dobrze zdefiniowane zachowanie, drugi ma takie same dobrze zdefiniowane zachowanie. W innych okolicznościach, takich jak gdy wartość x jest niezdefiniowana lub nieokreślona, oba mają niezdefiniowane zachowanie, albo zachowanie x = x jest zdefiniowany i nieokreślony, więc ten ostatni dostarcza się brak przewagi.

Z jakiego powodu pierwszy sposób może być niewystarczający?

Ponieważ zachowanie zarówno jest niezdefiniowane w przypadkach użytkowania, dla których wydają się być przeznaczone. To wcale nie jest zaskakujące, że różni kompilatory radzą sobie z inaczej.

Czy pierwsza droga jest wystarczająca tylko dla klga lub w innych przypadkach?

Oba wyrażenia "znaczenie i zachowanie są niezdefiniowane . W pewnym sensie nie można bezpiecznie stwierdzić, że albo wystarczy na nic. W empirycznym poczuciu tego, czy przy użyciu jednego lub innych głupców niektórzy kompilatory nie emitują ostrzeżeń, że inaczej byliby, a nadal powinni , emitują, że są, są, są i / lub będą Kompilatory, które obsługują niezdefiniowane zachowanie związane z obydwoma tymi wyrażeniami inaczej niż GCC i Clang.


* Wyjątkiem, gdy x jest zadeklarowany za pomocą klasy pamięci register, w którym to przypadku druga ekspresja ma niezdefiniowane zachowanie niezależnie od tego, czy x ma dobrze zdefiniowany wartość.

-5
John Bollinger 6 marzec 2020, 13:11