大二开始学习C语言,一晃几年过去了,一直在追赶新技术,底层的东西确总感觉没吃透,今日有空,查阅了许多资料后,有感而发,觉得一定要写下来,万一忘记了,以后也可以查阅。
再论数组和指针:
(1)区分C语言中的声明和定义
声明只是告诉编译器变量的类型和名字,定义才是真真决定内存分配的地方,在一个C程序中,定义只能有一个,而声明却可以有多个。
如文件1中代码:int a[10]; 定义数组,编译器会为数组a分配10个整型的内存
如文件2中代码:extern int a[];声明一个外部的数组,指向文件1中编译器分配了内存的变量a,声明中并不进行内存分配,所以可以不用指定数组大小
如文件3中代码:extern int a[];也声明一个外部的数组,也指向文件1中编译器分配了内存的变量a,声明中并不进行内存分配,所以可以不用指定数组大小
(2)左值和右值的问题
如 a=b;这样一条简单的语句,蕴含着许多微妙之处,这也从另外一个方面体现了C语言中指针的精髓。指针的值(地址)与指针所的指向(地址中放的内容)
这里a出现在等号左边,为左值,代表的含义是a所代表的地址,在编译时就确定了,左值表示存储结果的地方。
这里的b出现在等号右边,为右值,代表的含义是b所对应的地址的内容,右值在程序运行时才能知道。
C语言中有个非常重要的特性“可修改的左值”
如:int a=10;
int b=100;
a=b;
上面代码是能够正确运行的,但是这个特性确对数组不适应,我们知道数组名也相当于一个地址值,也是左值,但它不能作为赋值对象
如: int a[10];
int b[10];
a=b; //错误,a不是可以修改的左值
我们正常的理解等号左边的值应该都是可以赋值的,但是对数组例外,因此C语言中产生了“可修改的左值”这样的特殊术语。
(3) char * s1=“abcdefg”与char s2[]="abcdefg"的区别
在剖析它的区别之前,有必要先分析下C程序的内存分布情况,
上图左边为内存分布描述,中间为一段代码,箭头指向了所在的内存区域,右边为在VS2010里面调试时监视得到的个变量的值,这里着重讨论下char s1[]=“abcdefg”与char *s2="abcdefg"的区别:
s2为一个指针,指向的是字符串的首地址,s1为字符数组,其值也是指向的是字符串的首地址。尽管这两个字符串均为:“abcdefg”,但编译器对它们分配的存储空间有本质的区别,从上图左右边监视的结果就可知,s1的值为0x0012ff40且s1自己地址值也为0x0012ff40,这说明s1分配在栈空间上,且对应的字符串也在栈空间上。
再观察指针变量s2其值为0x0041573c,s2本身的地址值为0x0012ff34,这说明了s2本身这个符号被分配在了栈空间上,但是其指向的内容"abcdefg"却是在文字常量区。
正是这个本质的差别,导致了指针和数组访问方式的不同,如下代码:
char s1[]="abcdefg" ; char c= s1[1];编译器符号表s1具有一个确定的地址值0x0012ff40,运行时由于取偏移量1与0x0012ff40相加,取相加后地址值的内容。
char *s2="abcdefg"; char c=s2[1];编译器符号表s2具有一个确定的地址0x0012ff34,运行时取0x0012ff34中的内容即为0x0041573c,将偏移量1与0x0041573c相加,
取相加后的地址中的内容。
比较上面两种方式,指针多了一个步骤。
指针所对应的字符串是不能被修改的;而数组却可以
也就是说下面代码是错误的:
char *s2="abcdef";
s2[1]='k';
运行时会出现如下图错误:
而通过数组就可以修改,如代码:
char s1[]="abcdef";
s1[1]='k';
导致这个区别的根本原因就在上面的内存分配上,一个在栈区,一个在文字常量区。