C 语言常见误解/指标/空指标与NULL
指标(二)空指标与 NULL
编辑问:NULL
是空指标吗?型态是什么?
答:NULL
不一定是指标,但一定是空指标常数(两者概念不同)。空指标常数转型成指标后保证可以拿到正确的空指标,但自己不一定是指标。C99 (N1256) 中,空指标常数代表计算结果为零的整数常数表达式,如 0
或 0ULL
,或是前述表达式转型成 void*
, 如 (void*)(0U)
.
C++03 中空指标常数只能是计算结果为零的整数常数表达式,如 0
, 连指标都不是,在多载时可能会选了错误的函式。C++0x 引入 nullptr
企图解决这问题。(可参考较容易阅读的 More C++ Idioms/nullptr)
问:下列程式码一定可以拿到空指标吗?
int a = 0;
int *p = (int *)a;
答:不保证。只有常数表达式才保证会转型成空指标,执行期间才得到的零不保证可以成功转型成空指标。一个可能的原因是,不是所有机器的空指标都是零(见此题目),如果要允许执行期间的空指标转换,程式很可能需要在执行期间特别检查数字是不是零. 如果是常数,编译器可以在编译期间就转成适当的空指标。(参考 clc FAQ 5.18)
问:所以 printf("%p", NULL)
严格符合标准吗?
答:否。这样才严格符合标准:
printf("%p", (void*)NULL);
注意 NULL
不是空指标,但 NULL
转型成指标后一定是空指标。
问:如果每次用 NULL
都先转型成适当的型态(如写 (int*)NULL
)是不是就没有陷阱了?
答:是(虽然如果非常清楚标准的话,其实不用每个地方都写)。这个转换对 C++ 尤其重要,可以避免很多问题,见上方〈 NULL 是空指标吗?〉。
问:为什么这么麻烦?空指标一定是零不是吗?为什么不能直接把 NULL
定成空指标?
答:不是所有机器的空指标(的表示法)都是零,而且不是所有指标都长得一样(见〈指标表示法〉),所以很难定义一个万用空指标。C99 (N1256) 中规定空指标跟空指标常数的比较结果会相等,也可以把空指标常数写入一个指标(编译器会在编译期间做适当的转换)。(参考 clc FAQ 5.17 看实际机器)
问:既然空指标不一定是零,所以写 NULL
比写 0
好不是吗?因为可以应付不同的机器。
答:这无助于可携性,比较像是写作风格和美观的问题(可能有助于除错)。C99 (N1256) 中常数零一定是合法的空指标常数,一定可以成功转型成空指标,也一定可以和其他指标比较。
问:假设全域变数写
int *p;
既然空指标不一定是零,p
会初始化成什么呢?
答:C99 保证会初始化成空指标(即使在空指标不是零)。(参考 N1256 6.7.8p10)