C是否可以为非NULL指针定义类型?

问题描述 投票:1回答:3

是否可以为指向非NULL的地址的指针定义类型?

背景:

我经常写的功能如下 -

output_type *some_function(input_type *input) {
//confirm input and its members were correctly initialised
if (input == NULL) {
// error handling code
}
if (input->member_which_is_a_pointer == NULL) {
// error handling code
}
...
//return output computed from input
}

如果可以编码以下内容将更清楚 -

output_type *some_function(input_type_not_null *input) {
//return output computed from input
}
c pointers typedef
3个回答
7
投票

TL; DR:不。

你要求的是,基本上,C ++中的内容是由引用提供的。 C没有直接这样的东西(虽然见下文)。

虽然,即使在C ++中,也没有运行时保证引用有效。没有什么可以阻止您从空指针初始化引用。它只是未定义的行为,程序员有责任不编写可以执行此操作的代码。

因此,解决方案是永远不会有具有NULL值的thia类型的指针。使用工厂函数始终生成对象,并始终在定义时初始化指针,并将指针包装在struct中,并使用assert是帮助程序员在此中不失败的好工具。


编辑:实际上this other answer提供了一种或多或少等于C ++引用的方式,如果你有那种描述那种数组参数,那么通过调用你的函数,调用者promises参数将不会是NULL指针。但这不是硬检查,实际上并没有阻止任何事情,只是让编译器能够更频繁地警告你。


7
投票

是的,C有一个声明指针参数的方法,该参数不能为空。但是,它不提供您可能想要的行为。它建议编译器可以编译例程,期望参数永远不会为null,但它不一定会阻止调用例程错误地传递null。编译器可能会检测到对此类例程的一些不正确的调用,并发出有关它们的警告或错误,但无法检测到其他例程。

这是通过将static关键字放在参数声明中的很少使用的语法来完成的:

void foo(int p[static 1])
{
    … body of function …
}

这表示p必须提供对至少一个元素的数组的第一个元素的访问,根据C 2011 [N1570] 6.7.6.3 7.由于必须有p指向的元素,p可能不为空。例如,编译此代码时:

#include <stddef.h>

void foo(int p[static 1])

void bar(void) { foo(NULL); }

使用带有默认开关的Apple LLVM 9.0.0(clang-900.0.39.2),编译器会发出警告:

x.c:5:18: warning: null passed to a callee that requires a non-null argument
      [-Wnonnull]
void bar(void) { foo(NULL); }
                 ^   ~~~~
x.c:3:14: note: callee declares array parameter as static here
void foo(int p[static 1]) {}
             ^~~~~~~~~~~

但是,此代码编译时没有警告:

#include <stddef.h>

void foo(int p[static 1]) {}

void bar(int *p) { foo(p); }

void baz(void) { bar(NULL); }

所以看起来这个编译器只有在直接传递时才能检测到这个参数错误地传递了null。

使用此属性声明参数可能会删除您在函数中包含的运行时检查,从而对代码产生负面影响。当我编译这个:

void foo(int p[static 1])
{
    if (!p)
    {
        fprintf(stderr, "Invalid pointer passed to foo.\n");
        exit(EXIT_FAILURE);
    }
}

然后编译器完全从程序中删除fprintfexit。这是因为编译器知道p不能为空,所以!p永远不会为真,所以fprintfexit永远不会在正确的程序中执行。因此编译器会在优化期间删除它们。


1
投票

不,不可能。使用属性nonnull进行编译时检查并在函数开头添加断言。断言只是一行,有助于调试。相比:

output_type *some_function(nonnull_input_type *input) {

output_type *some_function(NONNUL input_type *input) {
   ASSERT(input);

打字并不多。除了ASSERT宏之外,还可以重新定义一些有意义的东西,比如longjump to safe frame如果你的程序需要优雅的崩溃。

© www.soinside.com 2019 - 2024. All rights reserved.