C Primer Plus 第11章_字符串和字符串函数_代码和练习题

本文详细探讨了C语言中字符串的表示和处理,包括字符串的定义、输入输出函数如gets()、fgets()、puts()、fputs()、scanf()及其优缺点。还介绍了字符串函数如strlen()、strcat()、strncat()、strcmp()、strncpy()、strcpy()、sprintf()的用法,并讲解了如何自定义输入输出函数。同时,文章还涵盖了字符串排序、ctype.h字符函数的应用及字符串转换为数字的方法。
摘要由CSDN通过智能技术生成

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

字符串是以空字符(\0)结尾的char类型数据。
strings1.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 pointing at me.";
	puts("Here are some strings:");
	puts(MSG);
	puts(words);		/* puts函数只显示字符串,而且在显示的字符串末尾加上换行符 */
	puts(pt1);
	words[8] = 'p';
	puts(words);

	printf("%s, %p, %c\n", "We", "are", *"space farers");

	return 0;
}

在这里插入图片描述

在程序中定义字符串

(1)字符串字面量(字符串常量)
用双引号括起来的内容称为字符串字面量。双引号中的字符和编译器自动加入末尾的\0字符,都作为字符串储存在内存中。
字符串常量属于静态存储类别(static storage class),这说明在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的生命期内存在,即使函数被调用多次。
用双引号括起来的内容被释为指向该字符串储存位置的指针。
(2)字符串数组和初始化

const char m1[40] = "Limit yourself to one line's worth.";
const char m2[] = "Limit yourself to one line's worth."; /* 建议在指针初始化为字符串字面量时使用const限定符 */

让编译器计算数组的大小只能在初始化数组时。
const char * pt1= “Limit yourself to one line’s worth.”;

注意:数组和指针的区别
数组名m2是常量,指针名pt1是变量。如果打算修改字符串,就不要用指针指向字符串字面量。
初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。

#include <stdio.h>
#define MSG "I'm special"

int main(void)
{
   
	char ar[] = MSG;
	const char* pt = MSG;
	printf("address of \"I'm special\":%p \n", "I'm special");
	printf("			address ar:%p\n", ar);
	printf("			address pt:%p\n", pt);
	printf("		address of MSG:%p\n", MSG);
	printf("address of \"I'm special\":%p \n", "I'm special");

	return 0;
}

在这里插入图片描述
(4)字符串数组
arrchar.c–指向字符串的指针数据和char类型数组的数组


#include <stdio.h>
#define SLEN 40
#define LIM 5

int main(void)
{
   
	const char* mytalents[LIM] = {
   
		"Adding number swiftly",
		"Multiplying accurately", "Stashing data",
		"Following instrcutions to the letter",
		"Understanding the C language"
	};
	char yourtalents[LIM][SLEN] = {
   
		"Walking in a straight line",
		"Sleeping", "Watching televisions",
		"Mailing letters", "Reading email"
	};
	int i;

	puts("Let's compare talents.");
	printf("%-36s  %-25s\n", "My talents", "Your talents");
	for (i = 0; i < LIM; i++) {
   
		printf("%-36s  %-25s\n", mytalents[i], yourtalents[i]);
	}
	printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
		sizeof(mytalents), sizeof(yourtalents));

	return 0;
}

在这里插入图片描述
综上所述,如果要用数组表示一系列待显示的字符串,请使用指针数组,因为它比二维字符数组的效率高。但是,指针数组也有自身的缺点。mytalents 中的指针指向的字符串字面量不能更改;而yourtalentsde 中的内容可以更改。所以,如果要改变字符串或为字符串输入预留空间,不要使用指向字符串字面量的指针。

11.2 字符串输入

如果想把一个字符串读入程序,首先必须预留储存该字符串的空间,然后用输入函数获取该字符串。

gets函数

C11标准委员会采取了更强硬的态度,直接从标准中废除了gets()函数。
问题出在 gets()唯一的参数是 words,它无法检查数组是否装得下输入行。上一章介绍过,数组名会被转换成该数组首元素的地址,因此,gets()函数只知道数组的开始处,并不知道数组中有多少个元素。
“Segmentation fault”(分段错误)似乎不是个好提示,的确如此。在UNIX系统中,这条消息说明该程序试图访问未分配的内存。

