-2

I am having a problem with my exercise in which I have to explain the running of pointers in C.

Can you explain what is the differences between char *pp and (char*) p and the outputs to me?

#include <stdio.h>
#include <stdlib.h>

/*
 * 
 */
int main(int argc, char** argv) {

    int n=260, *p=&n;
    printf("n=%d\n", n);
    char *pp=(char*)p;
    *pp=0;
    printf("n=%d\n",n);
    return (EXIT_SUCCESS);
}

n=260
n=256

I'm so sorry for the mistake I've done! Hope you guys can help me.

Jabberwocky
  • 48,281
  • 17
  • 65
  • 115
Tran Phong
  • 113
  • 1
  • 7
  • 3
    Your title has nothing to do with the body of your question. Also, you should copy/paste snippets of code; don't post a screenshot. – Jonathon Reinhart Mar 04 '20 at 02:16
  • 2
    I am well into 20 minutes spent on writing an answer that explains both the question asked in the question and how it relates to the title. Now the question is closed and I have wasted 20 minutes of my life. I think the answer I was making was really useful so I am disappointed and I think other people should be disappointed too. I hate the way StackOverflow is run sometimes - there should be a way to do this that doesn't exasperate people who are legitimately trying to help. – Jerry Jeremiah Mar 04 '20 at 02:20
  • Looks like people confused "the question needs clarity" for "the OP needs clarity". – ikegami Mar 04 '20 at 02:59
  • 1
    @Jerry Jeremiah, It's open now – ikegami Mar 04 '20 at 03:29
  • 1
    `int n=260, *p=&n;` so `p` holds the address of `n` as its values (i.e. points to `n`) and is a pointer to type `int`. `char *pp=(char*)p;` creates a pointer to `char` named `pp` that also holds the address of `n` as its value. There is no violation of [C11 Standard - §6.5 Expressions (p6,7)](http://port70.net/~nsz/c/c11/n1570.html#6.5p6) (the "*strict aliasing rule*") because `pp` is type `char`. The key is ***type*** controls pointer arithmetic, so `p++` advances `p` by 4-bytes to the next int. `pp++` advances `pp` by 1-byte to the next `char` (or byte in this case) – David C. Rankin Mar 04 '20 at 03:31
  • OT: regarding; `int main(int argc, char** argv) {` in the OPs posted code, those parameters are not used. This causes the compiler to output two warning messages about unused parameters. To avoid those warnings, suggest using the other valid `main()` signature: `int main( void )` – user3629249 Mar 04 '20 at 04:23

2 Answers2

5

Your question is a basic question, but one that every new C-programmer wrestles with and is fundamental to understanding C. Understanding pointers. While they are easy to understand once you understand them, getting to that point can be frustrating based on the way the information is presented in many books or tutorials.

Pointer Basics

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 n = 260;, a pointer (e.g. int *p = &n;) would simply hold the address where 260 is stored in memory.

If you need to access the value stored at the memory address pointed to by p, you dereference p using the unary '*' operator, (e.g. int j = *p; will initialize j = 260).

If you want to obtain a variables address in memory, you use the & (address of) operator. If you need to pass a variable as a pointer, you simply provide the address of the variable as a parameter.

Since p points to the address where 260 is stored, if you change that value at that address (e.g. *p = 41;) 41 is now stored at the address where 260 was before. Since p points to the address of n and you have changed the value at that address, n now equals 41. However j resides in another memory location and its value was set before you changed the value at the address for n, the value for j remains 260.

Pointer Arithmetic

Pointer arithmetic works the same way regardless of the type of object pointed to because the type of the pointer controls the pointer arithmetic, e.g. with a char * pointer, pointer+1 points to the next byte (next char), for an int * pointer (normal 4-byte integer), pointer+1 will point to the next int at an offset 4-bytes after pointer. (so a pointer, is just a pointer.... where arithmetic is automatically handled by the type)

In your case you create a second pointer of a different type char *pp = (char*)p;. The pointer pp now also holds the address of n but it is interpreted at type char on access instead of type int.

The C standard prohibits access of a value stored at an address though a pointer of a different type. C11 Standard - §6.5 Expressions (p6,7) (known as the strict-aliasing rule). There are exceptions to the rule. One exception (the last point) is that any value may be accessed through a pointer of char type.

What Happens to the Value of n In Your Case?

When you assign:

*pp = 0;

you storing the single-byte 0 (or 00000000 in binary) to the memory location held by pp. Here is where endianess (little-endian, big-endian) come into play. Recall, for little-endian computers (just about all x86 and x86_64 IBM-PC clone type boxes), the values are stored in memory with the Least-Significant Byte first. (big-endian stores values with the Most-Significan Byte first). So your original value of n (10000100in binary) is stored in memory on a little-endian box as

    n (little endian) : 00000100-00000001-00000000-00000000  (260)
                        ^
                        |
                        p   (type int)

The character pointer pp is assigned the address held by p, so both p and pp, hold the same address (the difference being one is a pointer to int the other a pointer to char:

    n (little endian) : 00000100-00000001-00000000-00000000  (260)
                        ^
                        |
                        p   (type int)
                        pp  (type char)

When you dereference pp (e.g. *pp) and assign the value zero (e.g. *pp = 0;), you overwrite the first byte of n in memory with zero. After the assignment, you now have:

    n (little endian) : 00000000-00000001-00000000-00000000  (256)
                        ^
                        |
                        p   (type int)
                        pp  (type char)

Which is the binary value 100000000, (256 or hex 0x0100) and what your code outputs for the value of n. Ask yourself this, if the computer you were using was big-endian, what would be resulting value have been?

Let me know if you have any further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • "Where you normally think of a variable holding an immediate values, such as `int n = 260;`, a pointer (e.g. `int *p = &n;`) would simply hold the address where `260` is stored in memory." I'm a bit confused. Shouldn't `&n` give the address of `n` rather than of `260`? – Nameless Apr 24 '22 at 04:48
  • 1
    The unary `&` (address of) operator returns the address where the value for the variable is stored in memory. When you declare a variable with automatic storage duration (a normal variable that is stored in the function stack), the variable name is essentialy a label to a memory address when the code gets compiled to assembly. The `&` operator returns that address. So `int n = 280;` in C declares such a variable with `280` stored at an address in the function stack. You can reference `208` directly with `n`. `int *p = &n;` assigns that address to `p` so the address can be referenced/used. – David C. Rankin Apr 24 '22 at 04:54
  • 1
    The *variable / pointer* relationship in C is such that you can use the variable for *direct* access to the value. A pointer provides *indirect* access to that same value through the address where the value is stored. (each `*`, as in `int *p ...` represents one level of *indirection*). To access the value through the pointer you *dereference* the pointer (removing one level of indirection) using the same `*`, e.g. `int m = *p;` would assign `280` to `m`.. – David C. Rankin Apr 24 '22 at 04:59
  • 1
    Also worth noting that the `[ ]` postfix syntax when accessing a value in an array also provides a *dereference*. For example `int arr[] = { 1, 2, 3 };` declares an array with automatic storage duration. On access `arr` is converted to a pointer to the first element. `*arr` and `arr[0]` are equivalent. Why? In pointer notation you access the element of an array with an offset and dereference, e.g. `int i = *(arr + 0);` would assign the value of the first element to `i`. The notation of `arr[0]` is a convenience form of access. So `*(arr + 0)` is simply `*(arr)` or `*arr`. – David C. Rankin Apr 24 '22 at 05:09
  • How would you get the address of a variable then? – Nameless Apr 24 '22 at 05:33
  • Well. that's what the `&` (address of) operator does. When you declare a normal variable, `int n = 280;`, you don't know the address of `n`. All you can do with `n` is access the value regardless where it is stored. Now, say you want to pass `n` to a function to update the value. You can't do `void add1 (int i) { i += 1; }` because the parameter `int i` is a local copy and any changes made in `add1()` are lost on return. So you need to pass the address (a pointer to) and update the value at that address, e..g `void add1 (int *i) { *i += 1; }`. Now you can call with `n` as `add1 (&n);` – David C. Rankin Apr 24 '22 at 05:39
3

char *pp declares the variable pp as a pointer to char - pp will store the address of a char object.

(char *)p is a cast expression - it means “treat the value of p as a char *”.

p was declared as an int * - it stores the address of an int object (in this case, the address of n). The problem is that the char * and int *types are not compatible - you can’t assign one to the other directly1. You have to use a cast to convert the value to the right type.


  1. Pointers to different types are themselves different types, and do not have to have the same size or representation. The one exception is the void * type - it was introduced specifically to be a “generic” pointer type, and you don’t need to explicitly cast when assigning between void * and other pointer types.

John Bode
  • 119,563
  • 19
  • 122
  • 198