Новое предупреждение о неверном вычислении размера массива в gcc 5.1

от автора

Хорошие новости для пользователей 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/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *