变长数组 - 转

http://ericwang.github.io/program/2010/02/10/c_Variable_length_arrays/

C中的Variable length arrays (变长数组)

Variable length arrays 是C99的特性,而不是 C++98 的,关于c99标准的变长数组, 在标准的6.7.5.2 Array declarators里面有这样的说明:

2.Only ordinary identifiers (as defined in 6.2.3) with both block scope
or function prototype scope and no linkage shall have a variably
modified type. If an identifier is declared to be an object with static
storage duration, it shall not have a variable length array type.

换而言之, 变长数组不能是在静态存储区(包括全局变量和静态变量)中的。

另外,VLA 需要支持 sizeof 运算, 动态sizeof 也是C99的一个特有特性。

目前很多C++编译器尚不能支持动态数组特性(VC++2005不支持此特性,
GCC3.2之后支持,之前的版本没有调查,不知道是从哪个版本开始支持此特性的)。gcc的文档里面说:Variable-length
automatic arrays are allowed in ISO C99, and as an extension GCC accepts them in C89 mode and in C++。所以,使用GCC,即使你打开 -ansi (等价于 —std=c89)选项,也仍然可以使用动态数组。

其实C99的VLA也不是真正意义上的变长数组—它只是在运行时可以根据一个变量生成一个数组,此数组此后并不会变化,而真正意义的变长数组可以在实际使用时可以动态伸缩。

过去的C 和现在的C++中,自动变量的偏移都是预定的,但是VLA不能这样实现。

下面的例子中,由于b, c大小不确定,所以它们的地址必须是运行时确定的

void test(int tmp)
{
	int a;
	scanf("%d", &a);
	int b[tmp];
	int c[a];
	printf ("size(b) = %d, size(c) = %d\n", sizeof(b)/sizeof(int), sizeof(c)/sizeof(int));
}

VLA在栈上分配空间,这个速度很快。 我们举一个典型的32位编译器下函数实现为例子。这个实现中,下面两个寄存器关注函数堆栈:

ebp: 函数占有内容起点即帧(frame)的位置, esp:当前栈顶

一般来说,一个函数中自动变量个数和大小都是确定的。比如共有4个int,一个大小为10的 float 数组。 编译器可以简单的 esp -= 56 就留出所有空间(以32位PC机为例)。 每个变量相对 ebp 的偏移也是确定的,所以,若他访问 int a,

$a = 4, 他会这样访问: $a [ ebp ]

对于 VLA 来说,事情复杂一些。 现在有一个 VLA c。 他的大小是不确定的 —— 所以它的偏移也是不确定的:如果它之前有另外一个 VLA b, 它不可能知道自己排到哪里。目前 GCC 中的做法是,在可变数组较少的情况下,直接用寄存器+偏移的方式来存取;在较多的情况下,都是先从堆栈中得到数组的起始地址, 然后再用偏移量的方式存取数组的元素。

这个操作是非常快的。不过有一个问题是,栈的大小是固定的,而且不大。你不能分配很大的东西。另外这个数组是临时的 —— 一旦退出当前作用域,他就可以被任何其他函数刷掉。

变长数组的特性综述(摘自2):

1. 变长数组是分配在堆栈上的, 其实从语义的角度也应该是这样, 变长数组还是一个数组, 还是一个局部变量, 在c语言中, 局部变量是分配在堆栈上的, malloc才是分配在堆上面的. 这里面没有不存在什么内存泄漏, 因为堆栈上的内存是不需要程序员管理的

2. 变长数组和其他变量共同存在于一个作用域之内时, 变长数组是在最后分配的, 也就是处在堆栈的最下面(地址最低). 这也是变长数组不同于普通变量的情况, 普通变量的内存分配是按照定义顺序来的, 先定义的先分配, 但是变长数组不是这样的, 在一个作用域内变长数组都是放到最后分配的, 原因很简单, 变长数组的大小在编译时无 法确定, 所以如果在变长数组后面分配普通变量的空间, 那么对后面普通变量的存取就不方便了,当然简单的情况可以通过优化解决, 但是如果有很多的变量数组和普通变量混合在一起的话,优化很难做到很好, 所以把变长数组都放在最后面分配, 是一个比较合理的办法;

当然, 不是说一定要放在最后, 按照普通的定义顺序来分配空间也是可以的,只是在存取可变数组后面的变量的时候, 需要首先找到可变数组的起始地址,
然后再往下(低地址) 找到后面变量的地址, 存取相应的变量。目前gcc所使用的放到最后的做法, 也可以理解成一种优化. 。

3. 对变长数组的元素存取, 可以把首地址放在寄存器里面, 也可以在把首地址放在堆栈里面,然后存取元素的时候先取出首地址, 然后在通过偏移量的方式存取里面的元素.

4.变长数组不能在静态存储区中, 这个很显然, 静态存储区在编译时就确定分配内存的大小,不像堆栈一样是动态的, 变长数组显然不能定义在静态存储区中.

