Is it safe to change data via a pointer when another pointer-to-const observes it?

132 Views Asked by At

Is it a safe code, or can the compiler optimize access through p, such that *p will result in 42?

#include <stdio.h>

int i = 42;
int *d = &i;

void test(int const *p) 
{
    *d = 43;
    printf("%d, %p\n", *p, (void *) p);
    printf("%d, %p\n", *d, (void *) d);
}

int main() {
    test(d);
    return 0;
}

I found out that *p is usually printed in 43, but I wonder if there are any pitfalls here, so that in some conditions printing *p could yield 42.

3

There are 3 best solutions below

0
KamilCuk On BEST ANSWER

Is it a safe code, or can the compiler optimize access through p, such that *p will result in 42?

Safe.

if there are any pitfalls here, so that in some conditions printing *p could yield 42.

No.

The compiler, if it applies such optimizations, has to be aware that you are modifying a global variable that may be aliased via another pointer and has to recompute the result at each access. const pointer only means that you cannot modify the memory using that pointer. You may consider researching restrict keyword and aliasing in C programming language.

2
Chris On

A key misunderstanding here may be how arguments are passed to functions in C. They are passed by value.

When d is passed to test, the value of the pointer (a memory address) is copied into the pointer-to-const p. Both contain the same memory address, but only p's target is constrained by const.

If you try to update the data at that address by modifying *p, you will run into an error, but d is not similarly constrained, so *d can be used to update the data at that memory address.

Both pointers still point to the same data, so printing the int pointed to by p shows 43.

5
einpoklum On

@Chris and @KamilCuk already explained why *p will necessarily yield 43. But there is more to add here:

Is it a safe code?

Your specific program is safe, but the kind of programming it demonstrates is generally not very safe.

One of these issues is aliasing via pointers is somewhat error-prone: It is more likely for you to forget to account for multiple kinds of access to the same piece of data. That is not to say you should always avoid it completely, but you should strive to minimize it.

Another is the use of global variables: It is not automatically a bad idea, but - testability, readability, understandability, predictability of your program is usually improved by avoiding them. See the discussion in this StackOverflow question:

Why are global variables bad, in a single threaded, non-os, embedded application

Now, in your specific case: If you're already passing a pointer to i - why make d global? And why make i global in the first place?

You could, for example, write:

void test(int const *p)
{
    // can only use p or *p here - not d nor i
}

int main() {
    int i = 42;
    int *d = &i;
    test(d);
    return 0;
}

no globals, and the lifetime of i as d is the entire duration of the program. You just exert tighter control over who has access to what.

And a few more nitpicks:

  • It's important to pick meaningful names for variables.
  • When a person sees d and p as variable names, they might intuit that p is a pointer, but they are quite likely to assume d isn't. Of course they can go read the variable declaration, but sometimes it's far away and the reader won't do so.