开发者

array_length in C

开发者 https://www.devze.com 2022-12-17 21:12 出处:网络
I wrote an array_length function like this: int array_length(int a[]){ return sizeof(a)/sizeof(int); } However it is returning 2 when I did

I wrote an array_length function like this:

int array_length(int a[]){
    return sizeof(a)/sizeof(int);
}

However it is returning 2 when I did

unsigned int len = array_length(arr);
printf ("%i" , len);

where I have

int arr[] = {3,4,5,6,1,7,2};
int * parr = arr;
开发者_Go百科

But when I just do

int k = sizeof(arr)/sizeof(int);
printf("%i", k);

in the main function, it returns 7.

What is the correct way to write the array_length function and how do I use it?


Computing array lengths, in C, is problematic at best.

The issue with your code above, is that when you do:

int array_length(int a[]){ 
    return sizeof(a)/sizeof(int); 
} 

You're really just passing in a pointer as "a", so sizeof(a) is sizeof(int*). If you're on a 64bit system, you'll always get 2 for sizeof(a)/sizeof(int) inside of the function, since the pointer will be 64bits.

You can (potentially) do this as a macro instead of a function, but that has it's own issues... (It completely inlines this, so you get the same behavior as your int k =... block, though.)


Your function won't work. C arrays and C pointers are different types, but the array will degenerate into a pointer if you look at it funny.

In this case, you're passing the array as a parameter, and it's turning into a pointer in the call, so you're measuring the sizeof(int *)/sizeof(int).

The only way to make this work is to use a macro:

#define ARRAYSIZE(x) (sizeof(x)/sizeof(*x))

and that will only work if x is declared in that scope as an array and not as a pointer.


Use a macro...

#define SIZEOF_ARRAY( arr ) sizeof( arr ) / sizeof( arr[0] )

It'll also have the bonus of working for any array data type :)


In general, it is impossible to measure array size C. In your main function, the compiler counts the elements you wrote between the braces for you, so you're really declaring int arr[7]. That has the size you expect.

In your function, however, int a[] is equivalent to int *a -- a pointer to an integer. You know it's an array, so there are more integers following, but your array_length function could be passed any integer pointer, so it can't know.

This is one of the many reasons to use std::vector instead of raw arrays whenever possible.


The simple answer to your question is: there is no way to write the array_length function. You might be able to get away with a macro definition, but that depends upon the context in which you will use the macro.

You have made the common mistake of confusing arrays and pointers in C. In C, an array's name, in most cases, is equivalent to a pointer to its first element. Your array_length function receives an array a in such a context. In other words, it is impossible to pass an array as an array in C. You function is as if it is defined like this:

int array_length(int *a){
    return sizeof(a)/sizeof (int);
}

which, basically divides the size of int * by the size of int. Also, by the above description, it is impossible to know the size of an array in C in a function.

Now, how can you determine its size properly outside of the function? The answer is that sizeof operator is one of the cases where the name of an array does not reduce to a pointer to its first element. I have explained the differences more elaborately in this answer. Also note that despite appearances, sizeof is an operator, not a function (as we just learned, it can't be a function, because then it won't be able to calculate the size of an array).

Finally, to determine the size of an array a of any type T, I prefer:

size_t sz = sizeof a / sizeof a[0];

The above is type-agnostic: a could be of any type above. Indeed, you could even change the type of a and would not need to change the above.


The problem is that function parameters can't really be arrays, even though C lets you make a declaration that looks like one. The parameter ends up being a simple pointer. I've said elsewhere:

This boils down to the fact that plain array parameters in C/C++ are a fiction - they're really pointers. Array parameters should be avoided as much as possible - they really just confuse matters.

That's why this type of construct to return the number of elements in an array ends up being a macro in C. See this previous SO answer for what I think is a good (if complicated) implementation of the macro:

  • Is there a standard function in C that would return the length of an array?

For ease of reference, here's the macro:

#define COUNT_OF(x) ((sizeof(x)/sizeof(0[x])) / ((size_t)(!(sizeof(x) % sizeof(0[x])))))

The complications in that macro make it safer to use than most (note that I don't claim to be the originator of the techniques used in the macro).


try _countof it's defined in WinNT.h as

// Return the number of elements in a statically sized array.
//   DWORD Buffer[100];
//   RTL_NUMBER_OF(Buffer) == 100
// This is also popularly known as: NUMBER_OF, ARRSIZE, _countof, NELEM, etc.
//
#define RTL_NUMBER_OF_V1(A) (sizeof(A)/sizeof((A)[0]))


The general procedure for computing the number of elements in an array is sizeof arr / sizeof arr[0] (or sizeof arr / sizeof *arr). Having said that...

Writing a function to compute the length of an array passed as an argument is doomed to failure, because what the function receives is a pointer, not an array. When you call your function with an array expression as the argument, the array expression will have its type implicitly converted from "array of T" to "pointer to T" and its value will be set to point to the first element in the array. Your function doesn't see an array object; it sees a pointer.

In the context of a function parameter declaration, int a[] is exactly the same as int *a, but that's only true for function parameter declarations (nothing would make me happier than to see the first form banished from all future versions of C, but that's not going to happen).


int arr[whatever] inside function argument list defines a pointer, not an array. Hence, the information about length is lost forever.

why!?

To see why, you must understand what C is all about. C never copies complex chunks around implicitly. So when you say "pass me an array" it actually means "I want to pass an address, which array's name usually is".

This is not a disadvantage. If you want more, you will have to pass it manually. The reward is that you know exactly what's going on on machine's level, giving you performance and other benefits. This is how you can have one universal programming language for all purposes.


there is no way to determine the size of an array passed into a function like this

 void foo(int a[]);

there is not enough information at compile time or at run time to work it out

