【问题标题】:Understanding Pointers (in C): converting int pointer to char and changing values理解指针(在 C 中):将 int 指针转换为 char 并更改值
【发布时间】:2017-02-24 21:22:57
【问题描述】:

我有以下代码,它处理 C 中的指针

void
f(void)
{
    int a[4];
    int *b = malloc(16);
    int *c;
    int i;

    printf("1: a = %p, b = %p, c = %p\n", a, b, c);

    c = a;
    for (i = 0; i < 4; i++)
         a[i] = 100 + i;

    c[0] = 200;
    printf("2: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
           a[0], a[1], a[2], a[3]);

    c[1] = 300;
    *(c + 2) = 301;
    3[c] = 302;
    printf("3: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
           a[0], a[1], a[2], a[3]);

    c = c + 1;
    *c = 400;
    printf("4: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
           a[0], a[1], a[2], a[3]);

    c = (int *) ((char *) c + 1);

    *c = 500;
    printf("5: a[0] = %d, a[1] = %d, a[2] = %d, a[3] = %d\n",
           a[0], a[1], a[2], a[3]);

    b = (int *) a + 1;
    c = (int *) ((char *) a + 1);
    printf("6: a = %p, b = %p, c = %p\n", a, b, c);
}

int
main(int ac, char **av)
{
    f();
    return 0;
}

输出:

1: a = 0x7fff5fbff710, b = 0x1003002e0, c = 0x100000000
2: a[0] = 200, a[1] = 101, a[2] = 102, a[3] = 103
3: a[0] = 200, a[1] = 300, a[2] = 301, a[3] = 302
4: a[0] = 200, a[1] = 400, a[2] = 301, a[3] = 302
5: a[0] = 200, a[1] = 128144, a[2] = 256, a[3] = 302
6: a = 0x7fff5fbff710, b = 0x7fff5fbff714, c = 0x7fff5fbff711
Program ended with exit code: 0

我明白为什么 a[1] 是 128144(当我们将 int 指针转换为 char +1 并覆盖 500 时,我们正在向前移动一个字节),但我不明白为什么 a[2] 是 256 [第一位将被覆盖,但这不会给出 256]。我将非常感谢帮助!在此先感谢:)

【问题讨论】:

  • 虽然这可能不是您的问题的原因,但我强烈建议您阅读严格的别名规则和未对齐的内存访问

标签: c pointers malloc


【解决方案1】:

简短回答:您修改了c,使其不再具有 4 字节整数对齐,并将其设置为 500 修改了两个数组元素。此外,这种未对齐的访问通常是禁止的,并且会在某些系统上导致对齐错误。

长答案:在第 4 步,a[1] 包含 0x00000190,即 400,a[2] 包含 0x0000012D,即 301。

假设a[0]从内存地址0开始。这是字节在little-endian系统中的排列方式(字节序在这里非常重要),从0开始从左到右到 15 点:

ADDR: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  
DATA: C8 00 00 00 90 01 00 00 2D 01 00 00 2E 01 00 00 

在第 5 步,您通过强制转换将 c 取消对齐一个字节到 char *,因此它指向地址 05 而不是 04。您将值 500 (0x000001F4) 分配给它,这会覆盖 05-08 的字节,从而产生此内存:

ADDR: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F  
DATA: C8 00 00 00 90 F4 01 00 00 01 00 00 2E 01 00 00  

当您阅读 a[1] 时,您会得到 0x0001F490,即 128144。
当您阅读 a[2] 时,您会得到 0x00000100,即 256。

【讨论】:

    【解决方案2】:

    要意识到这一点

    c = (int *) ((char *) c + 1);
    

    使 c 指向 a[1] 中的一些和 a[2] 中的一些。您将 c 移动一个字节,并使其成为 int 指针。这使得 *c 寻址 a[1] 的最后 3 个字节和 a[2] 的第一个字节。抽出所有的位,看看会发生什么

    【讨论】:

    • 我所做的正是它给出了 a[1] = 128144 (匹配)。但是,a[2] 不是 256,您可以尝试一下吗? (我可以在我的末端发布它的外观图像 - 如果有帮助;请告诉我)
    • 这可能是未定义的行为。
    • 安德鲁为我们完成了繁重的工作并向您展示了这是如何发生的,我把它作为练习留给您
    猜你喜欢
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    • 2011-10-27
    • 1970-01-01
    • 1970-01-01
    • 2018-06-08
    • 1970-01-01
    • 2019-10-07
    相关资源
    最近更新 更多