Подтвердить что ты не робот

Игнорирование возвращаемых значений в C

В последнее время я начал использовать lint для анализа статического кода. Одно из предупреждений, которые я иногда получаю, касается этой проблемы. Скажем, например, что у меня есть следующая функция:

uint32_t foo( void );

И скажем, что я небрежно игнорирую возвращаемое значение функции. Чтобы предупреждение исчезло, можно написать

(void) foo();

Мой вопрос: что такое "правильный" способ писать такой код, следует ли продолжать, как всегда, поскольку компилятор не жалуется на это, или я должен использовать пустоту для ясности, поэтому другой код сопровождающий будет знать, что я небрежно проигнорировал возвращаемое значение.

Когда я смотрю на код вроде этого (с пустотой), это выглядит довольно странно для меня...

4b9b3361

Ответ 1

Общим путем является просто вызов foo(); без внесения в (void).

Тот, кто никогда не игнорировал возвращаемое значение printf(), произвел первый камень.

Ответ 2

Мне лично нравятся "неиспользуемые" предупреждения, но иногда бывают случаи, когда я должен их игнорировать (например, write() для пользователя или fscanf(...,"%*s\n") или strtol()), где возвращаемое значение несущественно, и я просто нужно, чтобы побочный эффект [возможно] перемещал указатель файла.)

С gcc 4.6 это становится довольно сложным.

  • Листинг (void) больше не работает.
  • Переписывающие функции (особенно вариативные) являются утомительными и неуклюжими.
  • {ssize_t ignore; ignore=write(...);} вызывает другое предупреждение (назначено не используется).
  • write(...)+1 выдает еще одно предупреждение (вычисленное значение не используется).

Единственный хороший (если уродливый) способ подавить это - преобразовать возвращаемое значение во что-то, что компилятор согласен с тем, что вы можете игнорировать.

Например, (void)(write(...)+1).

Это, очевидно, прогресс. (И +0 не работает, BTW.)

Ответ 3

Один из способов сделать это с помощью компиляторов Clang и GCC - с pragma:

    /* ... */

#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-result" 

    foo(); /* this specific unused-result warning gets ignored during compilation */

#pragma GCC diagnostic pop 

    /* ... */

Комбинация push - pop обертывает директиву ignored, чтобы предупреждения могли запускаться в другом месте вашего кода. Это должно быть проще для всех, кто читает ваш исходный код по дороге, чтобы увидеть, что делает этот блок кода.

Ответ 4

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

Итак, вы должны оставить (void) или деактивировать проверку для printf. Теперь у вас есть несколько вариантов сделать это читабельным образом. Я использую для обертывания функции внутри новой функции, например,

void printf_unchecked(const char* format, ...)

где происходит не очень красивый актерский состав. Возможно, более практично это сделать, используя макрос препроцессора в этом случае из-за varargs...

Ответ 5

Еще один "красивый" способ показать неиспользуемые результаты:

/**
 * Wrapping your function call with ignore_result makes it more clear to
 * readers, compilers and linters that you are, in fact, ignoring the
 * function return value on purpose.
 */
static inline void ignore_result(long long int unused_result) {
    (void) unused_result;
}

...

ignore_result(foo());

С C++ это может быть продолжено до:

template<typename T>
inline void ignore_result(T /* unused result */) {}

Ответ 6

Мне нравится компилировать мои коды с флагами:

$gcc prog1.c -o prog1.x -Wall -Wextra -ansi -pedantic-errors -g -O0 -DDEBUG=1

И чтобы избежать -Wunused-result, мне не нравится идея добавления другого флага: -Wno-unused-result (если вы это делаете, это одно решение).

Я использовал для (void) для некоторых функций (не printf или других известных, поскольку компиляторы не предупреждают о них, просто странные). Теперь литье в (void) больше не работает (GCC 4.7.2)

Смешная шина советует:

Result returned by function call is not used. If this is intended,
can cast result to (void) to eliminate message. (Use -retvalother to
inhibit warning)

