3

I (think I) understand that you can only retrieve the size of an array (using sizeof) if it is declared at compile time on the stack, e.g.

int my_array[] = {1,2,3};
sizeof(my_array) == 3;

As soon as you start using pointers you lose this length information.

e.g. if you pass a pointer to int as a function parameter to get an int array into a function you can no longer use sizeof() in this way, it will just return the number of bytes used to store a pointer.

Clearly, it is vital to know how long your arrays are.

So which of the following options should I use when passing arrays around?

  1. Pass a pointer and an accompanying length parameter

    int my_func(int *my_array, size_t len_my_array)

  2. Create my own vector struct

    struct vector {
       int *my_array;
       size_t len;
    }
    
    int my_func(struct vector *my_vector)
    
  3. Use someone elses vector implementation. (Is there a default implementation for C as there is for C++?)

  4. Another approach which I've missed?

(I'm currently using the 1st option but its a bit unwieldy and I'm keen to know if this is considered poor programming practice)

Matt Hulse
  • 5,496
  • 4
  • 29
  • 37
bph
  • 10,728
  • 15
  • 60
  • 135

5 Answers5

7

One approach that you've missed is putting a terminating element at the end of the array, kind of like '\0' marks the end of a char*.

This and passing the length of the array as a different parameter are the usual ways of doing it.

Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
5

The standard way is to utilize the first approach and pass a pointer and a size especially if you want your code to be reused by others.

rerun
  • 25,014
  • 6
  • 48
  • 78
3

The simplest way in modern C (aka C99) to my opinion is to use array notation

int my_func(size_t len, int my_array[len]);

which is almost the same as you did but having the size first. The reason to do it in this order is that this scales well to multidimensional arrays

int my_func(size_t n, size_t m, int my_array[n][m]);

you'd then get the index computation correctly inside the function without any additional difficulties.

The only things that you'd have to ensure

  • have the sizes in the list before the arrays, so the sizes are known when you declare the arrays
  • use exactly the same declaration (notation for the dimension and so) in the prototype as you use afterwards for the definition, otherwise you will confuse your users
Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
  • this looks tidy, but is the 2nd len (in `my_array[len]`) actually doing anything? e.g. are these all equivalent: `int my_array[len]`, `int my_array[]` and `int *my_array`? – bph Nov 16 '11 at 16:40
  • 1
    no, in the first example this is just syntactic sugar, but documents well the expectation, I feel. For the second, this really changes things. Otherwise you wouldn't be able to do something like `my_array[i][j]` – Jens Gustedt Nov 16 '11 at 16:42
  • 1
    I note that the syntax you suggest is valid in C99, but invalid in C90. – Robᵩ Nov 16 '11 at 17:23
  • @Rob, right, I tend to forget that there are still amateurs of the ancient texts around. I'll add that. – Jens Gustedt Nov 16 '11 at 17:24
  • So declaring an array parameter as int my_array[len] makes sizeof my_array correctly return the array size? – Ferdi265 Oct 06 '17 at 12:30
  • 1
    @Ferdi265, no unfortunately not. The first dimension is still just a pointer. But the second dimension yes, for the code in my second example `sizeof(*myarray)` gives you the correct size of a line. – Jens Gustedt Oct 06 '17 at 15:33
1

There is no widely accepted answer, which means about the only way you can depend upon is the first. In particular, unless you write all your code yourself (i.e., never use any libraries) you can pretty much count on the fact that any vector struct you define yourself will differ from anything anybody else uses so at the "boundary" between the code bases, you'll need to your your first method in any case. They may have a vector type of their own that they use internally. You may have one of your own that you use internally -- but where the two talk to each other, they'll have to "unwrap" that type into its individual components.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
0

It's a bit out there but I've just been reading about arrays and pointers and you could consider using sizeof to determine the total amount of bytes and then divide by the size in bytes of that data type that the array contains to give you the array length...

Cocoadelica
  • 3,006
  • 27
  • 30
  • 1
    yes - i've seen macros defined to do that, I'm unclear as to the situations in which it would work though - as a pointer to an int has no knowledge of how many bytes long the array is that it is pointing to – bph Nov 16 '11 at 16:44
  • actually, thinking about it, you must be right - a pointer must always know how many bytes its pointing at or how else could free() work? It only takes on parameter, the name of the pointer, and manages to free the correct amount of memory on the heap... – bph Nov 16 '11 at 17:43
  • so this should do it - `#define sizearray(a) (sizeof(a) / sizeof((a)[0]))` – bph Nov 16 '11 at 18:25
  • 1
    This won't work, `sizeof` on array arguments return the size of the pointer type. @bph Pointers never store size information. Most operating systems store the size of dynamically allocated memory regions internally for `free()` to work, but there is no (portable) way to extract it. – osvein Oct 20 '17 at 22:11
  • @osvein What you say is only so if the argument to `sizeof` is a pointer by accident because you pass it a function parameter that has decayed to a pointer: https://stackoverflow.com/q/37538/2057969 – Lover of Structure Jul 21 '23 at 00:20