空指针有以下三种用法:
(1)用空指针终止对递归数据结构的间接引用。
递归是指一个事物由这个事物本身来定义。请看下例:
/*Dumb implementation;should use a loop */
unsigned factorial(unsinged i)
{
if(i=0 || i==1)
{
return 1;
}
else
{
return i * factorial(i-1);
}
}
在上例中,阶乘函数factoriai()调用了它本身,因此,它是递归的。
一个递归数据结构同样由它本身来定义。最简单和最常见的递归数据结构是(单向)链表,链表中的每一个元素都包含一个值和一个指向链表中下一个元素的指针。请看下例:
struct string_list
{
char *str; /* string(inthiscase)*/
struct string_list *next;
};
此外还有双向链表(每个元素还包含一个指向链表中前一个元素的指针)、键树和哈希表等许多整洁的数据结构,一本较好的介绍数据结构的书中都会介绍这些内容。
你可以通过指向链表中第一个元素的指针开始引用一个链表,并通过每一个元素中指向下一个元素的指针不断地引用下一个元素;在链表的最后一个元素中,指向下一个元素的指针被赋值为NULL,当你遇到该空指针时,就可以终止对链表的引用了。请看下例:
while(p!=NULL)
{
/*dO something with p->str*/
p=p->next;
}
请注意,即使p一开始就是一个空指针,上例仍然能正常工作。
(2)用空指针作函数调用失败时的返回值。
许多C库函数的返回值是一个指针,在函数调用成功时,函数返回一个指向某一对象的指针;反之,则返回一个空指针。请看下例:
if(setlocale(cat,loc_p)==NULL)
{
/* setlocale()failed;do something*/
/* ...*/
}
返回值为一指针的函数在调用成功时几乎总是返回一个有效指针(其值不等于零),在调用失败时则总是返回一个空指针(其值等于零);而返回值为一整型值的函数在调用成功时几乎总是返回一个零值,在调用失败时则总是返回一个非零值。请看下例:
if(raise(sig)!=0){
/* raise()failed;do something*/
/* ... */
}
对上述两类函数来说,调用成功或失败时的返回值含义都是不同的。另外一些函数在调用成功时可能会返回一个正值,在调用失败时可能会返回一个零值或负值。因此,当你使用一个函数之前,应该先看一下它的返回值是哪种类型,这样你才能判断函数返回值的含义。
(3)用空指针作警戒值
警戒值是标志事物结尾的一个特定值。例如,main()函数的预定义参数argv是一个指针数组,它的最后一个元素(argv[argc])永远是一个空指针,因此,你可以用下述方法快速地引用argv中的每一个元素:
/*
A simple program that prints all its arguments.
It doesn't use argc ("argument count"); instread.
it takes advantage of the fact that the last
value in argv ("argument vector") is a null pointer.
*/
# include <stdio. h>
# include <assert. h>
int main ( int argc, char * * argv)
{
int i;
printf ("program name = \"%s\"\n", argv[0]);
for (i=l; argv[i] !=NULL; ++i)
printf ("argv[%d] = \"%s\"\n", i, argv[f]);
assert (i = = argc) ; /* see FAQ XI. 5 */
return 0; /* see FAQ XVI. 4 */
}