C语言编程新手常听到的说法之一就是“数组和指针是相同的”。不幸的是,这是一种非常危险的说法,并不完全正确。
一、什么是声明,什么是定义。
注意下面声明的区别:
extern int *x;//声明x是一个int类型的指针; extern int y[]; //第二条语句声明y是个int类型的整形数组,长度尚未确定,其存储在别处定义;问题:我的下面的程序为什么不能运行?有什么错?
定义 | 只能出现在一个地方 | 确定对象类型并分配内存,用于创建新的对象。例如:int a; |
声明 | 可以出现多次 | 描述对象的类型,用于指代其他地方定义的对象(例如在其他文件里的定义:extern int a;) |
1、数组的下标引用
char a[10]="Hello world!"; char c=a[i];编译器符号表具有一个地址8000;
如下图二:
char *p="Hello workd!"; //p[6] char p[10]="Hello world!";//p[6]这两种情况下都能取得字符w,但是其执行路径完全不一样。
文件一:char *p="Hello workd!"; //c=p[6]
编译器符号表示一个p,地址为:4000;
运行步骤一:取地址4000的内容,即4567;
运行步骤二:取i的值,并与4567相加,得到新的地址;
运行步骤三:取c=(4567+i)的内容;
对指针进行下表引用的具体步骤如图三:
4、如果“原先定义为数组,但是我们声明为指针时”,会发生什么?指针和数组的区别?
这时候第一步得到的p[6]实际上就是字符w,但是按照指针的规则,规则是不能改的,无规矩不成方圆。此时编译器却将字符当成了一个指针,将ACSII字符解释为地址很显然是牛头不对马嘴。如果此时程序down掉,你应该额手称庆。否则的话,他可能会污染程序地址空间的内容。在以后可能出现莫名其妙的错误。这也是指针和数组的区别。
所以一个好的习惯是:一定要使声明和定义匹配。
那么开篇提到的问题解决也十分简单:
文件1:int *x;// 声明x是一个int类型的指针,申请一个地址容纳该指针,x本身始终位于同一个地址,但是内容可以不同; 文件2:extern int x; //声明和定义一致。
文件1:int array[100];//array定义分配了100个int空间,array数组的地址不能改变,它总是100个连续的空间,但是里面的内容可以改变; 文件2:extern int array[];//请保持一致;
指针 | 数组 |
---|---|
保存数据的地址 | 保存数据 |
间接访问数据,首先取得指针的内容,把它作为地址,然后从这个地址提取数据; 如果指针有一个下表[I],就把指针的内容加上I作为地址,从中取得数据。 |
直接访问数据,a[I]就是简单的以a+I为地址取得数据。 |
通常用于动态的数据结构 | 通常用于固定数目且数据类型相同的元素; |
相关的函数:malloc(),free() | 隐式的分配和删除 |
通常指向匿名数据,操纵匿名空间 | 自身即为数据名 |
char *p="Hello wirld!"; //次字符串常量被定义为只读。不能修改,否则出现未定义的错误。不要指望为int或float类型的数据分配空间:
int *p=4; //error float *p=3.14;//error;数组也可以用字符串常量来初始化 ,但是由字符串常量初始化的数组可以修改。
char p[20]="Hello world!"; strncpy(p,"beautiful",9);此时数组变为:beautiful world!。
程序的报错和课堂上老师都会告诉我们这样两个概念,左值和右值,下面来看一看它们的区别!
在这个上下文里,x代表的是地址 | 在这个上下文里,y代表的是地址的内容 |
---|---|
X被称为左值(由于它位于“左手边”或表示“地点”) | y被称为右值(由于它位于“右手边”) |
左值在编译器时可知,左值表示存储结果的地方,变量一直存于该地址 | 右值到运行的时候才知道,如无特别说明,右值表示“y的内容” |
左值出现在赋值语句的左边,表示内存空间。数组时左值,但是不能赋值。 | 右值就是具体的内容,可以改变 |