Но это уже не решение. Для этой проблемы необходимо использовать обновление.

Итак, чтобы избавиться от предупреждения очень совместимым способом, вот хороший MACRO:

/** Turn off -Wunused-result for a specific function call */
#define igr(M) if(1==((int)M)){;}

И назовите его следующим образом:

igr(PL_get_chars(t, &s, CVT_VARIABLE));

Его чистый взгляд, и любой компилятор исключит код. Представьте картинку моего предпочтительного редактора vi: левое окно, no igr(); среднее окно, используя igr(); правое окно, источник.

enter image description here

Вы можете видеть, что он точно такой же, совершенно безобидный код, который позволяет C делать то, что gcc не позволяет: игнорировать код возврата.

Сравнение 1==... необходимо только для того, чтобы избежать предупреждения шины, что это условие не равно BOOL. GCC все равно. В зависимости от функции вы можете получить предупреждение cast. Я сделал тест, игнорируя double с этим MACRO, и это было хорошо, но почему-то я не полностью убежден. Специально, если функция возвращает указатель или что-то более сложное.

В этом случае вам также понадобится:

#define pigr(M) if(NULL==((void *)M)){;}

Последняя вещь: {;} необходимо из-за предупреждения -Wempty-body (предлагать скобки вокруг пустого тела в выражении if).

И (теперь последнее) ; после вызова функции (строго), но ее хорошей практики. Делает ваши строки кода более однородными, все они заканчиваются на ;. (Он переводится как мнемоника NOP, а после оптимизации исчезает).


Запуск компилятора не дает никаких предупреждений или ошибок. Выполнение splint дает:

$ splint ./teste.c -I/usr/lib/swi-prolog/include/ -strict-lib
Splint 3.1.2 --- 20 Feb 2009

Finished checking --- no warnings

См. также этот ответ

Ответ 7

gnulib имеет следующее: http://git.savannah.gnu.org/cgit/gnulib.git/tree/lib/ignore-value.h

/* Normally casting an expression to void discards its value, but GCC
   versions 3.4 and newer have __attribute__ ((__warn_unused_result__))
   which may cause unwanted diagnostics in that case.  Use __typeof__
   and __extension__ to work around the problem, if the workaround is
   known to be needed.  */
#if 3 < __GNUC__ + (4 <= __GNUC_MINOR__)
# define ignore_value(x) \
    (__extension__ ({ __typeof__ (x) __x = (x); (void) __x; }))
#else
# define ignore_value(x) ((void) (x))
#endif

Ответ 8

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

Вот пример программы Splint-clean:

#include <stdio.h>

FILE /*@alt [email protected]*/ *fopen(const char *path, const char *mode);

static int /*@alt [email protected]*/ test(void)
{
   printf( "test called\n" );

   fopen( "test", "a" );

   return 0;
}

int main(void)
{  
   test();

   return 0;
}

Неприятная часть заключается в том, что вам нужно добавить дополнительный прототип в системную функцию с комментарием где-нибудь.

Btw, по умолчанию Splint не жалуется на возвращаемое значение printf и некоторых других функций libc, которые не используются. Однако можно активировать более строгий режим.

LINT позволяет что-то подобное, но я никогда не использовал его. Вот что говорит документация.

LINT позволяет вам отмечать функции с дополнительными значениями возврата с помощью директиву, аналогичному # директивам препроцессора C.

#pragma optresult

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

Ответ 9

Другим решением было бы фактически использовать значение. Затем вы можете удалить предупреждение unused variable с помощью макроса.

#define _unused(x) ((void)(x))

Тогда в вашем примере у вас будет:

val = foo();
_unused(val); 

Ответ 10

Весьма законно и приемлемо писать код, который в некоторых случаях игнорирует возвращаемое значение. В приведенной ниже программе очень мало причин проверять возвращаемое значение printf().

int main(void) {
  printf("Hello world\n");
  return 0;
}