在函数内找到数组长度

时间:2015-12-16 22:18:16

标签: c arrays

基于这个问题Calculate Length of Array in C by Using Function我真的需要解释。

假设我们有一个像这样的数组:

int arr[] = {1,2,3};

此处arr的长度为3,因此传入函数会衰减为指针,我们会失去对其长度的跟踪。

如果我们使用'\0' Null终止此数组,会发生什么:

int arr[] = {1,2,3,'\0'};

并将其传递给这样的函数:

void foo(int *arr){
    int length = 0;

    while(arr[length] != '\0'){
        length++;
    }

    printf("Length = %d\n",length);
}

这可以吗?

我写了以下代码:

#include<stdio.h>

void foo(int *arr);

int main(void){
    int arr1[]  = {10,'\0'};
    int arr2[]  = {12,44,'\0'};
    int arr3[]  = {87,1,71,'\0'};
    int arr4[]  = {120,15,31,82,'\0'};
    int arr5[]  = {28,49,16,33,11,'\0'};
    int arr6[]  = {19,184,90,52,38,77,'\0'};
    int arr7[]  = {2,17,23,41,61,78,104,'\0'};
    int arr8[]  = {16,92,11,35,52,118,79,44,'\0'};
    int arr9[]  = {20,44,33,75,49,36,9,2,11,'\0'};
    int arr10[] = {92,145,24,61,99,145,172,255,300,10,'\0'};

    foo(arr1);
    foo(arr2);
    foo(arr3);
    foo(arr4);
    foo(arr5);
    foo(arr6);
    foo(arr7);
    foo(arr8);
    foo(arr9);
    foo(arr10);
    return 0;
}


void foo(int *arr){
    int length = 0;

    while(arr[length] != '\0'){
        length++;
    }

    printf("Length = %d\n",length);
}

我得到了以下输出:

Length = 1
Length = 2
Length = 3
Length = 4
Length = 5
Length = 6
Length = 7
Length = 8
Length = 9
Length = 10

打印所有10个阵列的长度。 现在我在这里感到很困惑,因为就我所关注的问题而言,正如我在一些书中读到的那样,没有办法让它发挥作用。

为什么foo打印所有数组的长度? 使用像int arr[] = {1,2,3,'\0'};这样的东西是违法的吗? 我知道如果数组内部有0int arr[] = {1,2,0,3,4};的长度为2,但这不是我的问题。

4 个答案:

答案 0 :(得分:4)

  

使用int arr [] = {1,2,3,'\ 0'}之类的东西是违法的; ?

没有。这完全合法。 '\0'int,与0相同。使用任何数字作为标记来识别数组的结尾也没有什么不同。例如,如果数组仅包含正数,则可以使用-1。所以你的方法是有效的。

你通常不会在实践中看到这样的原因,当你可以简单地将它作为一个额外的参数传递时,不必迭代数组,从维护的角度来看这很容易理解。

int arr[1024];
size_t len = sizeof arr/sizeof a[0];

func(arr, len);

void func(int *a, size_t length) {
}

将此与您的方法进行比较。

此外,大小是在编译时计算的,而在您的方法中,您已遍历数组。选择标记可能会变得困难(o或-1或其他),如果它也需要是数组的元素。

答案 1 :(得分:4)

这就是C字符串标记其结尾和长度的方式。因为它们只是char数组,所以你自然也可以将它应用于其他类型的数组。

请记住,通过指针计算此类数组的长度具有线性时间复杂度。

答案 2 :(得分:2)

旁注:'\0'在这里真的为0,就像您的商店int一样。

你正在使用哨兵。几十年来,C风格的字符串一直在使用这种方法来标记字符串的完成位置。它具有相同的好处,但也有同样的缺点。

只要您维护不变量,即只在数组的最后位置发生哨兵,您将能够获得数组的长度。在O(N)时间内,您必须遍历序列。

请注意,您可以通过先前使用sentinel终止它来缩小序列:

1 2 3 4 0   //
1 2 3 0 *   // * as in: anything

但是一旦你这样做,就不能再知道数组的大小了。即使你可以在技术上附加一个额外的元素,一个不知道上下文的函数也不能安全地做到这一点。实质上,您知道序列的大小,但您不再知道数组的大小。

答案 3 :(得分:1)

如果你需要一个方法来让你携带阵列的数组长度,那么尝试使用这些方法之一。

将长度存储在数组的开头

所以(理想情况下)array[0],第一个元素是长度。

问题是,只有当您的数组具有合适的类型并且长度适合该类型时才能使用。您原则上可以使用union来定义一个足够大的元素来容纳不同类型的数据,包括长度,但这可能是浪费。

维护一个存储数组长度的结构和一个指向数组数据的指针。

这就像:

struct arrayinfo_s {
    int length ;
    char *data ;
    };

char name[1000] ;

struct arrayinfo a ;

a.length = sizeof(name) ;
a.data = name ;

myfunc( &arrayinfo ) ;

这种可能性有很多种。

“标准”惯例。

正如有人提到的那样,通常会跟踪数组长度并将其作为单独的参数传递给函数。

myfunc( array, length ) ;

如果array是固定大小,例如声明为int nums[100] ;然后,如果变量声明的函数与使用sizeof()或全局的函数相同,则可以使用sizeof(nums)

对于允许函数返回未知长度的数组,还有一个变体。通常你会做一些事情,比如将一个点返回到数组,但是传递一个参数,该参数是指向某个整数类型的指针,以存储新数组的长度。

char *newstuff( int *newlength )
{
    char *p = NULL ;

    p = malloc( 102 ) ;

    if( p == NULL )
    {
        *length = 102 ;
        return p ;
    }
    else
    {
        *length = 0 ;
        return NULL ;
    }
}