0

This question was inspired by this snipped from a C book I've been working through

Where is says:

int num;
int *pi=0; // Zero referes to the null pointer, NULL
pi = #
*pi = 0;   // Zero refers to the integer zero

We are accustomed to overloaded operators, such as the asterisk used to declare a pointer, to dereference a pointer, or to multiply. The zero is also overloaded. We may find this discomforting because we are not used to overloading operands.

So I did a bunch of investigating with the debugger and my answer is what the debugger showed me. It's undefined behavior, I get that, but I'm not writing a program I'm trying to figure something out. Whatever the case, the pointer is initialized to 1 every time I run it, I'm not depending on that in some calculation, it's what the debugger repeatedly shows.

This is all just one question really, but I had to write more or it wouldn't accept it. Why does *p=0 have different effects at different times? How is 0 overloaded? How can I tell if *p=0 is making p a null pointer or if it is assigning 0 to a variable that p is pointing to? And how does the compiler know to assign a null at the address of p rather than the address that p points too?

$ cat prac.c
#include <stdio.h>

int main()
{
  int num;
  int *p;                                

  printf("0) %p is %d\n",p,*p);

  *p=0;                                  //Why is this......

  printf("1) %p is %d\n",p,*p);

  num=5;
  p=&num;

  printf("2) Now %p is %d\n",p,*p);

  *p=0;                                  //....different than this

  printf("3) p is not null, %p : %d\n",p,*p);


  return 0;
}