The sizeof tricks only work in source locations where the array size is specified


Array reference will solve this, although I'd never suggest using such a thing (since it will only work if you either always pass by ref, or use it in the scope in which the array has been defined):

#include <iostream>

template <typename Arrtype>
unsigned mysize (Arrtype (&arr)) {
 return sizeof(arr) / sizeof(arr[0]);
}

int main () {
 unsigned arr[13];
 std::cout << mysize(arr) << std::endl; // prints 13
}

Also mentioned here: http://cplusplus.co.il/2009/09/06/more-on-arrays/

Edit: As suggested in the comments, another possible solution is this:

template <typename T, unsigned N>
unsigned mysize(T (&)[N]) {
    return N;
} 


The "int a[]" syntax used as a function argument is equivalent to "int *a".

This syntax:

int arr[] = {3,4,5,6,1,7,2};

Only works at compile time.

So while you can write: int arr[] = {3,4,5,6,1,7,2}; printf("size=%d\n", sizeof(arr));

This will only work at compile time.

One way you could get around this is to make the arrays dynamic and create your own array type.

e.g.

typedef struct array{
int length;
int *arr;
} array;

And use malloc to set it's size. You could populate it using the elipses.

populate_array(array, ...);

e.g. then calling populate_array(arr, 1,2,3,4,5,6); see stdarg.

However, since your C compiler is quite possibly a C++ compiler too. Think about using std::vector instead. The hard work is then done for you.


I found that was a trap made by our C Compiler! (So is it a standard of CPL? C99 or something else?) I wrote following code in visual studio 2010 on platform x86.

#include <stdio.h>
#include <stdlib.h>

int main()
{
    int a[3] = { 0xa, 0xb, 0xc }; // Declare an array which include 3 items.
    int len = sizeof(a) / sizeof(a[0]); // And the variable len refer to the code like @Neil Chowdhury o_O
    int * pa0 = a; // Then i declared a pointer point to the first element to the source array a.
    int len2 = sizeof(a) / sizeof(pa0[0]); // Len2 refer to the same action like previous statement.
    int len3 = sizeof(a) / sizeof(pa0); // Len3 refer to a's size(one int's size)divide may be a int pointer's size.
    int len4 = sizeof(pa0); // Len4 is the size of int pointer pa0.
    int len5 = sizeof(a); // And len5 is the size of pa0, the same theory as previous stmt represented.
    int len6 = sizeof(a[0]); // Len6 equal to sizeof(int)
    int len7 = sizeof(pa0[0]); // Len7 equal to sizeof(int) too.
    return 0;
}

Then i built them, And showed disassembly code. the results likes:

    int a[3] = { 0xa, 0xb, 0xc };
00D13108  mov         dword ptr [ebp-14h],0Ah  
00D1310F  mov         dword ptr [ebp-10h],0Bh  
00D13116  mov         dword ptr [ebp-0Ch],0Ch  
    int len = sizeof(a) / sizeof(a[0]);
00D1311D  mov         dword ptr [ebp-20h],3  // caution!
    int * pa0 = a;
00D13124  lea         eax,[ebp-14h]  
00D13127  mov         dword ptr [ebp-2Ch],eax  
    int len2 = sizeof(a) / sizeof(pa0[0]);
00D1312A  mov         dword ptr [ebp-38h],3   // caution!
    int len3 = sizeof(a) / sizeof(pa0);
00D13131  mov         dword ptr [ebp-44h],3   // caution!
    int len4 = sizeof(pa0);
00D13138  mov         dword ptr [ebp-50h],4  
    int len5 = sizeof(a);
00D1313F  mov         dword ptr [ebp-5Ch],0Ch  
    int len6 = sizeof(a[0]);
00D13146  mov         dword ptr [ebp-68h],4  
    int len7 = sizeof(pa0[0]);
00D1314D  mov         dword ptr [ebp-74h],4  
    return 0;
00D13154  xor         eax,eax  

So, what? look at these "caution" marked lines! How did our c compiler treated our C code? When a pointer comes, especially when we wrote a sentence like something = sizeof(one element of an array) / sizeof(the first element of that array) the Compiler parse our syntax as the number of an array! It's not finished yet.


I tested a "dynamic array", too. An array which allocate by malloc etc.

#include <stdio.h>
#include <stdlib.h>

int main()
{

    int * a = NULL;
    int len = 0;
    a = (int *) calloc(7, sizeof(int));
    len = sizeof(a) / sizeof(a[0]);
    return 0;
}

Build.. Disassemble...

    int * a = NULL;
013830FE  mov         dword ptr [a],0  
    int len = 0;
01383105  mov         dword ptr [len],0  
    a = (int *) calloc(7, sizeof(int)); 
0138310C  mov         esi,esp  
0138310E  push        4  
01383110  push        7  
01383112  call        dword ptr [__imp__calloc (13882CCh)]  
01383118  add         esp,8  
0138311B  cmp         esi,esp  
0138311D  call        @ILT+300(__RTC_CheckEsp) (1381131h)  
01383122  mov         dword ptr [a],eax  
    len = sizeof(a) / sizeof(a[0]);
01383125  mov         dword ptr [len],1  // Hey!
    return 0;
0138312C  xor         eax,eax  
}

Do you see that? compiler missed our syntax at this time. So the conclusion is: When you write a sentence especially like: something = sizeof(one element of an array) / sizeof(the first element of that array) Compiler will understand and parse our code, and sign the array capacity to something. Only if the source array is a fixed array, or an array has already declared size previously. (At this time, the size is a constant may be stored in compiler's variable table.) It basically like @rmn said. That's what i found. May be worthless.


This works pretty well:

int A[5] = {10, 20, 30, 40, 3};
int length = sizeof(A) / sizeof(A[0]);  // prints 5
0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号