繁体   English   中英

函数参数中数组的长度

[英]Length of array in function argument

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


sizeof应用于原始数组,则它只能用于查找数组的长度。

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

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

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

正如您已经聪明地指出 main 接收数组的长度作为参数(argc)。 是的,这是出于必要而不是多余的 (嗯,这有点多余,因为 argv 很方便地由空指针终止,但我离题了)

关于为什么会发生这种情况有一些推理。 我们怎样才能让 C 数组也知道它的长度呢?

第一个想法是在将数组传递给函数并继续将数组长度保留在类型系统中时,不要让数组衰减为指针。 这样做的坏处是您需要为每个可能的数组长度设置一个单独的函数,这样做并不是一个好主意。 (Pascal 这样做了,有些人认为这是它“输”给 C 的原因之一)

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

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

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

struct {
    size_t length;
    int * elements;
}

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

数组在传递时衰减为指针。

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


除此之外,想象一下函数有可能知道在指针中分配的内存大小。 您可以调用该函数两次或多次,每次使用不同长度的不同输入数组; 因此,长度必须以某种方式作为秘密隐藏变量传入。 然后考虑是否将偏移量传入另一个数组,或在堆上分配的数组( malloc和所有库函数 - 编译器链接到的东西,而不是看到和解释其主体的原因)。

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


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

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

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

比这更容易理解(尽管在两个函数中都没有阻止访问函数中的第四个元素):

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 函数的实例将与用于向量的维数一样多。

正如@Will 所述,衰减发生在参数传递期间。 绕过它的一种方法是传递元素的数量。 要添加到这一点,您可能会发现_countof()宏很有用 - 它的作用等同于您所做的 ;)

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

sizeof array / sizeof array[0]

这样您就不会重复类型名称,这当然可能会在声明中更改并导致您最终得到不正确的长度计算。 这是一个典型的不要重复自己的案例。

其次,作为次要的一点,请注意sizeof不是函数,因此上面的表达式不需要在sizeof的参数周围加上任何括号。

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

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

请注意,您经常使用malloc()返回的malloc() ,在这些情况下,您永远不会有一个“真”数组来计算其大小,因此设计函数以使用元素计数更加灵活。

关于 int main():

根据标准, argv指向一个以 NULL结尾的数组(指向以 NULL 结尾的字符串的指针)。 (5.1.2.2.1:1)。

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

因此,大小计算是由一个函数执行的,该函数是对strlen()一个微不足道的修改。

argc仅用于进行argv长度计算 O(1)。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM