C Primer Plus 学习笔记 第11章 字符串和字符串函数

现在进度已经过半,接下来这一章虽然很长,但是干货不算太多。

全书共分17章,这是关于本书第11章内容的博客。本章介绍了关于字符串的内容,并介绍了很多关于字符串的函数。博客的目录和书上目录是相似的。此系列博客的代码都在Visual Studio 2022环境下编译运行。

我目前大一刚刚结束,水平有限,博客中若有错误或者总结不到位的地方也请见谅。

目录

11.1 表示字符串和字符串I/O

11.1.1 在程序中定义字符串

11.1.1.1 字符串字面量(字符串常量)

11.1.1.2 字符串数组和初始化

11.1.1.3 数组和指针

11.1.1.4 数组和指针的区别

11.1.1.5 字符串数组

11.1.2 指针和字符串

11.2 字符串输入

11.2.1 分配空间

11.2.2 不幸的gets()函数

11.2.3 gets()的替代品

11.2.3.1 fgets()函数

11.2.3.2 gets_s()函数

11.2.3.3 s_gets()函数

11.2.4 scanf()函数

11.3 字符串输出

11.3.1 puts()函数

11.3.2 fputs()函数

11.3.3 printf()函数

11.4 自定义输入/输出函数

11.5 字符串函数

11.5.1 strlen()函数

11.5.2 strcat()函数

11.5.3 strncat()函数

11.5.4 strcmp()函数

11.5.5 strcpy()和strncpy()函数

11.5.6 sprintf()函数

11.5.7 其他字符串函数

11.6 命令行参数

11.7 把字符串转换为数字


11.1 表示字符串和字符串I/O

字符串十分常用,C库提供了很多专门用于处理字符串的函数。

下面程序有表示字符串的几种方式。

#include<stdio.h>
#define MSG "I am a symbolic string constant."
#define MAXLENGTH 81
int main(void)
{
	char words[MAXLENGTH] = "I am a string in an array.";
	const char* pt1 = "Something is poingting at me";
	puts("Here are some strings:");

	puts(MSG);
	puts(words);
	puts(pt1);
	words[8] = 'p';
	puts(words);
	return 0;
}

11.1.1 在程序中定义字符串

11.1.1.1 字符串字面量(字符串常量)

用双引号括起来的内容称为字符串字面量,也叫字符串常量。编译器会自动在结尾加入\0字符。双引号中的字符和编译器加入的\0字符都作为字符串存储在内存中。

如果字符串常量之间没有间隔,或者用空白字符分隔,C语言会将其视为串联起来的字符串常量。

如果在字符串内部使用双引号,必须在双引号前面加上一个反斜杠。

字符串常量输入静态存储类别,该字符串只会被存储一次,在整个程序的生命周期内存在。用双引号括起来的内容被视为指向该字符串存储位置的指针。

11.1.1.2 字符串数组和初始化

定义字符串数组时,必须让编译器知道需要多少空间。

可以用足够空间的数组存储字符串,初始化时等号后面是字符串,这样比标准形式的初始化方便很多。

指定数组大小时数组的元素个数至少比字符串长度多1(需要容纳空字符)。也可以让编译器确定大小(仅限于初始化时)。

字符串数组名也是首元素的地址。

也可以用指针表示法创建字符串。创建一个char类型指针,初始化时等号后接一个字符串。这种情况由字符串本身决定存储空间。

用数组和指针这两种表示字符串的方式并不完全相同。

11.1.1.3 数组和指针

数组形式:字符串存储在静态存储区中,但是程序开始运行时才会为数组分配内存,才将字符串的内容拷贝到数组中。此时字符串有两个副本,一个存放在静态内存中,一个存储在数组中。此后对于数组名可以进行关于指针的操作。

指针形式:字符串也存储在静态存储区中,开始执行程序后为指针留出一个存储位置,将字符串的地址存储在指针中。

字符串常量被视为const数据,如果指针指向字符串则不能修改内容,但是可以指向其他字符串。如果把字符串拷贝给数组就可以修改数组中的内容。

初始化数组把静态存储区的字符串拷贝到数组中,初始化指针只把字符串的地址拷贝给指针。

11.1.1.4 数组和指针的区别

建议把指针初始化为字符串常量时使用const限定符。

如果打算修改字符串,就不要用指针指向字符串常量。

11.1.1.5 字符串数组

访问多个字符串可以使用指向字符串的指针数组和char类型的二维数组。

二维数组内存的效率相对低,但是指针数组指向的内容不能修改。

11.1.2 指针和字符串

