0
votes

The following two blocks of code do the same thing:

void foo(int *num) {...}

int main(void)
{
    int num = 5;
    foo(&num);

    ...
}
void foo(int *num) {...}

int main(void)
{
    int *num;
    *num = 5;
    foo(num);

    ...
}

In both cases foo expects a pointer-to-int argument. In the first I declare an int and use the address operator (&) to pass it into foo. In the second I declare a pointer variable and directly pass it into foo.

Is there a reason to use either one, other than a stylistic preference? I prefer to declare all of my variables as pointers, as in the second, but I see the first a lot more frequently.

6
Only declare variables as pointers if you expect you will need to use them as such or you are dealing with a function which uses pointers as an argument / working with arrays, structs etc.Mitch
I don't really get how this got upvoted, since the latter program shouldn't even run most of the time... nor compile without warnings...Antti Haapala -- Слава Україні
I understand the issue and appreciate people taking the time to clarify. But it did run on my machine using the gcc compiler, I checked before posting. I wish it spit back a warning.MoF
@MoFink Try adding -Wall -Wextra -Werror to your gcc command, as that will show up a lot of dubious code (and the last one makes them appear as errors rather than ignore-able warnings!)Gwyn Evans

6 Answers

3
votes

No, those two snippets do very different thing. The first snippet is well-formed code. The second snippet is Undefined Behavior due to dereferencing an uninitialized pointer.

The second version is likely to crash or do some other undesired thing when executed. Also, compiled with warnings on any modern compiler, it would exhibit a warning, for example:

warning: variable 'num' is uninitialized when used here [-Wuninitialized]

2
votes

While the first code snippet gets the address of a variable and passes that to foo, the second dereferences an uninitialized pointer. What you instead could have done was:

int *num = malloc(sizeof(int));
*num = 5;

foo(num);

free(num);

Or:

int num = 5;
int *p = #
foo(p);

The main thing to take away from this is that the first form is the simplest. A valid pointer variable must be pointing to memory within your program's control, and that requires more work than just int *num; *num = 5;

1
votes

The following two blocks of code do the same thing:

No, they don't. Only the first has defined behavior at all. The second dereferences a wild pointer when it performs *num = 5 without having assigned a valid pointer to num.

In both cases foo expects a pointer-to-int argument. In the first I declare an int and use the address operator (&) to pass it into foo. In the second I declare a pointer variable and directly pass it into foo.

You seem to be suffering from from the distressingly common failing to distinguish between a pointer and the thing to which it points. They are different and largely orthogonal. Declaring int *num does not declare or reserve any space for an int. It declares a pointer that can point to an int, but the value of that pointer is invalid until you assign one that does point to an int.

Is there a reason to use either one, other than a stylistic preference?

Yes. The first is valid, and the second is not.

I prefer to declare all of my variables as pointers, as in the second, but I see the first a lot more frequently.

I'm sorry to hear that you see the second at all.

0
votes

Of course, as others have pointed out, in the second you need to initialize the point to valid allocated memory. So that's one point in favor of the first code.

For John Kugelman's example

int num;
int *p = #
*p = 5;
foo(p);

there doesn't seem to be any advantage over the first example. So if you fix the second example, you might just have more voluminous code, without any additional clarity.

Finally, and I think as a fair overall summary, the second example is really not getting the value of "declare all of my variables as pointers". You have to have actual memory somewhere, and the first example makes that clear. It is passed as a pointer, so except for the code which declared the memory, it is a pointer.

0
votes

one

void one(void) {
    int n = 42; // initialization
    foo(&n);
    putchar(n);
}

two

void two(void) {
    int *n;
    n = malloc(sizeof *n);
    *n = 42; // no initialization
    foo(n);
    putchar(*n);
    free(n);
}

three

void three(void) {
    int n[1] = { 42 }; // initialization
    foo(n);
    putchar(*n); //putchar(n[0]);
}
0
votes

I prefer to declare all of my variables as pointers, as in the second, but I see the first a lot more frequently.

This statement doesn't make much sense - surely not everything you work with is a pointer.

The code

int *num;
*num = 5;
foo( num );

is not well-formed, because num isn't pointing to anything (its value is indeterminate). You're attempting to assign 5 to some random memory address which may result in a segfault, or corrupt other data. It may work as intended with no apparent problems, but that's by accident, not design.

Just because a function takes a pointer as a parameter doesn't mean you have to pass a pointer object as that parameter. Yes, you could write something like

int num;
int *numptr = #
*numptr = 5;
foo( numptr )

but that doesn't buy you anything over simply writing

int num = 5;
foo( &num );

C requires us to use pointers in 2 cases:

  • When a function needs to update one of its input parameters;
  • When we need to track dynamically-allocated memory;

They also come in handy for building dynamic data structures like lists, queues, trees, etc. But you shouldn't use them for everything.