1

I am trying to print an array of strings. Here is the code I have so far:

#include <stdio.h>

void print_array (char * strings[]) {
    size_t array_length = sizeof(strings) / sizeof(strings[0]);
    for (int i=0; i < array_length; i++)
        printf("%s, ", strings[i]);
}

int main(void) {

    char * strings[] = {
        "Hello",
        "Zerotom",
        "new"
    };

    // it does work here...
    printf("Array size: %lu\n", sizeof(strings) / sizeof(strings[0]));

    // but not here
    print_array(strings);

    return 0;
}

From this, I get a warning that says:

strings.c:4:30: warning: sizeof on array function parameter will return size of 'char **' instead of 'char *[]' [-Wsizeof-array-argument] size_t array_length = sizeof(strings) / sizeof(strings[0]);

However, if I change the function arg from char * strings[] to char **, it returns the wrong length (1 instead of 3). What am I doing wrong here?

6
  • void print_array (char **strings) and you must pass the number of strings to the function (or use a sentinel NULL to mark the end of valid strings) size_t array_length = sizeof(strings) / sizeof(strings[0]); does NOT work after the array is passed as a parameter to a function (it is converted to a pointer) so you in effect have size_t array_length = sizeof(a_pointer) / sizeof(a_char); Commented Sep 27, 2019 at 2:31
  • @DavidC.Rankin why does it work "within the initial function" but when its passed to another function you cannot get the array length? Commented Sep 27, 2019 at 2:32
  • 1
    On access an array is converted to a pointer to the first element (passing as a parameter counts as an access). There are 4-exceptions when an array is not converted to a pointer, one of which is when used with the sizeof operator (but that must be within the scope in which the array was declared). After being passed as a parameter, it is just a pointer. See C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) Commented Sep 27, 2019 at 2:35
  • "it", string, is not the same in the 2 cases, With char * strings[], strings is not an array. Commented Sep 27, 2019 at 2:35
  • 1
    You just add the final element, e.g. ..., "new", NULL. Then you can pass strings to the function and do while (*strings != NULL) printf("%s, ", *strings++); or size_t i = 0; while (strings[i] != NULL) printf("%s, ", strings[i++]); Or, if you prefer for loops then for (size_t i = 0; strings[i]; i++) { ... } Commented Sep 27, 2019 at 2:40

1 Answer 1

2

The reason you can print strings in main() but not in print_array is the result of how an array is converted to a pointer on access. What this means is that when you access an array (subject to 4 exceptions below) the array is converted to a pointer to the first element in the array. After the conversion takes place, as it does when you pass an array as a parameter to a function, you have only a pointer, not an array.

The C11 Standard (as well as the C17 Standard) reads as follows:

Array pointer conversion

(p3) 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. C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3)

If you note above, when used with the sizeof operator, and array is not converted to a pointer, so in main(), sizeof(strings) / sizeof(strings[0]) provides the number of elements in the array of string. However, after you pass strings to print_array, the conversion to pointer has already occurred, so that in print_array your attempted use of sizeof results in:

size_t array_length = sizeof(a_pointer) / sizeof(a_char);

(which is 8 on x86_64, or 4 on x86)

You have two choices (1) pass the number of elements in your array as a second parameter to print_array, or (2) make the last pointers in strings NULL (to be used as a sentinel value). Then in print_array you can just iterate over strings[i] until it is NULL.

A couple of quick examples:

Passing the Number of Elements

#include <stdio.h>

void print_array (char **strings, size_t nptrs)
{
    for (size_t i = 0; i < nptrs; i++)
        printf("%s, ", strings[i]);
    putchar ('\n');
}

int main(void) {

    char *strings[] = { "Hello",
                        "Zerotom",
                        "new" };

    print_array (strings, sizeof strings/sizeof *strings);

    return 0;
}

Example Use/Output

$ ./bin/prnarray
Hello, Zerotom, new,

Adding a Sentinel NULL to strings

#include <stdio.h>

void print_array (char **strings)
{
    for (size_t i = 0; strings[i]; i++)
        printf("%s, ", strings[i]);
    putchar ('\n');
}

int main(void) {

    char *strings[] = { "Hello",
                        "Zerotom",
                        "new",
                        NULL };        /* sentinel NULL */

    print_array (strings);

    return 0;
}

(same output)

There are at least a handful of ways to loop using either for or while loops and either using a pointer to strings and pointer arithmetic, or using array indexing (the difference are simple semantics as you are doing the same thing). Look things over and let me know if you have further questions.

Sign up to request clarification or add additional context in comments.

Comments