c中的动态与静态数组 [英] Dynamic vs static array in c

查看:29
本文介绍了c中的动态与静态数组的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

以下代码使用 malloc 函数创建一个数组.但我知道这可以通过 int array[size] 更简单地完成.我认为这是静态数组.但是使用 malloc 函数是动态数组吗?我在网上找到了这段代码......真正发生了什么以及静态数组和动态数组(以及堆内存之间的静态内存)之间的区别是什么.您可以在运行时更改动态数组的大小吗?或者...我不知道什么...如果有人能解释我会很感激:)

The following code creates an array with the malloc function. But i know that this can be done much simplier with just int array[size]. I think this is static array. But with the malloc function is it dynamic array? I found this code in the net... What is really happening and whats the difference between a static array and a dynamic array(and static memory between heap memory). Can you change the size of the dynamic array during runtime? or... what i dont know exactly... If someone could explain i would appreciate :)

#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;
}

推荐答案

有几种类型的数组,具体取决于它们的声明方式和位置.

There are several flavors of arrays, depending on how and where they are declared.

定长数组

定长数组的大小必须在编译时确定.定长数组一经定义便无法更改其大小.

Fixed-length arrays must have their size determined at compile time. You cannot change the size of a fixed-length array after it has been defined.

定长数组以下列方式之一声明:

Fixed-length arrays are declared in one of the following ways:

T a[N];
T a[N] = { /* initializer list */ };
char_type a[N] = "string literal";
T a[]  = { /* initializer list */ };
char_type a[]  = "string literal";

在前三种情况下,N 必须是一个常量表达式,其值必须在编译时已知.前三种情况,数组的大小取自N;在后两种情况下,它取自初始化列表中的元素数或字符串字面量的大小.

In the first three cases, N must be a constant expression whose value must be known at compile time. In the first three cases, the size of the array is taken from N; in the last two cases, it's taken from the number of elements in the initializer list or the size of the string literal.

固定长度数组的初始内容取决于它的存储持续时间以及是否提供了初始化程序.

The initial contents of a fixed-length array depend on its storage duration and whether an initializer has been supplied.

如果数组具有 static 存储期(意味着它是在任何函数体之外的文件范围内声明的,或者是使用 static 关键字声明的)并且没有初始化程序存在,那么所有数组元素都被初始化为 0(对于标量)或 NULL(对于指针).如果 T 是聚合类型,例如 struct 或数组类型,则聚合的每个成员都用 0 初始化>NULL.union 类型同样被清零.

If the array has static storage duration (meaning it was declared at file scope outside of any function body, or was declared with the static keyword) and no initializer is present, then all of the array elements are initialized to 0 (for scalars) or NULL (for pointers). If T is an aggregate type such as a struct or an array type, then each member of the aggregate is initialized with a 0 or NULL. union types are similarly zeroed out.

如果数组具有 auto 存储持续时间(意味着它是在没有 static 关键字的函数或块中声明的)并且不存在初始化器,则该数组的内容数组不确定 - 基本上,垃圾.

If the array has auto storage duration (meaning it was declared within a function or block without the static keyword) and no initializer is present, then the contents of the array are indeterminate - basically, garbage.

如果数组是用初始化器列表声明的(不管存储时间长短),则数组元素的初始值对应于初始化器.如果初始化器中的元素比数组少(例如,N 是 10,但你只初始化了前 5 个元素),那么其余元素被初始化好像该数组具有 static 存储持续时间.IOW,给定声明

If the array is declared with an initializer list (regardless of storage duration), then the initial values of the array elements correspond to the initializer. If there are fewer elements in the initializer than the array (for example, N is 10 but you only initialize the first 5 elements), then the remaining elements are initialized as though the array had static storage duration. IOW, given the declaration

int a[10] = {0, 1, 2};

那么数组的初始内容为{0, 1, 2, 0, 0, 0, 0, 0, 0, 0}.

then the initial contents of the array are {0, 1, 2, 0, 0, 0, 0, 0, 0, 0}.

