Я хотел бы с вами поделиться той информацией, что сам узнал совсем недавно и которая показалась мне очень занятной. Если вдруг вам неинтересно программирование под Линукс и вы не знаете, что такое компилятор gcc, то можете не дочитывать этот пост до конца — там ничего интересного ;)
Я хочу рассказать про несколько расширений компилятора gcc, которые могут быть очень полезны программисту, ценящему качество кода и не игнорирующему предупреждения друга-компилятора. Первый из них это...
У меня в Сизифе есть несколько пакетов, которые я хотел бы собирать с флагами -Wall -W -Werror, но вот беда: gcc жалуется, что аргумент в функции не используется. Будь всё просто, я бы удалил этот аргумент и всё, но загвоздка в том, что эта функция обязательно должна принимать аргумент, даже если она его и не использует, к примеру, в том случае, если это обработчик сигнала. Знакома ситуация? До сих пор я не знал, что и делать: то ли отключить генерацию предупреждений о неиспользуемых аргументах, то ли отказаться от идеи с -W -Werror.
Вот простой и рабочий пример программы с сигналами, которая выдаст предупреждение будучи скомпилированной с флагами -W -Wall:
(Замечания: кажется, использовать printf() внутри обработчика сигнала это плохая идея, но для простого примера пойдёт)
Попробуем собрать:
Ну, что я вам говорил? :)
Так вот, есть способ сказать компилятору, что конкретно этот аргумент нужен, что он даже если и выглядит как неиспользуемый, на самом деле необходим. Для этого вам нужно использовать специальное расширение gcc — указание аттрибута для переменной или функции.
Измените определение функции signal_handler() на следующее:
И всё! Теперь нет никакого warning'а в данном конкретном случае.
Но это ещё не всё :) Есть ещё и...
Часто в проектах программисты определяют свою printf-подобную функцию: она может выводить цветное сообщение, или же понимать дополнительные идентификаторы для пользовательского типа данных, или же после отображения сообщения завершать программу, да мало ли чего может придти на ум :)
Вот ещё одна бесполезная, но работающая, програмка: она выводит в цвете приветствие пользователю. Имя пользователя должно быть передано в качестве первого аргумента. (BTW, функция color_printf() позаимствована из реального проекта, в котором я сейчас копаюсь...)
Одна тонкость: в ней есть ошибка — мы вызываем color_printf() с недостаточным количеством аргументов, видать программист торопился и забыл один. Попробуем собрать:
Видите, ни одного предупреждения от компилятора. В данном случае программа даже работает, хоть и вывела всякую случайную чушь из памяти, а ведь может и сегфолтнуться.
Если бы подобную ошибку вы допустили при использовании printf() или другой подобной стандартной функции, то gcc бы выдал предупреждение, но в случае использования самописной функции с неизвестным количеством аргументов он так не поступает. Впрочем, это можно исправить — надо просто ему об этом намекнуть ;)
Исправьте определение функции print_color() на следующее:
И попробуем скомпилировать ещё разочек:
Объясню лишь что значат аргументы у атрибута: printf значит, что наша функция подобна printf() (можно указать, что подобна scanf(), например), цифры означают какой из аргументов функции является строкой с форматом и когда начинаются переменные, которые необходимо будет подставить в строку формата.
Также есть несколько интересных атрибутов, вроде noreturn и const, но они более полезны компилятору при генерации кода, чем программисту, поэтому предлагаю изучить их самостоятельно, если кому интересно.
Updated(20070814): Ссылки по теме:
А также пример того, как обернуть директивы в макросы, чтобы не было проблем при сборке старой версией GCC или другим компилятором.
Я хочу рассказать про несколько расширений компилятора gcc, которые могут быть очень полезны программисту, ценящему качество кода и не игнорирующему предупреждения друга-компилятора. Первый из них это...
Атрибут unused.
У меня в Сизифе есть несколько пакетов, которые я хотел бы собирать с флагами -Wall -W -Werror, но вот беда: gcc жалуется, что аргумент в функции не используется. Будь всё просто, я бы удалил этот аргумент и всё, но загвоздка в том, что эта функция обязательно должна принимать аргумент, даже если она его и не использует, к примеру, в том случае, если это обработчик сигнала. Знакома ситуация? До сих пор я не знал, что и делать: то ли отключить генерацию предупреждений о неиспользуемых аргументах, то ли отказаться от идеи с -W -Werror.
Вот простой и рабочий пример программы с сигналами, которая выдаст предупреждение будучи скомпилированной с флагами -W -Wall:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
static void signal_handler(int sig) {
printf("Signal received!\n");
}
int main(void) {
if(fork() == 0) {
sleep(2);
kill(getppid() , SIGUSR1);
}
signal(SIGUSR1, signal_handler);
sleep(10);
return EXIT_SUCCESS;
}
(Замечания: кажется, использовать printf() внутри обработчика сигнала это плохая идея, но для простого примера пойдёт)
Попробуем собрать:
[c0der@rock ~/gcc-extensions]$ gcc unused.c -o unused -W -Wall
unused.c:8: предупреждение: unused parameter 'sig'
Ну, что я вам говорил? :)
Так вот, есть способ сказать компилятору, что конкретно этот аргумент нужен, что он даже если и выглядит как неиспользуемый, на самом деле необходим. Для этого вам нужно использовать специальное расширение gcc — указание аттрибута для переменной или функции.
Измените определение функции signal_handler() на следующее:
static void signal_handler(int sig __attribute__((unused))) {И всё! Теперь нет никакого warning'а в данном конкретном случае.
Но это ещё не всё :) Есть ещё и...
Аттрибут format.
Часто в проектах программисты определяют свою printf-подобную функцию: она может выводить цветное сообщение, или же понимать дополнительные идентификаторы для пользовательского типа данных, или же после отображения сообщения завершать программу, да мало ли чего может придти на ум :)
Вот ещё одна бесполезная, но работающая, програмка: она выводит в цвете приветствие пользователю. Имя пользователя должно быть передано в качестве первого аргумента. (BTW, функция color_printf() позаимствована из реального проекта, в котором я сейчас копаюсь...)
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
enum fg_colors {
RED = 31,
GREEN,
YELLOW,
BLUE,
VIOLET,
AZURE
};
static void color_printf(enum fg_colors color, const char *format, ...) {
printf("\033[%d;40m", color);
va_list args;
va_start (args, format);
vprintf (format, args);
va_end (args);
puts("\033[0m");
}
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s name\n", argv[0]);
return EXIT_FAILURE;
}
color_printf(GREEN, "Hello, %s!");
return EXIT_SUCCESS;
}
Одна тонкость: в ней есть ошибка — мы вызываем color_printf() с недостаточным количеством аргументов, видать программист торопился и забыл один. Попробуем собрать:
[c0der@rock ~/gcc-extensions]$ gcc -W -Wall format.c -o format
[c0der@rock ~/gcc-extensions]$ ./format
Usage: ./format name
[c1der@rock ~/gcc-extensions]$ ./format Slava
Hello, €©™ї\pк·!
Видите, ни одного предупреждения от компилятора. В данном случае программа даже работает, хоть и вывела всякую случайную чушь из памяти, а ведь может и сегфолтнуться.
Если бы подобную ошибку вы допустили при использовании printf() или другой подобной стандартной функции, то gcc бы выдал предупреждение, но в случае использования самописной функции с неизвестным количеством аргументов он так не поступает. Впрочем, это можно исправить — надо просто ему об этом намекнуть ;)
Исправьте определение функции print_color() на следующее:
static void __attribute__((format(printf, 2, 3))) color_printf(enum fg_colors color, const char *format, ...) {
И попробуем скомпилировать ещё разочек:
[c0der@rock ~/gcc-extentions]$ gcc -W -Wall format.c -o format
format.c: В функции 'main'
format.c:32: предупреждение: недостаточно аргументов для указанного формата
Объясню лишь что значат аргументы у атрибута: printf значит, что наша функция подобна printf() (можно указать, что подобна scanf(), например), цифры означают какой из аргументов функции является строкой с форматом и когда начинаются переменные, которые необходимо будет подставить в строку формата.
Также есть несколько интересных атрибутов, вроде noreturn и const, но они более полезны компилятору при генерации кода, чем программисту, поэтому предлагаю изучить их самостоятельно, если кому интересно.
Updated(20070814): Ссылки по теме:
- http://www.unixwiz.net/techtips/gnu-c-attributes.html
- http://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html
А также пример того, как обернуть директивы в макросы, чтобы не было проблем при сборке старой версией GCC или другим компилятором.
/* The attribute noreturn is not implemented in GCC versions earlier
* than 2.5
**/
#if defined(__GNUC__) && ___GNUC__ >= 2 && __GNUC_MINOR__ >= 5
#define __noreturn __attribute__((noreturn))
#else
#define __noreturn
#endif
#ifdef __GNUC__
#define __format(archetype, format_string, argument) \
__attribute__((format(archetype, format_string, argument)))
#else
#define __format(archetype, format_string, argument)
#endif