一、什么是字符数组
- 字符数组就是建立一连串的字符存储空间用来存储字符,多个字符也就形成了字符串。
- 字符数组中的一个元素用来存放一个字符,数组定义、元素赋值等操作类似于整形数组,但是字符数组的赋值方式多了一种(下面详细讲)。
二、字符数组的定义和赋值
1. 先定义一个数组,再为挨个元素进行赋值
这种定义方式的缺点是挨个赋值的代码量太多,没有太多意义,降低了可读性。
char c[10]; c[0]=’I’; c[1]=’ ’; c[2]=’a’; c[3]=’m’; c[4]=’ ’; c[5]=’h’; c[6]=’a’; c[7]=’p’; c[8]=’p’; c[9]=’y’;
存储的结构如下:
2. 在定义的同时给该字符数组进行初始化
2.1 错误的初始化方式
//错误的初始化方式: char c[10]={’I’,’ ’,’a’,’m’,’ ’,’h’,’a’,’p’,’p’,’y’};
存储的结构如下:
注意: 该赋值的方式是错误的,因为如果不是用字符串的形式命名,需要在结尾加一个’\0’。
2.2 正确的初始化方式
//正确的初始化方式 char c[10]={’c’,’ ’,’p’,’r’,’o’,’g’,’r’,’a’,’m’,0};
存储的结构如下:
重点:
*对比了正确的初始化方式和错误的初始化方式,我们可以从他们对应的存储结构上看到,正确的初始化方式多个’\0’。
为什么少了个’\0’就会差别这么呢?
这就涉及了字符串和字符串结束标志
(1)在C语言中,是将字符串作为字符数组来处理的
(2)关心的是字符串的有效长度而不是字符数组的长度 (有效长度 == 实际长度)
(3)为了测定字符串的实际长度,C语言规定了字符串结束标志 ’\0’
(4)’\0’代表ASCII码为0的字符
(5)从ASCII码表可以查到,ASCII码为0的字符不是一个可以显示的字符,而是一个“空操作符”,即它什么也不做。用它作为字符串结束标志不会产生附加的操作或增加有效字符,只起一个供辨别的标志
其实相对于上面的正确初始化方式,还有一种将整个字符串赋值给字符数组,代码如下:
char c[]={'I',' ','a','m',' ','h','a','p','p','y'}; //如上的定义初始化方式可以写成下面的三种形式 char c[]={"I am happy"}; //可写成 char c[]="I am happy"; //相当于 char c[11]={"I am happy"};
加深类比:
char c[10]={”China”}; //可写成 char c[10]=”China”; //从c[5]开始,元素值均为\0
3. 特别注意!!!
3.1 字符数组和整型数组一样不支持隔语句初始化!!!
代码如下:
//此种定义了整型数组后,对他再赋值的方式是错误的!!! int a[4] = {1}; a = { 1,2,3,5,6 }; //此种定义了字符数组后,对他再赋值的方式是错误的!!! char c[5] = { 'a' }; c = { 'a','b','c' };
三、字符串处理函数
在程序中往往需要对字符串作某些操作处理,两个字符串连接、两个字符串进行比较等在C函数库中提供了一些字符串处理函数,使用很方便。
函数 | 作用 |
gets(字符数组) | 从终端输入一个字符串到字符数组 |
puts(字符数组) | 将一个字符串(以’\0’结束的字符序列)输出到终端 |
strcat(字符数组1,字符数组2) | 连接两个字符数组中的字符串把字符串2接到字符串1的后面 |
strcpy(字符数组1,字符串2) | 将字符串2复制到字符数组1中去 |
strcmp(字符串1,字符串2) | 比较串1和串2。若串1=串2,则函数值为0;若串1>串2,则函数值为一个正整数;若串1<串2,则函数值为一个负整数 |
strlen(字符数组) | 测试字符串长度 |
strlwr(字符串) | 将字符串中大写字母换成小写字母 |
strupr(字符串) | 将字符串中小写字母换成大写字母 |
3.1 gets(字符数组)
其一般形式为:strlen (字符数组);它是测试字符串长度的函数,函数的值为字符串中的实际长度。如: gets(str); Computer↙
3.2 puts(字符数组)
puts函数(输出字符串函数)其一般形式为: puts (字符数组);作用是将一个字符串输出到终端,如: char str[20]=”China”;puts(str); 输出China。
3.3 strcat(字符数组1,字符串2)
strcat函数(字符串连接函数)其一般形式为:strcat(字符数组1,字符数组2);其作用是把两个字符串连接起来,把字符串2接到字符串1的后面,结果放在字符数组1中。
char str1[30]=”People”; char str2[]=”China”; printf(”%s”, strcat(str1,str2)); //输出:PeopleChina
3.4 strcmp(字符串1,字符串2)
比较的结果由函数值带回
如果字符串1=字符串2,则函数值为0
如果字符串1>字符串2,则函数值为一个正整数
如果字符串1<字符串2,则函数值为一个负整数
3.5 strlen(字符数组)
char str[10]=”China”;
printf(”%d”,strlen(str));
输出结果是5
也可以直接测试字符串常量的长度
strlen(”China”);
3.6 strlwr(字符串)和strupr(字符串)
其返回值是一个字符指针类型
(1)其一般形式为strlwr (字符串);函数的作用是将字符串中大写字母换成小写字母
(2)其一般形式为strupr (字符串);函数的作用是将字符串中小写字母换成大写字母
四、字符数组和字符指针
4.1 字符型指针
字符型指针用char*定义,它不仅可以指向一个字符型常量,还可以指向一个字符串。
char str[] = "I am happy"; char* c = str;
此时c变量就是存储了str数组的第一个元素的地址,代码如下:
4.2 字符指针作函数参数
(1)如果想把一个字符串从一个函数“传递”到另一个函数,可以用地址传递的办法,即用字符数组名作参数,也可以用字符指针变量作参数。
(2)在被调用的函数中可以改变字符串的内容
(3)在主调函数中可以引用改变后的字符串
例子如下:
例子1:有一字符数组a,内存有字符串"I am a boy.",要求把该字符串复制到字符数组b中。(要求用函数完成实现)
例子2:有字符串a=“I am a teacher.”, 字符串b=“You are a student.”,要求把字符串b连接到字符串a的后面。即字符串a的内容为“I am a teacherYou are a student.”
4.3 调用函数时实参与形参的对应关系
4.4 字符指针和字符数组的差别
虽然用字符数组和字符指针变量都能实现字符串的存储和运算,但它们二者之间是有区别的,不应混为一谈。主要有以下几点:
(1)字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),决不是将字符串放到字符指针变量中。
(2)赋值方式:对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值:
char str[14];
str=”I love China!″; (×)
而对字符指针变量,可用下面方法赋值:
char *a;
a=”I love China!″;
但注意赋给a的不是字符,而是字符串第一个元素的地址。
(3)对字符指针变量赋初值:
char *a=”I love China!″; 等价于 char *a;a=”I love China!″;
而对数组的初始化:
char str[14]={”I love China!″};不能等价于char str[14];str[]=”I love China!″;
无论是什么数组都不支持动态赋整条数据,例如int a[];a={1,2,3,4},此赋值方式是错误的!!!
(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址。
//第一种 char *a; scanf(“%s”,a); //错 char *a,str[10]; //第二种 a=str; scanf (“%s”,a); // 对
说人话就是,指针变量是用来存放地址的,而字符数组是在内存开辟了一个空间来存储字符的,两者的性质就不一样。scanf函数输入的是一个字符串是要放到一个存储空间里面,所以直接用一个存放指针的变量来存放一组字符串就不行了
(5)指针变量的值是可以改变的。
例如:
#include <stdio.h> void main() { char *a="I am Chinese!"; a=a+6; printf("%s\n",a); }
其中的char *a=“I am Chinese!”;a=a+6; 不能改成char a[]=“I am Chinese!”;a=a+6;。这里可以理解为数组名是一辆车上的司机,而字符指针变量是一个导游,元素是乘客。导游可以到处走,而司机不可以到处走。
(6)对字符数组可以用下标法和地址法引用数组元素(a[5],(a+5))。如果字符指针变量p=a,则也可以用指针变量带下标的形式和地址法引用(p[5],(p+5))。
(7)字符数组中各元素的值是可以改变的(可以对它们再赋值),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再赋值)。如:
char a[]="House"; char *b="House"; a[2]='r'; // 对 b[2]='r'; // 错
因为他们存储的位置不同,传递给字符型指针的字符串是存储在常量池里面,常量池里面的数据不可以修改。
五、多维数组的字符指针
首先得明确一个概念数组名a的意思是指向该二维数组的第一行,而 a+1,a+2表示的是第二行和第三行。如果加了一个*,如:*a,*(a+1)+1,*(a+2)+2,分别表示第一行的第一列、第二行的第二列、第三行的第三列。其中的数组名即代表第一行,也代表了第一行的第一列个元素的地址
表现形式 | 含义 | 地址 |
a | 一行一列元素的地址,也是第一行的地址 | 1000 |
*(a+1)+1 | 第二行第二列的元素地址 | 1004 |
*( *(a+1)+1) | 第二行第二列的元素值 | 值:5 |
特别注意:
(1)如果是用指针变量去读取数据,就只需一个*号就可以读取数据,因为指针与数组名不同,他就只是指向地址,所以只需要考虑地址位置就可以读取地址里面的值,而数组名不一样,他代表了一个数组的层次关系,它是一个数组的队头。例如二维数组中,既要表清楚行和列,所以需要a表示第一行,*a表示第一行第一列。
(2)用数组名去取值
六、多重指针