字符串绝大多数操作都是通过指针完成的。给指针初始化为字符串时实际上拷贝地址,没有拷贝具体内容。

11.2 字符串输入

要把字符串读入程序,首先要预留足够的空间,然后用输入函数获取内容。

11.2.1 分配空间

在输入字符串前为字符串预留空间时,要使用数组(不要用指针),并声明数组的大小。

11.2.2 不幸的gets()函数

gets()函数读取整行输入,直至遇到换行符,然后丢弃换行符,存储其余字符,并在末尾添加空字符以组成一个字符串。

gets()函数只有一个参数,就是一个存储输入的地址。

gets()函数虽然简单易用,但是无法检查数组是否容得下输入行。如果输入字符串过长,会导致缓冲区溢出,这可能导致一些意外。因此gets()函数不安全,有一定的隐患。

C11标准废除了gets()函数。

11.2.3 gets()的替代品

11.2.3.1 fgets()函数

fgets()函数有三个参数,第一个还是存储输入的地址,第二个参数是读入字符的最大数量,第三个参数是要读入的文件(从键盘输入就是stdin)。

如果第二个参数的值是n,函数最多读取n-1个字符,并且遇到换行符会停止。fgets()会将遇到的换行符存储在字符串中。最后会加上空字符。

fgets()返回指向char的指针,如果一切顺利,返回的地址与传入的第一个参数相同。如果读到末尾会返回空指针。空指针不会指向有效数据,C语言中通常用NULL表示。如果函数读入数据时出现错误,也返回空指针。

fgets()函数对换行符的处理可能带来一些麻烦,并且可能有多余的输入留在缓冲区,这也可能导致一些问题。

11.2.3.2 gets_s()函数

C11标准新增了可选的gets_s()函数,gets_s函数有两个参数,第一个还是存储输入的地址,第二个是存入字符的最大数量。gets_s()函数和fgets()函数第二个参数都是限制读入字符数量。

gets_s()函数也是丢弃换行符,如果读到最大字符数还没有读到换行符,会进行一些其他操作。

如果目标区域装得下输入行,fgets(),gets(),gets_s()都可以,但其中fgets()会保留换行符。如果输入行太长,gets()函数有安全隐患,gets_s()函数会进行一些操作,需要知道其他知识,fgets()函数最容易使用。fgets()是处理这类情况的最佳选择。

11.2.3.3 s_gets()函数

作者编写了s_gets()函数处理字符串输入。读取整行输入并用空字符代替换行符,或读取一部分输入丢弃其余部分。此书后面章节的很多程序都包含这个函数。代码如下:

char* s_gets(char* st, int n)
{
	char* ret_val;
	int i;
	ret_val = fgets(st, n, stdin);

	if (ret_val)
	{
		while (st[i] != '\n' && st[i] != '\0')
			i++;
		if (st[i] == '\n')
			st[i] = '\0';
		else
			while (getchar() != '\n')
				continue;
	}
	return ret_val;
}

11.2.4 scanf()函数

scanf()函数用%s读取字符串,scanf()函数读取时从第一个非空白字符作为开始,到下一个空白字符结束。如果指定了字段宽度,那么遇到空白字符或读取数量等于字段宽度时停止。

scanf()一般用来读取一个单词,读取并转换混合数据类型为标准形式。

11.3 字符串输出

11.3.1 puts()函数

puts()函数很好用,只要把字符串的地址作为参数即可。

puts()在显示字符串时会在末尾输出换行符。

puts()遇到空字符停止输出,如果没有空字符会输出其他内容(VS经典烫烫烫烫烫)。

11.3.2 fputs()函数

fputs()函数有两个参数,第一个是字符串的地址,第二个是要输出的文件(输出到屏幕上用stdout)。

fputs()不会在结尾添加换行符。

11.3.3 printf()函数

printf()把地址作为参数,使用%s转换说明。

printf()也不会自动输出换行符,需要加\n。

11.4 自定义输入/输出函数

不一定要使用C库中的标准函数,可以使用getchar()和putchar()自定义函数。

11.5 字符串函数

C库提供了多个处理字符串的函数,原型放在string.h头文件中。下面的函数不特殊说明都是在string.h头文件中,

11.5.1 strlen()函数

strlen()函数统计字符串的长度。该函数唯一的参数是一个指向字符串的指针。

11.5.2 strcat()函数

strcat()函数用于拼接字符串。该函数接受两个字符串作为参数,把第二个字符串附加在第一个字符串的结尾(包括空字符)。拼接后的字符串作为第一个字符串,第二个字符串内容不变。

