scanf("%s", a); printf("%s", a);
;puts(str)
,也可用printf("%s", str)
;字符串数组的输入推荐str = gets()
,也可用scanf("%s", str)
。注意,这些函数都只能处理那些以\0
结尾的字符串;数据在内存中存放,为了访问数据的内容,需要知道数据的内存地址。普通的变量本身就和内存地址关系密切,通过变量名即可快速获取变量所存储的数据。将多个数据连续存放到一块内存中时,为了随机访问多个数据中任意一个,就必须能快速获取数据的内存地址。如果多个数据所占的内存大小不固定,那么数据的内存地址就无法准确计算。而如果数组中每个数据都占用相同大小的内存空间,在数据连续存放的前提下,根据第一个数据的内存地址以及需要访问的数据在第几个位置就可以计算出需访问数据的内存地址,进而可获得数据的值。
数组的定义不是在计算机执行程序的时候完成的。
[]
借助于下标运算符[]
(subscript operator),可以获取数组中单独的元素。下标运算符需要两个操作数,最简单且最常见的情况下,左操作数是一个数组名称,而另一个操作数是一个整数,即a[2]
中,数组名a
为[]
的左操作数,而2
为[]
的右操作数。
运算符[]
的左操作数必须是一个指针类型表达式,它不一定需要是数组名称,而其右操作数必须是整数(因为涉及地址计算)。表达式iArr[x]
等价于*(iArr+x)
,即获取指针iArr
的内存地址移动x
个位置后的内存中存储的内容。
int iArr[2][3]={0,1,2,3,4,5}; // 定义整型二维数组
int **pp = iArr; // 出现编译错误:`error C2440: “初始化”: 无法从“int [2][3]”转换为“int **” `。说明,二维数组名不是二级指针!
int (*pArr)[3]
;iArr
指向一个数组,iArr+1
指向下一个数组,系统会根据指针的类型自动计算地址增量。在本例中,每个一维数组有3个整型数据,假设每个整型数占据4字节,那么下一个数组的地址为当前数组的地址往高地址增加12个字节的地址处。iArr
为“指向数组的指针”,因此 iArr[i] == *(iArr+i) 取出数组,这意味着iArr[i]
是一个数组(注意到数组名表示数组,因此iArr[i]可看成数组名)。iArr[i]
既是数组名,也是该数组首元素的地址iArr[2][3]
的每一行都可以看成一个一维数组,数组名分别为iArr[0], iArr[1], iArr[2]
,每个数组都有三个元素iArr[0][3], iArr[1][3], iArr[2][3]
(注意把iArr[0], iArr[1], iArr[2]
整体看成数组名),因此iArr[0], iArr[1], iArr[2]}
是地址;iArr
,虽然iArr[0]
和iArr
的值都和数组首元素地址相同,但二者指向的对象不同,iArr
为整型数组指针,iArr[0]
为数组名,也是数组首元素的地址,因此它的指针类型为整型指针。指针指向的对象不同,则指针自加 $1$ 时地址的增量也不同:
iArr[0]
的数组的首元素,对其进行*
算,得到的是一个数组元素值,即iArr[0]
数组首元素值,因此,*iArr[0]
与iArr[0][0]
是同一个值;iArr+i
指向的是第i
个行数组,即指向iArr[i]
。对iArr进行“*”运算,得到的是一维数组iArr[0]
的首地址,即*iArr
与iArr[0]
是同一个值。int *p;
定义指针p
时,p
的指向是一个int
型数据,而不是一个地址,因此,用iArr[0]
对p
赋值是正确的,而用iArr
对p
赋值是错误的。sizeof(iArr)
的时候,则不会产生这样的转化,所以iArr
占用内存该多大就多大;int iArr[3][3] = {1,2,3,4,5,6,7,8,9}; // 声明一:定义二维数组
int *piArr[3] = {iArr[0], iArr[1], iArr[2]}; // 声明二:定义一个指针数组
iArr[0], iArr[1], iArr[2]
。那么:iArr[0], iArr[1], iArr[2]
是整型地址吗?
iArr
为“指向数组的指针”,而iArr[i] == *(iArr+i)
取出了指针指向的数组,也就是iArr[i]
是一个一维数组,它既是数组名,也是数组首元素的地址。由于数组元素为整型数,故iArr[i]
为整型指针;*(piArr[i]+j)
:piArr[i]
为指针数组piArr
中第i+1
个元素,代表二维数组第i+1
个行数组首元素地址,piArr[i]+j
获取第i+1
行数组第j
个元素的地址,因此*(piArr[i]+j)
取得二维数组的元素iArr[i][j]
;piArr[i]
是指针数组piArr
中的元素i
,它等价于*(piArr+i)
。因此,*(piArr[i]+j)
等价于*(*(piArr+i)+j)
;iArr
(二维数组的数组名)找到二维数组每一行首元素的地址(这些元素的位置比较特殊),然后把这些地址存到一个指针数组中。事实上,你可以把二维数组其他元素的地址保存到指针数组中,并不限于每一行首元素的地址。相对有意义的是,可以根据一定规律把数组切分成更多的片段。但首元素地址相比其他元素的地址更容易获得,同时也把数组等分,更有现实意义。int k, iArr[k]
是错误的;strlen(str)
为有效字符的长度,即字符串长度不包括\0
。因此,字符串数组占用的内存长度为:字符串长度 + 1;\0
标记结尾的字符串都不能使用标准的字符串处理函数。比如以char ch[5] = {'C','H','I','N','A'}
定义的字符串,它的末尾无标记字符\0
;iArr[2][3]
并不存在iArr[1]
这样的元素;iArr, iArr[0], *(iArr+0), *iArr, &iArr[0][0]
虽然地址相等,但仍有不少差别:
iArr[0], *(iArr+0), *iArr
是等价的:首先显而易见的是*(iArr+0)
和*iArr
是一样的,其次根据下标运算符[]
的意义可知:iArr[0]
等价于*(iArr+0)
;这三者和一维数组的数组名等同,都指向一维数组首元素的地址。iArr
是一个指向一维数组的数组指针,虽然它的地址和iArr[0]
是相同的,但iArr
指向的数据类型不同,从而在执行自加 $1$ 运算时地址的增量也不同。&iArr[0][0]
先获取iArr[0][0]
的值(也就是二维数组第一个元素的值),再使用取地址运算符&
获取第一个元素的地址。该地址和第一个一维数组iArr[0]
的首元素地址相同,地址指向的类型也相同!