#include <stdio.h>
#include <string.h>
#define STLEN 81
int main(void)
{
   
	char words[STLEN];

	puts("Enter a string, please.");
	gets(words);
	printf("Your string twice:\n");
	printf("%s\n", words);
	puts(words);
	puts("Done.");

	return 0;
}

fgets()和fputs()函数用法

fgets()函数的第2个参数指明了读入字符的最大数量。如果该参数的值是n,那么fgets()将读入n-1个字符,或者读到遇到的第一个换行符为止。
如果fgets()读到一个换行符,会把它储存在字符串中。这点与gets()不同,gets()会丢弃换行符。
fgets()函数的第3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中

#include <stdio.h>
#include <string.h>
#define STLEN 14

int main(void)
{
   
	char words[STLEN];

	puts("Enter a string, please.");
	fgets(words, STLEN, stdin);			/* apple ie\n\0 被储存在数组中 */
	printf("Your string twice(puts(), then fputs()):\n");
	puts(words);
	fputs(words, stdout);		/* fputs()不在字符串末尾添加换行符 */

	puts("Enter another string, please.");
	fgets(words, STLEN, stdin);		/* fgets()只读入了13个字符,并把strawberry sh\0 储存在数组中。 */
	printf("Your string twice(puts(), then fputs()):\n");
	puts(words);		/* puts()函数会在待输出字符串末尾添加一个换行符 */
	fputs(words, stdout);
	puts("Done.");

	return 0;
}

在这里插入图片描述
fgets()函数返回指向 char的指针。如果一切进行顺利,该函数返回的地址与传入的第 1 个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊的指针:空指针(null pointer)。

下面程序演示了一个简单的循环,读入并显示用户输入的内容,直到fgets()读到文件结尾或空行(即,首字符是换行符)。

#include <stdio.h>
#include <string.h>
#define STLEN 10

int main(void)
{
   
	char words[STLEN];

	puts("Enter strings (empty line to quit):");
	while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
   
		fputs(words, stdout);
	}
	puts("Done.");
	
	return 0;
}

第一次读 By the wa\0
第二次读 y, the ge\0
最后一次读 tion\n\0在这里插入图片描述
fgets()储存换行符有好处也有坏处。坏处是你可能并不想把换行符储存在字符串中,这样的换行符会带来一些麻烦。好处是对于储存的字符串而言,检查末尾是否有换行符可以判断是否读取了一整行。如果不是一整行,要妥善处理一行中剩下的字符。
首先,如何处理掉换行符?一个方法是在已储存的字符串中查找换行符,并将其替换成空字符:

while(words[i] != '\n') {
   		// 假设\n在words中
	i++;
}
words[i] = '\0';

其次,如果仍有字符串留在输入行怎么办?一个可行的办法是,如果目标数组装不下一整行输入,就丢弃那些多出的字符:

while(getchar() != '\n') {
   		// 读取但不储存输入,包括\n
	continue;
}

程序清单11.9在程序清单11.8的基础上添加了一部分测试代码。该程序读取输入行,删除储存在字符串中的换行符,如果没有换行符,则丢弃数组装不下的字符。


#include <stdio.h>
#include <string.h>
#define STLEN 10

int main(void)
{
   
	char words[STLEN];
	int i;

	puts("Enter strings (empty line to quit):");
	while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
   
		i = 0;
		while (words[i] != '\n' && words[i] != '\0') {
   
			i++;
		}
		if (words[i] == '\n') {
   
			words[i] = '\0';
		}
		else {
   
			while (getchar() != '\n') {
   
				continue;
			}
		}
		puts(words);
	}
	puts("Done.");
	
	return 0;
}

在这里插入图片描述

s_gets()函数

如果 fgets()返回 NULL,说明读到文件结尾或出现读取错误,s_gets()函数跳过了这个过程。
如果字符串中出现换行符,就用空字符替换它;
如果字符串中出现空字符,就丢弃该输入行的其余字符,然后返回与fgets()相同的值。

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

	ret_val = fgets(st, n, stdin);
	if (ret_val != NULL) {
   
		while (st[i] != '\n' && st[i] != '\0') {
   
			i++;
		}
		if (st[i] == '\n') {
   
			st[i] = '\0';
		}
		
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值