-3
#include <stdio.h>

int main() {
  int *numPtrA = NULL;         // pointer to an integer pointer to null.. ascii value of zero
 
  printf("%p\n", numPtrA + 2); // prints 0x8 which i expect
  printf("%p\n", numPtrA + 5); // prints 0x14 and i dont know why
}

I am trying to print the address 0x14 by adding something to the ASCII code of the null pointer which I believe is the 0 address. However, when I add 5 to NULL and I don't understand why I get 0x14 instead of 0x20. I thought since ptrNumA is a pointer to an integer it should add about 5 blocks of integers (4 bytes each) and the new address should be 0x20

I appreciate any help. Thank you so much.

Hamza Kyamanywa
  • 417
  • 3
  • 8
  • 7
    14 hex is 20 decimal – mch Nov 04 '20 at 10:21
  • 1
    `5 * 4 = 20 = 0x14` – Sander De Dycker Nov 04 '20 at 10:21
  • 1
    Please don't post images unless necessary – klutt Nov 04 '20 at 10:26
  • The `0x` shows that the value is base 16. So `0x20` that you expected is decimal `32`. The `0x` does not mean "pointer value". – Weather Vane Nov 04 '20 at 10:27
  • Do not tag questions with both C and C++ unless they involve an interaction between two languages or there is some specific reason for asking about a difference between the two languages. C and C++ have different specifications regarding null pointers and address arithmetic, and asking about both at once confuses issues. If you want to inquire about both languages, you can do so in separate questions. – Eric Postpischil Nov 04 '20 at 11:39

3 Answers3

2

Technically, numPtrA doesn't point to an array nor even a singular object, and therefore adding anything other than 0 to it results in undefined behaviour in C++ because pointer arithmetic is only defined for pointers within bounds of arrays and objects.

Here is the quote from latest C++ standard draft:

[expr.add]

When an expression J that has integral type is added to or subtracted from an expression P of pointer type, the result has the type of P.

  • If P evaluates to a null pointer value and J evaluates to 0, the result is a null pointer value. [does not apply]
  • Otherwise, if P points to an array element i of an array object x with n elements ... [does not apply] (note: pointers to objects can be considered as arrays of 1 element for purposes of this rule)
  • Otherwise, the behavior is undefined.

Besides that technicality, 0x prefix denotes hexadecimal base. 5 * 4 = 20 = 0x14 and 0x20 = 32 != 20.

That said, the value of null pointer is not necessarily 0 (even though the literal 0 is always a null pointer literal), so the expectation is not portable to some systems where that isn't the case. This is typical on embedded systems where address 0 is wanted for actual storage.