5. 变长数组的意义, 我觉得在c里面还是有一些意义, 如果是在对性能,内存要求十分严格的地方, 允许声明变长数组还是比较方便的,
因为原来需要一块连续内存的地方要么声明的大一些,保证肯定够用, 但这样就浪费了, 在对内存要求严格的地方就不好了,
另外一种做法就是malloc动态分配, 但是这样的缺点是需要手动管理, 要手动free,
程序如果大的话内存管理不好的话容易内存泄漏,所以在c里面还是有一些意义. 但是在c++里面已经有vector了,
而且c++也不太多应用在对性能,内存要求非常高的地方,

6.使用变长数组, 最重要的当然是要检查数组的size的合法性了, 我前面举例为了减少干扰因素, 都没有做检查,
实际的程序肯定要做这个检查, 则面试的时候这样的程序肯定立刻被kick. 另外,
就是又多了一种堆栈溢出的方法,数组大小如果是负的话又可以跳到不该跳到的地方了

时间: 2024-10-09 15:28:57

变长数组 - 转的相关文章

变长数组_相乘取结果

//变长数组 相乘取结果 #include <stdio.h> int main(void){ // int array_01[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; int array_02[4][3] = {12,11,10,9,8,7,6,5,4,3,2,1}; int result[3][3] = {0}; int i, j, k; for (i = 0; i < 3; i ++){ //遍历array_01数组元素 for (j = 0;j

C之变长数组

变长数组是C99标准新加入的一个特性,它的加入大大方便了我们的编程,所谓变长数组,不是数组的长度可变,而是指允许使用变量来定义数组.这可以使我们写出更具通用性的函数.下面是一个例子,函数sum2d完成将一个二位数组中的所有数值相加并返回其和. #include<stdio.h> #define SIZE 10 #define LOC 2 #define ROW 4int sum2d(int loc, int row, int num[loc][row]); int main(void){ in

C 语言变长数组 struct 中 char data[0] 的用法

1.结构体内存布局(padding) 为了让CPU能够更舒服地访问到变量,struct中的各成员变量的存储地址有一套对齐的机制.这个机制概括起来有两点:第一,每个成员变量的首地址,必须是它的类型的对齐值的整数倍,如果不满足,它与前一个成员变量之间要填充(padding)一些无意义的字节来满足:第二,整个struct的大小,必须是该struct中所有成员的类型中对齐值最大者的整数倍,如果不满足,在最后一个成员后面填充. The following typical alignments are va

C99新增内容之变长数组(VLA)

我们在使用多维数组是有一点,任何情况下只能省略第一维的长度.比如在函数中要传一个数组时,数组的行可以在函数调用时传递,当属数组的列却只能在能被预置在函数内部.看下面一个例子: #define COLS 4 int sum2d(int ar[][COLS],int rows) { int r; int c; int tot=0; for(r=0;r<rows;r++) for(c=0;c<COLS;c++) tot+=ar[r][c]; return tot; } 现在假设定义了如下数组: in

(变长数组)变量也可做特殊数组的长度

这个问题困扰我好久,终于完美区分: 看一个例子: main() { int n=10; int a[n]; scanf("%d",&a[2]); printf("%d",a[2]); system("pause"); } 以上例子中,n明显是一个整型的变量,虽然付了值,但是他仍然不可做为数组的大小,按理说是编译不通过的,但有些编译器,却让着个编译过了, 我们说:因为定义数组时,分配空间是需要一个固定的值,来确定你所申请的空间的大小. 若i

变长数组与函数参数

代码: #include <stdio.h> #include <stdlib.h> void setval(const size_t, const size_t, int arr[*][*]); void display(const size_t, const size_t, int arr[*][*]); int main(void) { const size_t ROWS = 5; const size_t COLS = 3; int arr[ROWS][COLS]; set

c语言,变长数组

C语言变长数组data[0][总结]: char data[0]用法总结:

C语言变长数组 struct中char data[0]的用法

版权声明:本文为博主原创文章,未经博主允许不得转载. [cpp] view plain copy print? 今天在看一段代码时出现了用结构体实现变长数组的写法,一开始因为忘记了这种技术,所以老觉得作者的源码有误,最后经过我深思之后,终于想起以前看过的用struct实现变长数组的技术.下面是我在网上找到的一篇讲解很清楚的文章. 在实际的编程中,我们经常需要使用变长数组,但是C语言并不支持变长的数组.此时,我们可以使用结构体的方法实现C语言变长数组. struct MyData { int nL

快学Scala 第三课 (定长数组,变长数组, 数组循环, 数组转换, 数组常用操作)

定长数组定义: val ar = new Array[Int](10) val arr = Array("aa", "bb") 定长数组赋值: arr(0) = "cc" 变长数组定义: val ab = new ArrayBuffer[String]() val ab1 = ArrayBuffer[String]() 定长数组增加元素: ab += "aa" ab += ("bb", "cc&q