【问题标题】:How are ints in c stored in memory?c中的整数如何存储在内存中?
【发布时间】:2019-04-05 09:00:15
【问题描述】:

我认为 C 中的整数首先存储最高有效位,例如,数字 5 将是 0...0101。我想我可以通过强制 C 来操纵特定位,让我假装特定的内存地址是 int 并添加到那里的位,就像它们是 int 一样。

我尝试在内存中设置 0 位 0,然后尝试将 255 添加到不同的内存地址,它似乎工作起来,因为当我添加 1 时,最低有效位存储在最高有效位之前的内存中到我的内存地址并更改了那里的位,我得到了一个更大的数字而不是一个较小的数字。如果最高有效位较早地存储在内存中,则将255 添加到内存地址高 1 个字节根本不会影响原始地址处的数字,因为最后 8 位是下一个int 的开头。我想知道我是否正确解释了这一点,并且 int 以最低有效位在前存储。

#include "stdio.h"
#include "string.h"
#include "stdlib.h"

int main() {
    int *x = malloc(8); //getting 4 memory addresses
    int *y = malloc(8);
    int *z = malloc(8);
    int *a = malloc(8);

    x[0] = 0; //setting 64 bits past memory addresses to 0s
    x[1] = 0;
    y[0] = 0;
    y[1] = 0;
    z[0] = 0;
    z[1] = 0;
    a[0] = 0;
    a[1] = 0;

    *((int*)((int)x)) = 255; //adding to x's memory address
    *((int*)((int)y + 1)) = 255; //adding 1 byte over from y
    *((int*)((int)z + 2)) = 255; //adding 2 bytes over from z
    *((int*)((int)a + 3)) = 255; //adding 3 bytes over from a

    printf("%d\n", sizeof(int));
    printf("%d,%d\n", x[0], x[1]);
    printf("%d,%d\n", y[0], y[1]);
    printf("%d,%d\n", z[0], z[1]);
    printf("%d,%d\n", a[0], a[1]);

    printf("%d\n", x);
    printf("%d\n", &x[1]);
    return 0;
}

预期输出:

4
255,0
0,-16777216
0,16711680
0,65280
12784560
12784564

实际输出:

4
255,0
65280,0
16711680,0
-16777216,0
12784560
12784564

【问题讨论】:

  • 您必须考虑到特定于平台的字节顺序,有关更多详细信息,请参阅以下链接stackoverflow.com/questions/12791864/…
  • 我真的不明白你在这里想要完成什么。为什么所有的演员表?
  • @Max 这是否取决于编译器、机器或其他东西?
  • @LightnessRacesinOrbit 如果我没有先将内存地址转换为 int,+1 将被解释为 4 个字节而不是 1,但随后我不得不将其转换回 int* 并取消引用它是一个合适的左值,我可以将 255 分配给
  • 问题是“伪装”违反了语言规则。至少在 C++ 中会这样;公平地说,我不是 100% 在 C 中的。

标签: c memory


【解决方案1】:

我以为 c 中的 int 是先存储最高有效位的,例如数字 5 将是 0...0101

不,这取决于您的平台和工具链,而不是 C。

您描述的(几乎)方案称为big-endian

现在很多商品 PC 都是little-endian,所以正好相反(最低有效字节在前)。您可能就是这种情况。

请注意,字节序是指字节,而不是位。

最好不要尝试像这样操作数据。使用语言,使用不关心字节顺序的逻辑操作。

【讨论】:

    【解决方案2】:

    你的代码有一些问题:

    • bitbyte 之间似乎有些混淆。计算机内存可按字节寻址,在当前架构上通常包含 8 位。

    • 您不应该将指针转换为intint 可能没有足够的范围来容纳指针的值。将指向 unsigned char * 的指针转换为修补单个字节,但请注意,由于别名规则,这可能不会产生预期的结果:

      ((unsigned char *)x)[0] = 255; //adding to x's memory address
      ((unsigned char *)y)[1] = 255; //adding 1 byte over from y
      ((unsigned char *)z)[2] = 255; //adding 2 bytes over from z
      ((unsigned char *)a)[3] = 255; //adding 3 bytes over from a
      
    • 同样,您应该使用%zu 打印size_t 或将size_t 转换为int

    • 指针应转换为(void*) 并打印为%p
    • 如果以十六进制打印 int 值,更改的效果会更明显。

    这是修改后的版本:

    #include <stdio.h>
    #include <stdlib.h>
    
    int main() {
        // getting 4 memory addresses, each with enough space for 2 int, initialized to 0
        int *x = calloc(2, sizeof(int));
        int *y = calloc(2, sizeof(int));
        int *z = calloc(2, sizeof(int));
        int *a = calloc(2, sizeof(int));
    
        ((unsigned char *)x)[0] = 255; //adding to x's memory address
        ((unsigned char *)y)[1] = 255; //adding 1 byte over from y
        ((unsigned char *)z)[2] = 255; //adding 2 bytes over from z
        ((unsigned char *)a)[3] = 255; //adding 3 bytes over from a
    
        printf("%d\n", (int)sizeof(int));
        printf("%08x,%08x -- %d,%d\n", x[0], x[1], x[0], x[1]);
        printf("%08x,%08x -- %d,%d\n", y[0], y[1], y[0], y[1]);
        printf("%08x,%08x -- %d,%d\n", z[0], z[1], z[0], z[1]);
        printf("%08x,%08x -- %d,%d\n", a[0], a[1], a[0], a[1]);
    
        printf("%p\n", (void *)x);
        printf("%p\n", (void *)&x[1]);
        return 0;
    }
    

    输出:

    4 000000ff,00000000 -- 255,0 0000ff00,00000000 -- 65280,0 00ff0000,00000000 -- 16711680,0 ff000000,00000000 -- -16777216,0 0x7fd42ec02630 0x7fd42ec02634

    从上面的输出我们可以看出:

    • 类型int有4个字节
    • 指针使用 8 个字节(我的环境是 64 位,与您的不同)
    • int 以最低有效字节在前存储,与您的相同,称为 little-endian 架构

    您期待的是相反的大端架构,这在当前的台式机和笔记本电脑上相当少见,但在嵌入式架构和手机上却很常见。

    这两种方法都有优点和缺点。 C 透明地支持这两者,因此大多数程序员并没有意识到这些复杂性,但在某些情况下了解这些实现细节非常有用:

    • 系统编程、低级编程、设备驱动程序开发,
    • 图像处理
    • 读写二进制文件和流,
    • 处理二进制数据的网络传输,尤其是与不同设备之间的传输。
    • 与其他编程语言交互、编写库等

    【讨论】:

      猜你喜欢
      • 2013-09-02
      • 1970-01-01
      • 2012-06-28
      • 1970-01-01
      • 1970-01-01
      • 2018-04-20
      • 2011-01-16
      • 2011-06-28
      相关资源
      最近更新 更多