Creating a Pointer Array (Wrong)

I’m delighted to receive reader email regarding the various puzzles in the C programming language. Some of them involve creative thinking and approaches that seem like they work — but don’t. Pointers are one of the most common subjects.

Here is the code sent to me. See if you can determine what’s wrong:

2021_05_15-Lesson-a.c

#include <stdio.h>

int main()
{
    int *nums = { 1, 2, 3 };

    printf("%d %d %d\n",
            *(nums+0),
            *(nums+1),
            *(nums+2)
          );
    return(0);
}

It may appear that this code creates a pointer to an array of integers: nums references values 1, 2, and 3. The printf() statement outputs the values. Correct?

Nope.

The code compiles, which amazes me. It generates two warnings, which means many beginners attempt to run the code. Here is the output I see:

Segmentation fault: (core dumped)

Not good.

The warnings generated allude to the problem. The first tells you what’s wrong, though a beginner may not understand the message text:

5:16: warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Wint-conversion]

The second warning is related to the first, still mysterious for a beginner:

5:19: warning: excess elements in scalar initializer [-Wexcess-initializers]

The issue with both warnings is found at Line 5:

int *nums = { 1, 2, 3 };

This statement declares an integer pointer, nums. Remember that a pointer is a variable that holds an address, the memory location of another variable. In the code above, no other variables are declared in the program, the assignment is improper. Further, you cannot assign a pointer directly to a memory location. For example:

int *address = 0x7088;

This trick worked in the early days of personal computing, before multitasking operating systems were available. You could set a pointer to some address anywhere in the computer to do whatever. Today’s operating systems carefully allocate and control chunks of memory; a direct memory location assignment just doesn’t work.

Similar to directly assigning a memory location, you cannot declare a pointer and assign it to a clutch of values. This trick barely works with a string, but doesn’t work otherwise:

int *nums = { 1, 2, 3 };

These values are not memory locations. Enclosing them in brackets explains the second warning: The compiler is expecting a single address, not an array or “scalar initializer.”

The first warning relates to assigning a pointer to a non-address: incompatible integer to pointer conversion. Variable *nums is a pointer and { 1, 2, 3 } is collection of integers. It isn’t even an array because it’s not declared anywhere.

How can the expression be constructed properly? The key is to have the memory location already created, available before the pointer assignment:

2021_05_15-Lesson-b.c

#include <stdio.h>

int main()
{
    int array[3] = { 1, 2, 3 };
    int *nums = array;

    printf("%d %d %d\n",
            *(nums+0),
            *(nums+1),
            *(nums+2)
          );
    return(0);
}

At Line 5, array[] is declared, initialized to three int values. Line 6 creates pointer *nums and immediately assigns its address to array[]; the & (address-of) operator isn’t required because the compiler sees the array as an address. How it’s done is important! The array is created through the assignment; the { 1, 2, 3 } thing by itself isn’t an array.

With the fix in place, the code compiles and generates the predictable output:

1 2 3

It’s possible to declare a pointer like *nums and assign it int values 1, 2, 3. I’ll demonstrate how the process works in next week’s Lesson.

Leave a Reply