Cac*_*che 3 c arrays static memory-management dynamic
以下代码使用malloc函数创建一个数组.但我知道只需使用int array [size]就可以做得更简单.我认为这是静态数组.但是malloc函数是动态数组吗?我在网上发现了这个代码......真正发生了什么,以及静态数组和动态数组(以及堆内存之间的静态内存)之间的区别.您可以在运行时更改动态数组的大小吗?或者......我完全不知道......如果有人能解释我会很感激:)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int size;
int i;
printf("Choose size of array: ");
scanf("%d",&size);
/*---MALLOC/FREE---*/
int *m_array = (int *)malloc((size+1)*sizeof(int));
memset(m_array,'\0',size+1);
for(i=0; i<size ; i++)
{
m_array[i]=i;
printf("%d ",m_array[i]);
}
printf("\n");
free(m_array);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
Joh*_*ode 22
有几种类型的数组,具体取决于它们的声明方式和位置.
定长阵列
固定长度数组的大小必须在编译时确定.定义后,您无法更改固定长度数组的大小.
固定长度数组以下列方式之一声明:
T a[N];
T a[N] = { /* initializer list */ };
char_type a[N] = "string literal";
T a[] = { /* initializer list */ };
char_type a[] = "string literal";
Run Code Online (Sandbox Code Playgroud)
在前三种情况下,N必须是一个常量表达式,其值必须在编译时知道.在前三种情况下,阵列的大小取自N; 在最后两种情况下,它取自初始化列表中的元素数或字符串文字的大小.
固定长度数组的初始内容取决于其存储持续时间以及是否已提供初始化程序.
如果数组具有static存储持续时间(意味着它在任何函数体之外的文件范围内声明,或者使用static关键字声明)并且没有初始化器,则所有数组元素都初始化为0(对于标量)或NULL(对于指针) ).如果T是聚合类型(如a struct或数组类型),则聚合的每个成员都使用0或初始化NULL. union类型同样归零.
如果数组有auto存储持续时间(意味着它是在没有static关键字的函数或块中声明)并且没有初始化器,那么数组的内容是不确定的 - 基本上是垃圾.
如果使用初始化列表声明数组(无论存储持续时间如何),则数组元素的初始值对应于初始化程序.如果初始化程序中的元素少于数组(例如,N为10,但只初始化前5个元素),则初始化其余元素,就好像数组具有static存储持续时间一样.IOW,鉴于声明
int a[10] = {0, 1, 2};
Run Code Online (Sandbox Code Playgroud)
那么数组的初始内容是{0, 1, 2, 0, 0, 0, 0, 0, 0, 0}.
可以使用字符串文字初始化包含字符串值的固定长度数组.C允许"宽"字符串,因此char_type可以是char或wchar_t.规则初始化程序列表的规则是相同的,除了N(如果指定)必须至少比字符串终止符的字符串长度多1个.
这意味着
char a[10] = "test";
Run Code Online (Sandbox Code Playgroud)
将被初始化为{'t', 'e', 's', 't', 0, 0, 0, 0, 0, 0}和
char a[] = "test";
Run Code Online (Sandbox Code Playgroud)
将被初始化为{'t', 'e', 's', 't', 0}.
static存储具有存储持续时间的数组被存储,使得它们在加载程序时立即可用,并且在程序退出之前不会被释放.这通常意味着他们存储在存储器段状.data或.bss(或任何可执行文件格式的系统使用当量).
auto存储持续时间的数组被存储,使得它们在块或函数入口处被分配并在块或函数出口处被释放(实际上,它们可能在函数入口处被分配,而不管它们是否被限制在较小的范围内.功能) -这通常转化为堆,尽管它不具备对.
可变长度数组
在C99中添加了可变长度数组 - 它们的行为大多类似于固定长度数组,除了它们的大小是在运行时建立的; N不必是编译时常量表达式:
int n;
printf( "gimme the array size: ");
scanf( "%d", &n );
T a[n]; // for any type T
Run Code Online (Sandbox Code Playgroud)
与其名称所暗示的相反,您无法在定义可变长度数组后更改其大小."可变长度"仅意味着大小在编译时不固定,并且可以从定义更改为定义.
由于它们的大小直到运行时才设置,因此可能不会在文件范围或static关键字中声明可变长度数组,也不能使用初始化列表声明它们.究竟如何管理VLA的空间取决于实现; 它可能(并且通常是)从堆叠中取出,但AFAIK可以从其他地方取出.
动态数组
动态数组本身并不是"数组",至少就我们用来管理它们的对象的数据类型而言.动态数组所使用的一个在运行时分配的malloc,calloc或者realloc,和存储一直保持到一起的呼叫释放free.
T *p = malloc( sizeof *p * N ); // where N may be either a compile-time or
// run-time expression
...
free( p );
Run Code Online (Sandbox Code Playgroud)
可以使用realloc库函数调整动态数组的大小,如下所示:
/**
* If realloc fails, it will return NULL and leave the original array in
* place. We assign the result to a temporary variable so we don't risk
* losing our only reference to that memory.
*/
T *tmp = realloc( p, sizeof *p * new_size );
if ( tmp )
p = tmp;
Run Code Online (Sandbox Code Playgroud)
虽然数组元素本身的内存取自堆(或任何动态内存池),但指针变量 的内存p将根据存储持续时间(或)从a .bss或.datasegment或堆栈中分配. pstaticauto
分配malloc或未realloc初始化的内存; 那个记忆的内容将是不确定的.分配的内存calloc将用零初始化.
数组与指针
在某些时候,有人会告诉你"数组只是一个指针".那个人不对.
当您声明一个数组(固定长度或可变长度)时,为该数组的元素留出足够的存储空间而不是其他内容 ; 没有为任何元数据留出存储空间,例如数组长度或指向第一个元素的指针.鉴于声明
T a[N];
Run Code Online (Sandbox Code Playgroud)
然后存储将看起来像这样:
+---+
a: | | a[0]
+---+
| | a[1]
+---+
| | a[2]
+---+
...
+---+
| | a[N-1]
+---+
Run Code Online (Sandbox Code Playgroud)
a除了数组元素本身之外没有任何对象(或者更确切地说,对象a 是数组的元素),并且表达式a可能不是赋值的目标.
但...
表达a[i]被定义为*(a + i); 也就是说,给定一个指针值a,从该地址偏移i 元素(而不是字节!)并取消引用结果.但如果a不是指针,那该怎么办呢?
像这样 - 除非它是sizeof或一元运算&符的操作数,或者是在声明中用作数组初始值设定项的字符串文字,"- element array of " 类型的表达式将被转换("衰减")到表达式类型为"指向",表达式的值将是数组的第一个元素的地址. NTT
这有几个含义:
a,&a以及&a[0]将所有产生相同的值(所述阵列的所述第一元素的地址),但类型的表达式将是不同的(T *,T (*)[N],和T *,分别地);
[]同样适用于数组表达式和指针表达式(事实上,它被定义为处理指针表达式);
对于动态阵列,情况有所不同.鉴于这条线
T *p = malloc( sizeof *p * N );
Run Code Online (Sandbox Code Playgroud)
然后你的存储将看起来像这样:
+---+
p: | | ---+
+---+ |
... |
+------+
|
V
+---+
| | p[0]
+---+
| | p[1]
+---+
...
+---+
| | p[N-1]
+---+
Run Code Online (Sandbox Code Playgroud)
在这种情况下,p 是与数组分开的对象.因此,&p 不会给你相同的值p和&p[0],其类型为T **,而不是T (*)[N].此外,因为p它只是一个指针变量,你可以为它分配一个新值(虽然如果你这样做而没有free指向它的内存,你将创建一个内存泄漏).
同样,sizeof p 不会表现得像sizeof a; 它会简单地返回指针变量的大小,而不是分配的内存指针点的大小来.