array_length in C
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
精彩评论