Различные способы подавления «неинициализированных переменных предупреждений» в C - PullRequest
7 голосов
/ 06 марта 2020

Я обнаружил многократное использование макроса uninitialized_var(), предназначенного для избавления от таких предупреждений, как:

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

Для G CC (<linux/compiler-gcc.h>) он определен таким образом:

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

Но я также обнаружил, что <linux/compiler-clang.h> имеет один и тот же макрос, определенный по-другому:

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

Почему у нас два разные определения? По какой причине первый путь может оказаться недостаточным? Не достаточно ли первого способа только для Clang или в некоторых других случаях?


пример:

#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
}

Ответы [ 2 ]

5 голосов
/ 06 марта 2020

Компиляторы создаются для того, чтобы распознавать определенные конструкции как признаки того, что автор намеренно что-то намеревался, когда компилятор иначе предупредил бы об этом. Например, данные if (b = a), G CC и Clang предупреждают, что присвоение используется как условное, но они не предупреждают о if ((b = a)), даже если оно эквивалентно с точки зрения стандарта C. Эта конкретная конструкция с дополнительными скобками была просто установлена ​​как способ сообщить компилятору, что автор действительно намеревается использовать этот код.

Аналогично, x = x был задан как способ сказать G CC не предупреждать о x неинициализирован. Есть моменты, когда функция может появиться , чтобы иметь путь к коду, в котором объект используется без инициализации, но автор знает, что функция не предназначена для использования с параметрами, которые когда-либо вызывают этот конкретный код путь, который должен быть выполнен, и из соображений эффективности они хотят заставить замолчать предупреждение компилятора, а не добавлять инициализацию, которая на самом деле не нужна для корректности программы.

Clang был предположительно разработан, чтобы не распознавать идиому GCC для этого и нужен другой метод.

0 голосов
/ 06 марта 2020

Почему у нас есть два разных определения?

Неясно, но я предполагаю, что это потому, что Clang по-прежнему выдает предупреждение для x = x, когда x не инициализирован, но не для x = *(&(x)). Почти при всех обстоятельствах *, в которых одно из этих выражений имеет четко определенное поведение, другое имеет такое же четко определенное поведение. При других обстоятельствах, например, когда значение x является неопределенным или неопределенным, оба имеют неопределенное поведение, или поведение x = x определено, и поведение x = *(&(x)) неопределено, поэтому последнее не дает никаких преимуществ.

По какой причине первый способ может оказаться недостаточным?

Поскольку поведение обоих не определено в тех случаях использования, для которых они, по-видимому, предназначены. Поэтому совсем не удивительно, что разные компиляторы обрабатывают их по-разному.

Первый способ недостаточен только для Clang или в некоторых других случаях?

Оба значение и поведение выражений undefined . Таким образом, в одном смысле нельзя с уверенностью заключить, что одного из них достаточно для чего-либо. В эмпирическом смысле того, использует ли тот или иной дурак определенные компиляторы, чтобы они не выдавали предупреждений о том, что они в противном случае будут, и, тем не менее, должен излучать, вполне вероятно, что были, есть и / или будут компиляторы которые обрабатывают неопределенное поведение, связанное с обоими этими выражениями, иначе, чем G CC и Clang.


* Исключение составляют случаи, когда x объявляется с register класс хранения, в этом случае второе выражение имеет неопределенное поведение независимо от того, имеет ли x четко определенное значение.

...