您必须了解的是,C 没有像 C++ 那样的“通过引用”的概念。这意味着无论变量的类型如何,编译器都会创建您传递的任何变量的副本。副本的保存位置取决于编译器、体系结构、优化级别等。通常您不必担心也不知道它保存在哪里。真正重要的是在一个函数中你得到一个副本。
理论上,您可以通过执行以下操作来访问内存的任何地址:
int *var = ((int*) 0xdeadbeef);
*var = 3;
含义:将值0xdeadbeef
转换为整数指针并将其保存在var
. 然后将值 3 保存在从0xdeadbeef
. 虽然这是正确的 C,但您肯定会遇到段错误,因为今天的操作系统使用内存控制器让您可以访问一组有限的地址/块。
当你这样做时:
int var = 10;
编译器会将值 10 存储在某处(它可能存储在寄存器中,但我们假设编译器将其存储在 RAM 中)。作为 C 程序员,您通常不会关心它在哪里,链接器会处理正确的地址。
我们来看看这段代码
void foo(int f)
{
/* do something with f */
}
void bar(int g)
{
int f1 = 2;
f1 += g;
foo(f);
}
编译器如何将其翻译成汇编程序?它可能看起来像这样
foo:
0xa0000000: load in register 1 the 4-byte value @ 0x12abcd00
0xa0000004: /* do something with register 1 */
0xa0000008: /* do something else with register 1 */
...
bar:
0xa0000c00: load in register 3 the 4-byte value @ 0x12ad0004
0xa0000c04: load in register 4 the value 2
0xa0000c08: add register 3, register 4 (result saved in 'result register')
0xa0000c0b: save the content of 'result register' @ 0x12abcd00 (look at the first line)
0xa0000c10: jump 0xa0000000 (call foo)
这同样适用于指针。指针或多或少是一个整数变量。它们由指针存储的值是地址。与整数的区别在于您可以访问(=取消引用)存储在该地址的值。您可以通过使用*
运算符来执行此操作(参见第一个示例*var = 3
)。运算符返回变量的&
地址。
所以当你有
void foo(int *x)
{
*x = 3;
}
void bar(void)
{
int i = 9;
int *i_ptr = &i;
foo(i_ptr);
/* i is 3 */
}
C 创建 的副本i_ptr
,但副本的存储值如果i_ptr
仍然与 的地址相同i
,因此foo
可以取消引用它并修改 的值,i
即使i
未在 中定义foo
。
那么,我们什么时候在 C 中使用指针呢?通常,这些是使用指针时最常见的情况:
- 我们想修改一个我们没有在函数中声明的变量
- 我们使用动态分配的内存(参见
malloc
, realloc
, calloc
)
- 我们使用字符串