c中的动态与静态数组
以下代码使用malloc函数创建一个数组。 但我知道只需int array [size]就可以做得更简单。 我认为这是静态数组。 但是用malloc函数是动态数组吗? 我在网上发现了这个代码……真正发生了什么,以及静态数组和动态数组(以及堆内存之间的静态内存)之间的区别。 你可以在运行时更改动态数组的大小吗? 或者…我完全不知道…如果有人能解释我会很感激:)
#include #include #include 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; }
有几种类型的数组,具体取决于它们的声明方式和位置。
定长arrays
固定长度数组的大小必须在编译时确定。 定义后,您无法更改固定长度数组的大小。
固定长度数组以下列方式之一声明:
T a[N]; T a[N] = { /* initializer list */ }; char_type a[N] = "string literal"; T a[] = { /* initializer list */ }; char_type a[] = "string literal";
在前三种情况下, N
必须是一个常量表达式,其值必须在编译时知道。 在前三种情况下,arrays的大小取自N
; 在最后两种情况下,它取自初始化列表中的元素数或字符串文字的大小。
固定长度数组的初始内容取决于其存储持续时间以及是否已提供初始化程序。
如果数组具有static
存储持续时间(意味着它在任何函数体之外的文件范围内声明,或者使用static
关键字声明)并且没有初始化器,则所有数组元素都初始化为0
(对于标量)或NULL
(用于指针)。 如果T
是聚合类型(如struct
或数组类型),则聚合的每个成员都使用0
或NULL
初始化。 union
类型同样被清零。
如果数组具有auto
存储持续时间(意味着它是在没有static
关键字的函数或块中声明)并且没有初始化器,则数组的内容是不确定的 – 基本上是垃圾。
如果使用初始化列表声明数组(无论存储持续时间如何),则数组元素的初始值对应于初始化程序。 如果初始化程序中的元素少于数组(例如, N
为10但您只初始化前5个元素),则初始化其余元素, 就好像数组具有static
存储持续时间一样。 IOW,鉴于声明
int a[10] = {0, 1, 2};
那么数组的初始内容是{0, 1, 2, 0, 0, 0, 0, 0, 0, 0}
。
可以使用字符串文字初始化包含字符串值的固定长度数组。 C允许“宽”字符串,因此char_type
可以是char
或wchar_t
。 规则初始化程序列表的规则是相同的,除了N
(如果指定)必须至少比字符串终止符的字符串长度多1。
这意味着
char a[10] = "test";
将被初始化为{'t', 'e', 's', 't', 0, 0, 0, 0, 0, 0}
和
char a[] = "test";
将被初始化为{'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
与其名称所暗示的相反,您无法在定义可变长度数组后更改其大小。 “可变长度”仅意味着大小在编译时不固定,并且可以从定义更改为定义。
由于它们的大小直到运行时才设置,因此可能不会在文件范围或使用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 );
可以使用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;
虽然数组元素本身的内存取自堆(或任何动态内存池),但指针变量 p
的内存将从.bss
或.data
段或堆栈中分配,基于p
‘存储持续时间( static
或auto
)。
使用malloc
或realloc
分配的malloc
未初始化; 那个记忆的内容将是不确定的。 用calloc
分配的内存将用零初始化。
数组与指针
在某些时候,有人会告诉你“数组只是一个指针”。 那个人不对。
当您声明一个数组(固定长度或可变长度)时,为该数组的元素留出足够的存储空间,而不是其他内容 ; 没有为任何元数据留出存储空间,例如数组长度或指向第一个元素的指针。 鉴于声明
T a[N];
然后存储将看起来像这样:
+---+ a: | | a[0] +---+ | | a[1] +---+ | | a[2] +---+ ... +---+ | | a[N-1] +---+
除了数组元素本身之外没有对象(或者更确切地说,对象a
是数组的元素),并且表达式a
可能不是赋值的目标。
但…
表达式a[i]
定义为*(a + i)
; 也就是说,给定一个指针值a
,从该地址偏移i
元素 ( 不是字节! )并取消引用结果。 但如果a
不是指针,那该怎么办呢?
像这样 – 除非它是sizeof
或一元&
运算符的操作数,或者是在声明中用作数组初始值设定项的字符串文字,将转换类型为“ N
元素数组T
”的表达式 (“衰变”) )到“指向T
指针”类型的表达式,表达式的值将是数组的第一个元素的地址。
这有几个含义:
- 表达式
a
,&a
和&a[0]
都将产生相同的值 (数组的第一个元素的地址),但表达式的类型将是不同的(T *
,T (*)[N]
,和T *
,分别); - 下标operator
[]
同样适用于数组表达式和指针表达式(事实上,它被定义为处理指针表达式); - 将数组表达式传递给函数时, 实际传递的是指针值,而不是整个数组;
对于动态arrays,情况有所不同。 鉴于这条线
T *p = malloc( sizeof *p * N );
然后你的存储将看起来像这样:
+---+ p: | | ---+ +---+ | ... | +------+ | V +---+ | | p[0] +---+ | | p[1] +---+ ... +---+ | | p[N-1] +---+
在这种情况下, p
是与数组分开的对象。 因此, &p
不会给你与p
和&p[0]
相同的值,它的类型将是T **
而不是T (*)[N]
。 此外,由于p
只是一个指针变量,你可以为它指定一个新值(尽管如果这样做而没有free
它指向的内存,你将创建一个内存泄漏)。
同样, sizeof p
行为不会像sizeof a
; 它只会返回指针变量的大小,而不是指针指向的已分配内存的大小。
静态数组在编译时分配内存,内存在堆栈上分配。 然而,动态数组在运行时分配内存,内存从堆分配。
这是静态整数数组,即在运行时之前分配的固定内存
int arr[] = { 1, 3, 4 };
这是动态整数数组,即在运行时分配的内存
int* arr = new int[3];
您正在使用大小为+ 1的动态数组并向其添加元素(从0到大小)并在返回0之前释放空间。因此,在您的情况下,int * m_array是指向int的指针。 您在第13行所做的是声明:
(int *m_array) =...
和分配:
...(int *)malloc((size+1)*sizeof(int));
因为它是一个动态数组,所以你在堆上进行分配并保留直到它被释放(这就是为什么你最后有空闲(m_array))。 如果它是静态的,你可以用这种方式初始化数组:
int m_array[size];
并且它将被分配在静态存储区域(除非是自动的)并且一旦程序结束就会被释放。 您无法在C中更改静态数组的大小,因此您需要使用动态数组。 如果要更改为动态数组的大小,可以使用realloc 。