4.4 字符数组的定义及引用
字符数组的特殊之处在于,它是数组元素为字符的数组。其定义的一般形式和注意事项与之前讲解的一般数组类似,只是其中的类型说明符是char。当然,并不是说类型说明符只能是char,也可以是long、int等,但是由于char型只占用一个字节的大小,使用long型和int型来定义字符数组会造成资源的浪费,因此一般选择使用char型来定义字符数组。
1.一维字符数组
首先通过下面一段代码来看看一维字符数组的定义。
include<stdio.h>
define M 20
void main(void)
{
int i;
long arr_l[M]={'H','e','l','l','o','','W','o','r','l','d','!'};
char arr_c[M]={'H','e','l','l','o','','W','o','r','l','d','!'};
printf("long型字符数组占用的内存大小为:%d\n",sizeof(arr_l));
printf("char型字符数组占用的内存大小为:%d\n",sizeof(arr_c));
return;
}
运行结果:
long型字符数组占用的内存大小为:80
char型字符数组占用的内存大小为:20
在上面的代码中定义了不同类型的字符数组来存放相同的字符,可以看出,它们占用的内存大小相差很大,long型字符数组所占用内存大小是char型数组占用内存大小的4倍。从这点可以看出,选用char型作为数组类型避免了内存空间的浪费。下面通过一段代码来了解字符数组的初始化特点。
include<stdio.h>
define M 20
void main(void)
{
int i;
char arr[M]={'H','e','l','l','o','','W','o','r','l','d','!'};
for(i=0;i<20;i++)
printf("%c",arr[i]);
return;
}
运行结果为“Hello World!”,其中有一些空字符。看看上面代码中定义的arr数组,其数组长度为20,而初始化的字符元素的个数为12,初始化的字符元素个数小于数组长度,编译器在编译过程中将后面没有初始化的数组元素赋值为‘\0’,这也正是打印输出中含有空字符的原因。在打印的时候也可以将数组中的元素‘\0’视为数组结束的标志,例如:
include<stdio.h>
define M 20
void main(void)
{
int i;
char arr[M]={'H','e','l','l','o','','W','o','r','l','d','!'};
for(i=0;arr[i]!='\0';i++)
printf("%c",arr[i]);
printf("\n");
return;
}
运行结果:
Hello World!
这时的输出结果中就不含有任何空字符了,因为巧妙地使用了字符数组中的‘\0’标志。当然,也可以采用字符串常量的方式来对一维字符数组进行初始化,例如:
include<stdio.h>
define M 20
void main(void)
{
int i;
char arr[M]={"Hello World!"};
for(i=0;arr[i]!='\0';i++)
printf("%c",arr[i]);
printf("\n");
return;
}
运行结果:
Hello World!
在对一维字符数组进行定义和初始化的过程中,可以不指定其长度。使用字符常量列表和字符串常量的方式进行初始化的结果是不同的,例如:
include<stdio.h>
void main(void)
{
int i;
char arr_s[]={"Hello World!"};
char arr_c[]={'H','e','l','l','o','','W','o','r','l','d','!'};
printf("采用字符串常量进行初始化的arr_s数组的长度为:%d\n",sizeof(arr_s));
printf("采用字符常量列表进行初始化的arr_c数组的长度为:%d\n",sizeof(arr_c));
return;
}
运行结果:
采用字符串常量进行初始化的arr_s数组的长度为:13
采用字符常量列表进行初始化的arr_c数组的长度为:12
从运行结果发现,采用这两种方式得到的数组长度并不相同,在采用字符串常量对字符数组进行初始化的过程中,在内存中进行存储时会自动在字符串的后面添加一个结束符‘\0’,所以得到的字符数组长度是字符串常量的长度加1;而采用字符常量列表的方式对字符数组进行初始化就不会在最后添加一个结束符,所以利用这种方式定义的字符数组的长度就是字符常量列表中字符的个数。
在对字符数组进行初始化时需要注意,不能先定义字符数组再对字符数组进行一次性初始化,例如:
char arr[];
arr={'H','e','l','l','o','','W','o','r','l','d','!'};
arr={"Hello World!"};
以上这两种初始化方法都是不正确的,因为arr代表的是数组的首地址,不能将常量的值赋给数组地址。当然,数组之间也不可以进行赋值操作,例如:
char arr_a[20]={"safdsafdsa"};
char arr_b[20];
arr_b=arr_a;
这种赋值方式同样是错误的,因为两个数组名都是一个常量地址,不可以对其进行任何修改,先来看下面这段代码。
include<stdio.h>
void main(void)
{
char arr[]={"Hello World!"};
printf("字符数组的首地址为:%d\n",&arr[0]);
printf("字符数组的首地址为:%d\n",arr);
return;
}
运行结果:
字符数组的首地址为:1245036
字符数组的首地址为:1245036
由上面的运行结果发现,数组名就是数组元素的首地址,所以不能够对其进行前面那些错误操作。
虽然&arr[0]和arr的值相同,但是所指的内容并不相同,可以通过下面的代码来进一步加深印象。
include<stdio.h>
void main(void)
{
char arr[]={"Hello World!"};
printf("&arr[0]占用内存大小:%d\n",sizeof(&arr[0]));
printf("arr占用内存大小为:%d\n",sizeof(arr));
return;
}
运行结果:
&arr[0]占用内存大小:4
arr占用内存大小为:13
从运行结果可以清楚地知道,&arr[0]和arr所占用的内存大小并不相同,&arr[0]代表一个地址变量,由于在32位计算机中地址变量是由4字节大小来表示的,因此&arr[0]的大小为4字节,而arr的大小却是13字节,这是因为它代表的是整个数组,在采用字符串常量进行初始化时会在字符串常量的后面添加一个串结束符‘\0’,所以相当于定义一个数组长度为13的字符数组char arr[13],进而可以将arr视为int[13]这种特殊的类型,所以它占用内存是13字节的大小。
接下来通过下面的代码来看串结束符在字符数组输出中的作用。
include<stdio.h>
void main(void)
{
char arr[]={"Hello World!"};
printf("%s\n",arr);
return;
}
运行结果:
Hello World!
修改一下代码,然后再看看运行结果。
include<stdio.h>
void main(void)
{
char arr[]={'H','e','l','l','o','','W','o','r','l','d','!'};
printf("%s\n",arr);
return;
}
运行结果:
Hello World!?↕
由运行结果发现:在输出的字符串中出现了初始化时没有的字符。这是因为在使用%s格式输出的时候,如果想要得到正确的结果,那么字符必须以结束符‘\0’结尾。当然也可以采用%c格式分别输出字符数组中的每个元素。
2.二维字符数组
接下来看二维字符数组,其定义的一般形式为:
char 数组名[常量表达式1][常量表达式2];
以char str[M][N]为例,可以将其视为由两部分组成,str[M]和char[N],相当于定义了一个数组长度为M的一维数组,其中的每个元素又是含有N个字符长度的一维数组。对于多维字符数组,可以采用前面讲解的二维数组的方法来进行分析。接下来了解二维字符数组的使用,下面代码实现的是通过基姆拉尔森计算公式求解输入的某天是星期几。
include<stdio.h>
int main()
{
char weekname[][10]={
"Monday",
"Tuseday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday",
};
int year;
int month;
int day;
printf("请输入年份:");
scanf("%d",&year);
printf("请输入月份:");
scanf("%d",&month);
printf("请输入日期:");
scanf("%d",&day);
if((month==1)||(month==2))
{
month+=12;
year—;
}
int index;
index=(day+2month+3(month+1)/5+year+year/4-year/100+year/400)%7;
printf("%d/%d/%d这一天是%s\n",year,month,day,weekname[index]);
return 0;
}
运行结果:
请输入年份:2011
请输入月份:8
请输入日期:24
2011/8/24这一天是Wednesday
上面定义的二维数组没有指定第一维的长度,这时该长度由初始化的字符串常量的个数来决定。通过数组下标index来决定当前日期是星期几,然后用weekname[index]来输出。值得注意的是,weekname[index]是一个一维字符数组的首地址,其字符数组的长度为10。如图4-8所示为二维数组weekname在内存中的存储方式。
图 4-8 二维数组weekname在内存中的存储方式
由二维数组weekname在内存中的存储方式可知,没有指定初始化值的数组元素被赋值为串结束符‘\0’,在每个字符串常量的后面都有结束符,所以可以使用%s格式来进行输出。