函数参数中的空指针与对象

本文关键字:对象 空指针 参数 函数 | 更新日期: 2023-09-27 18:33:51

如果你

现在不正确地知道该类型在编译时是什么,那么在C++中传递一个 void 指针与将方法参数定义为object有多接近?

函数参数中的空指针与对象

让我们直接了解一些在其他答案中遗漏的事情。C# 中的所有对象(结构除外(都作为指针传递。C#(和Java(隐藏了指针语法,因为它可能出错的所有可怕事情,而且即使是最有经验的C/C++程序员也无意中通过指针滥用来霰弹枪,这是一件非常好的事情。

void * C 中的(在 C# 中 - 如果打开不安全的代码,它就会存在(是一个指向地址的指针,绝对不暗示它可能是什么。在C的早期,既没有void也没有void *。如果我们希望一个函数不返回任何内容,我们没有输入 return 语句。它仍然是隐式int,但价值是不可预测的。如果没有void *,我们通常使用char *来代替,但程序员不喜欢这样,因为它暗示了一个指向字符或字符串的指针,并暗示了如果sizeof(char) != 1如何在指针上执行数学运算。有了void *,并不意味着你指向任何特定类型,你程序员最清楚void *所指的内容(剧透:大多数程序员不会(。

那你为什么要用这个呢?一种典型的用法是在 API 中使用指针作为不透明类型。例如:

extern void *get_device();
extern int is_device_active(void *device);

这一切都很好 - 这个 API 的消费者不知道设备是什么。不幸的是,由于任何事情都是void *,因此此API可以使您做一些真正可怕的事情:

char *str = get_device(); /* wrong */
size_t len = strlen(str); /* doubly wrong */
int active = is_device_active("hi mom!"); /* can't express how wrong */

然而,你的编译器将允许这样做而不窥视,这实际上使void *在这种情况下不是那么有用。

在 C# 中,object完全是另一只野兽。它对指针上的内容有非常强烈的影响。它要么是null,要么是一个类的实例,可以让你执行以下操作:

  • 等于
  • 完成
  • 获取哈希码
  • 获取类型
  • 成员智能克隆
  • 到字符串

无论实际对象是什么,所有这些都保证存在。有了void *,除了 API 文档中的承诺之外,您绝对什么都没有。

void *在 C 中的最佳用途是malloc()(及其变体(和free(),因为它们不指定它们指向的类型。

此外,如果您想知道如何在 C 语言中实际呈现不透明的 API,典型的模式是:

/* device.h */
typedef struct t_device t_device;
extern t_device *get_device(); /* strongly typed now, but opaque */
extern int device_is_active(t_device *device);
/* device.c */
typedef struct t_device { /* this is private to the .c file */
    int isActive;         /* can't see it outside! Neener, neener! */
    /* etc. */
} t_device;
t_device *get_device() { return make_device(); }
int device_is_active(t_device *device) { return device ? device->isActive : 0; }

指针引用变量

object的实例引用一个

这是一个非常关键的区别。 使用指针,您可以改变另一个变量(通常是当前不在范围内的变量(。 使用 object 实例,您只能改变一个,可能是另一个变量也引用的值,但这仍然是一个关键的区别。

由于指针引用变量,因此可以通过指针观察到引用变量变量的突变。 指针还能够改变该变量。 另一方面,object实例不绑定到另一个变量。 即使它引用的对象被另一个变量引用,该变量中的更改也无法通过object实例观察到,并且它也无法改变另一个变量。

还有一个事实是,要使object引用值类型,需要对该值类型进行装箱。 这可能会对性能产生影响,因为值最终会被复制,但也会产生显著的语义差异。 源变量的突变对object引用不可见,而如果您使用void指针,则会看到这些突变。

我可能没有给你最终的答案,但我知道一些不同的事情:

  • 空指针可以指向任何内容。按照字面!这意味着在使用 void 指针时,你必须知道你在指向什么,否则你可能会在你的程序中弄乱很多东西。这意味着您需要在编译时知道类型才能正确使用它。这就是为什么处理 void 指针的函数会询问其类型大小(使用 sizeof (以及类似的东西以及指针。

  • 对象是您在 C# 中创建的任何类的基础。但与 C/C++ 不同的是,您可以使用反射来了解其类型。这意味着无论您以前不知道类型,都可以处理它。此外,对象是 C# 对象...这意味着您根本没有引用任何内容...您引用的是 C# 实例,不知道它可能是什么类型。