C语言:为什么动态分配的对象返回指针,静态分配的对象为您提供选择?

时间:2011-10-14 00:42:12

标签: c malloc

这实际上是一个比我之前提到的问题(对于任何关心的人)更简洁,更清晰的问题:C Language: Why does malloc() return a pointer, and not the value?(对不起那些最初认为我在发送垃圾邮件的人...我很抱歉希望它不会被解释为同一个问题,因为我认为我在那里使用它的方式使它无意中误导了)

- >基本上我要问的是:为什么C程序员需要一个指向动态分配的变量/对象的指针? (无论变量/对象之间有什么区别......)

如果C程序员可以选择只创建'int x'或者只是'int * x'(两者都是静态分配的),那么为什么他也不能选择JUST初始化他动态分配的变量/对象作为变量(并且不通过malloc()返回指针)?

*如果有一些模糊的方法可以做我上面解释的那么,那么,为什么malloc()似乎是大多数教科书关于动态分配的方式?

7 个答案:

答案 0 :(得分:9)

注意:在下文中,byte引用sizeof(char)

嗯,对于一个,malloc会返回void *。它只是不能返回一个值:这在C缺乏泛型的情况下是不可行的。在C中,编译器必须在编译时知道每个对象的大小;由于分配的内存大小在运行时才会知道,因此必须返回可以表示任何值的类型。由于void *可以表示任何指针,因此它是最佳选择。

malloc也无法初始化块:它不知道正在分配什么。这与C ++的operator new形成对比,它同时进行分配和初始化,以及类型安全(它仍然返回指针而不是引用,可能是出于历史原因)。

此外,malloc分配特定大小的内存块,然后返回指向该内存的指针(malloc代表的内容:内存分配)。你得到一个指针,因为这就是你得到的:一个原始记忆的单元化块。当您执行malloc(sizeof(int))时,您没有创建int,而是分配sizeof(int)个字节并获取这些字节的地址。然后,您可以决定将该块用作int,但您也可以在技术上将其用作sizeof(int) char的数组。

