函数参数中的数组长度

时间:2011-11-25 12:20:06

标签: c pointers

这是用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中存在冗余。


9 个答案:

答案 0 :(得分:59)

如果将数组应用于原始数组,

sizeof仅用于查找数组的长度。

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

但是,当数组衰减成指针时,sizeof将给出指针的大小而不是数组的大小。

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

正如您已经巧妙地指出,main接收数组的长度作为参数(argc)。 是的,这是不必要的,也不是多余的。 (好吧,它是一种还原剂,因为argv很方便地被一个空指针终止但我离题了)

有一些理由说明为什么会这样。我们怎样才能让C数组知道它的长度?

第一个想法是,当数组传递给函数并继续将数组长度保持在类型系统中时,数组不会衰减为指针。关于这一点的坏处是你需要为每个可能的数组长度都有一个单独的函数,这样做并不是一个好主意。 (帕斯卡尔这样做了,有些人认为这是“失去”C的原因之一

第二个想法是将数组长度存储在数组旁边,就像任何现代编程语言一样:

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

但是你只是在幕后创建了一个不可见的struct,C哲学并没有批准这种开销。也就是说,自己创建这样的结构对于某些问题通常是一个好主意:

struct {
    size_t length;
    int * elements;
}

您可以考虑的另一件事是C中的字符串如何以空终止而不是存储长度(如在Pascal中)。要存储一个长度而不用担心限制需要一个高达四个字节,这是一个难以想象的昂贵数量(至少当时)。有人可能想知道数组是否也可以像这样终止null,但那么你将如何允许数组存储null?

答案 1 :(得分:12)

数组在传递时将衰减到指针。

Section 6.4 of the C FAQ非常清楚地介绍了这一点并提供了K&amp; R参考资料等。


除此之外,假设函数可以知道指针中分配的内存大小。您可以调用该函数两次或更多次,每次使用可能不同长度的不同输入数组;因此,长度必须以某种方式作为秘密隐藏变量传递。然后考虑是否将偏移量传入另一个数组,或者在堆上分配的数组(malloc并且所有都是库函数 - 编译器链接到的东西,而不是看到和关于正文的原因)。 / p>

很难想象如果没有一些幕后切片对象,这可能会如何工作呢?


Symbian确实有一个AllocSize()函数,它返回了malloc()分配的大小;这只适用于malloc返回的文字指针,如果你要求它知道无效指针的大小或偏离指针的指针,你会得到gobbledygook或崩溃。

你不想相信它不可能,但它真的不是。知道传递给函数的内容长度的唯一方法是自己跟踪长度并将其作为单独的显式参数传递给自己。

答案 2 :(得分:2)

如@Will所述,衰变发生在参数传递期间。解决它的一种方法是传递元素的数量。要添加到此,您可能会发现_countof()宏很有用 - 它与您所做的完全相同;)

答案 3 :(得分:2)

首先,当实际数组声明在范围内时,更好地用于计算元素数量:

sizeof array / sizeof array[0]

这样你就不会重复类型名称,这当然可以在声明中改变,并最终导致长度计算不正确。这是don't repeat yourself的典型案例。

其次,作为一个小问题,请注意sizeof不是函数,因此上面的表达式不需要围绕sizeof的参数的任何括号。

第三,C没有引用,因此您在声明中使用&将不起作用。

我同意正确的C解决方案是将长度(使用size_t类型)作为单独的参数传递,并且如果参数是a,则在调用的位置使用sizeof “真正的”阵列。

请注意,通常使用例如由malloc(),在这些情况下,你永远不会有一个“真”数组来计算大小,所以设计使用元素计数的函数更灵活。

答案 4 :(得分:2)

关于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 用于通用数组输入。您需要手动指定大小作为第二个参数。

答案 5 :(得分:1)

这是一个老问题,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];
}

在这种情况下,编译器将确保您不添加带有2D向量的4D向量(这在C中是不可能的,而不将每个维度的维度作为函数的参数传递)。 vec_add函数的实例将与向量使用的维数一样多。

答案 6 :(得分:0)

一个数组的长度(int 型),sizeof: sizeof(array)/sizeof(int)

答案 7 :(得分:-1)

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

这对我有用:)

答案 8 :(得分:-3)

最好的例子就在这里

谢谢#define SIZE 10

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

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