Ответ 1
Общим путем является просто вызов foo();
без внесения в (void)
.
Тот, кто никогда не игнорировал возвращаемое значение printf()
, произвел первый камень.
В последнее время я начал использовать lint для анализа статического кода. Одно из предупреждений, которые я иногда получаю, касается этой проблемы. Скажем, например, что у меня есть следующая функция:
uint32_t foo( void );
И скажем, что я небрежно игнорирую возвращаемое значение функции. Чтобы предупреждение исчезло, можно написать
(void) foo();
Мой вопрос: что такое "правильный" способ писать такой код, следует ли продолжать, как всегда, поскольку компилятор не жалуется на это, или я должен использовать пустоту для ясности, поэтому другой код сопровождающий будет знать, что я небрежно проигнорировал возвращаемое значение.
Когда я смотрю на код вроде этого (с пустотой), это выглядит довольно странно для меня...
Общим путем является просто вызов foo();
без внесения в (void)
.
Тот, кто никогда не игнорировал возвращаемое значение printf()
, произвел первый камень.
Мне лично нравятся "неиспользуемые" предупреждения, но иногда бывают случаи, когда я должен их игнорировать (например, write()
для пользователя или fscanf(...,"%*s\n")
или strtol()
), где возвращаемое значение несущественно, и я просто нужно, чтобы побочный эффект [возможно] перемещал указатель файла.)
С gcc 4.6 это становится довольно сложным.
(void)
больше не работает.{ssize_t ignore; ignore=write(...);}
вызывает другое предупреждение (назначено не используется).write(...)+1
выдает еще одно предупреждение (вычисленное значение не используется).Единственный хороший (если уродливый) способ подавить это - преобразовать возвращаемое значение во что-то, что компилятор согласен с тем, что вы можете игнорировать.
Например, (void)(write(...)+1)
.
Это, очевидно, прогресс. (И +0
не работает, BTW.)
Один из способов сделать это с помощью компиляторов 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
, чтобы предупреждения могли запускаться в другом месте вашего кода. Это должно быть проще для всех, кто читает ваш исходный код по дороге, чтобы увидеть, что делает этот блок кода.
Для того чтобы статическая проверка кода была полезной, она должна также сообщать о игнорируемых возвращаемых значениях, которые могут часто приводить к трудным отслеживания ошибок или отсутствию обработки ошибок.
Итак, вы должны оставить (void)
или деактивировать проверку для printf
. Теперь у вас есть несколько вариантов сделать это читабельным образом. Я использую для обертывания функции внутри новой функции, например,
void printf_unchecked(const char* format, ...)
где происходит не очень красивый актерский состав. Возможно, более практично это сделать, используя макрос препроцессора в этом случае из-за varargs...
Еще один "красивый" способ показать неиспользуемые результаты:
/**
* 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 */) {}
Мне нравится компилировать мои коды с флагами:
$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()
; правое окно, источник.
Вы можете видеть, что он точно такой же, совершенно безобидный код, который позволяет 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
См. также этот ответ
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
Обычно не так много функций, значение которых вы хотите игнорировать. Например, 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 не дает ошибку сообщений, если результат игнорируется.
Другим решением было бы фактически использовать значение. Затем вы можете удалить предупреждение unused variable
с помощью макроса.
#define _unused(x) ((void)(x))
Тогда в вашем примере у вас будет:
val = foo();
_unused(val);
Весьма законно и приемлемо писать код, который в некоторых случаях игнорирует возвращаемое значение. В приведенной ниже программе очень мало причин проверять возвращаемое значение printf().
int main(void) {
printf("Hello world\n");
return 0;
}