【问题标题】:Move between members of a structure using only pointer arithmetic仅使用指针算法在结构的成员之间移动
【发布时间】:2020-05-10 21:18:32
【问题描述】:

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

#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 = '\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;
}

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

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

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

Id       = 27
Username = 
Password = 

谁能告诉我哪里错了?

【问题讨论】:

  • 您是否尝试过在调试器中运行上述程序并查看它是否符合您的想法?
  • 你用的是什么编译器?对于您正在做的工作,您要么需要打包结构,要么需要了解编译器如何对齐结构中的字段。
  • 指针以指向对象的步长递增,而不是字节。也就是说,将int 指针增加3 意味着移动3*sizeof(int) 字节(注意sizeof(int) 不一定是4)。
  • 前两个程序给了我想要的价值。只有最后一个不起作用,但仅在可视化中。我在 64 位苹果上使用 gcc --version Apple clang 版本 11.0.3 (clang-1103.0.32.59)。

标签: c struct pointer-arithmetic


【解决方案1】:

增加指向结构的指针会将其移动到结构的末尾。

想象一个数组,其中每个元素都是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。这意味着编译器可能会在结构字段之间插入填充。

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

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

【讨论】:

  • @RobertoRocco 也是个坏主意 :)。请参阅此问题和答案以获得解释:stackoverflow.com/questions/3523145/…
  • 对不起,我不小心删除了我的评论
  • 对 void* 的算术在 C 和 C++ 中都是非法的。谢谢
  • p += 4; 可能变成 byteptr += offsetof(struct user, username);(假设出于某种原因我们不想直接从下一个字段重置 byteptr)
  • @M.M 好点。 offsetof 是从 C89 开始的 C 标准的一部分,因此也非常便携。
猜你喜欢
  • 2014-12-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多