这个问题的灵感来自我正在研究的一本C书中的摘录
在哪里说:
int num;
int *pi=0; // Zero referes to the null pointer, NULL
pi = #
*pi = 0; // Zero refers to the integer zero
我们习惯于重载运算符,例如使用的星号 声明指针,取消引用指针或乘法。的 零也过载。我们可能会感到不适,因为我们 不习惯重载操作数。
所以我对调试器做了很多调查,我的回答是调试器向我展示了什么。这是未定义的行为,我明白了,但是我没有编写我想弄清楚的程序。无论如何,每次运行指针都会将其初始化为1,在某些计算中我并不依赖于此,而是调试器反复显示的内容。
这实际上只是一个问题,但是我不得不写更多,否则它不会接受。 为什么* p = 0在不同时间会有不同的影响? 0如何过载? 我怎么知道* p = 0是使p成为空指针还是将0赋给p指向的变量?编译器如何知道在p的地址而不是p指向的地址分配空值?
$ cat prac.c
#include <stdio.h>
int main()
{
int num;
int *p;
printf("0) %p is %d\n",p,*p);
*p=0; //Why is this......
printf("1) %p is %d\n",p,*p);
num=5;
p=#
printf("2) Now %p is %d\n",p,*p);
*p=0; //....different than this
printf("3) p is not null, %p : %d\n",p,*p);
return 0;
}
$ ./prac
0) 0x7ffeb36b5c30 is 1 //pointer is initialized to 1, not-null, definitely out-of-bounds though
1) 0x7ffeb36b5c30 is 0
2) Now 0x7ffeb36b5b3c is 5
3) p is not null, 0x7ffeb36b5b3c : 0
答案 0 :(得分:1)
您要询问的概念是空指针常量。
C标准将空指针常量定义为
具有值
0
的整数常量表达式,或这样的表达式强制转换 键入void *
因此,0
类型的表达式int
也是一个空指针常量。 (请注意,空指针常量不必是指针类型。)
标准进一步指出:
如果将空指针常量转换为指针类型,则 得到的指针称为 null指针,可以保证进行比较 不等于指向任何对象或函数的指针。
给出:
int n;
int *p;
此:
p = 0;
将int
表达式0
隐式转换为类型int*
的空指针,而这:
n = 0;
或
*p = 0;
不执行隐式转换,因为正在初始化的对象已经是int
类型。
赋值运算符的描述中说明了何时调用隐式转换的规则。相同的规则适用于初始化,参数传递和return
语句。这些规则明确声明将空指针常量转换为目标类型。
请参阅C11标准的N1570草案(大PDF)。
答案 1 :(得分:1)
我对您引用的书或文章不熟悉,但是它无法帮助您理解和使用C语言中的指针。
什么是指针?
指针只是一个普通变量,它以其他值作为地址。换句话说,指针指向可以找到其他内容的地址。在通常情况下,您会想到一个包含立即数的变量,例如int a = 5;
,指针将仅保存5
存储在内存中的地址,例如int *b = &a;
。无论指针指向哪种对象,其工作方式都相同。 (一个指针,仅仅是一个指针...。)
如何声明,分配地址并引用指向的值
让我们以您的示例为例,看看每一行的作用(忽略p
和pi
的错别字,而仅使用p
):
int num; /* declares an integer 'num' whose value is uninitialized */
int *p = 0; /* declares a pointer 'p' and ininializes the address
* to a the constant expression 0. (a Null Pointer Constant)
*/
请参见C11 Standard - Null Pointer Constant (p3),它是一个值为0
的常量表达式,或者是强制转换为类型void *
的表达式(例如(void *)0
)。几乎所有C编译器,如果有的话,C标准为此提供NULL
。 (请参阅:C11 Standard - 7.19 Common definitions )因此,您通常会看到:
int *p = NULL; /* declare and initialize 'p' to the Null Pointer Constant */
接下来,一元运算符'&'
用于获取对象的地址。在这里:
p = # /* assigns the address of 'num' as the value of 'p', so
* 'p' holds the address of 'num' (e.g. 'p' points to the
* memory location where 'num' is stored).
*/
一元'*'
操作符是 dereference操作符,它提供了一种直接访问指针所持有(指向)的内存地址上的值的方法,因此:
*p = 0; /* sets the value at the address held by 'p' to zero */
现在让我们看一下代码并输出:
int num;
int *p;
printf("0) %p is %d\n",p,*p);
p
持有的地址是什么,num
的值是什么?
您试图访问未初始化指针和未初始化整数的值。您刚刚调用了 未定义行为 。您的代码定义的操作已经结束,任何事情都可能发生,无论是看起来正常还是出现SegFault。您无法访问未初始化的值。
*p=0;
printf("1) %p is %d\n",p,*p);
p
现在已初始化为空指针常量,您不能取消引用NULL指针-再次出现未定义的行为。
num=5;
p=#
printf("2) Now %p is %d\n",p,*p);
哈利路亚! num
现在已初始化为5
,p
现在拥有(指向)一个有效的内存地址! (但是很遗憾,一旦您调用 Undefined Behavior ,游戏就结束了,您可能会或可能不会获得正确的输出,但幸运的是,您似乎可以了。
*p=0;
printf("3) p is not null, %p : %d\n",p,*p);
p
指向什么? (提示:num
)。 取消引用运算符做什么? (提示:允许您访问指针保存的存储位置中的值)。那么*p = 0;
做了什么? (提示:您只需将num
的值设置为0
,这就是p
拥有的地址上的值)
如果您还有其他问题,请告诉我。