这实际上是一个比我之前在这里问的问题(对于任何关心这个问题的人来说(更简洁、更清晰的问题:C语言:为什么malloc((返回指针而不是值?(很抱歉那些最初认为我在发垃圾邮件的人……我希望这不是同一个问题,因为我认为我在那里的措辞无意中误导了它(
->基本上,我想问的是:为什么C程序员需要一个指向动态分配的变量/对象的指针?(无论变量/对象之间有什么区别…(
如果C程序员可以选择只创建"int x"或"int*x"(两者都是静态分配的(,那么为什么他不能选择将动态分配的变量/对象初始化为变量(而不是通过malloc((返回指针(?
*如果有一些模糊的方法可以做到我上面解释的,那么,为什么malloc((似乎是大多数教科书关于动态分配的方式?
注意:在下文中,byte
指sizeof(char)
例如,malloc
返回一个void *
。它只是不能返回一个值:这在C缺乏泛型的情况下是不可行的。在C语言中,编译器必须在编译时知道每个对象的大小;由于分配的内存大小要到运行时才能知道,因此必须返回一个可以表示任何值的类型。由于void *
可以表示任何指针,因此它是最好的选择。
malloc
也无法初始化块:它不知道正在分配什么。这与C++的operator new
形成了对比,后者同时执行分配和初始化,并且是类型安全的(可能由于历史原因,它仍然返回指针而不是引用(。
此外,malloc
分配一个特定大小的内存块,然后返回一个指向该内存的指针(这就是malloc
的意思:内存分配(。你得到了一个指针,因为这就是你得到的:一个统一的原始内存块。当您执行malloc(sizeof(int))
时,您不是在创建int
,而是在分配sizeof(int)
字节并获取这些字节的地址。然后,您可以决定将该块用作int
,但从技术上讲,您也可以将其用作sizeof(int)
char
s的数组。
各种备选方案(calloc
、realloc
(的工作方式大致相同(calloc
在处理数组时更容易使用,零填充数据,而realloc
在需要调整内存块大小时很有用(。
假设您在函数中创建了一个整数数组并想要返回它。所述数组是函数的局部变量。不能返回指向局部变量的指针。
但是,如果使用malloc,则会在堆上创建一个范围超过函数体的对象。你可以返回一个指向它的指针。你只需要稍后销毁它,否则就会发生内存泄漏。
这是因为用malloc()
分配的对象没有名称,所以在代码中引用该对象的唯一方法是使用指向它的指针
当你说int x;
时,它会创建一个名为x
的对象,并且它可以通过该名称引用。当我想将x
设置为10
时,我可以只使用x = 10;
。
我也可以设置一个指针变量,用int *p = &x;
指向那个对象,然后我也可以用*p = 10;
设置x
的值。请注意,这一次我们可以在不具体命名x
的情况下谈论它(除了我们获得对它的引用之外(。
当我说malloc(sizeof(int))
时,它会创建一个没有名称的对象。我不能直接按名称设置该对象的值,因为它没有名称。但是,I可以通过使用指向它的指针变量来设置它,因为该方法不需要命名对象:int *p = malloc(sizeof(int));
后面跟着*p = 10;
。
您现在可能会问:"那么,为什么我不能告诉malloc给对象一个名称?">——类似于malloc(sizeof(int), "x")
。答案有两个:
- 首先,C不允许在运行时引入变量名。这只是语言的一个基本限制
- 其次,考虑到第一个限制,名称必须在编译时固定:如果是这种情况,C已经有了可以满足您需要的语法:
int x;
您认为事情不对。这并不是说int x是静态分配的,malloc(sizeof(int((是动态的。两者都是动态分配的。也就是说,它们都是在执行时分配的。编译时没有为它们保留空间。大小在一种情况下可以是静态的,在另一种情况中可以是动态的,但分配总是动态的。
相反,它是int x分配堆栈上的内存,malloc(sizeof(int((分配堆上的内存。堆上的内存需要有一个指针才能访问它。堆栈上的内存可以直接引用,也可以用指针引用。通常您直接执行,但有时您希望使用指针算术对其进行迭代,或将其传递给需要指针的函数。
-
一切都使用指针。"int x"只是一种方便——某个地方的某个人厌倦了处理内存地址,这就是具有人类可读变量名的编程语言诞生的原因。
-
动态分配是…动态的。在程序运行之前,您不必知道程序运行时需要多少空间。你可以选择何时执行和何时撤消。它可能会失败。使用静态分配的简单语法很难处理所有这些。
-
C的设计考虑到了简单性,编译器的简单性就是其中的一部分。这就是为什么您会接触到底层实现的怪癖。所有系统都存储静态大小的本地临时变量(寄存器、堆栈(;这就是静态分配所使用的。大多数系统都有用于动态、自定义生存期对象的存储,以及用于管理它们的系统调用;这就是动态分配所使用和公开的内容。
有一种方法可以满足你的要求,它叫做C++。在那里,"MyInt x = 42;
"是一个或两个函数调用。
我想你的问题可以归结为:
如果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: %dn", i);
printf("j: %dn", j);
printf("k: %dn", k);
printf("l: %pn", l);
printf("m: %pn", m);
printf("n: %pn", n);
return 0;
}
$ make stack
cc stack.c -o stack
$ ./stack
i: 0
j: 0
k: 32767
l: 0x400410
m: (nil)
n: 0x4005a0
l
和n
指向内存中的某个值,但这些值只是垃圾,可能不属于可执行文件的地址空间。如果我们把任何东西存储到这些指针中,程序可能会死。不过,如果将不相关的结构映射到程序的地址空间中,它可能会破坏这些结构。
m
至少是一个NULL
指针——如果你试图写入它,这个程序肯定会在现代硬件上消亡。
这三个指针实际上还没有指向整数。那些整数的内存不存在。指针的内存确实存在,并且在本例中最初填充了垃圾值。
维基百科上关于L值的文章——大多过于迟钝,无法完全推荐——提出了一点,这对我第一次学习C:时来说是一个相当大的障碍。在有可分配变量的语言中,有必要区分变量的R值(或内容(和L值(或位置(
例如,您可以写:
int a;
a = 3;
这将整数值CCD_ 47存储到被分配用于存储变量CCD_。
如果你以后写:
int b;
b = a;
这将获取存储在a
引用的内存中的值,并将其存储到为b
分配的内存位置中。
使用指针的相同操作可能如下所示:
int *ap;
ap=malloc(sizeof int);
*ap=3;
第一个ap=
分配将内存位置存储到ap
指针中。现在ap
实际上指向一些内存。第二个赋值*ap=
将值存储到该内存位置中。它根本不更新ap
指针;它读取存储在名为CCD_ 56的变量中的值以找到用于分配的存储器位置。
以后使用指针时,您可以选择使用与指针关联的两个值中的哪一个:指针的实际内容或指针指向的值:
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;
之间的区别令人困惑。我希望我发现了让你和困惑的地方希望我没有让事情变得更糟。
也许您误解了声明"int x"one_answers"int*x"之间的区别。第一个为int值分配存储;第二个没有——它只是为指针分配存储空间。
如果你要"动态分配"一个变量,那么动态分配无论如何都没有意义(除非你取了它的地址,这当然会产生一个指针(——你还可以静态地声明它。想想代码会是什么样子——你为什么要麻烦:
int x = malloc(sizeof(int)); *x = 0;
当你可以做:
int x = 0;