C-C++到底支不支持VLA以及两种语言中const的区别

C-C++到底支不支持VLA以及两种语言中const的区别

到底支不支持VLA

VLA就是variable-length array,也就是变长数组。数组

最近写程序的时候无心间发现,gcc中居然支持下面这种写法:函数

int n = 10;spa

int a[n];3d

注意上面的语句是在函数内部写的,也就是n和a都是自动变量。指针

当时十分疑惑,C语言中数组的长度不该该是常量或常量表达式吗?为何变量也能够。我将代码在VC中跑了一下,发现编译出错,提示数组的大小未知,说明VC中是不支持VLA的。code

那既然有的编译器支持VLA,又有的编译器不支持VLA,那么C标准究竟是怎样规定的呢?而后我看是看书、在网上查资料。对象

 

C Primer Plus一书中是这样描述的:blog

C90标准中并不支持VLA,C99开始支持VLA,很大的一个缘由:FORTRAN中支持这种写法。C99中对对VLA有一些限制,好比变长数组必须是自动存储类型,也就是说,若是我上面两句放在函数外面就就不能经过编译了,这是由于在函数外面定义的是全局变量,此外,使用VLA不能对数组进行初始化,由于它的长度在运行时才能肯定。内存

此外VLA并非真正的变长,它实际上只是将数组的长度推迟到运行时肯定而已,也就是说C90标准中,数组的长度必须在编译时期就知道,但C99支持VLA后,数组的长度能够推迟到运行时知道,可是,一旦长度肯定,数组的长度就不能变了。编译器

 

此外,网上的大神说,C++的标准中不管是C++90仍是C++99仍是C++11都不支持VLA的这种写法

 

鉴于以上缘由,在C语言中,若是想用变长的数组,仍是老老实实用malloc分配吧,在C++中固然有更好的选择,就是vector,固然C++11中又推出了一个array,并且这两种都是真正的变长,也就是数组的长度随时均可以改变。

 

下面我还想说一下C和C++中const关键字的区别。

const关键字最先是C++中的产物,后来才引入到C语言中。const在C语言中和在C++中是至关不同的。

在C语言中const修饰的,被认为是一个只读的、或者叫不可改变的变量,它实际上只是一个变量,只不过对这个变量作了一些限制。而C++中const修饰的才是真正的常量。固然这其中还有不少细节的地方,有些地方我也还有些模糊,下面,我就经过例子,把本身已经理解的东西写出来。

先来一个例子:

const int a = 10;

int array[a];

int main()

{

         return 0;

}

 

 

用gcc和g++编译的结果分别以下:

 

 

能够看到gcc下不能经过编译,可是g++下能够经过,说明C语言中有错,在C++中没错。

缘由解释:

首先说C语言中:

首先说明,即便在支持VLA的编译器下,(个人gcc是支持的),前面提到了VLA数组是有限制的,VLA必须是自动存储类型,而上面的代码中数组是全局变量,因此并不存在是否支持VLA的问题。上面提到,在C语言中const被认为是一个受到必定限制的变量,是变量就要被分配数据区(或者运行时的栈区等)内存空间,因为a是全局变量,全局变量位于数据区,空间在编译时期就分配了。而,须要注意,编译时期,编译器是不能读取数据区的内存的(它能够分配数据区的内存,并初始化内存,可是不能从数据区的牛叉女内存中读取数据)。因此在编译时期,编译器其实并不知道a的值是什么,由于它不能读数据区的内存而a的值是在内存中的。可是,对于数组array编译器是必定要知道数组的长度才行的,也就是必需要知道a的值,这样就矛盾了,因此编译器就报错了!

那在C++中有为何可以经过呢?

缘由就是C++真的把const当成常量看待。

详细解释一下:

const int a = 10;这条语句中10是咱们所说的字面量,不管是在C中仍是在C++中字面量都是保存在代码段中,编译初期会将其保存在符号表中。C++尽可能不对const分配数据区(或者运行时的栈区)的内存空间,只在必须分配内存时才分配(这个后面再说)。下面一条语句int array[a],编译器必定要知道a的值的,C语言要想知道a的值,必须读内存,可是C++却不须要,直接读取代码段中的的符号表便可,编译时期访问符号表是没有任何问题的,可是访问数据区的内存是作不到的。因此上面的语句在C++中是没有问题的。

再来讲说,什么叫尽量不为const分配内存。

若是代码是这样

 

const int a = 10;

const int *p = &a;

int array[a];

 

int main()

{

         return 0;

}

 

注意 const int *p = &a;这句,对a取地址操做,咱们知道位于代码段的数据是不取地址的,因此这个时候,只能给a在数据区分配空间了。

因而就出现了新的问题,既然给a分配了数据区的空间,那是否是编译时期就不知道a的值了,由于毕竟编译时期是不能读取数据区的内存的,那么后面数组的定义也就不行了吧?可是答案却相反,依然能够,这是由于当编译器读a的值的时候,不是从数据区的内存中,而是程序段的符号表中读取的那个字面常量10。因此在编译实际依然可以肯定数组的长度。

下面的例子应该更能说明这个问题:

 

#include <stdio.h>

int main()

{

         const int a = 10;

         int *pa = (int *)&a;

         printf("pa指向的地址为:%p  a的地址为:%p\n",pa,&a);

         (*pa)++;

         printf("a = %d,*pa = %d\n",a,*pa);

 

         return 0;

}

 

咱们分别用gcc和g++编译他,而后分别看结果,以下:

 

 

 

