C 语言常见误解/指标/空指标与NULL

指标(二)空指标与 NULL

编辑

问:NULL 是空指标吗?型态是什么?

答:NULL 不一定是指标,但一定是空指标常数(两者概念不同)。空指标常数转型成指标后保证可以拿到正确的空指标,但自己不一定是指标。C99 (N1256) 中,空指标常数代表计算结果为零的整数常数表达式,如 00ULL,或是前述表达式转型成 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)