包含字符串值的定长数组可以使用字符串文字进行初始化.C 允许宽"字符串,因此 char_type 可以是 charwchar_t.规则对于常规初始值设定项列表是相同的,除了 N(如果指定)必须至少比字符串长度多 1 以考虑字符串终止符.

Fixed-length arrays containing string values may be initialized using a string literal. C allows for "wide" character strings, so char_type may be either char or wchar_t. The rules are the same for regular initializer lists, except that N (if specified) must be at least 1 more than the length of the string to account for the string terminator.

这意味着

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(或系统使用的任何可执行格式的等效格式).

Arrays with static storage duration are stored such that they are available as soon as the program is loaded, and aren't released until the program exits. This usually means that they're stored in a memory segment like .data or .bss (or the equivalent for whatever executable format your system uses).

具有 auto 存储期的数组被存储,以便它们在块或函数入口分配并在块或函数出口释放(实际上,它们可能会在函数入口分配,无论它们是否仅限于函数内的较小范围) - 这通常会转化为堆栈,尽管它没有.

Arrays with auto storage duration are stored such that they are allocated at block or function entry and released at block or function exit (in practice, they'll probably be allocated at function entry, regardless of whether they're limited to a smaller scope within the function) - this typically translates to the stack, although it doesn't have to.

变长数组

可变长度数组是在 C99 中添加的 - 它们的行为大部分类似于固定长度数组,除了它们的大小是在运行时确定的;N 不必是编译时常量表达式:

Variable-length arrays were added in C99 - they behave mostly like fixed-length arrays, except that their size is established at run time; N does not have to be a compile-time constant expression:

int n;
printf( "gimme the array size: ");
scanf( "%d", &n );
T a[n]; // for any type T

与其名称所暗示的相反,您不能在定义可变长度数组后更改其大小.可变长度"只是意味着大小在编译时不是固定的,并且可以随定义而变化.

Contrary to what their name implies, you cannot change the size of a variable-length array after it has been defined. "Variable-length" simply means that the size isn't fixed at compile time, and can change from definition to definition.

因为它们的大小直到运行时才设置,变长数组不能在文件范围内或用 static 关键字声明,也不能用初始化列表声明.究竟如何管理 VLA 的空间取决于实施;它可能(通常是)从堆栈中取出,但 AFAIK 可能从其他地方取出.

Since their size isn't set until runtime, variable-length arrays may not be declared at file scope or with the static keyword, nor can they be declared with an initializer list. Exactly how the space for VLAs is managed is up to the implementation; it may be (and usually is) taken from the stack, but AFAIK may be taken from somewhere else.

动态数组

动态数组并不是真正的数组",至少在我们用来管理它们的对象的数据类型方面是这样.动态数组在运行时使用 malloccallocrealloc 之一进行分配,并且该存储被保持直到通过调用 免费.

Dynamic arrays are not really "arrays" as such, at least in terms of the data types of the objects we use to manage them. Dynamic arrays are allocated at runtime using one of malloc, calloc, or realloc, and that storage is held until released with a call to free.

T *p = malloc( sizeof *p * N ); // where N may be either a compile-time or
                                // run-time expression
...
free( p );

可以使用 realloc 库函数调整动态数组的大小,如下所示:

A dynamic array may be resized using the realloc library function, like so:

