Есть предупреждения, применимые только для C++ (например:
-Weffc++, -Woverloaded-virtual, -Wold-style-cast
). Я не пробовал включать их для си, могу предположить, что последует ругань и правильно. А поскольку для C они не имеют смысла - в данном контексте они не интересны.Для обоих языков в первом приближении я отобрал три варнинга:
-Wsign-conversion -Wconversion -pedantic
. Остановимся на них поподробнее.Начать стоит с того, что в си enum - всегда int.
enum { TEST1 = 0x100000000ULL, TEST2 = 0xffffffffU, TEST3 = 0x7fffffff }; $ gcc -c -std=c99 -pedantic test.c test.c:2: предупреждение: в ISO C значения перечислимого типа ограничены диапазоном типа ‘int’ test.c:3: предупреждение: в ISO C значения перечислимого типа ограничены диапазоном типа ‘int’Причем это справедливо и для amd64, в этом легко убедиться например так:
BOOST_STATIC_ASSERT(sizeof(int) == 4);В то же время в C++ с enum не возникает никаких проблем. В C++ тип enum есть собственно enum, даже знаковость которого определяется позже. Единственный возникающий в данной ситуации момент - стандарт C++ 99 года не любит определений ULL, считая их C99 long long integer constant, Для C++ даже U писать не обязательно, главное, чтобы значение могло уместиться в long, то есть быть не больше 32-64 бит в зависимости от типа платформы.
Это наводит на мысль, что переносимое приложение не должно использовать в enum значения больше 32 бит длиной.
Значение
sizeof(TEST1)
в С++ варьируется в зависимости от наполнения enum от 4 до 8.Другой интересный момент, справедливый для обоих языков:
const unsigned int t1 = TEST1; const unsigned int t2 = 0; const unsigned int t3 = argc > 1 ? TEST1 : 0; $ gcc -m64 -o test -std=c99 -Wsign-conversion test.c test.c:3: предупреждение: conversion to ‘unsigned int’ from ‘int’ may change the sign of the resultОшибка возникает в третьей строке фрагмента, и лечится заменой 0, на 0U... Хотя тот же 0 во второй строке не вызывает проблем.
И напоследок - ошибочная ситуация в gcc:
При попытке присвоить битовому полю значение какой-то переменной практически всегда происходит ошибка, и обойти ее практически не представляется возможным.
unsigned int x = 0x7f; struct { unsigned int a:7; } t = { .a = x }; $ gcc -std=c99 -Wconversion test.c test.c:4: предупреждение: conversion to ‘unsigned char:7’ from ‘unsigned int’ may alter its valueНе существует способа привести переменную к битовому полю.
В принципе я не вижу большой необходимости из кожи вон лезть, чтобы задушить любую ругань компилятора. Наша цель - обеспечить корректность кода. Ругань компилятора - это средство для привлечения внимания к подозрительным местам.
Главное чтобы борьба за чистоту кода производилась с пониманием первопричин. Тупые приведения типов (через reinterpret_cast конечно) до добра еще никого не доводили. Даже если эта конструкция и находит применение в коде - она достаточно бросается в глаза и вызывает стойкое желание избавиться от нее. Но лучше оставить предупреждение, которое бросается в глаза еще сильнее.
Можно предложить борцам на чистоту кода периодически устраивать параноидальные проверки с большим количеством опций, которые, возможно, не удастся расчистить целиком. А в обычные дни обходится более скромным подмножеством опций, но при этом обязательно используя -Werror. :)
PS: Интересное на этом конечно же не заканчивается - это лишь первый взгляд. Может быть последует продолжение, если будет не лень, совсем я что-то забыл про блог.
0 коммент.:
Отправить комментарий