Длина массива в аргументе функции

c pointers

182668 просмотра

8 ответа

Это хорошо известный код для вычисления длины массива в C:

sizeof(array)/sizeof(type)

Но я не могу определить длину массива, переданного в качестве аргумента функции:

#include <stdio.h>

int length(const char* array[]) {
  return sizeof(array)/sizeof(char*);
}

int main() {
  const char* friends[] = { "John", "Jack", "Jim" };
  printf("%d %d", sizeof(friends)/sizeof(char*), length(friends)); // 3 1
}

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

int length(const char**& array);

Я считаю передачу длины массива в качестве второго аргумента избыточной информацией, но почему стандартное объявление выглядит mainтак:

int main(int argc, char** argv);

Пожалуйста, объясните, возможно ли узнать длину массива в аргументе функции, и если да, то почему существует избыточность в main.


Автор: Jan Turoň Источник Размещён: 12.11.2019 09:30

Ответы (8)


61 плюса

Решение

sizeof работает только для определения длины массива, если вы примените его к исходному массиву.

int a[5]; //real array. NOT a pointer
sizeof(a); // :)

Однако к тому времени, когда массив превращается в указатель, sizeof будет давать размер указателя, а не массива.

int a[5];
int * p = a;
sizeof(p); // :(

Как вы уже указали, main получает длину массива в качестве аргумента (argc). Да, это по необходимости и не является избыточным . (Ну, это своего рода избыточность, так как argv удобно завершается нулевым указателем, но я отступаю)

Есть некоторые причины того, почему это произошло. Как мы можем сделать так, чтобы массив C также знал его длину?

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

Вторая идея - хранить длину массива рядом с массивом, как это делает любой современный язык программирования:

a -> [5];[0,0,0,0,0]

Но тогда вы просто создаете невидимое structза кулисами, и философия Си не одобряет такого рода накладные расходы. Тем не менее, создание такой структуры самостоятельно часто является хорошей идеей для некоторых видов проблем:

struct {
    size_t length;
    int * elements;
}

Еще одна вещь, о которой вы можете подумать, это то, как строки в C завершаются нулем, а не сохраняют длину (как в Pascal). Чтобы хранить длину, не беспокоясь об ограничениях, нужны колоссальные четыре байта, невообразимо дорогое количество (по крайней мере, тогда). Можно задаться вопросом, могут ли массивы также быть нулевыми и заканчиваться таким образом, но тогда как вы позволите массиву хранить ноль?

Автор: hugomg Размещён: 25.11.2011 12:29

12 плюса

При передаче массив распадается на указатель.

Раздел 6.4 часто задаваемых вопросов C охватывает это очень хорошо и предоставляет ссылки K & R и т. Д.


Кроме того, представьте, что функция могла узнать размер памяти, выделенной в указателе. Вы можете вызывать функцию два или более раз, каждый раз с разными входными массивами, которые потенциально могут иметь разную длину; следовательно, длина должна быть передана как секретная скрытая переменная. И затем подумайте, передали ли вы смещение в другой массив или массив, выделенный в куче ( mallocи все это функции библиотеки - то, на что компилятор ссылается, а не видит и рассуждает о теле).

Становится трудно представить, как это могло бы работать без каких-то закулисных срезов объектов и тому подобного?


В Symbian была AllocSize()функция, которая возвращала размер выделения с помощью malloc(); это сработало только для литерального указателя, возвращенного malloc, и вы получите gobbledygook или аварийный сбой, если попросите его узнать размер недопустимого указателя или смещение указателя от единицы.

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

Автор: Will Размещён: 25.11.2011 12:21

2 плюса

Как утверждает @Will, затухание происходит во время передачи параметра. Один из способов обойти это - передать количество элементов. Чтобы добавить к этому, вы можете найти _countof()макрос полезным - он делает эквивалент того, что вы сделали;)

Автор: Mike Kwan Размещён: 25.11.2011 12:28

2 плюса

Во-первых, лучшее использование для вычисления количества элементов, когда фактическое объявление массива находится в области видимости:

sizeof array / sizeof array[0]

Таким образом, вы не будете повторять имя типа, которое, конечно, может измениться в объявлении и привести к неверному вычислению длины. Это типичный случай не повторять себя .

Во-вторых, в качестве второстепенного замечания, обратите внимание, что sizeofэто не функция, поэтому приведенное выше выражение не нуждается в скобках вокруг аргумента to sizeof.

В-третьих, C не имеет ссылок, поэтому ваше использование &в объявлении не будет работать.

Я согласен, что правильное решение C состоит в том, чтобы передать длину (используя size_tтип) в качестве отдельного аргумента и использовать sizeofв месте, где выполняется вызов, если аргумент является «реальным» массивом.

Обратите внимание, что часто вы работаете с памятью, возвращенной, например malloc(), и в этих случаях у вас никогда не будет «истинного» массива для вычисления размера, поэтому разработка функции для использования счетчика элементов является более гибкой.

Автор: unwind Размещён: 25.11.2011 12:38

2 плюса

Относительно int main ():

Согласно стандарту, argvуказывает на нуль- завершается массив (указателей на строки с завершающим нулевым). (5.1.2.2.1: 1).

То есть argv = (char **){ argv[0], ..., argv[argc - 1], 0 };.

Следовательно, вычисление размера выполняется функцией, которая является тривиальной модификацией strlen().

argcтолько для argvрасчета длины O (1).

Метод count-before-NULL НЕ будет работать для ввода универсального массива. Вам нужно будет вручную указать размер в качестве второго аргумента.

Автор: moshbear Размещён: 25.11.2011 12:52

2 плюса

Это старый вопрос, и OP, кажется, смешивает C ++ и C в своих намерениях / примерах. В C, когда вы передаете массив функции, он распадается на указатель. Таким образом, нет способа передать размер массива, кроме как с помощью второго аргумента в вашей функции, который хранит размер массива:

void func(int A[]) 
// should be instead: void func(int * A, const size_t elemCountInA)

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

void func(int A[3][whatever here]) // That's almost as if read "int* A[3]"

Использование обозначения массива в сигнатуре функции по-прежнему полезно для разработчика, поскольку может помочь определить, сколько элементов ожидает ваша функция. Например:

void vec_add(float out[3], float in0[3], float in1[3])

легче понять, чем этот (хотя ничто не препятствует доступу к 4-му элементу в функции в обеих функциях):

void vec_add(float * out, float * in0, float * in1)

Если бы вы использовали C ++, то вы могли бы на самом деле захватить размер массива и получить то, что вы ожидаете:

template <size_t N>
void vec_add(float (&out)[N], float (&in0)[N], float (&in1)[N])
{
    for (size_t i = 0; i < N; i++) 
        out[i] = in0[i] + in1[i];
}

В этом случае компилятор гарантирует, что вы не добавите 4D-вектор с 2D-вектором (что невозможно в C без передачи измерения каждого измерения в качестве аргументов функции). Будет столько же экземпляров функции vec_add, сколько число измерений, используемых для ваших векторов.

Автор: xryl669 Размещён: 13.05.2016 03:51

-1 плюса

int arsize(int st1[]) {
    int i = 0;
    for (i; !(st1[i] & (1 << 30)); i++);
    return i;
}

Это работает для меня :)

Автор: Baris Размещён: 22.10.2017 06:39

-3 плюса

Лучший пример здесь

спасибо # определить размер 10

void size(int arr[SIZE])
{
    printf("size of array is:%d\n",sizeof(arr));
}

int main()
{
    int arr[SIZE];
    size(arr);
    return 0;
}
Автор: Ravisankar Reddy Размещён: 29.10.2014 04:32
Вопросы из категории :
32x32