4

Possible Duplicate: Calculating size of an array

This question has been asked before, but I want to confirm it. Let's say I have the following C++ function:

#include <stdio.h>
#include <stdin.h>
#define length(a) sizeof(a)/sizeof(a[0])

int main()
{
    double c[] = {1.0, 2.0, 3.0};
    printf("%d\n", getLength(c));
    return 0;
}

int getLength(double c[]) { return length(c);}

This should return the wrong value because size of will return the size of the pointer as opposed to the size of the array being pointed at. This:

template<typename T, int size>
int length(T(&)[size]){return size;}

I know it works directly, but I want to know if I can somehow call it indirectly i.e. via a helper function. I understand that two possible alternatives are to either store the length of the array in a separate slot or use a vector, but what I want to know is:

Given a function getLength:

getLength(double arr[])
{
...
}

Is there a way to compute the length of arr without passing any more information?

Community
  • 1
  • 1
BlackJack
  • 2,726
  • 1
  • 18
  • 25
  • @AUpadhyay: I can't get your second example to compile. –  Jun 30 '11 at 17:27
  • fixed 2nd example that compiles: http://codepad.org/0fSl8FKX –  Jun 30 '11 at 17:31
  • Your first example has a bug too, fixed here: http://codepad.org/6TctYm8p –  Jun 30 '11 at 17:34
  • THanks 0A0D, but my test code example was meant to not compile. The getLength function is redundant, but I wanted to know if there was any way to get an array's size if it is passed as a parameter to another function. – BlackJack Jun 30 '11 at 17:35
  • @AUpadhyay: That's not what the other answerers are inferring. –  Jun 30 '11 at 17:36
  • @0A0D - Although the first function now compiles, the answer is incorrect. And yes, let me clarify my question to make that more clear. – BlackJack Jun 30 '11 at 17:38

3 Answers3

4

The question boils down to the difference between compile-time information and run-time information.

The template works because the size of the array is known at compile time, and the template is evaluated at compile time.

The other function doesn't work, because all that's known at compile time is that the function will be called with an array. The size of the array isn't specified in the function prototype. There's no run-time information passed about the size of the array either, unless you add another parameter and pass it explicitly.

