10

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;

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?

SuperString
  • 21,593
  • 37
  • 91
  • 122
  • 7
    Although some people below have provided a macro that works, it's important to internalize that C is different from other languages (Java, C#, etc.), arrays are really just pointers to a block of memory, and *it's up to you to keep track of the length*. If you "get" this, it will make working with C much, much easier. – benzado Jan 18 '10 at 21:24

14 Answers14

15

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.)

Reed Copsey
  • 554,122
  • 78
  • 1,158
  • 1,373
  • Isn't it possible for `sizeof(int)==sizeof(int*)` on a 64-bit system? The size of `int` is implementation dependent, but always at least 32-bit, or so I thought. – dreamlax Jan 18 '10 at 20:51
  • Yes but there is no c compiler on earth who sets int = 64 bit. Unless you write or patch one. – Lothar Jan 18 '10 at 20:53
  • @dreamlax: it's possible, but I don't know of any that do that. MS + GNU + Intel + most other compilers set sizeof(int) == 32... – Reed Copsey Jan 18 '10 at 20:56
  • My understanding is that most 64-bit Unix systems use the `ILP64` model where `int` is 64-bits (http://www.unix.org/version2/whatsnew/lp64_wp.html). Of course, since I have little experience on Unix boxes, I could be wrong. (Windows uses the LLP64 model where `int` is 32-bits: http://blogs.msdn.com/oldnewthing/archive/2005/01/31/363790.aspx). – Michael Burr Jan 18 '10 at 21:47
  • How can computing array lengths be "problematic at best" when it is completely, utterly precise and unambiguously specified by the C Language Standard? – Jens Oct 18 '12 at 20:20
  • @chris-dodd I see why they named it that. – lindhe Aug 24 '16 at 18:17
12

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.

David Thornley
  • 56,304
  • 9
  • 91
  • 158
10

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 :)

Goz
  • 61,365
  • 24
  • 124
  • 204
  • Naturally ... but thats just the side effect of macros. It requires the USER to use them appropriately. – Goz Jan 18 '10 at 20:55
  • 5
    It does have the virtue of potentially working correctly, unlike the function. – David Thornley Jan 18 '10 at 20:59
  • 2
    @David: And isn't that what's most important: Code that potentially works correctly, sometimes? – BlueRaja - Danny Pflughoeft Jan 18 '10 at 21:07
  • 1
    The problem is the OP's understanding of arrays and pointers, which the above answer doesn't explain. – Alok Singhal Jan 18 '10 at 21:39
  • @BlueRaja: code that works when invoked *correctly* can be deemed good enough for your necessity. – Leonardo Herrera Jan 18 '10 at 21:39
  • @BlueRaja: Good point, but it arguably beats code that definitely won't work. Man, I'm glad C++ has `std::vector`. – David Thornley Jan 18 '10 at 21:40
  • @michael: I thought about bracketing the macro expansion but didn't see the point. A more complex statement wouldn't be a macro and therefore the code couldn't work anyway ... – Goz Jan 18 '10 at 22:04
  • @Goz: aside from the fact that a macro expansion should always be enclosed in parens unless that would specifically break the intended use of the macro (I also personally allow myself to skip the parens if the macro is a simple number, but even then I sometimes feel like I'm doing something wrong), if your macro as given is used as part of another expression, it might introduce an unintentional overflow or truncation (particularly if multiplication or division is involved). – Michael Burr Jan 18 '10 at 22:23
  • @michael: But then the input would be invalid for such a macro. So whats the point? A macro expansion should only be enclosed in parentheses if it makes sense to do so. In this case I can't think of a single case where it WOULD make sense to do so ... – Goz Jan 19 '10 at 07:19
5

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.

Asher Dunn
  • 2,384
  • 16
  • 12
  • Saying this is impossible is a bit of an exaggeration - there are certainly problems, and it can't always be done cleanly. But there are techniques that work often enough to be very useful in spite of the drawbacks. – Michael Burr Jan 18 '10 at 21:50
  • Aah, I'm so used to C++... and yes, I know there are macro techniques as posted by other people, but they seem extremely problematic to me. I don't like the idea of a "function" (macro) that works for variables declared in the same scope, but not for parameters. Then every time you want to use it, you have to remember to check where you declared the variable you're calling it on. If you forget, it'll compile just fine but yield blatantly wrong results. – Asher Dunn Jan 18 '10 at 22:18
  • that's a legitimate point of view. Personally I find the utility of the macro to outweigh the drawbacks. – Michael Burr Jan 18 '10 at 22:30
5

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.

Community
  • 1
  • 1
Alok Singhal
  • 93,253
  • 21
  • 125
  • 158
4

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:

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).

Community
  • 1
  • 1
Michael Burr
  • 333,147
  • 50
  • 533
  • 760
  • In C++ it's possible using templates as demonstrated in another answer. I have an example (stolen from Microsoft) in this SO answer, http://stackoverflow.com/questions/95500/can-this-macro-be-converted-to-a-function/95714#95714, but I decided to keep this answer simple since it seemed to be C specific. – Michael Burr Jan 18 '10 at 21:20
3

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]))
Bartosz Wójcik
  • 1,079
  • 2
  • 13
  • 31
2

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).

John Bode
  • 119,563
  • 19
  • 122
  • 198
2

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.

Pavel Radzivilovsky
  • 18,794
  • 5
  • 57
  • 67
2

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

pm100
  • 48,078
  • 23
  • 82
  • 145
1

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;
} 
rmn
  • 2,386
  • 1
  • 14
  • 21
  • mhm, good point :o Sorry, did not notice that. I'll leave th comment tho, since maybe he's able to use C++ as well. – rmn Jan 18 '10 at 21:18
  • If he can use C++, he should prefer the canonical `std::size_t size(T (&)[N]) {return N;}` version in order to have a stronger type check. – avakar Jan 18 '10 at 21:21
  • A potential drawback to the C++ solutions that return a value from a function are that they aren't compile-time constants (I understand that that's not what the OP was looking for, but it's something to be aware of). For a solution that's 100% compile-time and typesafe (but still requires a bit of macro use to have the desired ease of use), look at Microsoft's implemntation using C++ templates in WinNT.h – Michael Burr Jan 18 '10 at 21:58
  • @Michael Burr: not all arrays are created at compile-time, so the size would have to be calculated at runtime anyway. – dreamlax Jan 18 '10 at 22:45
  • @dreamlax: the template function solutions given here wouldn't compile (even if only to evaluate at runtime) for arrays whose size isn't statically known at compile time either. – Michael Burr Jan 18 '10 at 22:49
  • @Michael Burr: I wasn't saying they were, I was just saying that not all array sizes can be determined at compile time. – dreamlax Jan 18 '10 at 22:51
  • @dreamlax: I guess I thought you were trying to point out that the C++ solutions here had some advantage for that situation. – Michael Burr Jan 18 '10 at 23:16
1

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.

hookenz
  • 36,432
  • 45
  • 177
  • 286
1

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.

0x2333
  • 42
  • 3
0

This works pretty well:

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