仅使用指针算法在结构成员之间移动

Move between members of a structure using only pointer arithmetic

参考下面的代码,我可以这样向一个结构体的成员中插入数据:

#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;
}

上面提到的程序可以运行。现在,我想尝试使用指针算法,既用于赋值又用于显示结构成员,我想我会按如下方式进行:

#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 = '[=11=]';

    p += 13;

    *(char*)p = 'P';
    *(char*)++p = '4';
    *(char*)++p = '_';
    *(char*)++p = '4';
    *(char*)++p = 't';
    *(char*)++p = '4';
    *(char*)++p = 'r';
    *(char*)++p = '[=11=]';


    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;                            // [=11=]

    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;                            // [=11=]

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

    return 0;
}

我从两个来源的比较中了解到右箭头选择(->)运算符非常有用。 现在我想到了使用指针的算法将值分配给结构的成员,并使用右箭头选择 (->) 运算符显示它们的内容,如下所示:

#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 = '[=12=]';

    p += 13;

    *(char*)p = 'P';
    *(char*)++p = '4';
    *(char*)++p = '_';
    *(char*)++p = '4';
    *(char*)++p = 't';
    *(char*)++p = '4';
    *(char*)++p = 'r';
    *(char*)++p = '[=12=]';

    p -= 31;

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

    return 0;
}

但在后一种情况下,我得到了这个结果:

Id       = 27
Username = 
Password = 

谁能告诉我哪里错了?

递增指向结构的指针会将其移动到刚好超过结构的末尾。

想象一个数组,其中每个元素都是一个 struct user。如果 p 声明为 struct user *p 并指向该数组的开头,p += 4 将使其指向该数组中的第五个元素(struct user)。

这意味着在你执行的时候

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

您超出了您分配的结构的范围。

如果您希望指针增加较小类型的大小,您需要先将其转换为该类型的指针:

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

然后使用 byteptr 执行写入。

这里还有一个隐藏的问题是,编译器和硬件架构的不同组合导致了不同的alignment requirements。这意味着编译器可能会在结构字段之间插入填充。

使用指针运算来计算指向结构字段的指针是个坏主意,因为您需要知道填充是什么。最好的办法是直接使用 ->(在结构指针上)或 .(在结构值上)来引用结构字段。

你在这里做的是一个很好的学习练习。您可能应该使用 strncpy 而不是 strcpy.