-2

I know we are not allowed to write the value in the memory place where beyond our pointers tied when the pointer declared. So the following code will compile error.

    int a = 2, b = 3, c = 4;
    int* d = &b;
    *(d+1)=6; //ERROR

However, then why in this case:

    char str[] = "hello";
    char* strB = &str[0];
    *(strB + 1) = 'E';
    int i;
    for (i = 0; i < 400; i++) {
        printf("%c\n", *(strB + i)); // hEllo followed by some trash value
    }

will work. Isn't there any problem in *(strB + 1)=...? The pointer strB originally pointed to a char type; however, it seems illegal that strB can "see" or "write" the value stored in str[1], str[2], and so on. If, the pointer can access the region outside its legal region when that region is already owned by our program, then why did the first program not work? I think the address of d+1 will correspond to the place where the variable c or a live.

Eric
  • 185
  • 7
  • 4
    *why in this case ... will work?* Undefined behavior is ... **undefined**. – Andrew Henle Oct 09 '21 at 16:16
  • For C programs, "It works in this particular example at this particular instant" is not evidence of the general statement "It works". Focus on what is *allowed* instead of what appears to "work". – Nate Eldredge Oct 09 '21 at 16:20
  • 1
    The first program will *not* cause a compiler error in most implementations. It *might* cause an error at runtime. Then again, it might not. – Nate Eldredge Oct 09 '21 at 16:22
  • Does this answer your question? [Access element beyond the end of an array in C](https://stackoverflow.com/questions/1021021/access-element-beyond-the-end-of-an-array-in-c) – Mark Benningfield Oct 09 '21 at 17:23
  • See: [Undefined Behavior](https://en.cppreference.com/w/c/language/behavior) – Oka Oct 09 '21 at 17:32

1 Answers1

1

I think you are just slightly confused on how pointers work. Hopefully I can clear things up for you. "*(strB + 1) =..." is legal and does make sense. A variable, lets take int b = 3 for example, has a memory address and a value like this: (I made up the memory addresses)

Variable Name       Address             Value
b                   0x7ffc2ede6efc      3

Now if we create a pointer to point at that variable then that pointer will also have a memory address and a value. Here we are declaring a new pointer variable called "d" of type integer and want to set its value to the address of the variable b.

int* d = &b;

Here is what it looks like in memory.

Variable Name       Address             Value
d                   0x8fcd2aff6ec4      0x7ffc2ede6efc

Notice the value is the memory address above for the variable "b". If we go ahead and print the value of the pointer "d" it will print a memory address. if we want to actually print the value at that memory address then you first need to dereference it with "*" which will print the value of the pointer's value, if that makes sense. So why the first part is wrong is because you are adding 1 to the value of "d" before you dereference it. So you are really changing the memory address of where "d" points to, which may be something but also may be nothing. If you dereference "d" first and then add 1 you will be able to increment it but know that you are really incrementing "b". Also if you create more integer variables it does not mean that they are going to be right after one another in memory. print our their memory addresses and see for yourself!

Now in the second case you are working with an array of characters. arrays are similar to pointers.

char str[] = "hello";
char* strB = &str[0];

"str" value is the address of the first element in the array. so instead of calling the address of operator on the first element in the array you can just do:

char* strB = str;

Also this is what the string looks like in memory:

Variable Name       Address             Value
str[0]              0x7ffc2ede6ef1      'h'
str[1]              0x7ffc2ede6ef2      'e'
str[2]              0x7ffc2ede6ef3      'l'
str[3]              0x7ffc2ede6ef4      'l'
str[4]              0x7ffc2ede6ef5      'o'
str[5]              0x7ffc2ede6ef6      '\0'

The addresses of each element in the array are right next to each other which is why you can add 1 to strB and it still works. Because adding 1 will move on to the next address which in this case is the next character in the array. Another way to print it can be like this:

for (; *strB != '\0'; strB++) printf("%c", *strB);

Every string ends with a null character so you can start at the beginning of an array and print every character by adding 1 to the address until you reach the null character.

sottosanti
  • 26
  • 3
  • I think that you should also point out that sometimes you _can_ access data beyond what you asked for without an access violation, simply because the MM gave you a bigger block of memory than what you asked, and further that successfully doing so does not mean that it isn't playing with UB. – Dúthomhas Oct 09 '21 at 17:09
  • But in the second case, I originally declared the pointer `strB` as a `char` type (*namely if we dereference `strB`, we will go to a `sizeof(char)` -byte memory and can play within it*), however next I access beyond the `sizeof(char)` scope of the memory, is that legal in the specification of standard C? Btw, @Dúthomhas, may I ask what is MM and UB? – Eric Oct 09 '21 at 17:13
  • MM is _memory manager_; UB is _undefined behaviour_. It is never safe (nor legal) to access memory beyond the bounds you are given. Hence, it might work if you are lucky, but it is sure to go wrong when you least expect it. – Dúthomhas Oct 09 '21 at 17:26