写在前面:学习的第一门语言是Java,之前对C也了解一点,也只是了解一点,在加上长时间没有接触了,基本就只会一个Hello World了。现在由于准备升本考试,不得不从头开始学C。这里从零开始,记录C语言学习点滴。欢迎正在学习C语言的小伙伴一起学习,未来可期,一起加油!
在C语言里边是没有字符串数据类型的,但在平时开发中肯定是少不了字符串操作的。因为字符串都是有字符组成的,所以在C语言中字符串是通过一维字符数组来实现的。
字符串常量就是用一对双引号括起来的字符序列,即一串字符,它有一个结束标志’ \0 '。例如,字符串 “happy” 有6字符组成,分别为 ’ h ‘、’ a ‘、’ p ‘、’ p ‘、’ y ’ 和 ’ \0 ‘,其中5个是字符串的有效字符,’ \0 ’ 是字符串结束符。
字符串的有效长度就是有效字符的个数,例如,“happy”的有效长度是5。
字符串可以放在一维字符数组中。例如:
static char s[6] = { 'h', 'a', 'p', 'p', 'y', '\0' };
数组 s 中存放了字符串“happy”。
字符数组的初始化还可以使用字符串常量,上述初始化等价于:
static char s[6] = { "happy" };
或
static char s[6] = "happy";
将字符串存入字符数组时,由于它有一个结束符’ \0 ',数组长度至少是字符串的有效长度+1。例如,字符串“happy”的有效长度为5,存储它的数组的长度至少应为6。
如果数组长度大于字符串的有效长度+1,则数组中除了存入的字符串,还有其他内容,即字符串只占用了数组的一部分。例如:
char str[90] = "happy“;
上述代码只对数组的前6个元素(str[0] ~ str[5])赋初值,其他元素的值不确定。但这并不会影响对字符串“happy”的处理,由于字符串遇‘ \0 ’结束,所以,数组中第一个’ \0 ’ 前面的所有字符和第一个’ \0 ’ 一起构成了字符串“happy”,也就是说,第一个‘ \0 ’之后的其他数组与该字符串无关。
字符串由有效字符和字符串结束符’ \0 '组成。
将字符串存入数组,除了上面介绍的初始化数组,还可以采用赋值和输入的方法。例如:
static char s[80];
s[0] = 'a';
s[1] = '\0';
采用赋值的方法将字符串 “a” 存入数组s。它等价于:
static char s[80] = "a";
区分 “a” 和 ‘a’,前者是字符串常量,包括 ‘a’ 和 ‘\0’ 两个字符,用一维字符数组存放;后者是字符常量,只有一个字符,可以赋给字符变量。
输入的情况有些特殊,由于字符串结束符 ‘\0’ 代表空操作,无法输入,因此,输入字符串时,需要事先设定一个输入结束符。一旦输入它,就表示字符串输入结束,并将输入结束符转换为字符串结束符 ‘\0’。例如:
#include
//定义输出函数
void pin(char str[]);
int main(){
int i = 0;
//定义字符数组
char str[80] = "a";
//输出字符串
pin(str);
//输入字符串,重新赋值,输入回车结束
while((str[i] = getchar()) != '\n'){
i++;
}
str[i] = '\0'; //将结束符'\0'存入数组
//输出新字符串
pin(str);
return 0;
}
//输出字符串函数
void pin(char str[]){
int i;
for(i = 0; str[i] != '\0'; i++){ //当字符为'\0'时字符串结束
printf("%c", str[i]);
}
printf("\n");
}
先定义一个字符数组赋值为“a”,调用输出函数输出字符串“a”,之后在输入一个新的字符串到字符数组中,回车结束输入,调用输出函数,输出新的字符串。运行结果如下:
在使用字符串处理函数之前,需要引入stdio.h和string.h头文件。
函数输入和输出最常见的就是scanf()和printf()。除此之外字符串还可以用gets()和puts()函数输入输出。
1、scanf(格式控制字符串, 输入参数)
格式控制字符串中使用格式控制说明%s,输入参数必须是字符型数组名。改该函数遇回车或空格输入结束,并自动将输入的数据和字符串结束符 ‘\0’ 存入数组中。例如:
scanf("%s", s); //s为字符型数组
2、printf(格式控制字符串,输出参数表)
格式控制字符串中相应的格式控制说明用%s,输出参数可以是字符数组名或字符串常量。输出 ‘\0’ 结束。例如:
printf("%s", s);
由于在C语言中是使用一维字符数组存放字符串的,因此可以在字符串操作函数中直接使用数组名进行输入与输出。
3、字符串输入函数gets(s)
参数s是字符数组名。函数从输入得到一个字符串,输入回车结束,自动输入的数据和 ‘\0’ 送入数组中。采用函数gets()输入的字符串允许带空格。
实际上函数gets()有返回值,如果输入成功则返回值是字符串第一个字符的地址,如果输入失败则返回NULL。但一般情况下使用gets()主要是为了输入字符串,并不会关心它的返回值。
4、字符串输出函数puts(s)
参数s可以是字符数组名或字符串常量。输出时遇’\0’自动将其转换为’\n’,即输出字符串后换行。同样函数puts()也有返回值,如果成功执行了输出字符串的操作,则返回换行符号’\n’,否则返回EOF。
scanf和printf函数输入与输出案例:
#include
int main(){
//定义一个字符数组
char str[80];
scanf("%s", str);
printf("%s", str);
printf("hello");
return 0;
}
运行结果如下:
printf()函数输出遇 ‘\0’ 结束。但不会自动将其转换为’\n’。所以不会换行。
scanf和printf函数输入与输出案例:
#include
int main(){
//定义一个字符数组
char str[80];
gets(str);
puts(str);
puts("Hello");
return 0;
}
运行结果如下:
puts()函数输出时遇 ‘\0’ 自动将其转换为 ‘\n’,即输出字符串后换行。
字符串复制、连接、和比较以及计算字符串长度的函数,在系统头文件string.h中定义。所以在使用之前,需要先引入string.h头文件。
1、字符串复制函数 char *strcpy(char *s1, char *s2)
该函数把字符串s2复制到s1,直到遇到s2中 ‘\0’ 为止。s1要有足够的空间容纳s2,且s1中的内容被覆盖,函数返回s1。同样可以简化以上函数的表达形式为:strcpy(s1, s2);
参数s1必须是字符型数组基地址,参数2可以是字符数组名或字符串常量。 例如:
int i;
char str1[80], str2[80], from[80] = "happy"; /* 初始化数组from */
strcpy(str1, from);
strcpy(str2, "key");
上述代码中,第三条语句调用了函数strcpy()把from中的字符串复制给str1; 第四条语句把字符串常量“key”复制给str2后,数组str1中存放了“happy”,数组str2中存放了“key”。
2、字符串连接函数strcat(s1, s2)
参数s1必须是字符数组基地址,参数s2可以是字符数组名或字符串常量。 strcat()函数将字符串s2接到字符串s1的后面,此时,s1中原有的结束符 ‘\0’ 被放置在连接后的结束位置上。数组s1的长度要足够大,以便存放连接后的新字符串。例如:
char str1[80] = "hello ", str2[80], t[80] = "world";
strcat(str1, t);
strcpy(str2, str1);
strcat(str2, "!");
先调用函数strcat()连接str1和t,结果放在str1中。如下图:
在调用函数strcpy()将str1中的字符串赋给str2,最后调用函数strcat()连接str2和字符串常量 “!” 后,str2中存放了“Hello world!”。
C语言中不允许使用算术运算符加将字符串数组直接连接。即str1 = str1 + t是错误的。
3、字符串比较函数strcmp(s1, s2)
和函数strcpy()中对参数的要求不同,函数strcmp()中的参数s1和s2可以是字符数组名或字符串常量。函数strcmp()返回一个整数,给出字符串s1和s2的比较结果:
设str1和str2都是字符串,在C语言中,str1 == str2、str1 > str2 和 str1 <= str2比较的是两个字符串的起始地址,而strcmp(str1, str2) == 0、strcmp(str1, str2) > 0 和 strcmp(str1, str2) <= 0 比较两个字符串的内容。
字符串比较的规则是:从两个字符串的首字符开始,依次比较相对应的字符(比较字符的ASCII码),直接出现不同的字符或遇 ‘\0’ 为止。如果所有的字符都相同,返回0;否则,以第一个不相同字符的比较结果为准,返回这两个字符的差,即第一个字符串中的字符减去第二个字符串中的字符得到的差。例如:
strcmp("sea", "sea"); //值为0,说明“sea”, "sea"相等
strcmp("compute", "compare"); //值为('u' - 'a')是个正数,说明"compute"大于"compare"。
strcmp("happy", "z") //值为('h' - 'z')是个负数,说明"happy"小于"z"。
strcmp("sea", "seat") //值为('\0' - 't')是个负数,说明“sea”小于“seat”。
4、字符串长度函数strlen(s1)
参数s1可以是字符数组名或字符串常量。函数strlen()返回字符串s1的‘\0’之前的字符个数。即字符串有效字符的个数(不包括字符结束符’\0’)。
例如:strlen(“happy”)的值是5,strlen(“A”)的值是1。
上面字符串都是以一维字符数组实现的。除了字符数组还可以使用字符型指针去接收字符串常量值。
如果定义一个字符指针接收字符串常量的值,该指针就指向字符串的首字符。这样,字符数组和字符指针都可以用来处理字符串。例如:
char sa[] = "array";
char *sp = "point";
printf("%s ", sa); /* 数组名sa作为输出参数 */
printf("%s ", sp); /* 字符指针sp作为输出的参数 */
printf("%s\n", "string"); /* 字符串常量作为输出的参数 */
运行结果如下:
调用printf()函数,以%s的格式输出字符串时,作为输出参数,数组名sa,指针sp和字符串“string”的值都是地址(起始地址),从该地址所指定的单元开始连续输出其中的内容,直到遇到 ‘\0’ 结束。因此,字符串中其他字符的地址也能作为输出参数。例如:
printf("%s ", sa+2); /* 数组元素sa[2]的地址作为输出参数 */
printf("%s ", sp+3); /* sp+3作为起始地址 */
printf("%s\n", "string" + 1);
运行结果如下:
字符数组与字符指针都可以处理字符串,但两者之间还是有区别的。例如:
char sa[] = "This is a string";
char *sp = "This is a string";
字符数组sa在内存中占用了一块连续的单元,有确定的地址,每个数组元素放字符串的一个字符,字符串就存放在数组中。字符指针sp只占用一个可以存放地址的内存单元,存储字符串首字符的地址,而不是将字符串放到字符指针变量中去。如图:
如果需要改变数组sa所代表的字符串,只能改变数组元素的内容。如果要改变指针sp所代表的字符串,通常直接改变指针的值,让它指向新的字符串。因为sp是指针变量,它的值可以改变,指向其他单元。例如:
strcpy(sa, "Hello");
sp = "Hello";
分别改变了sa和sp所表示的字符串。而sa = “Hello”; 是错误的,因为数组名是常量,不能对它赋值。
定义字符指针后,如果没有对它赋值,指针的值是不确定的,不能明确它指向的内存单元。因此,如果引用未赋值的指针,对指针指向的单元赋值会出现问题。例如:
char *s;
scanf("%s", s);
没有对指针s赋值,却对s指向的单元赋值。如果该单元已分配给其他变量,其值就改变了。
char *s, str[20];
s = str;
scanf("%s", s);
上述定义是正确的。数组str有确定的存储单元,s指向数组str的首元素,并对数组赋值。
为了尽量避免引用未赋值的指针所造成的错误,在定义指针时,可以先将它的初值置为空,如char *s = NULL。
由于初学C语言,上述内容如有错误地方,恳请各位大佬指出!