Как заставить компилятор проверять длину массива? - PullRequest
1 голос
/ 19 июня 2020

Я определил функцию:

void myFunction(char string[20])
{
    // update my string here, maximal size is 20.
}

Вызов функции с помощью

char st[5];
myFunction(st);

является ошибкой, потому что myFunction разбивает стек, что приводит к неопределенному поведению. Конечно, st должен быть размером 20 (или больше).

Есть ли способ заставить компилятор c жаловаться при возникновении таких ошибок и каков рекомендуемый способ кодирования таких вызовов?

Ответы [ 3 ]

3 голосов
/ 19 июня 2020

Конечно st должен быть размером 20 (или больше).

Вы можете использовать функцию индексов массива stati c в C99, доступную для использования в деклараторы функций .

Если ключевое слово stati c также появляется внутри [и] производного типа массива, то для каждого вызова функции значение соответствующего фактического Аргумент должен обеспечивать доступ к первому элементу массива, содержащему как минимум столько элементов, сколько указано в выражении размера.

Итак, вы можете объявить функцию как

void myFunction(char string[static 20])
{
    /* ... */
}

вместо. Это вызовет предупреждение от компилятора, если переданный аргумент массива содержит менее 20 элементов.

Например, при вызове myFunction с st типа char [5], clang 10 дает мне warning: array argument is too small; contains 5 elements, callee requires at least 20 [-Warray-bounds]

Однако на данный момент только clang, похоже, поддерживает -Warray-bounds, а G CC планирует добавить поддержку в будущем .

Обратите внимание, что это работает только во время компиляции для массивов, но также предупреждает при передаче указателей NULL (если это можно определить во время компиляции). Однако кто-то может передать указатель на динамически выделяемый массив элементов менее 20, и компилятор не может статически проверить это во время компиляции.

Таким образом, в этом случае он работает хорошо только тогда, когда объявленный тип аргумент - char [N], где N - его размер, а N - по крайней мере 20. Если вы используете sh для обработки обоих случаев, потребуется явный параметр размера и проверка внутри функции, чтобы вернуть ошибку в случае, если переданный размером меньше 20.

2 голосов
/ 20 июня 2020

Вы можете инкапсулировать массив:

struct string20 { char string[20]; };
void myFunction(struct string20 *string)
{
}

или передавать указатели на массив (! = Указатели на первый элемент):

void myFunction( char (*string)[20] )
{
}
//....
char st[5];
char st20[20];
myFunction(&st); //WRONG; diagnosed error
myFunction(&st20); //OK

Используя любой из этих подходов, вы получит диагностику компилятора независимо от вашей платформы (если это стандартная платформа).

1 голос
/ 20 июня 2020

Ваша функция выглядит так:

void myFunction(char string[20])
{
    // update my string here, maximal size is 20.
}

Но компилятор видит это так:

void myFunction(char *string)
{
    // update my string here, maximal size is 20.
}

Размер «20» буквально ничего не значит для компилятора. Вы можете вызвать функцию с массивом любой длины.

Что касается вызова с:

char st[5];
myFunction(st);

Все в порядке.

Если вы хотите принудительно применить специфику c размер массива, вы можете обернуть массив в структуру.

...