字节和地址在 C 中如何关联?
How do bytes and addresses correlate in C?
我看到有很多与这个主题相关的问题,但我无法推断出答案,所以我决定在这里问我的第一个问题关于堆栈溢出。目前,我的问题是关于字节和地址,每个地址是否实际上代表一个地址,这意味着如果我要初始化一个地址,例如0x55555555d156
但如果我要初始化一个 int,它将需要 4 个地址,这意味着它的范围从例如0x55555555d156
到 0x55555555d160
?所以让我困惑的是,一个指针会保存一个地址,对吧?
假设指针保存地址,例如0x55555555d156
如果我要引用那个地址,我会得到那个 int 的值,对吧?如果我尊重其他 3 个地址呢?我无法通过编写 C 程序来获取该信息。
if I were to deference that address I would get the value of that int, right?
是的。
what about the other 3 addresses, if I deference them?
如果您有 int *p = &some_integer;
,那么 *(int *)((char *)p + 1)
(取消引用 p
“移位”一个 字节)将尝试读取 4 个字节从那个新地址并将它们解释为一个整数。您的程序是否具有 权限 来读取内存中 some_integer
旁边的最后一个字节,这是另一回事:如果没有,您将遇到分段错误或其他内存访问问题。
或者你可能没有得到任何错误并读取垃圾数据。
例子
#include <stdio.h>
int main(void) {
int my_int = 0x12345678;
int *ptr = &my_int;
printf("%x\n", *ptr);
printf("%x\n", *(int *)((char *)ptr + 1));
}
输出:
~/test $ clang so.c && ./a.out
12345678
80123456
^^
|-- This "random" byte was read as part of
--- the "new" int shifted by 1 byte
不同的微处理器具有不同的可寻址内存单元。大多数,包括 x86 系列和 ARM,都可以以一个字节为单位进行寻址。因此,例如,一个 32 位 int 将存储在四个连续的内存地址中(LSB 在前,除非 ARM 设置为“Big Endian”模式)。
其他处理器,如 PIC,可能有一个地址指向一个 16 位内存字。
您的 C 代码可能不应该以任何一种方式做出假设,除非您确定代码 运行 的内容。
您不能“取消引用地址”——您可以取消引用指针。指针值是地址,但指针也有类型。取消引用的结果取决于指针的类型。
取消引用指针的结果不是存储在所指向的内存位置的值。它是指定对象的表达式。这在 C 中称为 lvalue。
如果这一切都不清楚;首先检查您是否了解代码中发生的事情:
int x = 0;
x = 5;
在第二行中,使用 x
不会检索值 0
。表达式 x
是一个 lvalue ,这意味着它指定了一个由几个字节组成的内存区域。每个字节都有自己的地址。如果你输出 &x
,你可能会看到与输出 x
第一个字节的地址相同的结果(尽管这不是标准要求),但类型不同。
当左值表达式出现在代码中时,是否检索存储的值取决于表达式的上下文。例如,如果它出现在赋值运算符的左侧,则不会检索该值。
一旦你理解了 x = 5;
,那么 *p = 5;
的行为就相同了; *p
的含义就好像标签 x
存在于 p
指向的内存区域一样。
我看到有很多与这个主题相关的问题,但我无法推断出答案,所以我决定在这里问我的第一个问题关于堆栈溢出。目前,我的问题是关于字节和地址,每个地址是否实际上代表一个地址,这意味着如果我要初始化一个地址,例如0x55555555d156
但如果我要初始化一个 int,它将需要 4 个地址,这意味着它的范围从例如0x55555555d156
到 0x55555555d160
?所以让我困惑的是,一个指针会保存一个地址,对吧?
假设指针保存地址,例如0x55555555d156
如果我要引用那个地址,我会得到那个 int 的值,对吧?如果我尊重其他 3 个地址呢?我无法通过编写 C 程序来获取该信息。
if I were to deference that address I would get the value of that int, right?
是的。
what about the other 3 addresses, if I deference them?
如果您有 int *p = &some_integer;
,那么 *(int *)((char *)p + 1)
(取消引用 p
“移位”一个 字节)将尝试读取 4 个字节从那个新地址并将它们解释为一个整数。您的程序是否具有 权限 来读取内存中 some_integer
旁边的最后一个字节,这是另一回事:如果没有,您将遇到分段错误或其他内存访问问题。
或者你可能没有得到任何错误并读取垃圾数据。
例子
#include <stdio.h>
int main(void) {
int my_int = 0x12345678;
int *ptr = &my_int;
printf("%x\n", *ptr);
printf("%x\n", *(int *)((char *)ptr + 1));
}
输出:
~/test $ clang so.c && ./a.out
12345678
80123456
^^
|-- This "random" byte was read as part of
--- the "new" int shifted by 1 byte
不同的微处理器具有不同的可寻址内存单元。大多数,包括 x86 系列和 ARM,都可以以一个字节为单位进行寻址。因此,例如,一个 32 位 int 将存储在四个连续的内存地址中(LSB 在前,除非 ARM 设置为“Big Endian”模式)。
其他处理器,如 PIC,可能有一个地址指向一个 16 位内存字。
您的 C 代码可能不应该以任何一种方式做出假设,除非您确定代码 运行 的内容。
您不能“取消引用地址”——您可以取消引用指针。指针值是地址,但指针也有类型。取消引用的结果取决于指针的类型。
取消引用指针的结果不是存储在所指向的内存位置的值。它是指定对象的表达式。这在 C 中称为 lvalue。
如果这一切都不清楚;首先检查您是否了解代码中发生的事情:
int x = 0;
x = 5;
在第二行中,使用 x
不会检索值 0
。表达式 x
是一个 lvalue ,这意味着它指定了一个由几个字节组成的内存区域。每个字节都有自己的地址。如果你输出 &x
,你可能会看到与输出 x
第一个字节的地址相同的结果(尽管这不是标准要求),但类型不同。
当左值表达式出现在代码中时,是否检索存储的值取决于表达式的上下文。例如,如果它出现在赋值运算符的左侧,则不会检索该值。
一旦你理解了 x = 5;
,那么 *p = 5;
的行为就相同了; *p
的含义就好像标签 x
存在于 p
指向的内存区域一样。