简体   繁体   中英

Incrementing pointer to pointer by one byte

#include <stdio.h>

int main(){
 int a = 5;
 int *p = &a;
 int **pp = &p;

 char **cp = (char **)pp;  
 cp++;                    // This still moves 8 bytes
 return 0;
}

Since the size of a pointer is 64 bits on 64 bit machines, doing a pp++ will always move 8 bytes. Is there a way to make it move only 1 byte?

Is there a way to make it move only 1 byte?

Maybe.

All object pointers can be converted to void * and since char * has the same representation, to char * . ++ increments a char * by 1.

#include <stdio.h>

int main() {
 int a = 5;
 int *p = &a;
 int **pp = &p;
 char **cp = (char **)pp;  

 char *character_pointer = (char *) cp;
 character_pointer++; // Increment by 1

Now is the tricky part. Can that incremented pointer convert back to a char ** . C allows that unless

If the resulting pointer is not correctly aligned for the referenced type, the behavior is undefined. C17dr § 6.3.2.2 7

 cp = (char **) character_pointer;
 return 0;
}

Reading *cp can readily cause undefined behavior as cp does not certainly point to a valid char * . Unclear as to OP's goal at this point.

C is not assembly. What you are trying to do is undefined behavior , and compiler might not do what you ask , and the program might do anything , including possibly what you think it should do if C were just "assembly" with different syntax.

That being said, you can do this:

 int a = 5;
 int *p = &a;
 int **pp = &p;
 uintptr_t temp;
 memcpy(&temp, &pp, sizeof temp);
 temp++;
 memcpy(&pp, &temp, sizeof temp);

Above code is likely to do what you want, even though that last memcpy already triggers undefined behavior, because it copies invalid value to a pointer (that is enough for it to be UB). Actually using pp , which now has invalid value, has increasing chance of messing things up.


To understand why having any UB is indeed UB: compiler is free to decide that the effect of the code, which can be proven to have UB, is nothing, or is never reached. So if that last memcpy is inside if , and compiler can prove UB occurs if condition is true, it may just assume condition is never true and optimize whole if away. Presumably C programmer knows to write their condition so that it would never result in UB, so this optimization can be made at compile time already.

Yeah, it is a bit crazy. C is not just assembly with different syntax!

Incrementing pointer to pointer by one byte

If you find an implementation where the size of a pointer to pointer variable contains only 8 bits, (ie one that uses 1 byte addressing, btw, very unlikely ), then it will be doable, and only then would it be safe to do so. Otherwise it would not be considered a practical or safe thing to do.

For an implementation that uses 64 bit addressing, 64 bits are needed to represent each natural pointer location. Note however though _[t]he smallest incremental change is [available as a by-product of] the alignment needs of the referenced type. For performance, this often matches the width of the ref type, yet systems can allow less._ (per @Chux in comments) but de-referencing these locations could, and likely would lead to undefined behavior.

And in this statement

 char **cp = (char **)pp; //where pp is defined as int **

the cast, although allowing a compile without complaining, is simply masking a problem. With the exception of void * , pointer variables are created using the same base type of the object they are to point to for the reason that the sizeof different types can be different, so the pointers designed to point to a particular type can represent its locations accurately.

It is also important to note the following:

            sizeof char ** == sizeof char * == sizeof char *** !!= sizeof char`   
     32bit  4 bytes           4 bytes            4 bytes             1 byte 
     64bit  8 bytes           8 bytes            8 bytes             1 byte  

            sizeof int ** == sizeof int * == sizeof int *** !!= sizeof int`   
     32bit  4 bytes           4 bytes            4 bytes             4 bytes (typically)
     64bit  8 bytes           8 bytes            8 bytes             4 bytes (typically) 

So, unlike the type of a pointer, its size has little to do with it's ability to point to a location containing an object that is smaller , or even larger in size than the pointer used to point to it.

The purpose of a pointer ( eg char * ) is to store an address to an object of the same base type, in this case char . If targeting 32bit addressing, then the size of the pointer indicates it can point to 4,294,967,296 different locations (or if 64 bits to 18,446,744,073,709,551,616 locations.) and because in this case it is designed to point to char , each address differs by one byte.

But this really has nothing to do with your observation that when you increment a pointer to pointer to char that you see 8 bytes , and not 1 byte . It simply has to do with the fact that pointers, in 64bit addressing, require 8 bytes of space, thus the successive printf statements below will always show an increment of 8 bytes between the 1st and 2nd calls:

 char **cp = (char **)pp; 
 size_t size = sizeof(cp);
 printf("address of cp before increment: %p\n", cp);
 cp++;                    // This still moves 8 bytes
 printf("address of cp after increment: %p\n", cp);
 return 0;  

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM