题
我已经写C只有很少几个星期并没有时间来担心我自己太多 malloc()
.但最近,一程序的矿返回的一串幸福的笑脸,而不是true/false values我已经预计到。
如果我创建了一个结构是这样的:
typedef struct Cell {
struct Cell* subcells;
}
然后后来初始化这样
Cell makeCell(int dim) {
Cell newCell;
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim -1);
}
return newCell; //ha ha ha, this is here in my program don't worry!
}
我要结束了访问幸福面临的存储器中存储的地方,或者,也许写了以前存在的细胞,或者别的什么?我的问题是,如何C分配的记忆时我还没有实际malloc()ed适当数量的记忆?什么是默认的?
解决方案
没有默认值为你的指针。你的指针就会指向不管它商店。因为你还没有初始化它,线
newCell.subcells[i] = ...
有效地访问一些不确定的部分记忆。记得,子电池[i]相当于
*(newCell.subcells + i)
如果左侧包含一些垃圾,你将最终加入 i
到一个垃圾的价值和访问的存在不确定的位置。正如你正确地说,你会有初始化的指针指向一些有效的记忆区域:
newCell.subcells = malloc(bytecount)
在这之后你可以访问的许多字节。关于其他来源的存储器,有不同种类的储存,都有其用途。什么你得取决于什么样的对象必须和其储存的类你告诉编译器的使用。
malloc
返回指向一个目的没有类型。你可以做一个指针指向这个地区的存储器,并对象的类型将有效地成为这种类型的指为对象的类型。存储器不是初始化为任何值和接入通常较慢。对象,以便获得被称为allocated objects
.- 你可以把对象在全球范围。他们的记忆中将被初始化为零。点,你会得到空指针,对于浮你会得到一个适当的零。你可以依靠一个适当的初始价值。
- 如果你有地方的变量,但使用
static
储存的类符的,然后你会有相同的初始价值的规则,作为对全球对象。存储器通常分配的相同方式如全球对象,但这是不必要的。 - 如果你有地方的变量没有任何储存的类说明符,或与
auto
, 然后你变量将被分配在组(即使没有定义以通过,这是什么编译器做实际上当然)。你可以把其地址,在这种情况下,编译器将有省略优化喜欢把它放入登记册的课程。 - 当地使用的变量与存储类别说明
register
, 都标志着作为具有特殊的存储。为此,你无法把它的地址了。在最近编译器,那里通常是没有必要使用register
了,因为他们的复杂的优化.如果你是真正的专家,然后你可以得到一些表现出它如果采用它,虽然。
对象具有相关储存的持续时间,可用于显示不同的初始化的规定(正式的,他们只有定义,至少对象的居住)。目的声明 auto
和 register
有自动持续时间和存储是 不 初始化。你必须明确地初始化他们,如果你要它们含有一些价值。如果你不这样做,他们将含有任何编译器留在堆之前他们就开始生。对象是分配 malloc
(或另一种功能的家庭,就像 calloc
)已经分配储存的持续时间。他们储存 不 初始化。一个例外是当使用 calloc
, ,在这种情况下的记忆是初始化为零("真正的"零。i。e所有的字节0x00,没有考虑到任何空指针表示)。对象的声明 static
和全球变量有静态存储时间。他们储存 是 初始化为零适合于他们各自的类型。注意,对象必须没有一种类型,但是唯一的方式得到的一类-不对象是使用分配的储存。(对象在C是一个"地区储存").
那么,什么是什么?这里是固定码。因为一旦你分配了一块记忆你不能回来了多少个项目分配,最好是始终存储数的地方。我已经介绍了一variale dim
向结构的,获取的数储存。
Cell makeCell(int dim) {
/* automatic storage duration => need to init manually */
Cell newCell;
/* note that in case dim is zero, we can either get NULL or a
* unique non-null value back from malloc. This depends on the
* implementation. */
newCell.subcells = malloc(dim * sizeof(*newCell.subcells));
newCell.dim = dim;
/* the following can be used as a check for an out-of-memory
* situation:
* if(newCell.subcells == NULL && dim > 0) ... */
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim - 1);
}
return newCell;
}
现在,东西看起来像这样暗淡=2:
Cell {
subcells => {
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
},
Cell {
subcells => {
Cell { subcells => {}, dim = 0 }
},
dim = 1
}
},
dim = 2
}
请注意,在C、返回值的功能不需要一个对象。没有储存所有需要存在。因此,不允许改变它。例如,以下是不可能的:
makeCells(0).dim++
你会需要一个"自由"功能,免费的分配的存储器。因为储存用于分配的对象是不会释放自动的。你要叫 free
以免费,为每一个存储器 subcells
指针在你的树上。它是作为一个练习为你写了:)
其他提示
简答:没有为您分配。
答案稍长: subcells
指针未初始化,可能指向任何地方。这是一个错误,你永远不应该允许它发生。
更长的答案:自动变量在堆栈上分配,全局变量由编译器分配,并且通常占用特殊段或可能在堆中。默认情况下,全局变量初始化为零。自动变量没有默认值(它们只是获取内存中的值),程序员负责确保它们具有良好的起始值(尽管许多编译器会在您忘记时尝试提示您)。
您的函数中的 newCell
变量是自动的,并且未初始化。你应该解决这个问题。要么及时给 newCell.subcells
一个有意义的值,要么指向 NULL
,直到为它分配一些空间。这样,如果您在为其分配内存之前尝试取消引用它,则会抛出分段违规。
更糟糕的是,您按值返回 Cell
,但在尝试填充 subcells
数组时将其分配给 Cell *
。返回指向堆分配对象的指针,或将值赋给本地分配的对象。
通常的习惯用法就像
一样Cell* makeCell(dim){
Cell *newCell = malloc(sizeof(Cell));
// error checking here
newCell->subcells = malloc(sizeof(Cell*)*dim); // what if dim=0?
// more error checking
for (int i=0; i<dim; ++i){
newCell->subCells[i] = makeCell(dim-1);
// what error checking do you need here?
// depends on your other error checking...
}
return newCell;
}
虽然我已经给你留下了一些问题要敲定......
请注意,您必须跟踪最终需要解除分配的所有内存位...
在堆上分配的任何内容(通过 malloc
和类似的调用)都会在堆栈上分配。因此,当函数结束时,在没有 malloc
'd的情况下在特定函数中创建的任何东西都将被销毁。包括返回的对象;当函数调用后展开堆栈时,返回的对象被复制到调用函数在堆栈中为它预留的空间。
警告:如果要返回一个指向其中对象的对象,请确保在堆上创建指向的对象,更好的是,在该对象上创建该对象。堆也是,除非它不打算在创建它的函数中存活。
我的问题是,当我实际上没有malloc()编辑适当的内存量时,C如何分配内存?什么是默认值?
不分配内存。您必须明确地在堆栈上创建它或动态地创建它。
在您的示例中,子单元指向未定义位置,这是一个错误。您的函数应该在某个时刻返回指向Cell结构的指针。
我最终会在某个地方访问存储在内存中的快乐面孔,或者可能是在先前存在的单元格上写字,还是什么?
你很幸运,你有一张幸福的脸。在其中一个不幸的日子里,它可能会擦干你的系统;)
我的问题是,当我实际上没有malloc()编辑适当的内存量时,C如何分配内存?
没有。但是,当你定义Cell newCell时,会发生什么,subCells指针被初始化为垃圾值。这可能是0(在这种情况下你会崩溃)或一些足够大的整数使它看起来像一个实际的内存地址。在这种情况下,编译器会愉快地获取驻留在那里的任何值并将其带回给您。
默认是什么?
如果您没有初始化变量,这是 行为。而 makeCell
函数看起来有点欠发达。
实际上可以分配三个部分 - 数据,堆栈和数据。堆。
在你提到的情况下,它将被分配在堆栈上。在堆栈上分配内容的问题在于它仅在函数持续时间内有效。函数返回后,将回收该内存。因此,如果返回指向堆栈上分配的内容的指针,则该指针将无效。如果您返回实际对象(不是指针),则会自动生成该对象的副本以供调用函数使用。
如果您已将其声明为全局变量(例如,在头文件中或函数外部),则会将其分配给内存的数据部分。本节中的内存在程序启动时自动分配,并在完成后自动解除分配。
如果你使用malloc()在堆上分配一些东西,那么只要你想使用它,那个内存就是好的 - 直到你调用free(),然后释放它。这使您可以根据需要灵活地分配和释放内存(而不是使用全局,其中所有内容都预先分配,只有在程序终止时才会释放)。
局部变量被“分配”。在堆栈上。堆栈是预分配的内存量,用于保存这些局部变量。当函数退出时变量不再有效,并且将被接下来的任何内容覆盖。
在您的情况下,代码无效,因为它不会返回您的结果。此外,当范围退出时,指向堆栈上对象的指针也将停止有效,所以我想在你的确切情况下(你似乎在做一个链表),你需要使用malloc()。
我要假装我是这里的计算机,阅读本代码...
typedef struct Cell {
struct Cell* subcells;
}
这告诉我:
- 我们有一个结构类型叫做细胞
- 它包含一个指称为子电池
- 指针应该是什么类型的结构单元
它不会告诉我是否是指针转到一个单元或一系列单元。当一个新的小区是制作,值得指针是不确定的,直到一个价值是分配给它。这是坏消息使用指针之前,确定他们。
Cell makeCell(int dim) {
Cell newCell;
新的小区结构,有一个未定义的子电池的指针。所有这些不是保留一小块的存储器可被称为newCell,是大小的细胞结构。它不会改变的价值观在内存-他们可能是任何东西。
for(int i = 0; i < dim; i++) {
newCell.subcells[i] = makeCell(dim -1);
为了获得newCell.子电池[i],一个计算是为了偏离子电池的我,那么这是 取消引用.具体而言,这意味着价值是拉从存储器的地址。举例来说,i==0...然后我们将取消引用子电池指本身的(没有偏移量)。由于子电池是不确定的,它可能是任何东西。从字面上的东西!因此,这将要求一个值从某个地方完全随机存储器中。没法保证的任何结果。它可以打印一些东西,它可能会崩溃。它肯定不应该被完成。
}
return newCell;
}
任何时候你的工作指针,重要的是要确保它设定为一个价值之前你引用。鼓励编译器来给你任何警告,它可以,许多现代的编译器可以抓住这样的事情。你也可以给指针爱默认值像0xdeadbeef(是啊!这是一个数量进,这只是也一个字,因此它看起来很滑稽),以便他们站出来。(%P选择printf是有用的指针显示,作为一个粗形式的调试。调试器的程序,也可以显示他们的相当不错。)