与仅在 C 中需要时将其用作指针相比,使变量永久成为指针有什么好处?

What are the benefits of making a variable permanently a pointer as opposed to only using it as a pointer when needed in C?

我正在查看代码,作为我目前正在从事的项目的参考。我注意到,不是有一个变量,然后只是暂时将一个指针转换为它作为参数传递,它只是被设置为始终是一个指针类型。 (显示 Sprite* createSprite 的那一行)

Sprite* createSprite(Sprite* model, int x, int y) {
  Sprite* self = malloc(sizeof(Sprite));
  initSprite(model, self, x, y);
  return self;
}

我认为这是在使变量永久成为指针。

而不是只在需要时才使用指针:

int a;

someFunction(&a); // We can still use a, and we can also pass it to someFunction as a pointer

此代码将 sprite 对象永久转换为指针:

int *a;

someFunction(a); // While we can still use a, it will never be a normal variable.

人们为什么要这样做,这样做有什么好处?

在你的第一个例子中

 int a;
 someFunction(&a);

'a' 存在于堆栈中,因此它有一个名称,您可以使用它并获取地址等。

精灵示例

Sprite* createSprite(Sprite* model, int x, int y) {
  Sprite* self = malloc(sizeof(Sprite));
  initSprite(model, self, x, y);
  return self;
}

几乎肯定是在堆上动态分配其数据。引用动态分配内存的唯一方法是通过指针

假设您的程序中需要多个精灵。你可以做类似

的事情
Sprite s1;
initSprite(model, &s1, x, y);
Sprite s2;
initSprite(model, &s2, x, y);
Sprite s3;
initSprite(model, &s3, x, y);

然后,如果您需要更多,您可以以同样的方式进行:

Sprite s4;
initSprite(model, &s4, x, y);
Sprite s5;
initSprite(model, &s5, x, y);

不过,这迟早会变得乏味且不可行,因此您需要动态内存分配。这就是 createSprite 函数所做的。其中的关键是 call

Sprite* self = malloc(sizeof(Sprite));

它为另一个精灵动态分配内存。这样,您的程序可以包含任意数量的 sprite,很可能是一个直到运行时才知道的数字,一旦用户开始做事。但是动态分配的内存 always 最终涉及指针。它不能使用像你的 int a 或我的 Sprite s1 这样的静态变量名称,因为根据定义,它们只能有一个固定的静态数字(即你选择的数字写了程序)。

为了进行比较,在不使用动态内存分配的情况下,查看 createSprite 函数的其他三种编写方式可能会有所启发。

Sprite* createSprite2(Sprite* model, int x, int y) {
  Sprite self;
  initSprite(model, &self, x, y);
  return &self;
}

这会创建一个 Sprite 类型的局部变量(不是 pointer-to-Sprite),但它根本不起作用。该局部变量在 createSprite2 return 时消失,因此指向它的指针立即无效。

Sprite* createSprite3(Sprite* model, int x, int y) {
  static Sprite self;
  initSprite(model, &self, x, y);
  return &self;
}

这里我们把局部Sprint变量设为static,所以在createSprite3 return时它不会消失。但这也行不通,因为现在实际上只有一个 Sprite 对象,由所有调用过 createSprite3 的调用者共享,这几乎肯定不是我们想要的,也不会工作。

但还有另一种可能性,实际上 起作用:

Sprite createSprite4(Sprite* model, int x, int y) {
  Sprite self;
  initSprite(model, &self, x, y);
  return self;
}

请注意,此 createSprite4 不是 return 指针 -- 它 return 是 Sprite 的实际实例.所以调用者可能看起来像

Sprite s1 = createSprite4(model, x, y);

Sprite manysprites[10];
for(int i = 0; i < 10; i++)
    manysprites[i] = createSprite4(model, x, y);

正如我所说,这可以很好地工作,并且有点反驳我的断言“动态分配的内存 总是 最终涉及指针”。 (但从技术上讲,这里仍然没有动态分配内存,正如我们从源代码中看到的那样,恰好分配了 1 个或 10 个 Sprites。)

createSprite returns 指向 self 的指针;如果它是这样的(我猜你也希望这样做):

Sprite* createSprite(Sprite* model, int x, int y) {
  Sprite self;
  initSprite(model, &self, x, y);
  return &self;
}

分配给self的内存将在函数执行结束时失效;尝试通过返回的指针访问它很可能会导致段错误。