Новое предупреждение о неверном вычислении размера массива в 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 the 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.
Предупреждение включено по умолчанию, разработчикам останется только правильно исправить проблемный код.
Дмитрий Мещеряков,
департамент продуктов для разработчиков
Comments 35
Only users with full accounts can post comments. Log in, please.