C语言返璞归真之修改const常量和使用变量定义数组长度

 http://hi.baidu.com/deep_pro/blog/item/f793d655f9a7baceb645ae08.html

 

首先要说,c语言是对汇编的简单封装,c语言的标准不能囊括所有程序员尝试的奇怪行为,
标准之外的事情,那就没准了
所以这里说的两种情况也是编译器相关的,如同i+++i 和++i++ 一样是没有实际意义的东西
可以拿来忽悠忽悠菜鸟,装成一副大牛的样子
工作还是要守规矩,不要写天书,没事别跟编译器较劲,稳定压倒一切

网上流传一种修改常量的说法

#include <stdio.h>
int main(int argc, char* argv[])
{
const long lng=10;
long *p1=(long *)&lng;
long *p2=p1;
*p1=1000;
printf("%d %d %d\n",lng,*p1,*p2);
printf("%p %p %p\n",&lng,p1,p2);
return 0;
}


第一行打印出来是 10 1000 1000
有人说这表示const常量能被修改,有人说这只能算半修改,原来的常量值根本没变
看第二行打印:
0xbfbc0b58 0xbfbc0b58 0xbfbc0b58
TMD是同一个地址,既然是同一地址,为什么打出来不一样的值?
直接打印地址所存的值
printf("%d\n",*((long *)0xbfbc0b58));
得到1000

就是说, long *p1=(long *)&lng;
表面上给了他们相同的指针(这个const常量在函数体内),但是const常量是保存在数据段(只读)的
这个地址0xbfbc0b58查内存文件得知是属于堆栈段
也就是说,虽然地址相同,但是访问时直接使用常量的符号名访问,读取的是数据段
通过指针访问,读取到的是堆栈段

以上代码,如果把const long lng=10;放在全局变量位置,
那就会产生一个段错误,在windows下就是一个经典的错误框:xxxx内存不能write
此时 long *p1=(long *)&lng; 就真正得到了数据段的指针,const常量还是不能修改的

const提供了一种保护机制,能在编译阶段阻止一些mud的举动,但是变成汇编成二进制之后就不复存在







然后再来吹吹如何使用变量定义数组长度
这个每个人第一次学C语言开始就知道是错误的
因为变量值在编译阶段是未知的,如果编译阶段就要决定数组大小时,变量自然是不行的(const常量试了也不行)
可是这样的规定也会封杀编译时不需要决定数组大小的情况
比如定义一个全局的大数组并赋初值,再定义一个局部的大数组,比较一下生成的可执行文件大小就有所发现

局部数组存在于堆栈段(堆栈只有栈的意思,没有堆的意思)中,不会在编译阶段就需要把它的大小确定下来,
所以局部数组不能用变量定义大小的话总是一种遗憾,缺乏一点灵活度
当然,如果给了这点灵活度,往往会带来更大的问题,使用者会
char str[i];之后,改变了i的值,却仍然认为str的大小是最初的i。

所以VC是封的死死的,但是gcc加上 -std c89 和 -ansi 选项之后仍然能通过以下代码,
姑且认为Linux程序员需要更多的智慧

#include <stdio.h>

int main(int argc, char* argv[])
{
int i=100;
char p[i][i];
return 0;
}

进程堆栈的大小是有限的,Linux默认是8MB,不要定义巨大的局部数组。如果需要超大空间存数据,应该向数据段或堆段要;如果要的很频繁,最好向数据段要,同时注意程序的可重入性。

你可能感兴趣的:(C语言返璞归真之修改const常量和使用变量定义数组长度)