malloc 与 C 中的数组
malloc vs array in C
我正在上哈佛大学的公开在线课程 CS50。我上的最后一课是关于内存分配和指针(这两个概念对我来说绝对是新的)。
所教的是 malloc(10*sizeof(char))
在堆上分配足够的字节来存储 10 个字符和 returns 指向第一个字节的指针,该指针可以保存在另一个变量中,如下所示 char *x = malloc(10*sizeof(char))
.要释放内存,可以使用 free(x)
。
但还有另一种方法可以让计算机保留足够的内存来存储 10 个字符,即 char c[10]
。
- 上面的代码片段中
c
也是char*
类型的指针吗?
char c[10]
是否也像 malloc
那样在堆 上保留内存 ?
- 分配内存的方法是否等效?
char c[3] = "aaa";free(c);
returns 运行时错误;所以我似乎无法释放我用 char c[3]
分配的内存。这是为什么?
我非常感谢为刚刚了解指针的人量身定制的答案。
"char c[10];" 保留 space 在本地堆栈或程序加载时创建的全局数据区域中,它是 而不是 堆就程序而言的内存(对于 OS 它可能不同)。
当在表达式中使用时,数组衰减为指向其第一个元素的指针(例如,这就是类似以下内容的工作方式):
您不能释放使用 var[size] 类型语法声明的数组,因为它要么是当前函数的局部变量,要么是整个程序的全局变量。堆内存不以这种方式绑定到任何特定的上下文(用于引用堆内存的变量不是指向的内存)。
- Is in the code snippet above c also a pointer of type char*?
不,不是。它是一个包含十个 char
.
的数组
但是,当在需要指针的上下文中使用时,数组的名称可以转换为指针,因此可以像指针一样有效地使用它。
- Does char c[10] also reserve memory on the heap as malloc does?
没有。堆和栈也不是完全准确的术语,但我不会进一步展开。
malloc()
所做的按照标准叫做"dynamic memory allocation"。
char c[10];
的行为取决于上下文。
- 如果它在块范围内(在一对
{}
内),它会创建一个自动存储持续时间的数组。就您的程序而言,当范围退出时,该数组不再存在(例如,如果函数 returns)。
- 如果它在文件范围内(在函数之外),那么它会创建一个静态存储持续时间的数组。该数组将被创建一次,并一直存在直到程序终止。
- Are the ways to allocate memory equivalent?
没有。
char c[3] = "aaa";free(c); returns a runtime error; so it seems I can not free the memory I have allocated with char c[3]. Why is that?
因为 free()
只有在将指针传递给动态分配的内存时才具有定义的行为 - 即由 malloc()
、calloc()
或 realloc()
或 NULL
指针(导致 free()
什么都不做)。
c
是静态或自动存储持续时间的数组,具体取决于上下文,正如我上面提到的。它不是动态分配的,因此将其传递给 free()
会产生未定义的行为。一个常见的症状是运行时错误,但不是唯一可能的症状。
What was taught is that malloc(10*sizeof(char))
allocates enough
bytes on the heap to store 10 characters and returns a pointer to the
first byte which can be saved in another variable as follows char *x
= malloc(10*sizeof(char))
. To free the memory one would use free(x)
.
"On the heap"是一个实现概念,不是C语言的概念。 C 语言本身并不关心将内存划分为具有不同特性的单独区域,事实上,任何给定的 C 实现实际上都不一定是这样。
即使在介绍性课程中——也许 尤其是 在介绍性课程中——使用 C 语言概念比使用特定实现风格的概念要好。本例中相关的C语言概念是存储时长:
An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated.
您的 malloc()
调用分配的对象,(在)您的指针 x
指向的对象具有 "allocated" 持续时间。这意味着它的生命周期一直持续到调用 free()
释放该对象为止。请注意变量 x
和具有自动存储持续时间的指针与 x
最初指向的对象之间的区别,这是一个大小为 10 char
s.[=45 的无类型对象=]
还有(很多)更多内容,但是您旅程中的这一点对于深入研究标准还为时过早。不过,我发现这种描述对于解决您提出的问题更有用。
But there is another way to make a computer to reserve enough memory
to store 10 characters i.e. char c[10]
.
是的,没错。
- Is in the code snippet above c also a pointer of type
char*
?
没有。在该声明的范围内,标识符 c
引用 10 char
的数组。数组和指针之间有着密切的关系,但它们根本不是一回事。这是非常重要的一点,很多新的 C 程序员都被这一点绊倒了,所以我重复一遍:数组和指针不是一回事。但是,详细信息将提供另一个完整的答案,您已经可以在 SO 上多次找到这个答案。
换句话说,标识符 c
指定了 x
的值可以指向的一种事物,但请记住 x
的(指针)值是不同于它指向的对象。
- Does
char c[10]
also reserve memory on the heap as malloc
does?
如果您对 c
的声明出现在函数内部,那么它会声明一个具有自动存储持续时间的数组。这意味着数组的生命周期一直持续到标识符 c
超出范围。该数组的存储位置是实现的关注点,但在提供堆/堆栈区别的实现中,存储很可能在堆栈上,而不是堆上。
- Are the ways to allocate memory equivalent?
没有。 malloc()
分配一个具有分配存储持续时间的对象,程序负责显式管理其生命周期。另一个分配一个具有自动存储持续时间的对象,其生命周期由标识符的范围决定。
char c[3] = "aaa";free(c);
returns a runtime error; so it seems I can not free the memory I have allocated with char c[3]
. Why
is that?
最直接的原因是 free()
函数的规范明确说明
[I]f the argument does not match a pointer earlier returned by a memory
management function, or if the space has been deallocated by a call to
free or realloc, the behavior is undefined.
也就是说,如果您尝试释放指向具有自动持续时间的对象的指针,该标准不需要运行时错误(或任何特定的其他行为),但它明确否认您可以通过这种方式释放内存的任何承诺.
但我认为一个更令人满意的答案是 free()
是如何标记具有分配存储持续时间的对象的生命周期结束,而不是具有自动(或其他)持续时间的对象。对象的存储位置(例如堆栈 与 堆)是辅助的。
语法注意事项:
首先,c
和x
的类型不同:x
的类型是你所期望的char*
,而[=的类型15=] 是 char[10]
,这是一个包含十个字符元素的数组。
因此,x
和 c
不能完全等价:当您说 x
时,编译器只考虑单个 char
的单个地址。但是,当您说 c
时,编译器会考虑整个数组对象及其所有十个 char
元素。因此,代码
printf("sizeof(x) = %zd\n", sizeof(x));
printf("sizeof(*x) = %zd\n", sizeof(*x));
printf("sizeof(c) = %zd\n", sizeof(c));
将打印
sizeof(x) = 8
sizeof(*x) = 1
sizeof(c) = 10
在 64 位机器上。 sizeof(x)
给出存储地址所需的字节数,sizeof(*x)
给出指针 x
指向的字节数,sizeof(c)
给出存储所需的字节数十个 char
个元素的完整数组。
那么,为什么我几乎可以在任何可以在 C 中使用 x
的地方使用 c
?
这个技巧叫做 array-pointer decay:每当你在需要指针的上下文中使用数组时,编译器会默默地将数组衰减为指向它的第一个指针的指针元素。 C 中只有两个地方可以实际使用数组。第一个是sizeof()
(这就是sizeof(x) != sizeof(c)
的原因),第二个是地址运算符&
。 在所有其他情况下,任何使用 c
都会调用数组指针衰减。 这包括 c[3]
之类的东西。此表达式被定义为等效于 *(c+3)
,因此编译器将数组 c
衰减为指向其第一个元素的指针,然后应用指针算法 c+3
,然后取消引用结果指针。听起来很复杂,令人难以置信,但访问数组的第四个元素具有预期的效果。
无论如何,抛开语法上的考虑,让我们看看实际的内存分配:
malloc()
保留给定大小的内存块,并且该块一直有效,直到您在 malloc()
[=119= 的指针上调用 free()
]ed.
这与程序中的控制流无关: 一个函数可以return将malloc()
的结果传递给它的调用者,让调用者自由它。或者它可能将 malloc()
的结果传递给其他释放它的函数。或者它可能 return 结果给它的调用者,调用者将它传递给其他一些函数以释放它。或者结果可能会在其他内存对象中存储一段时间。等等等等。可能性与世界各地正在编写的源代码一样多种多样。
必须强调的是,在释放指针之后再使用它是一个很大的错误。如果你这样做,就C标准而言,有可能出现粉红色的大象并将你踩死。作为程序员,您的工作是确保每个 malloc'ed 指针恰好被释放一次。如果你不这样做,那么,所有的赌注都会被取消。
如果你说
char c[10];
在文件范围内(在函数或 struct
定义之外),您正在声明一个 全局变量 。这个数组将在 main()
被调用之前存在,直到你的进程结束(通过 returning 从 main()
,或者通过执行 exit()
)。
如果你说
char c[10];
在一个函数中,您声明了一个局部变量。该数组在其声明执行时存在,并在封闭块的末尾(一对大括号{}
之间的部分)不复存在。
因此,分配和释放与程序的控制流严格相关。
如果你说
char c[10];
在 struct
的定义中,您声明了一个 成员变量 。只要封闭对象 (struct
) 存在,这个数组就会存在。如果封闭对象是全局的,则数组的生命周期是全局的;如果封闭对象是局部的,则数组的生命周期是局部的;如果封闭对象是某个其他对象的成员,则数组的生命周期是另一个对象的(这是递归的)。
我正在上哈佛大学的公开在线课程 CS50。我上的最后一课是关于内存分配和指针(这两个概念对我来说绝对是新的)。
所教的是 malloc(10*sizeof(char))
在堆上分配足够的字节来存储 10 个字符和 returns 指向第一个字节的指针,该指针可以保存在另一个变量中,如下所示 char *x = malloc(10*sizeof(char))
.要释放内存,可以使用 free(x)
。
但还有另一种方法可以让计算机保留足够的内存来存储 10 个字符,即 char c[10]
。
- 上面的代码片段中
c
也是char*
类型的指针吗? char c[10]
是否也像malloc
那样在堆 上保留内存 ?- 分配内存的方法是否等效?
char c[3] = "aaa";free(c);
returns 运行时错误;所以我似乎无法释放我用char c[3]
分配的内存。这是为什么?
我非常感谢为刚刚了解指针的人量身定制的答案。
"char c[10];" 保留 space 在本地堆栈或程序加载时创建的全局数据区域中,它是 而不是 堆就程序而言的内存(对于 OS 它可能不同)。
当在表达式中使用时,数组衰减为指向其第一个元素的指针(例如,这就是类似以下内容的工作方式):
您不能释放使用 var[size] 类型语法声明的数组,因为它要么是当前函数的局部变量,要么是整个程序的全局变量。堆内存不以这种方式绑定到任何特定的上下文(用于引用堆内存的变量不是指向的内存)。
- Is in the code snippet above c also a pointer of type char*?
不,不是。它是一个包含十个 char
.
但是,当在需要指针的上下文中使用时,数组的名称可以转换为指针,因此可以像指针一样有效地使用它。
- Does char c[10] also reserve memory on the heap as malloc does?
没有。堆和栈也不是完全准确的术语,但我不会进一步展开。
malloc()
所做的按照标准叫做"dynamic memory allocation"。
char c[10];
的行为取决于上下文。
- 如果它在块范围内(在一对
{}
内),它会创建一个自动存储持续时间的数组。就您的程序而言,当范围退出时,该数组不再存在(例如,如果函数 returns)。 - 如果它在文件范围内(在函数之外),那么它会创建一个静态存储持续时间的数组。该数组将被创建一次,并一直存在直到程序终止。
- Are the ways to allocate memory equivalent?
没有。
char c[3] = "aaa";free(c); returns a runtime error; so it seems I can not free the memory I have allocated with char c[3]. Why is that?
因为 free()
只有在将指针传递给动态分配的内存时才具有定义的行为 - 即由 malloc()
、calloc()
或 realloc()
或 NULL
指针(导致 free()
什么都不做)。
c
是静态或自动存储持续时间的数组,具体取决于上下文,正如我上面提到的。它不是动态分配的,因此将其传递给 free()
会产生未定义的行为。一个常见的症状是运行时错误,但不是唯一可能的症状。
What was taught is that
malloc(10*sizeof(char))
allocates enough bytes on the heap to store 10 characters and returns a pointer to the first byte which can be saved in another variable as followschar *x = malloc(10*sizeof(char))
. To free the memory one would usefree(x)
.
"On the heap"是一个实现概念,不是C语言的概念。 C 语言本身并不关心将内存划分为具有不同特性的单独区域,事实上,任何给定的 C 实现实际上都不一定是这样。
即使在介绍性课程中——也许 尤其是 在介绍性课程中——使用 C 语言概念比使用特定实现风格的概念要好。本例中相关的C语言概念是存储时长:
An object has a storage duration that determines its lifetime. There are four storage durations: static, thread, automatic, and allocated.
您的 malloc()
调用分配的对象,(在)您的指针 x
指向的对象具有 "allocated" 持续时间。这意味着它的生命周期一直持续到调用 free()
释放该对象为止。请注意变量 x
和具有自动存储持续时间的指针与 x
最初指向的对象之间的区别,这是一个大小为 10 char
s.[=45 的无类型对象=]
还有(很多)更多内容,但是您旅程中的这一点对于深入研究标准还为时过早。不过,我发现这种描述对于解决您提出的问题更有用。
But there is another way to make a computer to reserve enough memory to store 10 characters i.e.
char c[10]
.
是的,没错。
- Is in the code snippet above c also a pointer of type
char*
?
没有。在该声明的范围内,标识符 c
引用 10 char
的数组。数组和指针之间有着密切的关系,但它们根本不是一回事。这是非常重要的一点,很多新的 C 程序员都被这一点绊倒了,所以我重复一遍:数组和指针不是一回事。但是,详细信息将提供另一个完整的答案,您已经可以在 SO 上多次找到这个答案。
换句话说,标识符 c
指定了 x
的值可以指向的一种事物,但请记住 x
的(指针)值是不同于它指向的对象。
- Does
char c[10]
also reserve memory on the heap asmalloc
does?
如果您对 c
的声明出现在函数内部,那么它会声明一个具有自动存储持续时间的数组。这意味着数组的生命周期一直持续到标识符 c
超出范围。该数组的存储位置是实现的关注点,但在提供堆/堆栈区别的实现中,存储很可能在堆栈上,而不是堆上。
- Are the ways to allocate memory equivalent?
没有。 malloc()
分配一个具有分配存储持续时间的对象,程序负责显式管理其生命周期。另一个分配一个具有自动存储持续时间的对象,其生命周期由标识符的范围决定。
char c[3] = "aaa";free(c);
returns a runtime error; so it seems I can not free the memory I have allocated with charc[3]
. Why is that?
最直接的原因是 free()
函数的规范明确说明
[I]f the argument does not match a pointer earlier returned by a memory management function, or if the space has been deallocated by a call to free or realloc, the behavior is undefined.
也就是说,如果您尝试释放指向具有自动持续时间的对象的指针,该标准不需要运行时错误(或任何特定的其他行为),但它明确否认您可以通过这种方式释放内存的任何承诺.
但我认为一个更令人满意的答案是 free()
是如何标记具有分配存储持续时间的对象的生命周期结束,而不是具有自动(或其他)持续时间的对象。对象的存储位置(例如堆栈 与 堆)是辅助的。
语法注意事项:
首先,c
和x
的类型不同:x
的类型是你所期望的char*
,而[=的类型15=] 是 char[10]
,这是一个包含十个字符元素的数组。
因此,x
和 c
不能完全等价:当您说 x
时,编译器只考虑单个 char
的单个地址。但是,当您说 c
时,编译器会考虑整个数组对象及其所有十个 char
元素。因此,代码
printf("sizeof(x) = %zd\n", sizeof(x));
printf("sizeof(*x) = %zd\n", sizeof(*x));
printf("sizeof(c) = %zd\n", sizeof(c));
将打印
sizeof(x) = 8
sizeof(*x) = 1
sizeof(c) = 10
在 64 位机器上。 sizeof(x)
给出存储地址所需的字节数,sizeof(*x)
给出指针 x
指向的字节数,sizeof(c)
给出存储所需的字节数十个 char
个元素的完整数组。
那么,为什么我几乎可以在任何可以在 C 中使用 x
的地方使用 c
?
这个技巧叫做 array-pointer decay:每当你在需要指针的上下文中使用数组时,编译器会默默地将数组衰减为指向它的第一个指针的指针元素。 C 中只有两个地方可以实际使用数组。第一个是sizeof()
(这就是sizeof(x) != sizeof(c)
的原因),第二个是地址运算符&
。 在所有其他情况下,任何使用 c
都会调用数组指针衰减。 这包括 c[3]
之类的东西。此表达式被定义为等效于 *(c+3)
,因此编译器将数组 c
衰减为指向其第一个元素的指针,然后应用指针算法 c+3
,然后取消引用结果指针。听起来很复杂,令人难以置信,但访问数组的第四个元素具有预期的效果。
无论如何,抛开语法上的考虑,让我们看看实际的内存分配:
malloc()
保留给定大小的内存块,并且该块一直有效,直到您在malloc()
[=119= 的指针上调用free()
]ed.这与程序中的控制流无关: 一个函数可以return将
malloc()
的结果传递给它的调用者,让调用者自由它。或者它可能将malloc()
的结果传递给其他释放它的函数。或者它可能 return 结果给它的调用者,调用者将它传递给其他一些函数以释放它。或者结果可能会在其他内存对象中存储一段时间。等等等等。可能性与世界各地正在编写的源代码一样多种多样。必须强调的是,在释放指针之后再使用它是一个很大的错误。如果你这样做,就C标准而言,有可能出现粉红色的大象并将你踩死。作为程序员,您的工作是确保每个 malloc'ed 指针恰好被释放一次。如果你不这样做,那么,所有的赌注都会被取消。
如果你说
char c[10];
在文件范围内(在函数或
struct
定义之外),您正在声明一个 全局变量 。这个数组将在main()
被调用之前存在,直到你的进程结束(通过 returning 从main()
,或者通过执行exit()
)。如果你说
char c[10];
在一个函数中,您声明了一个局部变量。该数组在其声明执行时存在,并在封闭块的末尾(一对大括号
{}
之间的部分)不复存在。因此,分配和释放与程序的控制流严格相关。
如果你说
char c[10];
在
struct
的定义中,您声明了一个 成员变量 。只要封闭对象 (struct
) 存在,这个数组就会存在。如果封闭对象是全局的,则数组的生命周期是全局的;如果封闭对象是局部的,则数组的生命周期是局部的;如果封闭对象是某个其他对象的成员,则数组的生命周期是另一个对象的(这是递归的)。