Хорошие новости для пользователей gcc — при использовании gcc 5.1 и выше им будет проще быстро находить вот такую распространенную ошибку вычисления размера массива, объявленного как параметр функции:
void something( char arr[100] ) { // this loop is broken for( size_t index = 0; index < sizeof(arr)/sizeof(arr[0]); index++ ) { //WHATEVER } }
Хотя параметр и объявлен как массив известного размера, с точки зрения компиляторов C и C++ это указатель типа char*, поэтому sizeof(arr) даст то же значение, что и sizeof(char*) – скорее всего, 4 или 8. Цикл, скорее всего, будет работать не так, как ожидалось.
Другой вариант:
void something( char encryptionKey[9000] ) { // WHATEVER, PROFIT // this call is broken SecureZeroMemory( encryptionKey, sizeof(encryptionKey)); // erase they key }
здесь разработчики хотели перезаписать нулями какие-то данные, но из-за ошибки будет перезаписан только первый байт. Такую ошибку сложнее найти тестированием, чем первую.
Чтобы найти такой код было проще, в gcc 5.1 и новее на такой код выдается предупреждение, и оно включено по умолчанию.
Отдельные читатели уже спешат в комментарии, чтобы рассказать о кривизне рук авторов кода из примеров выше. Тем не менее, проблема настолько распространенная, что в C++ рекомендуется использовать следующий фокус (отсюда) с шаблонной функцией:
template<typename StoredType, size_t Size> char ( &ArrayElementsCountHelper(StoredType( &Array )[Size]) )[Size]; #define countOfElements(Array) sizeof(ArrayElementsCountHelper (Array))
Использование countOfElements() в коде выше приведет к ошибке компиляции, зато такой код:
char arr[100] for( size_t index = 0; index < countOfElements(arr); index++ ) { //WHATEVER }
скомпилируется и будет работать правильно™.
Помимо явного указания sizeof(smth)/sizeof(smth[0]) также используют макрос:
// in a header far, far away... #define errorProneCountOfElements( arr ) (sizeof(arr)/sizeof((arr)[0])) for( size_t index = 0; index < errorProneCountOfElements (arr); index++ ) { //WHATEVER }
Посмотрим, как новое предупреждение работает в перечисленных случаях. Пробовать будем на gcc.godbolt.org
Сначала выберем в качестве компилятора gcc 4.9.2 – с параметрами по умолчанию предупреждений о неверном вычислении размера не будет ни в одном из примеров. Потом поменяем на gcc 5.1.0 – в примерах с циклом получаем на строку с заголовком цикла
warning: ‘sizeof’ on array function parameter ‘arr’ will return size of ‘char*’ [-Wsizeof-array-argument]
В таком коде:
void somethingExplicitCount( char arr[] ) { for( size_t index = 0; index < sizeof(arr)/sizeof(arr[0]); index++ ) { //WHATEVER } }
выдается то же предупреждение. Аналогично и в коде с макросом:
void somethingMacroCount( char arr[9000] ) { for( size_t index = 0; index < errorProneCountOfElements(arr); index++ ) { //WHATEVER, PROFIT } }
Код с перезаписью тоже дает предупреждение (используем переносимый memset() только для демонстрации):
void somethingMemset( char key[9000] ) { //WHATEVER, PROFIT memset(key, 0, sizeof(key)); // don't use memset for sensitive data }
ПРИБЫЛЬ.
Отдельного внимания заслуживает тот факт, что clang версии 3.0 уже умел выдавать то же самое предупреждение. Об этом в блоге LLVM было некогда сказано, что это специфичное для clang предупреждение и gcc так не умеет™. NO MOAR.
Предупреждение включено по умолчанию, разработчикам останется только правильно исправить проблемный код.
Дмитрий Мещеряков,
департамент продуктов для разработчиков
ссылка на оригинал статьи http://habrahabr.ru/post/263609/
Добавить комментарий