惊奇地发现,虽然都能顺利经过编译,可是C的执行和C++的执行居然不同!

好吧,下面解释缘由。

仍是要声明一下,C语言中const就是一个值不能改变的变量,就是个受限制的变量,可是,咱们虽然咱们不能经过a修改那块内存的值,可是咱们能够经过指针间接去修改。这里要注意那个强制类型转换,若是不写强制类型转换,编译器就会报错,是容许将const int *赋值给int*的。在C++中,这一点和C是同样的,就是虽然咱们不能经过a自己修改那块内存的值,可是咱们能够经过指针间接去修改。可是为何C和C++中的输出不同呢?缘由就是C++在读a的时候,实际上是去代码段中读字面常量10去了,而C是读a所标识的那块栈区的内存。其实a所标识的内存的内容都已经变成11了,不管是C仍是C++都是同样,区别就在于C读const数据和读普通变量同样,都是从数据段(若是是局部变量就是从栈区)读取数组,而C++倒是读取代码段的字面常量!(间接修改const的时候,固然都是修改的数据区或栈区的内存,而不是代码段,由于代码段是只读的)

因此C++中const修饰的能够认为就是常量!可是C语言中却不能这么认为。

最后要当心C++中的const蜕变成C语言中的const。

其实经过上面的分析,咱们应该能够得出一个结论:C++中的const之因此和C语言中的const不同,C++中的const之因此可以当作常量,就是由于C++在读取const的时候,实际上读取的是代码段的字面常量,而不是数据区(对于全局变量来讲是静态区,对于局部变量来讲是栈区)的内存中的数值。

那么问题来了:若是const保存的不是一个字面常量呢?

看下面代码:

 

#include <stdio.h>

int main()

{

         int i = 10;

         const int a = i;

         int *pa = (int *)&a;

         printf("pa指向的地址为:%p  a的地址为:%p\n",pa,&a);

         (*pa)++;

         printf("a = %d,*pa = %d\n",a,*pa);

 

         return 0;

}

 

几乎仍是一样的代码,只是先把字面常量10赋值给了变量i,而后用i初始化const int a,可是咱们发现,在C++中,执行结果却变了。

 

 

为何呢?前面强调了,C++读取const的值,其实是读取代码段的字面常量,那么,若是咱们初始化const的时候,给它的不是字面量(或者是常量表达式),那么他就没有字面量能够读啦!这时候就只能退而求其次,去读数据区内存中的值啦!这个时候,C++中的const和C语言中的const就同样了。

须要注意的是 sizeof是C和C++中的运算符,并且他的值一般都是在编译时肯定的,能够认为是一个字面常量。

好比:

 

#include <stdio.h>

int main()

{

         const int a = sizeof(int);

         int *pa = (int *)&a;

         printf("pa指向的地址为:%p  a的地址为:%p\n",pa,&a);

         (*pa)++;

         printf("a = %d,*pa = %d\n",a,*pa);

 

         return 0;

}

 

 

 

此外在类中使用const修饰类成员变量的时候也要当心,由于也会退化成C语言中的const

好比:

 

#include <stdio.h>

class A{

         public:

                  const int a;

                  int array[a];

                 

                  A(int i):a(i)

                  {

 

                  }

};

int main()

{

         return 0;

}

 

编译器会报错:

 

 

首先,类只是定义类型的地方,不能再类中初始化成员变量,因此在类定义中写const int a = 10是不对的,const成员的初始化要放在初始化列表中。咱们知道,在对象建立以前才会调用构造函数进行对象的初始化,因此在编译时期咱们根本就不知道a的值。因此把a当作数组的长度是有问题的。

咱们能够这样:

 

#include <stdio.h>

class A{

         public:

                  static const int a = 10;

                  int array[a];

                 

                  A()

                  {

 

                  }

};

int main()

{

         return 0;

}

 

这样定义的a就能够当成一个常量来使用了。注意的是,这时候,a的语句不是声明了,而是定义+初始化,并且C++只容许这种状况能够在类内初始化,就是当变量被 static 和const修饰,且为int类型时。(固然这种状况依然能够在类外定义和初始化,只不事后面就不能用a定义数组的长度了,也会提示数组的长度不肯定)

 

简单总结一下就是:

C语言中const就是一个受限制的变量,读取const时是从数据区的内存读取的(全局从静态去,局部从栈区),能够用指针间接修改其标识的数据区的内存区域,在读取const的值时,也是读取它标识的数据区的内存中的值。

在C++中,const大多数状况下能够当成常量来使用,这是由于,虽然C++也会为const在数据区开辟内存(C++尽可能不这样左),咱们也能够经过指针(或者很是量的引用)来简介修改其标识的数据区的内存区域的值,可是,在读取const时,不是读取数据区的内存区域中的值(也颇有可能根本就没有分配内存),而是读取代码段的字面常量。因此能够达到常量的效果。

最后要警戒C++中const退化成C语言中的const,有两种状况,一种是初始化的const的时候是用变量初始化的,而不是字面常量(或常量表达式)。第二种状况就是const修饰类中成员变量的时候。

给一个建议:若是咱们想用const定义常量,那么咱们就要用字面常量或常量表达式初始化const,并且要将const用static修饰(注意在C++中若是定义的是全局的const,默认为static修饰,可是在类中并非这样的,这也是为何咱们在类中定义常量要用static修饰)。在类中定义常量的时候要同时用cosnt和static修饰,并且尽可能在类的内部进行初始化。

 

若是你以为对你有用,请赞一个吧

相关文章
相关标签/搜索