$ ./prac
0) 0x7ffeb36b5c30 is 1    //pointer is initialized to 1, not-null, definitely out-of-bounds though
1) 0x7ffeb36b5c30 is 0
2) Now 0x7ffeb36b5b3c is 5
3) p is not null, 0x7ffeb36b5b3c : 0
flerb
  • 644
  • 9
  • 14
  • 4
    Your code just causes undefined behaviour by using uninitialized variables, the output is meaningless – M.M Jun 19 '18 at 22:11
  • Note that with `*p=0` you don't make the pointer point somewhere, you're not assigning to the pointer. You're assigning to what the pointer *is pointing to*. – Some programmer dude Jun 19 '18 at 22:13
  • 1
    if you have a variable `int *p; , then the statement `p = 0;` assigns 0 to `p` , that is, `p` is then a NULL pointer. The statement `*p = 0;` on the other hand assigns 0 to whatever `p` points to. You are not allowed to do that unless `p` points to something valid - which your code has many examples of not adhering to. – nos Jun 19 '18 at 22:15
  • 1
    I think you are mistaking *pointer* and *index* notation. With an array, `array[n]` index notation is equivalent to `*(array + n)` in pointer notation (where `n` is simply the index (or offset) of the dereferenced element within the array) Obviously when `n = 0` pointer notation simplifies to `*array` to reference the 1st element of the array. – David C. Rankin Jun 19 '18 at 22:15
  • Possible duplicate of [Why can you assign an integer value to an uninitialized pointer](https://stackoverflow.com/questions/36962226/why-can-you-assign-an-integer-value-to-an-uninitialized-pointer) – AGN Gazer Jun 19 '18 at 22:15
  • The pointer was actually "initialized" to 0x7ffeb36b5c30, not 1. And they aren't different. Why do you think they were different? – user253751 Jun 20 '18 at 00:59
  • Good to see re-posted. – chux - Reinstate Monica Jun 20 '18 at 03:29
  • `int * p = 0;` is roughly the same as `int * p; p = 0;`. But definitely ***not*** the same as `int * p; *p = 0;`. The `*` in the variable definition is a different from the de-referencing operator `*`. – alk Jun 20 '18 at 06:05
  • See [this answer](https://stackoverflow.com/a/32136460/584518). – Lundin Jun 20 '18 at 09:18

2 Answers2

1

The concept you're asking about is null pointer constants.

The C standard defines a null pointer constant as

An integer constant expression with the value 0, or such an expression cast to type void *

So the expression 0, which is of type int, is also a null pointer constant. (Note that a null pointer constant needn't be of pointer type.)

The standard further says that:

If a null pointer constant is converted to a pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal to a pointer to any object or function.

Given:

int n;
int *p;

this:

p = 0;

implicitly converts the int expression 0 to a null pointer of type int*, while this:

n = 0;

or

*p = 0;

does not perform an implicit conversion, because the object being initialized is already of type int.

The rules for when implicit conversions are invoked are stated in the description of the assignment operator. The same rules apply to initialization, argument passing, and return statements. These rules explicitly state that a null pointer constant is converted to the target type.

See the N1570 draft of the C11 standard (large PDF).

  • Null pointer constants and null pointers: 6.3.2.3
  • Simple assignment: 6.15.6.1
Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
1

I'm not familiar with the book or article you cite, but it failed spectacularly in helping you Understand and Use Pointers in C.

What is a Pointer?

A pointer is simply a normal variable that holds the address of something else as its value. In other words, a pointer points to the address where something else can be found. Where you normally think of a variable holding an immediate values, such as int a = 5;, a pointer would simply hold the address where 5 is stored in memory, e.g. int *b = &a;. It works the same way regardless what type of object the pointer points to. (a pointer, is just a pointer....)

How to Declare, Assign an Address to & Reference the Value Pointed to

Let's take your example and look at what each line does (ignoring your p & pi typos and using p only):

int num;    /* declares an integer 'num' whose value is uninitialized */

int *p = 0; /* declares a pointer 'p' and ininializes the address
             * to a the constant expression 0. (a Null Pointer Constant)
             */

See C11 Standard - Null Pointer Constant (p3) which is a constant expression with the value 0, or such an expression cast to type void * (e.g. (void *)0). Virtually all C compilers, and if fact the C-Standard provide NULL for this purpose. (see: C11 Standard - 7.19 Common definitions ) So you would generally see:

int *p = NULL;  /* declare and initialize 'p' to the Null Pointer Constant */

Next the unary operator '&' is used to obtain the address of an object. Here:

p = &num;   /* assigns the address of 'num' as the value of 'p', so 
             * 'p' holds the address of 'num' (e.g. 'p' points to the
             * memory location where 'num' is stored).
             */

The unary '*' opertor is the dereference operator and provides a way to directly access the value located at the memory address held (pointed to) by a pointer, so:

*p = 0;     /* sets the value at the address held by 'p' to zero */

Now lets look at your code and output:

int num;
int *p;                                

printf("0) %p is %d\n",p,*p);

What is the address held by p and what is the value of num?

You have attempted to access the value of an uninitialized pointer and an uninitialized integer. You have just invoked Undefined Behavior. The defined operation of your code is over and anything can happen, from appearing to behave normally or SegFault'ing. You cannot access uninitialized values.

*p=0;

printf("1) %p is %d\n",p,*p);

p is now initialized to the Null Pointer Constant, you cannot dereference a NULL pointer -- Undefined behavior again.

num=5;
p=&num;

printf("2) Now %p is %d\n",p,*p);

Hallelujah! num is now initialized to 5 and p now holds (points to) a valid memory address! (but unfortunately once you invoke Undefined Behavior, the game is over, and you may or may not get the correct output, but luckily you appear to.

*p=0;

printf("3) p is not null, %p : %d\n",p,*p);

What does p point to? (hint: num). What does the dereference operator do? (hint: allows you to access the value at the memory location held by a pointer). So what did *p = 0; do? (hint: you just set the value of num to 0, which is the value at the address held by p)

Let me know if you have further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • In fairness to the book, I just started it, came across the interesting piece of code *p=0 having differing results at times and screwed myself up with the debugger instead of checking the docs...or continuing to read the book. – flerb Jun 20 '18 at 18:42
  • There is nothing wrong with chasing every rabbit-trail to its end. That's how we learn. We all screwed up pointers when starting to code in C. While pointers and pointer arithmetic are simple, it's a difficult concept to wrap your head around at first. Once you make peace with what a pointer is and that its `type` controls the number of bytes (offset) between `*p` and `*(p + 1)`, then you are on your way to a full understanding. Good luck with your coding. – David C. Rankin Jun 20 '18 at 20:06