Mark Ransom
  • 271,357
  • 39
  • 345
  • 578
  • So am I correct in understanding that length(), when called from getLength(), fails because the size of the array isn't known when getLength() is executed? – BlackJack Jun 30 '11 at 17:36
  • @AUpadhyay: Of course it is known, the array `c` is allocated with 4 elements at compile time. You created a macro in the first example. –  Jun 30 '11 at 17:38
  • 1. It's allocated with 3 elements, not 4. I think you may be confusing arrays with strings here. – Joel Jun 30 '11 at 17:44
  • 2. The size of the array is "forgotten" by the runtime. In the case of array allocated this way (i.e. on the stack) the array elements are plopped on the stack at the beginning, then when the function returns, the stack frame is adjusted to where it was at the start of the function call. The runtime doesn't know or care how big the array is. – Joel Jun 30 '11 at 17:47
  • @Joel: Oops, was looking at the second example. –  Jun 30 '11 at 17:52
  • 1
    @AUpadhyay: When the compiler processes `getLength()`, it does not know what possible arguments you are going to pass, that range from `int*` to any sized array. The standard dictates that an argument of type *array of size N of T* is transformed into *pointer to T* by the compiler (that is why I don't like using the array syntax in function arguments: if it is a pointer, call it a pointer!) – David Rodríguez - dribeas Jun 30 '11 at 20:24
  • @Joel: the size of the array is *known* when the function is called, but is unknown (even at compile time) inside the function. It is not forgotten at runtime, it is already unknown at compile time. – David Rodríguez - dribeas Jun 30 '11 at 20:26
3

The template version would work. Who said it will not work?

template<typename T, int size>
int length(T(&)[size])
{
   return size;
}

This is correct. It will return the length of the array. You just need to call this function directly. It seems you want to call this function from getLength(). Don't do that, because the way you've written getLength function is equivalent to this:

int getLength(double *c) { return length(c);}

There is absolutely no difference between your written getLength() and the above version. That is, once you call this function, (or your function), the array is already decayed into a pointer to double. You've already lost the size information with the decay of the array (which is why this is called decaying of array). So you should NOT call this function, instead call the length() function directly..

However, there is little problem with length() as well. You cannot use this function where const-expression is needed, as such:

int anotherArray[length(c) * 10]; //error

So the solution would be this:

template < std::size_t N >
struct value2type { typedef char type[N]; };

template < typename T, std::size_t size >
typename value2type<size>::type& sizeof_array_helper(T(&)[size]);

#define length(a) sizeof(sizeof_array_helper(a))

Now you can write:

int anotherArray[length(c) * 10]; //ok
Nawaz
  • 327,095
  • 105
  • 629
  • 812
  • 3
    The template works, but it's being called from a function that has already lost the array dimension. – Mark Ransom Jun 30 '11 at 17:19
  • I added the test code I've used where the template version doesn't work. Am I writing it incorrectly? Also, I know it works directly, but I want to know if I can somehow call it indirectly i.e. via a helper function. – BlackJack Jun 30 '11 at 17:24
  • 2
    @AUpadhyay: You can't call length from getLength(..). That's your problem. –  Jun 30 '11 at 17:30
  • @0A0D - Why though? Using either the macro or the template version, I want to understand why we can't call length from getLength(). – BlackJack Jun 30 '11 at 17:45
  • @AUpadhyay: Now, I've added the explanation why you should call `getLength` and what happens if you call it. – Nawaz Jun 30 '11 at 17:53
  • 1
    That makes perfect sense, thank you. I don't code in C++ (only C) but I've heard the expression "Arrays and pointers are the same, except when they're not.", and I guess this is one of those "not's"! Appreciate it – BlackJack Jun 30 '11 at 17:56
  • Another alternative for the const expression that does not require the `value2type` helper would be `template char (&size_of_array_helper(T(&)[size]))[size];` A little harder to parse for humans, but the compiler should have no problems with that. – David Rodríguez - dribeas Jun 30 '11 at 20:17
  • @AUpadhyay: Arrays and pointers are not the same in C either, the types differ: `void f( int (*p)[10] ) {} int main() { int array[10]; f(&array); }` compiles and does what is expected. If you change the array size or if you let the pointer decay, then the call will fail. – David Rodríguez - dribeas Jun 30 '11 at 20:20
1

To fix your first example, you have to do the following (length macro taken from this answer):

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

#define length(a) ((sizeof(a)/sizeof(0[a])) / ((size_t)(!(sizeof(a) % sizeof(0[a])))))

int main()
{
    double c[] = {1.0, 2.0, 3.0};
    printf("%d\n", length(c));
    return 0;
}

Note that I did not call length from getLength() because that would cause a divide by zero problem.

You can replace my macro with #define length(a) (sizeof(a) / sizeof(*a)) but that will return 1 as the size, which is wrong.

Your first example, after it has been fixed (see my comment to your answer), will return a size of zero.

You are not permitted to call the templated function from your getLength() function.

The following code will fix your templated example:

#include <stdlib.h>
#include <stdio.h>
int getLength(double c[]);

template<typename T, int size>
int length(T(&)[size])
{
   return size;
}

int main()
{
    double c[] = {1.0, 2.0, 3.0, 4.0};
    printf("%d\n", length(c));

    return 0;
}

so to answer your question, no you have to do it one of two ways like above. You cannot pass the array as you are doing because the array decays into a pointer. That's why pass by reference is preferred. By reference will give the proper sizeof(). See this answer.

Community
  • 1
  • 1
  • Thank you very much for your help 0A0D. I just have one question. When you say "You are not permitted to call the templated function from your getLength() function." Why? Is this a general rule? If so, do you know where I could read more about it? Thanks – BlackJack Jun 30 '11 at 17:51
  • @AUpadhyay: See my update at the bottom my answer and the associated link. –  Jun 30 '11 at 17:58