GCC -Wuninitialized / -Wmaybe-неинициализированные проблемы

У меня возникла очень странная проблема с использованием gcc-4.7 (Ubuntu/Linaro 4.7.2-11precise2) 4.7.2. Я не могу скомпилировать следующий допустимый код без предупреждения:

extern void dostuff(void);

int test(int arg1, int arg2)
{
    int ret;

    if (arg1) ret = arg2 ? 1 : 2;

    dostuff();

    if (arg1) return ret;

    return 0;
}

Параметры компиляции и вывод:

$ gcc-4.7 -o test.o -c -Os test.c -Wall
test.c: In function ‘test’:
test.c:5:6: warning: ‘ret’ may be used uninitialized in this function [-Wmaybe-uninitialized]

Однако следующий код компилируется без предупреждения (хотя и с немного менее эффективной сборкой):

extern void dostuff(void);

int test(int arg1, int arg2)
{
    int ret;

    if (arg1 && arg2) ret = 1;
    if (arg1 && !arg2) ret = 2;

    dostuff();

    if (arg1) return ret;

    return 0;
}

Я несколько застрял и считаю это ошибкой компилятора. Какие-нибудь мысли?


person user593062    schedule 03.01.2013    source источник
comment
Вы, наверное, имеете в виду ret == arg2 ? 1 : 2;??   -  person Alok Save    schedule 03.01.2013
comment
Нет, синтаксис в порядке. Я имею в виду вернуть 0, если arg1=0, arg2=0, вернуть 1, если arg1=1, arg2=1, вернуть 2, если arg1=1, arg2=0. Этот фрагмент представляет собой упрощенный случай гораздо более серьезной проблемы, с которой я сталкиваюсь.   -  person user593062    schedule 03.01.2013
comment
Создание ret volatile также решает проблему, но на самом деле это не идеально.   -  person user593062    schedule 03.01.2013
comment
int ret; не означает ret == 0. Если вы измените int ret = 0;, это должно решить вашу проблему и также правильно вписаться в вашу логику. Код, который у вас есть в настоящее время, не инициализирует ret в 0.ret имеет неопределенное значение, поскольку это локальная/автоматическая переменная, которая не инициализирована явно. Но это не отвечает на основную аномалию.   -  person Alok Save    schedule 03.01.2013
comment
Спасибо, Алок, но, возможно, этот сконструированный пример приведет к ошибочным решениям. Плотность кода здесь чрезвычайно важна, а в реальной функции ret — это большой массив, который я не хочу инициализировать, если он не используется. Глядя на мою первую программу, ret действительно никогда не используется неинициализированным, поэтому предупреждение неверно, не так ли?   -  person user593062    schedule 03.01.2013
comment
Да, похоже, проблема с gcc. Небольшое исследование, и я смог его найти. Я добавил ответ.   -  person Alok Save    schedule 03.01.2013
comment
@AlokSave Приведенный выше код либо инициализируется и возвращает ret, либо не инициализируется, но при этом вообще никогда не использует ret. Ваше возражение неверно, код, показанный выше, наверняка никогда не получит доступ к ret до его инициализации, явная нулевая инициализация не требуется, и если компилятор заявляет что-то еще, он не работает.   -  person Mecki    schedule 24.12.2020


Ответы (2)


Действительно, это известная проблема в gcc.
gcc печально известен сообщениями об неправильные неинициализированные переменные.
Недостатки были должным образом отмечены, и предпринимаются усилия по их устранению:
Улучшенные неинициализированные предупреждения:

Сборник компиляторов GNU предупреждает об использовании неинициализированных переменных с опцией -Wuninitialized. Тем не менее, текущая реализация имеет некоторые очевидные недостатки. С одной стороны, некоторым пользователям хотелось бы более подробных и последовательных предупреждений. С другой стороны, некоторые пользователи хотели бы получать как можно меньше предупреждений. Цель этого проекта — реализовать обе возможности и в то же время улучшить текущие возможности.

Инициатива направлена ​​на предоставление более качественных предупреждений и приводит пример случая, похожего на ваш случай. Соответствующая часть:

То, что пользователь понимает как ложное срабатывание, может отличаться для конкретного пользователя. Некоторых пользователей интересуют случаи, которые скрыты из-за действий оптимизаторов в сочетании с текущим окружением. Однако многие пользователи этого не делают, поскольку этот случай скрыт, потому что он не может возникнуть в скомпилированном коде. Канонический пример

int x;
if (f ())
     x = 3;
return x;

где 'f' всегда возвращает ненулевое значение для текущей среды, и поэтому его можно оптимизировать. Здесь группа пользователей хотела бы получить неинициализированное предупреждение, поскольку 'f' может возвращать ноль при компиляции в другом месте. Тем не менее, другая группа пользователей посчитала бы ложным предупреждение о ситуации, которая не может возникнуть в компилируемом исполняемом файле.

person Alok Save    schedule 03.01.2013
comment
С одной стороны, некоторые пользователи хотели бы -pedantic и -Werror. Таким образом, эти пользователи должны отключить Wuninitialized для gcc 4, 5, 6, 7. В 2018 году он все еще не работает. - person puchu; 20.01.2019

Не уверен, что gcc уже исправлено. Если нет, вы можете попробовать clang. ИМХО, это гораздо лучший компилятор, и он намного лучше анализирует код.

Просто потому, что в некоторых комментариях утверждается, что компилятор прав, ret может использоваться неинициализированным, вот доказательство обратного. Код

int test(int arg1, int arg2)
{
    int ret;
    if (arg1) ret = arg2 ? 1 : 2;
    dostuff();
    if (arg1) return ret;
    return 0;
}

можно легко преобразовать в следующий код, просто объединив два идентичных оператора if в один:

int test(int arg1, int arg2)
{
    if (arg1) {
        int ret = arg2 ? 1 : 2;
        dostuff();
        return ret;
    }
    dostuff();
    return 0;
}

Это эквивалентный код, и теперь должно быть очевидно, что ret нельзя использовать неинициализированным. Компилятор ошибается, предупреждение бессмысленно.

Но опять же, код можно еще больше упростить:

int test(int arg1, int arg2)
{
    dostuff();
    return (arg1 ? (arg2 ? 1 : 2) : 0);
}

Проблема решена, ret больше нет.

person Mecki    schedule 24.12.2020