函数参数中数组的长度

这是用C来计算数组长度的众所周知的代码:

sizeof(array)/sizeof(type) 

但我似乎无法find作为parameter passing给函数的数组的长度:

 #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有冗余。


sizeof只能用于查找数组的长度,如果您将其应用于原始数组。

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

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

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

正如你已经巧妙地指出,主接收数组的长度作为参数(argc)。 是的,这是不必要的,并不是多余的 。 (好吧,这是一种reduntant,因为argv方便地由一个空指针终止,但我离题了)

有一些推理为什么会发生。 我们怎么可能做一个C数组也知道它的长度?

第一个想法是当数组传递给一个函数并继续保持types系统中的数组长度时,数组不会衰减成指针。 坏处在于,你需要为每个可能的数组长度分配一个函数,这样做不是一个好主意。 (帕斯卡这样做,有人认为这是它“失去”C)的原因之一)

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

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

但是,你只是在幕后创build一个不可见的struct ,而C哲学不赞成这种开销。 也就是说,自己创build这样的结构对于某些问题来说通常是一个好主意:

 struct { size_t length; int * elements; } 

你可以考虑的另一件事是C中的string是如何终止的,而不是存储一个长度(如在Pascal中)。 要存储一个长度而不用担心限制需要高达四个字节,这是一个难以想象的昂贵的数量(至less在当时)。 有人可能会怀疑,如果数组也可以像这样的空终止,但那么你会如何让数组存储空?

数组传递时会衰减到一个指针。

C FAQ的第6.4节涵盖了这一点,并提供K&R参考资料等。


除此之外,想象一下函数可以知道指针中分配的内存的大小。 您可以调用函数两次或更多次,每次使用不同长度的input数组; 因此这个长度将不得不作为秘密的隐藏variables以某种方式传入。 然后考虑是否将偏移量传递到另一个数组或堆中分配的数组( malloc和所有的库函数 – 编译器链接的东西,而不是查看和正文的原因)。

它难以想象如果没有一些幕后的切片对象和这样的权利,这是如何工作的?


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

你不想相信它是不可能的,但它确实不是。 要知道传入函数的东西的长度,唯一的方法是自己跟踪长度,并将其作为单独的显式parameter passing给自己。

正如@威尔所说,衰减发生在parameter passing过程中。 解决这个问题的一个方法是传递元素的数量。 为了增加这个,你可能会发现_countof()macros有用 – 它和你所做的相同;)

首先,当实际的数组声明在作用域中时,计算元素数量的一个更好的用法是:

 sizeof array / sizeof array[0] 

这样你不会重复types名称,这当然可以在声明中改变,并使您最终计算不正确的长度。 这是一个典型的案例, 不要重复自己 。

其次,作为一个小点,请注意sizeof不是一个函数,所以上面的expression式不需要在sizeof的参数周围有任何括号。

第三,C没有引用,所以你在一个声明中的用法是行不通的。

我同意正确的C解决scheme是将长度(使用size_ttypes)作为单独的parameter passing,如果参数是“真实”数组,则在调用的地方使用sizeof

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

关于int main():

根据标准, argv指向以NULL 结尾的数组(指向以NULL结尾的string的指针)。 (5.1.2.2.1:1)。

也就是说, argv = (char **){ argv[0], ..., argv[argc - 1], 0 };

因此,大小计算是由strlen()的微小修改的函数执行的。

argc只是在那里做argv长度计算O(1)。

count-until-NULL方法不适用于通用数组input。 您将需要手动指定大小作为第二个参数。

这是一个老问题,OP似乎将C ++和C混合在他的意图/例子中。 在C中,当你将一个数组传递给一个函数时,它会被腐蚀成指针。 因此,除了在函数中使用第二个参数来存储数组大小之外,没有办法传递数组大小:

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

他们是很less的情况下,你不需要这个,就像当你使用multidimensional array时:

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

在函数签名中使用数组符号对于开发人员来说仍然是有用的,因为它可能有助于告诉您的函数需要多less个元素。 例如:

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

比这个更容易理解(尽pipe没有任何东西阻止访问函数中的第四个元素):

 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]; } 

在这种情况下,编译器将确保不添加具有2Dvector的4Dvector(如果不将每个维度的维度作为函数的parameter passing,则这在C中是不可能的)。 vec_add函数的实例将与用于vector的维数相同。

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

这对我有用:)

最好的例子是在这里

谢谢#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; } 

你可以使用&运算符

这里是源代码。

 #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