strcat()函数是char*类型,返回第一个字符串的地址。

11.5.3 strncat()函数

strcat()函数不检查第一个数组是否能容纳第二个字符串,可能会导致溢出。

strncat()函数前两个参数和strcat()一样,第三个参数是最大添加字符数。在达到最大添加字符数或遇到空字符后停止。strncat()函数最后一定添加空字符。

11.5.4 strcmp()函数

strcmp()函数可以用来比较两个字符串的内容。如果两个字符串完全相同,返回0。如果比较字符串不同,那么返回非零值。如果第一个字符串在第二个字符串前,返回一个负值(VS为-1),如果第一个字符串在第二个字符串后,返回一个正值(VS为1)。前后的判断是依次比较,直至遇到第一个不相同字符为止,第一个不相同字符大的字符串在后面。strcmp()比较的是ASCII码值,且比较所有字符。

strncmp()函数与strcmp()函数类似,前两个参数相同,第三个参数是比较的最大字符数。在比较字符数量达到最大字符数或遇到空字符后停止比较。比较部分都相同返回0,第一个在第二个前返回负值,第一个在第二个后返回正值。

11.5.5 strcpy()和strncpy()函数

如果拷贝整个字符串,需要strcpy()函数。strcpy()有两个参数,都是字符串。第二个参数指向的字符串被拷贝到第一个参数指向的数组中(包括空字符)。要确保第一个参数的空间足够。拷贝后会覆盖原有内容。

strcpy()返回第一个字符串的地址。第一个参数不必指向数组的开始。

strncpy()函数前两个参数和strcpy()一样,第三个参数是最大数量。如果第二个字符串字符数小于最大数量,strncpy()拷贝整个字符串(包括空字符)。但是,如果到达最大数量后,还没有遇到空字符,就不会增加空字符。

11.5.6 sprintf()函数

sprintf()函数声明在头文件stdio.h中。和printf()类似,但是把数据写入字符串。第一个参数是目标字符串的地址,其余参数和printf()相同。sprintf()可以把多个元素组合成一个字符串。

11.5.7 其他字符串函数

strchr()函数有两个参数,第一个是一个字符串,第二个是一个字符。如果前者包含后者,函数返回该字符首次出现的指针,否则返回NULL。

strrchr()函数有两个参数,第一个是一个字符串,第二个是一个字符。如果前者包含后者,函数返回该字符最后一次出现的指针,否则返回NULL。

strstr()函数有两个参数,两个参数都是字符串。如果前者包含后者,函数返回第二个字符串首元素第一次出现的指针,否则返回NULL。

11.6 命令行参数

命令行是在命令行环境中,用户为运行程序输入命令的行。命令行参数是同一行的附加项。

C语言允许main()没有参数或者有两个参数。当有两个参数时,第一个参数是命令行的字符串数量,习惯用argc表示。系统用空格表示一个字符串的结束和下一个字符串的开始。字符串中除第一个外都供程序使用。程序把命令行字符串存储在内存中,把每个字符串的地址存储在指针数组中。数组的地址被存放在第二个参数中。这个指向指针的指针一般称为argv。一些系统把程序本身名称赋给argv[0],随后第一个字符串是argv[1],以此类推。

Microsoft Visual Studio可以在项目->属性->调试中输入命令行。

11.7 把字符串转换为数字

atoi()函数可以把字母数字转化为整数,接受一个字符串作为参数,返回相应的整数值。如果命令行参数不是数字,结果是未定义的。

atoi()函数定义在stdlib.h头文件中。该头文件还有atof()函数和atol()函数,atof()函数把字符串转换成double类型的值,atol()函数把字符串转换成long类型的值。

strtol()函数把字符串转换成long类型的值,strtoul()函数把字符串转换成unsigned long类型的值。strtod()把字符串转换成double类型的值。这些函数能识别和报告字符串首字符是否是数字。

strtol()和strtoul()函数还可以指定数字的进制。

strtol()函数有三个参数,第一个是指向待转换字符串的指针,第二个参数是一个指针的地址,该指针被设置为标识输入数字结束字符的地址,第三个参数表明以什么进制写入数字。

最多可以转换三十六进制,'a'-'z'都可用作数字,表示10-35。

strtoul()函数原理相似,strtod()函数只以十进制转换,不需要第三个参数。

许多实现使用itoa()和ftoa()函数分别把整数和浮点数转换成字符串,但是这两个不是C标准库的成员,可以用兼容性更好的sprintf()代替。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值