user3558488
user3558488

Reputation: 29

C Passing array of unknown size to a function in a single variable

In C, I have to pass an array to a function in a single variable, and without knowing what the size the array will be before the program runs. The on-paper solution is to have an extra element to the array where you'll store the array's size (I think this is called a "sentinel value"). Ok, but I'm having problems implementing this.

Passing array[] as a function argument doesn't seem to work. I suppose I can send a pointer to the first element, but how do I then access the rest of the array?

Upvotes: 1

Views: 10775

Answers (3)

michaelmeyer
michaelmeyer

Reputation: 8205

The choice of the sentinel value depends on the type of data the array stores. For anything involving a pointer, use NULL, and NAN for floating points, e.g.:

char *strings[] = {"foo", "bar", "baz", NULL};
double doubles[] = {1.0, 2.4, 6.5, NAN};

Now checking where the array ends amounts to walking along the array until you find the sentinel:

#include <stddef.h>  // for NULL

size_t count_strings(char **strings) {
    size_t num_strings = 0;
    while ((*strings)++ != NULL) {
        num_strings++;
    }
    return num_strings;
}


#include <math.h>    // for NAN and isnan(…)

size_t count_doubles(double *doubles) {
    ...
    while (!isnan(*doubles++)) {
        ...
    }
}

Finding a sentinel is more difficult for some data types (say, int), but you can then choose a conventional value.

Upvotes: 2

Deduplicator
Deduplicator

Reputation: 45654

In C, an array decays to a pointer to its first element in most contexts:

6.3.2.1 Lvalues, arrays, and function designators

[...] Except when it is the operand of the sizeof operator, the _Alignof operator, or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. If the array object has register storage class, the behavior is undefined.

That is useful, because array indexing a[i], is defined using pointer arithmetic: *(a+i).
So, you can use the same operations on the pointer as on the array.
Still, there is one downside to this consistency: You cannot pass an array by value without wrapping it in a struct.

Next, a sentinel is an invalid value of the element type used as a stop marker, like for strings the 0 and for pointers mostly NULL.
What you actually described was a counted array, having the length prepended at index ((size_t*)a)[-1] or some such.

Upvotes: 2

Eric Z
Eric Z

Reputation: 14505

You iterate over the array whose address is passed in as the first parameter, and check the range by the array length that's passed in as the second parameter.

// Or, void f(int *a, int size) as the array decays
// to a pointer when passed into this function
void f(int a[], int size)
{
   // It's your own responsibility to make sure you
   // don't access the out-of-range elements.
   for (int i = 0; i < size; ++i)
   {
      printf ("%d\n", a[i]);
   }
}

// use of f()
int a[10];
// or f(&a[0], sizeof(a) / sizeof(a[0]))
f(a, sizeof(a) / sizeof(a[0]));

Upvotes: -1

Related Questions