Форсирование размера массива в параметре функции в C при передаче массива - PullRequest
4 голосов
/ 16 мая 2019

Контекст

В C у меня есть функция, которая принимает массив в качестве параметра.Этот параметр используется как выход в этой функции.Выход всегда одинакового размера.Я бы:

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

Потенциальное решение

Я нашел здесь: https://hamberg.no/erlend/posts/2013-02-18-static-array-indices.html что-то похожее на решениено я не могу получить предупреждение или ошибку во время компиляции, если я пытаюсь передать меньший массив, чем требуемый размер.

Вот моя полная программа main.c:

void test_array(int arr[static 5]);

int main(void)
{
    int array[3] = {'\0'};

    test_array(array); // A warning/error should occur here at compilation-time
                       // telling me my array does not meet the required size.

    return 0;
}

void test_array(int arr[static 5])
{
    arr[2] = 0x7; // do anything...
}

В отличие от этого блога, я использую gcc (версия 7.4.0) вместо clang с помощью следующей команды:

gcc -std=c99 -Wall -o main.out main.c

В моем коде мы видим, что функции test_array () нужно 5 элементовмассив.Я прохожу 3 элемента один.Я ожидал бы сообщение от компилятора об этом.

Вопрос

В C, как заставить параметр функции, являющийся массивом, иметь заданный размер?Если это не так, это должно быть заметно во время компиляции.

Ответы [ 2 ]

6 голосов
/ 17 мая 2019

Если вы передадите указатель на массив вместо указателя на его первый элемент, вы получите предупреждение о несовместимом указателе:

void foo(int (*bar)[42])
{}

int main(void)
{
    int a[40];
    foo(&a);  // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
    // note: expected 'int (*)[42]' but argument is of type 'int (*)[40]'

    int b[45];
    foo(&b);  // warning: passing argument 1 of 'foo' from incompatible pointer type [-Werror=incompatible-pointer-types]
    // note: expected 'int (*)[42]' but argument is of type 'int (*)[45]'
}

Скомпилируйте с -Werror, чтобы сделать его ошибкой.

Годболт

4 голосов
/ 17 мая 2019

Чтобы проверить, что размер передаваемого массива (не указателя) составляет не менее 5 элементов, можно использовать Static_assert, а необходимые _Static_assert можно вставить через макрос препроцессора.

После объявления функции вставьте:

#define test_array(arr) \
    do \
    { \
        _Static_assert(sizeof (arr) / sizeof *(arr) >= 5, "Array is too small."); \
       test_array(arr); \
    } while (0)

(do … while (0) - это классический способ определения макроса для синтаксического действия как оператора, так что за ним может следовать ;и выполняйте, как ожидается, с if операторами и т. д.)

Перед определением функции вставьте:

#undef test_array

(Если последует какое-либо дальнейшее использование функции, еще одна копия#define должен быть вставлен. В качестве альтернативы, функция может быть определена раньше в исходном файле, после чего следует #define, что устраняет необходимость в дополнительных директивах #undef или #define.)

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

...