变量地址的异常大值和负值
Unusually Large values and negative values of addresses of a variable
所以我在学习和练习C语言中指针和变量地址的概念。但是有一件事让我很好奇。我运行的代码是-
#include <stdio.h>
int main()
{
int *p, n;
p = &n;
int *c = NULL;
printf("Address of variable = %p\n", p);
printf("Address of variable = %lu\n", p);
printf("Address of c variable = %lu\n", c);
return 0;
}
我确信这段代码打印地址是正确的,我得到的输出是-
pointer.c: In function ‘main’:
pointer.c:10:37: warning: format ‘%lu’ expects argument of type ‘long unsigned
int’, but argument 2 has type ‘int *’ [-Wformat=]
10 | printf("Address of variable = %lu\n", p);
| ~~^ ~
| | |
| | int *
| long unsigned int
| %ls
pointer.c:11:39: warning: format ‘%lu’ expects argument of type ‘long unsigned
int’, but argument 2 has type ‘int *’ [-Wformat=]
11 | printf("Address of c variable = %lu\n", c);
| ~~^ ~
| | |
| | int *
| long unsigned int
| %ls
Address of variable = 0x7fffc5a57474
Address of variable = 140736509342836
Address of c variable = 0
所以,我想知道这些编译器警告是什么意思,我应该关注这些警告吗?
此外,当我使用 %d
而不是 %p
或 %lu
时,我得到的地址值是一个“负”值,所以负地址可以存在于内存中?
此外,输出中的地址值异常大。它们甚至比我的 16 GB RAM 的大小还要大,我的变量怎么可能存储在一个不存在的位置?
您的代码不正确。
转换说明符 lu
期望其对应参数的类型为 unsigned long
,而 d
期望其对应参数的类型为 int
; p
和 c
的类型为 int *
,因此出现警告。是的,这些警告很重要 - 正如您所发现的,至少您会得到乱码输出。
指针类型不是整数类型;它们不必具有与整数类型相同的大小或表示形式(在 x86_64、int
上是 32 位宽,但指针类型是 64 位宽)。为指针类型定义的唯一转换说明符是 p
,它期望其对应的参数具有类型 void *
.
你的代码应该写得更像
printf( "p = %p\n", (void *) p );
printf( "c = %p\n", (void *) c );
如果您将指针视为数字,则必须小心。在内部,它们通常是地址,通常是数字,但它们是 unsigned 数字。所以,不,您通常不会有“负地址”。
您可能还没有了解计算机如何表示负数。这是常见“two's complement”表示的快速演示,仅使用三位。关键是相同的位模式可以有两种不同的解释,这取决于您是否关心负值:
bit pattern
signed int
unsigned int
000
0
0
001
1
1
010
2
2
011
3
3
100
-4
4
101
-3
5
110
-2
6
111
-1
7
因此,如您所见,如果您有一个很大的无符号数,但您将其解释为有符号数(例如,通过使用 %d
打印它,您会得到一个负数。
另外两件事要记住:
- 指针不一定是整数。它们可能——在 currently-popular x86_64 架构上,它们 是 ——比整数大。因此,尝试使用 %d 打印它们是双重错误的,并且可能会给你一个完全误导的答案。
- 您的程序经常在“地址space”的不同部分使用内存。例如,程序的指令及其全局变量通常分配在内存的低位部分,从地址 0 或附近开始。但是对于通常存储局部变量的“堆栈”来说,开始是很常见的地址 space 顶部的某处,然后向下生长。因此,局部变量的地址通常是一个非常大的数字,看起来比您机器中的内存量还大。但实际上发生的是,两者之间看似巨大的内存量根本没有分配,没有“映射”到您的地址 space,因此不计入物理内存量你的电脑里有。
所以我在学习和练习C语言中指针和变量地址的概念。但是有一件事让我很好奇。我运行的代码是-
#include <stdio.h>
int main()
{
int *p, n;
p = &n;
int *c = NULL;
printf("Address of variable = %p\n", p);
printf("Address of variable = %lu\n", p);
printf("Address of c variable = %lu\n", c);
return 0;
}
我确信这段代码打印地址是正确的,我得到的输出是-
pointer.c: In function ‘main’:
pointer.c:10:37: warning: format ‘%lu’ expects argument of type ‘long unsigned
int’, but argument 2 has type ‘int *’ [-Wformat=]
10 | printf("Address of variable = %lu\n", p);
| ~~^ ~
| | |
| | int *
| long unsigned int
| %ls
pointer.c:11:39: warning: format ‘%lu’ expects argument of type ‘long unsigned
int’, but argument 2 has type ‘int *’ [-Wformat=]
11 | printf("Address of c variable = %lu\n", c);
| ~~^ ~
| | |
| | int *
| long unsigned int
| %ls
Address of variable = 0x7fffc5a57474
Address of variable = 140736509342836
Address of c variable = 0
所以,我想知道这些编译器警告是什么意思,我应该关注这些警告吗?
此外,当我使用 %d
而不是 %p
或 %lu
时,我得到的地址值是一个“负”值,所以负地址可以存在于内存中?
此外,输出中的地址值异常大。它们甚至比我的 16 GB RAM 的大小还要大,我的变量怎么可能存储在一个不存在的位置?
您的代码不正确。
转换说明符 lu
期望其对应参数的类型为 unsigned long
,而 d
期望其对应参数的类型为 int
; p
和 c
的类型为 int *
,因此出现警告。是的,这些警告很重要 - 正如您所发现的,至少您会得到乱码输出。
指针类型不是整数类型;它们不必具有与整数类型相同的大小或表示形式(在 x86_64、int
上是 32 位宽,但指针类型是 64 位宽)。为指针类型定义的唯一转换说明符是 p
,它期望其对应的参数具有类型 void *
.
你的代码应该写得更像
printf( "p = %p\n", (void *) p );
printf( "c = %p\n", (void *) c );
如果您将指针视为数字,则必须小心。在内部,它们通常是地址,通常是数字,但它们是 unsigned 数字。所以,不,您通常不会有“负地址”。
您可能还没有了解计算机如何表示负数。这是常见“two's complement”表示的快速演示,仅使用三位。关键是相同的位模式可以有两种不同的解释,这取决于您是否关心负值:
bit pattern | signed int |
unsigned int |
---|---|---|
000 | 0 | 0 |
001 | 1 | 1 |
010 | 2 | 2 |
011 | 3 | 3 |
100 | -4 | 4 |
101 | -3 | 5 |
110 | -2 | 6 |
111 | -1 | 7 |
因此,如您所见,如果您有一个很大的无符号数,但您将其解释为有符号数(例如,通过使用 %d
打印它,您会得到一个负数。
另外两件事要记住:
- 指针不一定是整数。它们可能——在 currently-popular x86_64 架构上,它们 是 ——比整数大。因此,尝试使用 %d 打印它们是双重错误的,并且可能会给你一个完全误导的答案。
- 您的程序经常在“地址space”的不同部分使用内存。例如,程序的指令及其全局变量通常分配在内存的低位部分,从地址 0 或附近开始。但是对于通常存储局部变量的“堆栈”来说,开始是很常见的地址 space 顶部的某处,然后向下生长。因此,局部变量的地址通常是一个非常大的数字,看起来比您机器中的内存量还大。但实际上发生的是,两者之间看似巨大的内存量根本没有分配,没有“映射”到您的地址 space,因此不计入物理内存量你的电脑里有。