与仅在 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
的内存将在函数执行结束时失效;尝试通过返回的指针访问它很可能会导致段错误。
我正在查看代码,作为我目前正在从事的项目的参考。我注意到,不是有一个变量,然后只是暂时将一个指针转换为它作为参数传递,它只是被设置为始终是一个指针类型。 (显示 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
的内存将在函数执行结束时失效;尝试通过返回的指针访问它很可能会导致段错误。