/**
 * 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 或<代码>自动).

While the memory for the array elements themselves is taken from the heap (or whatever dynamic memory pool), the memory for the pointer variable p will be allocated from either a .bss or .data segment or from the stack, based on p's storage duration (static or auto).

使用mallocrealloc 分配的内存未初始化;该内存的内容将是不确定的.使用 calloc 分配的内存将初始化为零.

Memory allocated with malloc or realloc is not initialized; the contents of that memory will be indeterminate. Memory allocated with calloc will be initialized with zeros.

数组与指针

在某些时候,有人会告诉你数组只是一个指针".那个人不对.

At some point, somebody is going to tell you that "an array is just a pointer". That person is not correct.

当您声明一个数组(固定长度或可变长度)时,会为该数组的元素留出足够的存储空间,没有其他内容;没有为任何元数据(例如数组长度或指向第一个元素的指针)预留存储空间.鉴于声明

When you declare an array (either fixed- or variable-length), enough storage is set aside for the elements of that array and nothing else; no storage is set aside for any metadata such as the array length or a pointer to the first element. Given the declaration

T a[N];

然后存储看起来像这样:

then the storage will look something like this:

    +---+
 a: |   | a[0]
    +---+
    |   | a[1]
    +---+
    |   | a[2]
    +---+
     ...
    +---+ 
    |   | a[N-1]
    +---+

没有对象 a 除了数组元素本身(或者,更准确地说,对象 a is 是数组的元素),并且表达式 a 可能不是赋值的目标.

There is no object a apart from the array elements themselves (or, more properly, the object a is the elements of the array), and the expression a may not be the target of an assignment.

可是……

表达式a[i]定义*(a + i);也就是说,给定一个指针值 a,从该地址偏移 i elements(不是字节!)并取消引用结果.但是如果 a 不是一个指针,那它怎么工作呢?

The expression a[i] is defined as *(a + i); that is, given a pointer value a, offset i elements (not bytes!) from that address and dereference the result. But if a is not a pointer, how can that work?

像这样 - 除非它是 sizeof 或一元 & 运算符的操作数,或者是在声明中用作数组初始值设定项的字符串文字,类型为N-T"的元素数组的表达式将被转换(衰减")为一个表达式类型为指向T的指针",表达式的值将是数组第一个元素的地址.

Like this - except when it is the operand of the sizeof or unary & operators, or is a string literal used as an array initializer in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.

这有几个含义:

  • 表达式a&a&a[0] 都将产生相同的(数组第一个元素的地址),但是表达式的类型会不同(T *, T (*)[N]>, 和 T *, 分别);
  • 下标运算符[] 对数组表达式和指针表达式同样有效(实际上,它定义 可以处理指针表达式);
  • 当你将一个数组表达式传递给一个函数时,你实际上传递的是一个指针值,而不是整个数组;
  • The expressions a, &a, and &a[0] will all yield the same value (the address of the first element of the array), but the types of the expressions will be different (T *, T (*)[N], and T *, respectively);
  • The subscript operator [] works equally well with both array expressions and pointer expressions (indeed, it's defined to work on pointer expressions);
  • When you pass an array expression to a function, what you are actually passing is a pointer value, not the entire array;

对于动态数组,情况就不同了.鉴于行

For dynamic arrays, the situation is different. Given the line

T *p = malloc( sizeof *p * N );

那么您的存储空间将如下所示:

then your storage will look something like this:

   +---+
p: |   | ---+
   +---+    |
    ...     |
     +------+
     |
     V
   +---+
   |   | p[0]
   +---+
   |   | p[1]
   +---+
    ...   
   +---+
   |   | p[N-1]
   +---+

在这种情况下,p 一个独立于数组的对象.因此,&p 不会为您提供与 p&p[0] 相同的值,它的类型将是 T ** 而不是 T (*)[N].此外,由于 p 只是一个指针变量,您可以为其分配一个新值(尽管如果您这样做时没有 free 它首先指向的内存,您会造成内存泄漏).

In this case, p is a separate object from the array. Thus, &p won't give you the same value as p and &p[0], and its type will be T ** as opposed to T (*)[N]. Also, since p is just a pointer variable, you can assign a new value to it (although if you do so without freeing the memory it points to first, you'll create a memory leak).

类似地,sizeof p 不会表现得像sizeof a;它将简单地返回指针变量的大小,而不是指针指向的分配内存的大小.

Similarly, sizeof p won't behave like sizeof a; it will simply return the size of the pointer variable, not the size of the allocated memory that the pointer points to.

这篇关于c中的动态与静态数组的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