注意:和普通变量一样,定义的未初始化全局数组和静态局部数组存放在BSS段,定义的已初始化全局数据和静态局部数组存放在静态数据区。而函数内定义的局部数组(不包括静态数组)存放在栈空间中。
“长度表达式”可以包含常量,但不能包含变量。也就是说,C语言不允许对数组的大小做动态定义,即数组的大小不依赖于程序运行过程中变量的值。
由于const定义的常量具有变量的性质,这类常量也不能作为定义数组的“长度表达式”,例如“const int NUM=10;int a[NUM];”在C语言中编译错误,在C++中编译正确。
注意,在定义数组时“长度表达式”可以包含常量,但是不能包含变量。
对于局部数组,若没有进行初始化,其所有元素值为垃圾值,若初始化时仅对部分元素赋了初值,其余元素取默认值(数值型为0,字符型为空字符);
除了初始化,数组名不能作为左值,因为,它是一个表示首元素地址的常量。例如定义 int a[3]后执行a={1,2,3}是错误的
在C语言中,规定数组名代表数组的首元素的地址,也就是说,数组名就具有地址的概念,而且是一个地址常量,因此可以将数组名(即在内存中存放该数组的首地址)赋给指针。
特别注意:数组名a代表的是该数组首元素的地址,而不是数组a的首地址,a与&a[0]的含义相同,如“a=&a[0]”返回真,是正确的比较。
&a表示整个数组的首地址,但是在执行语句printf("%x,%x\n",a,&a);时输出的a和&a是相同的,那么两者有什么区别呢?其中区别主要是步长的不同,a+i=a+isizeof(int),其步长为sizeof(int),即一个数组元素的长度,而&a+i=&a+i(a数组的大小),其中步长为a数组的大小。尽管a和&a的值相同,但表示不同的含义,"&a==a"是错误的比较。
为了清楚地说明&a和a的差别,这里采用地址分级的概念,数组元素的地址称为一级地址(其值可以赋给一级指针),而一级地址的地址称为二级地址,以此类推,不同级别的地址是不能比较的,因为对应的步长不同。一维数组名a是一级地址,而&a中加了一个取地址运算符升级为二级地址。
对于一维数组元素,char s[]=“abc”;
定义的是一个字符数组,所以相当于定义了一些空间来存放“abc”,如果数组s是已初始化的全局数组或者静态局部则存放在静态数据区,如果它是在函数内部定义的局部数组则存放在栈空间中。
对于*p="abc"中的abc是常量,存储在静态局部数据区域。
另外还要特别强调的是*(a+i)a[i] *(p+i) 和p[i]是等价的
p++=1的功能是将p所指的元素修改为1,然后让p指向下一个元素;
++p=1的功能是先执行++p再执行p,先让p指向下一个元素,并将这个元素的值修改为1.
(p)++的功能是先指向p。然后再将p的值加1;
指针变量的sizeof:指针变量的sizeof等于计算机内部地址总线的宽度,所以在32位计算机中一个指针变量的返回值必定是4(注意结果是以字节为单位)
数组的sizeof,对数组做sizeof运算等效于对其元素类型做sizeof的结果乘以数组元素的个数,即sizeof返回整个数组在内存中占用的内存字节数。
char a[]="abc";
int b[3];
sizeof(a); //结果为4,字符末尾还存在一个'\0'结尾符
sizeof(b);//结果为3×4=12(依赖于int的长度)
sizeof的副作用:
sizeof是运算符,跟加减乘除的性质是其实是一样的,在编译的时候就开始执行了,而不是在程序执行时才执行。
sizeof(i++);
其中i++并不执行,因此i++的作用被消除了。
二维数组元素的引用方式如下:
数组名[下标表达式1][下标表达式2]
其中,下标表达式可以是整型常量或整型表达式。注意不能为变量或者const
对于给了数组部分元素赋初值的,其余自动赋值为0;
int a[3][4]={{1,2,3,4},{5,6,7,8}};
注意允许行,但是不能允许省略列,二维数组在初始化时候必须指定列长度。
由于二维数组a[3][4]也就相当于a={a[0][4],a[1][4],a[2][4]},
每个一维数组元素又包含有4个元素。这种降维的思路可以扩展到3维和四维以上的数组。a数组有三行,将它们看成3个一维数组元素,即a={a[0],a[1],a[2]},每个一维数组元素又含有4个元素。这种降维的思路可以扩展到3维或4维以上。
数组名a代表的是该二维数组首元素a[0]的首地址,即a与&a[0]的含义相同.也就是说a与&a[0]的含义相同,“a=&a[0]”返回真,是正确的比较。其中a[0]又和&a[0][0]的含义相同,所以a==&&a[0][0].
二维数组名是一个二级地址(例如**a的结果为a[0][0]),三维数组名是一个三级地址。
&a是整个二维数组的首地址,为三级地址,所以“a==&a”的比较是错误的。
其实,我们只要记住数组名a代表的是首元素的地址,二维数组降解为一维数组来看。
此外变址运算符号“【】”相当于*(+)
所以a[i][j],*(a[i]+j), *(*(a+i)+j)
三者相同都表示第i行j列元素
可以把二维数组看成是以一维数组作为元素的二维数组
#include
#include
void main()
{
int i;
int a[3][2]={{0,1},{2,3},{4,5}};
int *p=a[1];
for (i=0;i<2;i++)
printf("%d",*p++);
}
输出2 3
若有定义int a[3][4] ,不能表示a[1][1]的是()
A、*(&a[0][0]+5)
B、*(*(a+1)+1)
C、*(&a[1]+1)
D、*(a[1]+1)
正确答案C
C选项表示的是a[2][2]
字符数组的定义
由于C语言中没有直接提供字符串类型,字符串被定义为一个字符数组。例如:
char str[10];
一个字符数组的字符构成一个字符串,这个字符串结束标志是ASCII码为0的字符,即空字符,表示成‘\0’。例如上面定义的str字符数组最多可以存储9个字符,还剩一个字符位置用来存放结尾符。
字符数组初始化的两种方式:
char s[5]={'A','B','C','D','\0'}
char s[5]={"ABCD"}
注意:
与普通数组一样,字符数组名是地址常量,其值为数组本身在内存中存放区域的首地址,即字符串中第一个字符的存储地址,所以赋值语句s="ABCD"是错误的。
初始化少于定义的数组元素的个数,这时候被赋予空格符 (空格符不同于空字符,空字符的ASCII 码为0,空格符的ASCII码为32)。
C语言中常用的字符串处理函数:
字符串数组
字符串数组的定义:它的每个元素都是一个字符串。字符串数组是二维数组。
S【0】表示数组中第一个字符串首元素的地址,为一维地址。
注意S【0】=“ABCD”这种赋值是错误的,因为S【0】是一个地址常量,不允许对它赋值;
而S[0][0]="ABCD"也是错误的,因为S【0】【0】是字符,不是字符串。
使用scanf或者gets函数赋值
使用scanf或者gets函数只能给一个字符串赋值,即每次只能给字符串数组中的一个一维地址的元素赋值。
scanf("%s",name[0]); //输入的字符串不能含有空格
gets(name[0]); //输入的字符串可以含有空格
使用标准字符串函数赋值:
使用标准字符串函数strcpy等实现字符串的复制等,例如:
strcpy(name[0],"Smith");
strlen函数用于求一个字符串的实际长度,从开始字符到遇见第一个’\0’,如果只定义没有给它赋予初值,这结果是不定的,它会从首地址一直找下去,知道遇到‘\0’停止。sizeof返回的是变量定义后所占内存的字节数,不是实际长度。
例如, char a[5],strlen(a)的结果是不定的,因为数组a没有赋初值,而sizeof(a)的结果为5
strlen的结果是要在执行时才能计算出来,是用来计算字符串的长度,不是类型占内存的大小;而sizeof不能返回动态分配的空间大小。
当多个基类型相同的指针变量集合成数组时,就形成了指针数组。指针数组时指针的集合,它的每个元素都是一个指针变量。