Furthermore, printf specifier %p requires the argument to be of type void* while you pass an int* as argument. As a result of violating this constraint, the behaviour of the program is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326
  • The answer is wrong, i think! There is nothing wrong about the code until you dereference the pointer. – Wör Du Schnaffzig Nov 04 '20 at 10:32
  • @pqans No, the answer is correct. You probably mean calculating the address 1 after the last element of an array, which is valid, but you are not allowed to dereference it. Calculating an address behind that is undefined, even without dereferencing it. – mch Nov 04 '20 at 10:39
  • 2
    @pqans It is undefined behavior to simply do pointer arithmetic out of bounds, regardless of whether you de-reference the result or not. – Lundin Nov 04 '20 at 10:39
  • @pqans I added standard quote. – eerorika Nov 04 '20 at 10:40
  • I still think that the exception applies here: _note: pointers to objects can be considered as arrays of 1 element for purposes of this rule_. In that terms, the pointer points to an int object at address 0. – Wör Du Schnaffzig Nov 04 '20 at 10:44
  • 1
    @pqans Pointer to null is not (at least guaranteed to be) a pointer to an object. And even if it were, then adding a value greater than one would overflow that virtual array of one element, resulting in undefined behaviour. – eerorika Nov 04 '20 at 10:45
  • There is no pointer to NULL. `int **ptr = NULL` would be a pointer to NULL. There is only a pointer to an int which is at address 0. The output of the sample program indeed does not indicate undefined behaviour. Have you tried with other compilers? I tried with msvc, https://www.onlinegdb.com/online and wsl gcc. Show me a compiler which compiles it differently. – Wör Du Schnaffzig Nov 04 '20 at 10:52
  • 2
    @pqans `The output of the sample program indeed does not indicate undefined behaviour.` That is irrelevant. Lack of output indicating UB does not in any way prove lack of UB. Regardless of how many compilers you try. The fact that most compilers do what you expect is the reason why I regard this violation as a technicality. – eerorika Nov 04 '20 at 10:54
  • `There is no pointer to NULL. int **ptr = NULL would be a pointer to NULL. There is only a pointer to an int which is at address 0` *(referring to `int *numPtrA = NULL;`)* This makes no sense to me. By pointer to null I mean null pointer i.e. a pointer with the null value whatever that may be on the target system. The type of the pointer is irrelevant. – eerorika Nov 04 '20 at 11:01
  • @pqans "There is no pointer to NULL" Indeed - the concept of "pointer to NULL" does not exist in C (or C++). Only the concept _null pointer_, which is something slightly different. "int **ptr = NULL would be a pointer to NULL" No it wouldn't. It would also be a null pointer though with different object type. "There is only a pointer to an int which is at address 0" Not correct. "Have you tried with other compilers?" You can't provide undefined behavior but showing one particular run-time output. It's as broken an argument as saying "All cars are red. Look! Here is a car and it's red". – Lundin Nov 04 '20 at 11:21
  • Sorry, i apologize. `NULL` must not be mapped to the sentinel `(void*)0`. `NULL` may be mapped to any other sentinel as far it's distinguishable from valid pointers. In the above example: Replacing `int *numPtrA = NULL;` by `int *numPtrA = (int *)0;` would refer to an int at address 0 although it would be quite undefined what a read or write to that location may trigger. – Wör Du Schnaffzig Nov 04 '20 at 11:23
  • 1
    @pqans: In C (the question is tagged both C and C++), `numPtrA` initialized as shown does not point to an `int` at address 0. Certainly its **type** is “pointer to `int`”, but its **value** is a null pointer. First, note that a null pointer does not necessarily point to “address 0”. Because of the way the C standard defines a null pointer constant, a constant 0, when used as a pointer, is converted by the compiler to whatever combination of bits the implementation uses to represent null pointers. That could be 0, 0xFFFFFFFF, 0xDEADBEEF, or any other value… – Eric Postpischil Nov 04 '20 at 11:31
  • 1
    … Second, in the C model of computing, there is no object there. C 2018 6.3.2.3 3 specifies that a null pointer “is guaranteed to compare unequal to a pointer to any object or function.” So if `numPtrA` did point to an `int` at “address 0” or any other address, then `numPtrA == numPtrA` would have to be true (by the rules in 6.5.9 6), but then we would have a null pointer (`numPtrA`) comparing equal to a pointer to an object (also `numPtrA`), in violation of 6.3.2.3 3. So the model used in the C standard is that a null pointer does **not** point to an object. – Eric Postpischil Nov 04 '20 at 11:34
  • 1
    Re “adding anything other than 0 to it results in undefined behaviour in C++”: Since the question is unfortunately tagged C and C++, I will point out this differs between them. C++ defines adding 0 to a null pointer; C++ defines it: “If the value 0 is added to or subtracted from a null pointer value, the result is a null pointer value” (C++ 2017 draft n4659 8.7 7). C appears to leave it undefined by omission (I do not see an explicitly stated constraint), defining pointer addition upon only a pointer to an object (C 2018 6.5.6 7 and 8), which a null pointer is not. – Eric Postpischil Nov 04 '20 at 11:42
0

The answer is in the comments. In your case sizeof(int) == 4 and therefore numPtr + 2 points to address 8 = 2*4 and numPtr + 5 points to address 20 = 5*4, which is indeed 0x14 in hexadecimal representation.

  • See this: https://godbolt.org/z/8n5acv. Where the mnemonic `ud2` gives an invalid opcode that simply generates a hardware instruction trap. Now kindly explain why that happened in case of `(void*)0` and not `(void*)1`. – Lundin Nov 04 '20 at 11:30
0

You aren't allowed to do pointer arithmetic on pointers that don't point to valid objects - it's undefined behavior (as per 6.5.6, details & quotes here). So you can get any result out of this.

Furthermore, a null pointer is not guaranteed to have the binary representation zero in C. I'd recommend studying What's the difference between null pointers and NULL?.

And then finally, %p gives the output in hex. The basics of pointer arithmetic is that typeptr + 5 equals adding sizeof(*typeptr) * 5, expressed in bytes. Since sizeof(int) is likely 0x4, you get 0x4 * 0x5 = 0x14.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • The pointer in this example points to a valid integer at address 0. – Wör Du Schnaffzig Nov 04 '20 at 10:41
  • @pqans No it doesn't (necessarily). Please read the provided link about how null pointers work. – Lundin Nov 04 '20 at 11:03
  • @pqans To illustrate, you might want to ponder why `bananas_t* (*bananas) (bananas_t*) = (void*)0;` compiles cleanly but `bananas_t* (*bananas) (bananas_t*) = (void*)1;` gives the compiler error "ISO C forbids initialization between function pointer and `void *`" – Lundin Nov 04 '20 at 11:13
  • Re “You aren't allowed”: I have done it many times and have not been stopped once. – Eric Postpischil Nov 04 '20 at 11:44
  • Re “`%p` gives the output in hex”: In OP’s implementation and commonly, not by requirement of the standards. – Eric Postpischil Nov 04 '20 at 11:46
  • @EricPostpischil I guess the police doesn't prioritize undefined behavior crimes very high nowadays. – Lundin Nov 04 '20 at 12:01