【问题标题】:Length of array in function argument函数参数中数组的长度
【发布时间】:2012-01-06 07:55:53
【问题描述】:

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


【问题讨论】:

  • 数组是二等 C 公民。在大多数情况下,它们会衰减为指向其第一个元素的指针。这发生在函数调用(和原型)中。在您的示例中,friends 数组衰减为传递给函数的指针(在函数内部它仍然是指针)
  • func(type* array[]) == func(type** array), 信息为数组,栈可能会丢失。
  • 可能的重复是相关的,但没有那么密切相关。
  • 请注意,空指针解决方案不是确定从函数内部传递给函数的数组大小问题的通用解决方案。实际上,一般来说,您无法确定从函数内部传递给函数的数组的大小,并且该大小通常作为另一个参数传入(考虑,除其他外,fgets()qsort()fread() , fwrite() 在标准 C 库中)。

标签: c pointers


【解决方案1】:

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 中)。要在不担心限制的情况下存储长度需要高达四个字节,这是一个难以想象的昂贵数量(至少在当时)。有人可能想知道数组是否也可以像这样以空值终止,但是您将如何允许数组存储空值?

【讨论】:

    【解决方案2】:

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

    Section 6.4 of the C FAQ 很好地涵盖了这一点,并提供了 K&R 参考等。


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

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


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

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

    【讨论】:

    • 我知道,但我可以通过参考解决它吗?还是有其他的态度?我不敢相信在如此低的编程水平上不可能找出作为指针传递的分配内存块的长度!
    【解决方案3】:

    正如@Will 所说,衰减发生在参数传递期间。解决它的一种方法是传递元素的数量。除此之外,您可能会发现 _countof() 宏很有用 - 它的功能与您所做的相同;)

    【讨论】:

    • _countof() 不是 C,但可能是供应商特定的扩展。使用它会破坏代码的可移植性。
    【解决方案4】:

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

    sizeof array / sizeof array[0]
    

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

    其次,作为一个小点,请注意sizeof 不是一个函数,所以上面的表达式不需要任何括号括住sizeof 的参数。

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

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

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

    【讨论】:

    • 不确定size_t 将用于传递元素的数量,因为它通常用于字节计数:msdn.microsoft.com/en-us/library/aa383751(v=vs.85).aspx
    • please note that sizeof is not a function, so the expression above doesn't need any parenthesis around the argument to sizeof 是的,你说得对,但这不是有效的p = malloc(5 * sizeof char),但这是是的p = malloc(5 * sizeof(char)); 所以在某些时候需要括号。不是将sizeof 视为函数,而是关于在某些时候您是否需要或不需要括号。这不会将sizeof 视为函数
    【解决方案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 方法适用于通用数组输入。您需要手动指定大小作为第二个参数。

    【讨论】:

      【解决方案6】:

      这是一个老问题,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 函数的实例与向量使用的维数一样多。

      【讨论】:

        【解决方案7】:
        int arsize(int st1[]) {
            int i = 0;
            for (i; !(st1[i] & (1 << 30)); i++);
            return i;
        }
        

        这对我有用:)

        【讨论】:

        • 更多描述需要。
        • 不知道st1指向的内存是如何被调用者初始化/设置的,这个答案没有任何意义。
        【解决方案8】:

        具有 sizeof 的数组长度(int 类型): sizeof(array)/sizeof(int)

        【讨论】:

        • 请添加更多描述(并改进代码格式)以阐明您想说的内容。
        【解决方案9】:

        最好的例子在这里

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

        【讨论】:

          猜你喜欢
          • 2015-07-05
          • 2020-08-16
          • 2012-12-03
          • 1970-01-01
          • 2022-01-08
          • 2023-03-04
          • 1970-01-01
          • 1970-01-01
          • 2014-01-15
          相关资源
          最近更新 更多