各种替代方法(callocrealloc)的工作方式大致相同(calloc在处理数组时更容易使用,并且数据为零填充,而{{1当你需要调整一块内存时,它很有用。)

答案 1 :(得分:1)

假设您在函数中创建一个整数数组并想要返回它。所述数组是函数的局部变量。您不能返回指向局部变量的指针。

但是,如果使用malloc,则会在堆上创建一个范围超出函数体的对象。您可以返回指向该指针的指针。你只需要稍后销毁它,否则你会发生内存泄漏。

答案 2 :(得分:1)

这是因为用malloc()分配的对象没有名称,所以在代码中引用该对象的唯一方法是使用指向它的指针。

当您说int x;时,会创建一个名为x的对象,并且可以通过该名称引用它。当我想将x设置为10时,我可以使用x = 10;

我也可以 设置指针变量以指向int *p = &x;的对象,然后我可以使用x设置*p = 10;的值。请注意,这次我们可以在没有特别命名的情况下谈论x(超出我们获取对它的引用的点)。

当我说malloc(sizeof(int))时,会创建没有名称的对象。我不能直接按名称设置该对象的值,因为它没有一个。但是,我可以使用指向它的指针变量来设置它,因为该方法不需要命名对象:int *p = malloc(sizeof(int));后跟*p = 10;

您现在可能会问:“那么,为什么我不能告诉malloc给对象一个名字?” - 类似于malloc(sizeof(int), "x")。答案是双重的:

  • 首先,C只是不允许在运行时引入变量名。这只是语言的基本限制;
  • 其次,鉴于第一个限制,名称必须在编译时修复:如果是这种情况,C已经具有符合您需要的语法:int x;

答案 3 :(得分:0)

也许你误解了声明'int x'和'int * x'之间的区别。第一个为int值分配存储空间;第二个没有 - 它只是为指针分配存储空间。

如果你要“动态分配”一个变量,那么动态分配中没有任何意义(除非你随后取了它的地址,这当然会产生一个指针) - 你也可以静态地声明它。考虑一下代码的外观 - 你为什么要打扰:

int x = malloc(sizeof(int)); *x = 0;

当你可以做的时候:

int x = 0;

答案 4 :(得分:0)

你在想错事。并非int x是静态分配的,而malloc(sizeof(int))是动态的。两者都是动态分配的。也就是说,它们都是在执行时分配的。编译时没有为它们保留空间。大小在一种情况下可以是静态的,而在另一种情况下可以是动态的,但分配总是动态的。

相反,它是int x在堆栈上分配内存而malloc(sizeof(int))在堆上分配内存。堆上的内存要求您有一个指针才能访问它。堆栈上的内存可以直接引用或使用指针引用。通常你直接这样做,但有时你想用指针算术迭代它或将它传递给需要指向它的指针的函数。

答案 5 :(得分:0)

  1. 一切都可以使用指针。 “int x”只是一个方便 - 有些人厌倦了处理内存地址,这就是人类可读变量名称编程语言诞生的原因。

  2. 动态分配是......动态的。程序运行时 - 程序运行之前,您不必知道需要多少空间。您可以选择何时执行此操作以及何时撤消它。它可能会失败。使用静态分配的简单语法很难处理所有这些。

  3. C的设计考虑了简单性,编译器的简单性就是其中的一部分。这就是为什么你暴露于底层实现的怪癖。所有系统都有静态大小的本地临时变量(寄存器,堆栈)存储;这是静态分配使用的。大多数系统都有动态,自定义生命周期对象和系统调用的存储来管理它们;这就是动态分配使用和公开的内容。

  4. 有一种方法可以做你所要求的,它叫做C ++。在那里,“MyInt x = 42;”是一个或两个函数调用。

答案 6 :(得分:0)

我认为你的问题归结为:

  

如果C程序员可以选择只创建int x或只创建int *x(两者都是静态分配的)

第一个语句为整数分配内存。根据语句的位置,它可能会在当前正在执行的函数的堆栈上分配内存,或者它可能在程序的.data.bss部分中分配内存(如果它是全局变量)或static变量,在文件范围或函数范围内)。

第二个语句为指向整数的指针分配内存 - 它实际上没有为整数本身分配内存。如果您尝试使用指针*x=1分配值,则会收到非常快速SIGSEGV分段违规损坏某些随机内存。 C不会预先在堆栈上分配内存:

$ cat stack.c
#include <stdio.h>

int main(int argc, char *argv[]) {
    int i;
    int j;
    int k;
    int *l;
    int *m;
    int *n;
    printf("i: %d\n", i);
    printf("j: %d\n", j);
    printf("k: %d\n", k);
    printf("l: %p\n", l);
    printf("m: %p\n", m);
    printf("n: %p\n", n);
    return 0;
}
$ make stack
cc     stack.c   -o stack
$ ./stack
i: 0
j: 0
k: 32767
l: 0x400410
m: (nil)
n: 0x4005a0

ln指向内存中的某些内容 - 但这些值只是垃圾,可能不属于可执行文件的地址空间。如果我们将任何东西存储到那些指针中,程序可能会死掉。但是,它可能会破坏不相关的结构,如果它们被映射到程序的地址空间中。

m至少是一个NULL指针 - 如果你试图写它,程序肯定会死在现代硬件上。

这三个指针中没有一个实际指向到一个整数。这些整数的内存不存在。指针的内存存在 - 并且在这种情况下最初用垃圾值填充。

Wikipedia article on L-values - 大部分过于迟钝而无法完全推荐 - 在我第一次学习C时,为我提出了一个非常重要的障碍:在具有可分配变量的语言中,有必要区分在R值(或内容)与变量的L值(或位置)之间。

例如,您可以写:

int a;
a = 3;

这将整数值3存储到分配用于存储变量a内容的内存中。

如果你以后写:

int b;
b = a;

这将值存储在a 引用的内存中,并将其存储到为b分配的内存位置。

使用指针的相同操作可能如下所示:

int *ap;
ap=malloc(sizeof int);
*ap=3;

第一个ap=赋值将内存位置存储到ap指针中。现在ap实际上指向了一些记忆。第二个赋值*ap=将值存储到该内存位置。它根本不更新ap指针;它读取存储在名为ap的变量中的值,以查找赋值的内存位置。

当您稍后使用指针时,您可以选择与指针关联的两个值中的哪一个使用:指针的实际内容指针指向的值:

int *bp;
bp = ap; /* bp points to the same memory cell as ap */

int *bp;
bp = malloc(sizeof int);
*bp = *ap; /* bp points to new memory and we copy
              the value pointed to by ap into the
              memory pointed to by bp */

我发现汇编比C多了多年,因为我发现foo = malloc();*foo = value;之间的区别令人困惑。我希望我发现令你困惑的事情和认真希望我没有让事情变得更糟。