оператор sizeof в сочетании с массивом переменной длины в качестве аргументов функции

Согласно документации GNU по Arrays of Variable Length можно использовать оператор sizeof для определения размера массива переменной длины, переданного в функцию:

Вы также можете использовать массивы переменной длины в качестве аргументов функций:

struct entry
tester (int len, char data[len][len])
{
  /* … */
}

Длина массива вычисляется один раз при выделении хранилища и запоминается для области действия массива, если вы обращаетесь к нему с помощью sizeof.

Однако при попытке использовать этот пример с приведенным ниже полным примером кода оператор sizeof возвращает размер указателя, а не размер выделенного vla, как ожидается на основе приведенного выше фрагмента кода GNU.

Я понимаю, что передача массивов в C сродни передаче указателя на первый элемент массива, но, поскольку в этом случае мы указываем размер в сигнатуре функции, я ожидаю, что поведение будет имитировать при использовании sizeof в области видимости где объявлен массив.

Я также понимаю, что могу использовать аргумент len для определения размера; но для удобства и понимания реализации GNU я все еще думаю, что это интересный (если не важный) вопрос.

Заранее благодарю всех, кто может дать представление об этом!

// file: vla.c
#include <stdio.h>

void foo(int len, char data[len][len]) {
  printf("sizeof \"foo\" data: %lu\n", sizeof(data));
}

int main(int argc, char * argv[]) {
  char data[argc][argc];
  printf("sizeof \"main\" data: %lu\n", sizeof(data));
  foo(argc, data);
}

Составлено с использованием:

gcc vla.c -o vla -std=c11

Вызывается с использованием:

./vla 2 3 4 5

Вывод:

sizeof "main" data: 25
sizeof "foo" data: 8

Анализ:

Размер main data имеет смысл; argc равно 5, поэтому это (5 * 5 = 25) байтовый 2D-массив.

Ожидалось, что размер foo data также будет равен 25, но вместо этого он соответствует размеру указателя. По-видимому, компилятор не использует тот факт, что он знает размер data в пределах foo, так как это часть сигнатуры функции.


person dtracy    schedule 03.08.2018    source источник
comment
Я подозреваю, что вы обнаружите, что обычные правила применяются к аргументам функции (вы получаете размер указателя). Текст, который вы цитируете, в основном относится к массивам, определенным внутри функции (вы не можете определять массивы переменного размера в глобальной области видимости). Сказав это, ссылка на руководство определенно подразумевает, что то, что вы делаете, должно работать.   -  person Jonathan Leffler    schedule 03.08.2018
comment
В сторону: лучше использовать "%zu" с size_t --> printf("%zu\n", sizeof(...));   -  person chux - Reinstate Monica    schedule 03.08.2018
comment
После некоторых безумных размышлений я думаю, что память не выделяется при вызове функции. Я думаю, что абзац после примера кода не имеет смысла. Но это не очевидно из того, что написано в инструкции.   -  person Jonathan Leffler    schedule 03.08.2018
comment
@JonathanLeffler, я согласен с этой оценкой. Также спасибо за исправления форматирования, очень признателен!   -  person dtracy    schedule 03.08.2018


Ответы (1)


Аргумент, объявленный char data[len][len], на самом деле является char (*data)[len] указателем на VLA. Не существует такой вещи, как фактический аргумент массива, и первый len в объявлении не имеет смысла. Однако, если вы используете sizeof *data, вы получите обратно len, поскольку тип, на который указывает указатель, изменяется.

Если вы хотите, чтобы оба len были осмысленными, вы можете передать тип указателя на весь массив, а не указатель на первый элемент, объявив аргумент char (*data)[len][len]. Теперь sizeof *data это len*len. Но вам нужно будет использовать (*data)[i][j] для доступа к нему. Вам также потребуется использовать оператор & при вызове функции:

int l = 42;
char array[l][l];
foo(l, &array);
person R.. GitHub STOP HELPING ICE    schedule 03.08.2018
comment
Следует отметить, что страница, на которую ссылается OP, написана плохо. В нем говорится: «Вы также можете использовать массивы переменной длины в качестве аргументов функций» и приводится пример аргумента, объявленного char data[len][len]. Это может быть технически правильно, так как массив можно «использовать» в качестве аргумента для этой функции, но, тем не менее, он преобразуется в указатель, а формулировка вводит в заблуждение, возможно, предполагая, что GCC поддерживает какое-то расширение C или отклонение… - person Eric Postpischil; 03.08.2018
comment
… Тип char data[len][len], вероятно, предназначен для того, чтобы указать, что второе измерение имеет переменную длину. Формулировка, вероятно, должна была сказать, что массивы переменной длины могут использоваться как части типов, передаваемых функциям, используя такой пример, как char (*data)[len]. - person Eric Postpischil; 03.08.2018