3

For the following scenario, how can I get the size (3) of the array a via the pointer c? What is the pattern for solving this sort of problems?

struct struct_point {
  int x;
  int y;
  int z;
};

typedef struct struct_point point;

int test_void_pointer () {
  point a[3] = {{1, 1, 1}, {2, 2, 2}};
  void * b;
  point * c;
  b = a;
  c = b;
  /* get_size_p (c) */
}
象嘉道
  • 3,657
  • 5
  • 33
  • 49

3 Answers3

7

You can't. The pointer is just an address, a number, and it doesn't hold any information about the data it points to except its type.


Side note: that's why they say "arrays decay to pointers". They "decay" because inherently a pointer holds less information compared to an array.

As nims points out in the comments when passing an array to a function, it automatically decays to a pointer to the first element - and doing sizeof in the function doesn't yield the expected result. Incidentally there's also a C FAQ about this.

cnicutar
  • 178,505
  • 25
  • 365
  • 392
4

In C, no information about the size of the array is stored with the array. You have to know how big it is to work with it safely.

There are a few techniques for working around this. If the array is declared statically in the current scope, you can determine the size as:

size_t size = (sizeof(a) / sizeof(a[0]);

This is useful if you don't want to have to update the size every time you add an element:

struct point a[] = {{1, 1, 1}, {2, 2, 2}};
size_t size = (sizeof(a) / sizeof(a[0));

But if you have an arbitrary array, that has been passed in from somewhere else, or converted to a pointer as in your example, you'll need some way of determining its size. The usual ways to do this are to pass the size in along with the array (either as a separate parameter, or as a struct containing the array), or if the array is of a type which can contain a sentinel value (a value of the given type that is not valid), you can allocate an array one bigger than you need add a sentinel to the end of the array and use that to determine when you've reached the end.

Here's how you might pass in a length as a separate argument:

struct point myfunction(struct point array[], size_t n) {
    for (size_t i = 0; i < n; ++i) {
        struct point p = array[i];
        // do something with p ...
    }
}

Or as a structure containing the length:

struct point_array {
    size_t n;
    struct point elems[];
}
struct point myfunction(struct point_array a) {
    for (size_t i = 0; i < a.n; ++i) {
        struct point p = a.elems[i];
        // do something with p ...
    }
}

It would probably be hard to use sentinel values with an array of struct point directly, as there is no obvious invalid value that is still of the same type, but they are commonly used for strings (arrays of char which are terminated by a '\0' character), and arrays of pointers which are terminated by a null pointer. We can use that with struct point by storing pointers to our structures rather than storing them inline in the array:

struct point *myfunction(struct point *a[]) {
    for (size_t i = 0; a[i] != NULL; ++i) {
        struct point *p = a[i];
        // do something with p ...
    }
}
Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
0

There's a way to determine the length of an array, but for that you would have to mark the end of the array with another element, such as -1. Then just loop through it and find this element. The position of this element is the length.

Sohil Omer
  • 1,171
  • 7
  • 14