Search code examples
cstructpointer-arithmetic

Move between members of a structure using only pointer arithmetic


Referring to the following code, I can insert data into the members of a structure in this way:

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

struct user {
    int id;
    char username[20];
    char password[20];
};

int main(void) {
    struct user *p;
    p = malloc(sizeof(struct user));
    p->id = 27;
    strcpy(p->username, "roberto");
    strcpy(p->password, "P4_4t4r");

    printf("Id       = %d\n", p->id);
    printf("Username = %s\n", p->username);
    printf("Password = %s\n", p->password);

    return 0;
}

The program mentioned above will work. Now, I would like to try using pointer arithmetic, both for assignment and for displaying structure members and I thought I would do it as follows:

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

struct user {
    int id;
    char username[20];
    char password[20];
};

int main(void) {
    struct user *p;
    p = malloc(sizeof(struct user));

    *(int*)p = 27;      // id = 27

    p += 4;             // int is 4 bytes, so let's move 4 bytes ...

    *(char*)p = 'r';
    *(char*)++p = 'o';
    *(char*)++p = 'b';
    *(char*)++p = 'e';
    *(char*)++p = 'r';
    *(char*)++p = 't';
    *(char*)++p = 'o';
    *(char*)++p = '\0';

    p += 13;

    *(char*)p = 'P';
    *(char*)++p = '4';
    *(char*)++p = '_';
    *(char*)++p = '4';
    *(char*)++p = 't';
    *(char*)++p = '4';
    *(char*)++p = 'r';
    *(char*)++p = '\0';


    p -= 31; // I put the pointer back to the first byte of the memory block
    // Output

    printf("Id       = ");
    printf("%d\n", *(int*)p);

    p += 4;

    printf("Username = ");
    printf("%c", *(char*)p);        // r
    printf("%c", *(char*)++p);      // o
    printf("%c", *(char*)++p);      // b
    printf("%c", *(char*)++p);      // e
    printf("%c", *(char*)++p);      // r
    printf("%c", *(char*)++p);      // t
    printf("%c\n", *(char*)++p);    // o
    ++p;                            // \0

    p += 13;

    printf("Password = ");
    printf("%c", *(char*)p);        // P
    printf("%c", *(char*)++p);      // 4
    printf("%c", *(char*)++p);      // _
    printf("%c", *(char*)++p);      // 4
    printf("%c", *(char*)++p);      // t
    printf("%c", *(char*)++p);      // 4
    printf("%c\n", *(char*)++p);    // r
    ++p;                            // \0

    printf("\n");   
    p -= 31; // I put the pointer back to the first byte of the memory block

    return 0;
}

I realize from the comparison of the two sources that the right arrow selection (->) operator is very useful. Now I thought of assigning the values to the members of the structure using the arithmetic of the pointers and displaying their contents with the right arrow selection (->) operator, like this:

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

struct user {
    int id;
    char username[20];
    char password[20];
};

int main(void) {
    struct user *p;

    p = malloc(sizeof(struct user));
    *(int*)p = 27;

    p += 4;

    *(char*)p = 'r';
    *(char*)++p = 'o';
    *(char*)++p = 'b';
    *(char*)++p = 'e';
    *(char*)++p = 'r'; 
    *(char*)++p = 't'; 
    *(char*)++p = 'o';
    *(char*)++p = '\0';

    p += 13;

    *(char*)p = 'P';
    *(char*)++p = '4';
    *(char*)++p = '_';
    *(char*)++p = '4';
    *(char*)++p = 't';
    *(char*)++p = '4';
    *(char*)++p = 'r';
    *(char*)++p = '\0';

    p -= 31;

    printf("Id       = %d\n", p->id);
    printf("Username = %s\n", p->username);
    printf("Password = %s\n", p->password);

    return 0;
}

But in the latter case I get this result:

Id       = 27
Username = 
Password = 

Could anyone tell me where I'm wrong?


Solution

  • Incrementing a pointer to a struct will move it just past the end of the struct.

    Imagine an array where each element is a struct user. If p is declared as struct user *p and points to the start of that array, p += 4 will make it point to the fifth element (a struct user) in that array.

    This means that by the time you are executing

    *(char*)p = 'r';
    *(char*)++p = 'o';
    ...
    

    you are outside the bounds of the struct you allocated.

    If you want the pointer to be incremented by the size of a smaller type, you need to first cast it to a pointer of that type:

    unsigned char *byteptr = (unsigned char *)p;
    

    Then use byteptr to perform your writes.

    Another hidden problem here is that different combinations of compilers and hardware architectures result in different alignment requirements. This means that the compiler might insert padding between the struct fields.

    Using pointer arithmetic to compute pointers to struct fields is a bad idea, because you need to know what that padding is. Your best bet is to refer to struct fields directly using -> (on a struct pointer) or . (on a struct value).

    What you are doing here is a good learning exercise though. You should probably use strncpy instead of strcpy.