Подтвердить что ты не робот

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

Это хорошо известный код для вычисления длины массива в 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.


4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

struct {
    size_t length;
    int * elements;
}

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

Ответ 2

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

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


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

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


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

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

Ответ 3

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

Ответ 4

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

sizeof array / sizeof array[0]

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

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

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

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

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

Ответ 5

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

В соответствии со стандартом argv указывает на массив NULL с завершением (указателей на строки с нулевым завершением). (5.1.2.2.1:1).

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

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

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

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

Ответ 6

Это старый вопрос, и OP, похоже, смешивает С++ и 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 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)

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

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, как количество измерений, используемых для ваших векторов.

Ответ 7

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

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

Ответ 8

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

спасибо #define РАЗМЕР 10

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

int main()
{
    int arr[SIZE];
    size(arr);
    return 0;
}

Ответ 9

Вы можете использовать и оператор

Вот исходный код.

#include<stdio.h>
#include<stdlib.h>
int main(){

    int a[]= {0,1,2,3,4};

    int *p; 

    printf("%d\n", a); 
    printf("%d\n", (&a+1));
    printf("---- diff----\n");
    printf("%d\n", sizeof(a[0]));
    printf("The size of array a is %d\n", ((int)(&a+1)-(int)a)/(sizeof(a[0])));


    return 0;
};

Вот пример вывода

1384074164
1384074184
---- diff----
4
The size of array a is 5