tags:

views:

292

answers:

12
+2  Q: 

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;

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?

+7  A: 

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
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
Yes but there is no c compiler on earth who sets int = 64 bit.Unless you write or patch one.
Lothar
@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
@Lothar: the old Cray C compiler I used several decades ago had 64 bit ints...
Chris Dodd
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
+5  A: 

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
This won't work. Try `int *foo = NULL; SIZEOF_ARRAY( foo );`
wheaties
Naturally ... but thats just the side effect of macros. It requires the USER to use them appropriately.
Goz
It does have the virtue of potentially working correctly, unlike the function.
David Thornley
@David: And isn't that what's most important: Code that potentially works correctly, sometimes?
BlueRaja - Danny Pflughoeft
Please put parens around the whole macro expansion...
Michael Burr
The problem is the OP's understanding of arrays and pointers, which the above answer doesn't explain.
Alok
@BlueRaja: code that works when invoked *correctly* can be deemed good enough for your necessity.
Leonardo Herrera
@BlueRaja: Good point, but it arguably beats code that definitely won't work. Man, I'm glad C++ has `std::vector`.
David Thornley
@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
@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
@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
+4  A: 

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
`std::vector` is syntax error in C. The OP is using C.
Alok
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
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
@Asher - that's a legitimate point of view. Personally I find the utility of the macro to outweigh the drawbacks.
Michael Burr
+1  A: 

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
+5  A: 

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
+1  A: 

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

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
The question is about C.
avakar
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
That's not C, though...
pib
If he can use C++, he should prefer the canonical `std::size_t size(T (}` version in order to have a stronger type check.
avakar
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
@Michael Burr: not all arrays are created at compile-time, so the size would have to be calculated at runtime anyway.
dreamlax
@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
@Michael Burr: I wasn't saying they were, I was just saying that not all array sizes can be determined at compile time.
dreamlax
@dreamlax: I guess I thought you were trying to point out that the C++ solutions here had some advantage for that situation.
Michael Burr
+1  A: 

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

Michael Burr
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
+2  A: 

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.

Alok
A: 

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

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.

Matt H
A: 

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