第十章 字符串和字符串函数

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

  字符串是以空字符(\0)结尾的char数组。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月13日08:18:30
//strings.c--使用字符串与用户交互
#include <stdio.h>
#define MSG "You must have many talents. Tell me some."  //一个符号字符串常量
#define LIM 5
#define LINELEN 81  //最大字符串常量+1

int main(void)
{
	char name[LINELEN];
	char talents[LINELEN];
	int i;
	const char m1[40] = "Limit yourself to one line's worth.";
	const char m2[] = "If you can't think of anything,fake it.";
	const char *m3 = "\nEnough about me - what's your name?";
	const char* mytal[LIM] = 
	{
		"Adding number swiftly",
		"Multiplying accurately",
		"Stashing data",
		"Following instructions to the letter",
		"Understanding the C language"
	};
	printf("Hi! I'm Clyde the Computer. " " I have many talents.\n");
	printf("Let me tell you some of them.\n");
	puts("What were they? Ah, yes, here's a partial list.");
	for(i = 0; i < LIM; i++)
		puts(mytal[i]);  //打印计算机功能的列表
	puts(m3);
	gets(name);
	printf("Well, %s, %s\n", name, MSG);
	printf("%s\n%s\n", m1, m2);
	gets(talents);
	puts("Let's see if I've got that list:");
	puts(talents);
	printf("Thank for the information, %s.\n", name);


	return 0;
}

结果:

Hi! I'm Clyde the Computer.  I have many talents.
Let me tell you some of them.
What were they? Ah, yes, here's a partial list.
Adding number swiftly
Multiplying accurately
Stashing data
Following instructions to the letter
Understanding the C language

Enough about me - what's your name?
Nigel Barntwit
Well, Nigel Barntwit, You must have many talents. Tell me some.
Limit yourself to one line's worth.
If you can't think of anything,fake it.
Fencing yodeling, maling, cheese tasting, and sighing.
Let's see if I've got that list:
Fencing yodeling, maling, cheese tasting, and sighing.
Thank for the information, Nigel Barntwit.

10.1.1在程序中定义字符串

  基本的办法是使用字符串常量、char数组、char指针和字符串数组。

一、字符串常量

  字符串常量,又称字符串文字,是指位于一对双引号中的任何字符。双引号里的字符加上编译器自动提供的结束标志\0字符,作为一个字符串被存储在内存中。可以用#define来定义字符串常量。

  字符串文字中间没有间隔或者间隔的是空格符,ANSI C会将其串联起来。例如,

char greeting[50] = "Hello, and" "how are" "you" "today!";
char greeting[50] = "Hello, and how are you today!";

相等。

  字符串常量属于静态存储(static storage)类。静态存储是指如果在一个函数中使用字符串常量,即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。整个引号中的内容作为指向该字符串存储位置的指针。这一点与把数组名作为指向数组存储位置的指针类似。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月13日09:03:52
//quotes.c--把字符串看作指针
#include <stdio.h>
int main(void)
{
	printf("%s, %p, %c\n", "We", "are", *"space farers");

	return 0;
}

结果:

We, 00B17B40, s

  %s格式将输出字符串We。%p格式将产生一个地址。因此如果“are"是个地址,那么%p应该输出字符串中第一个字符的地址(ANSI之前的实现可能用%u或%lu而不用%p)。最后,*"space farers"应该产生所指向的地址中的值,即字符串"space farers"中的第一个字符。

二、字符串数组及其初始化

const char m1[40] = "Limit yourself to one line's worth.";

  const表明这个字符串不可以改变。

  指定数组大小时,一定要确保数组元素比字符串长度多1(多出来的1个元素用于容纳空字符)。未被使用的元素均被自动初始化为0.这里的0是char形式的空字符,而不是数字字符0。

  初始化字符数组是体现由编译器决定数组大小的优点的例子。这是因为字符串处理函数一般不需要知道数组的大小,因为它们能够简单地通过查找空字符来确定字符串结束。

  声明一个数组时,数组的大小必须为整型常量,而不能是运行时得到的变量值。编译时数组大小被锁定到程序中(事实上,C99中可以使用变长数组,但仍然无法预先知道数组大小应为多大)。

int n = 8char cakes[2+5];//合法,数组大小是一个常量表达式
char crumbs[n];//在C99之前是无效的,在C99之后是一个变长数组(VLA)
下面的例子对于数组成立
m1 = &m1[0], *m1 == 'L',and *(m1 + 1) == m1[1] =='i';

三、数组与指针

  通常,被引用的字符串存储在可执行文件的数据段部分;当程序被加载到内存中时,字符串也被加载到内存中。被引用的字符串被称为位于静态存储区。但是在程序开始运行后才维数组分配存储空间。这时候,把被引用的字符串复制到数组中。此后,编译器会把数组名m3看做是数组首元素的地址&m3[0]的同义词。在数组形式中m3是个地址常量。不能更改m3,因为这意味着更改数组存储的位置。可以使用运算符m3+1来标识数组里的下一个元素,但是不允许使用++m3。增量运算符只能用在变量名前。

  指针形式(*m3)也在静态存储区为字符串预留38个元素的空间。此外,程序一旦开始执行,还要为指针变量m3另外预留一个存储位置,以在该指针变量中存储字符串的地址。这个变量初始时指向字符串的第一个字符,但是它的值是可以改变的。因此可以对它使用增量运算符。例如,++m3指向第二个字符E。

  数组初始化是从静态存储区把一个字符串复制给数组,而指针初始化只是复制字符串的地址。

四、数组和指针的差别

char heart[] = "I love Tillie!";
char *head = "I love Millie!";

  主要的差别在于数组名heart是个常量,而指针head是个变量。

  实际使用中,首先,两者都可使用数组符号:heart[i]、head[i],其次,两者都可以使用指针加法:*(heart+i)、*(head+i),但是只有指针可以使用增量运算符:*(head++)。数组名不能作为左值。

char *word = "frame";
word[1] = '1';

  有些编译器会允许上面的情况,但按照当前的C标准,编译器不允许这样做。这种语句会导致内存访问错误。原因在于编译器可能选择内存中的同一个单个的拷贝,来表示所有相同的字符串文字。以下语句都指向字符串“Klingon”的同一个单独的内存位置。

char * p1 = "Klingon";
p1[0] = 'F';//OK?
printf("Klingon");
printf(": Beware the %ss!\n", "Klingon");

  编译器可以用相同的地址来替代每一个“Klingon"的实例。如果编译器使用这个单个拷贝表示法并且允许把p1[0]改为‘F’的话,那会将影响到所有对这个字符串的使用。于是,打印字符串文字"Klingon"的语句实际就变成"Flingon"。实际上,有些编译器确实是按照这种容易混淆的方式工作,而其他得一些则会产生程序异常中断。因此,建议的做法是初始化一个指向字符串文字的指针时使用const修饰符:

const char * p1 = Klingon";//推荐用法

  用一个字符串文字来初始化一个非const的数组,则不会导致此类问题,因此数组从最初的的字符串得到了一个拷贝。

五、字符串数组

程序:

const char* mytal[LIM] = 
{
	"Adding number swiftly",
	"Multiplying accurately",
	"Stashing data",
	"Following instructions to the letter",
	"Understanding the C language"
};

  mytal是一个由5个指向char的指针组成的数组。mytal是个一维数组,而数组里的每一个元素都是一个char类性值的地址。每个指针指向相应字符串的第一个字符。mytal数组实际上不存放字符串,它只是存放字符串的地址(字符串存在程序用来存放常量的那部分内存中)。可以把mytal[0]看作第一个字符串,*mytal[0]表示第一个字符串的第一个字符,mytal[0][0]表示第一个字符串的第一个字符。

  字符串数组的初始化遵循数组初始化的规则。花括号里那部分的形式如下:

{{...},{...},{...},{...},{...},};

  关键之处是第一对双引号对应着一对花括号,用于初始化第一个字符串指针。第二对双引号对应着一堆话括号,用于初始化第二个字符串指针。另一个方法是建立一个二维数组:

char mytal_2[LIM][LINLIM];

  在这种情况下,字符串本身也被存储在数组里。两者差别之一就是第二种方法选择建立了一个所有行的长度都相同的矩形数组。而指针数组建立的是一个不规则的数组,每一行的长度由初始化字符串决定。另一个区别就是mytal1和mytal_2的类型不同;mytal是一个指向char的指针的数组,而mytal_2是一个char数组的数组。mytal存放5个地址,而mytal_2存放5个完整的字符数组。

10.1.2指针和字符串

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月13日10:37:03
//p_and_s.c--指针和字符串
#include <stdio.h>

int main(void)
{
	char * mesg = "Don't be a fool!";
	char * copy;

	copy = mesg;
	printf("%s\n", copy);
	printf("mesg = %s; &mesg = 0x%p; value = 0x%p\n", mesg, &mesg, mesg);
	printf("copy = %s; &copy = 0x%p; value = 0x%p\n", copy, &copy, copy);

	return 0;
}

结果:

Don't be a fool!
mesg = Don't be a fool!; &mesg = 0x0075FD7C; value = 0x00BB7B30
copy = Don't be a fool!; &copy = 0x0075FD70; value = 0x00BB7B30

10.2字符串输入

10.2.1创建存储空间

  C提供了三个读取字符串的函数:scanf()、gets()和fgets()。

10.2.2gets()函数

  它从系统的标准输入输出设备(通常是键盘)获得一个字符串。它读取换行符之前(不包括换行符)的所有字符,在这些字符后添加一个空字符(\0),然后把这个字符串交给调用它的程序。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月13日10:53:55
//name1.c--读取一个名字
#include <stdio.h>
#define MAX 81

int main(void)
{
	char name[MAX];  //分配空间
	printf("Hi, what's your name?\n");
	gets(name);  //把字符串放进name数组中
	printf("Nice name, %s.\n", name);

	return 0;
}

结果:

Hi, what's your name?
The Mysterious Davina D'Lema
Nice name, The Mysterious Davina D'Lema.

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日07:33:26
//读取一个名字
#include <stdio.h>
#define MAX 81

int main(void)
{
	char name[MAX];
	char *ptr;

	printf("Hi, what's your name?\n");
	ptr = gets(name);
	printf("%s? Ah! %s!\n", name, ptr);

	return 0;
}

  gets()函数通过两种方式获得输入:

●它使用一个地址把字符串赋予name。

●gets()的代码使用return关键字返回字符串的地址,程序把这个地址分配给ptr。注意到ptr是一个char指针,这意味着gets()必须返回一个指向char的指针值。与所传递给它的指针是同一个。若出错或者如果gets()遇到文件结尾,它就返回一个空(或0)地址。这个空地址被称为空指针,并用stdio.h里定义的常量NULL来表示。因此gets()中还加入了一些错误检测,这使它可以很方便地以如下形式使用:

while(gets(name) != NULL)

  这样的指令使您既可以检查是否到了文件结尾,又可以读取一个值。如果遇到了文件结尾,name中什么也不会读入。这样一举两得的方法就比getchar()函数所采用的的方法简洁的多,getchar()只返回一个值而没有参数:

while((ch = getchar()) != EOF)

10.2.3fgets()函数

  gets()的一个不足是它不检查预留存储区是否能够容纳实际输入的数据。多出来的字符简单的溢出到相邻的内存区。fgets()函数改进了这个问题,它让您指定最大读入字符数。由于fgets()是为文件I/O而设计的,在处理键盘输入时就不如gets()方便。fgets()和gets()有三方面不同:

●它需要第二个参数来说明最大读入字符数。如果这个参数值为n,fgets()就会读取最多n-1个字符或者读完一个换行符为止,由这而这种最先满足的那个来结束输入。

●如果fgets()读取到换行符,就会把它存到字符串里,而不是像gets()那样丢弃它。

●它还需要第三个参数来说明读取哪一个文件。从键盘上读取数据时,可以使用stdin(代表standard input)作为改参数,这个标识符在stdio.h中定义。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日08:13:12
//name3.c--使用fgets()读取一个名字
#include <stdio.h>
#define MAX 81
int main(void)
{
	char name[MAX];
	char *ptr;

	printf("Hi, what's your name?\n");
	ptr = fgets(name, MAX, stdin);
	printf("%s? Ah! %s!\n", name, ptr);

	return 0;
}

结果:

Hi, what's your name?
Jon Dough
Jon Dough
? Ah! Jon Dough
!

●对于重要的编程,应该使用fgets()而不是gets()

10.2.4scanf()函数

  scnaf()更基于获取单词而不是获取字符串;而gets()函数,正如所看到的,会读取所有字符,直到遇到第一个换行符为止。scanf()使用两种方法决定输入结束。无论哪种方法,字符串都是以遇到的第一个非空白字符开始。如果使用%s格式,字符串读到(但不包括)下一个空白字符(比如空格、制表符或换行符)。如果制定了字段宽度,比如%10s,scanf()就会读入10个字符或直到遇到第一个空白字符,由二者中最先满足的那一个终止输入。scanf()函数返回一个整数值,这个值是成功读取的项目数;或者当遇到文件结束时返回一个EOF。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日08:33:29
//scan_str.c--使用scanf()
#include <stdio.h>
int main(void)
{
	char name1[11],name2[11];
	int count;

	printf("Please enter 2 names.\n");
	count = scanf("%5s %10s", name1, name2);
	printf("I read the %d names %s and %s.\n", count, name1, name2);

	return 0;
}

结果:

Please enter 2 names.
Jesse Jukes
I read the 2 names Jesse and Jukes.

10.3字符串输出

10.3.1puts()函数

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日08:42:13
#include <stdio.h>
#define DEF "I am a #defined string."
int main(void)
{
	char str1[80] = "An array was initialized to me.";
	const char *str2 = "An pointer was initialized to me.";

	puts("I'm an argument to puts().");
	puts(DEF);
	puts(str1);
	puts(str2);
	puts(&str1[5]);
	puts(str2 + 4);

	return 0;
}

结果:

I'm an argument to puts().
I am a #defined string.
An array was initialized to me.
An pointer was initialized to me.
ray was initialized to me.
ointer was initialized to me.

  与printf()不同,puts()显示字符串时自动在其后添加一个换行符。

  双引号中的字符是字符串常量,并被看作地址。同样,字符数组字符串的名字也被看作是地址。puts()何时停止?遇到空字符时它就会停下来,所以应该确保有空字符存在。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日08:53:22
//nono.c--不要效仿这个程序!
#include <stdio.h>

int main(void)
{
	char side_a[] = "SIDE A";
	char dont[] = {'W', 'O', 'W', '!'};
	char side_b[] = "SIDE B";

	puts(dont);  //dont不是一个字符串

	return 0;
}

结果:

WOW!烫烫烫烫SIDE A

 dont缺少一个表示结束的空字符,因此它不是一个字符串,这样puts()就不知道应该到哪里停止。它只是一直输出内存中dont后面的字符,直到发现一个空字符。为了使这个空字符不太遥远,程序把dont存储在两个真正的字符串之间。这里用到的特定的编译器在内存中把side_a数组存储在dont数组之后。put()函数继续执行直到遇到了side_a中的空字符。运行改程序时编译器在内存中存储数据的方式不同,得到的结果也不同。如果程序漏掉了side_a和side_b怎么办?通常内存中有很多空字符,如果幸运的话,puts()可能会很快发现一个,但是这个并不可靠。

10.3.2fputs()函数

  fputs()函数是gets()的面向文件版本。两者之间的区别是:

●fputs()需要第二个参数来说明要写的文件。可以使用stdout(代表standard output)作为参数来进行输出显示,stdout在stdio.h中定义。

●与puts()不同,fputs()并不为输出自动添加换行符。

  注意,gets()丢掉输入里的换行符,但是puts()为输出添加换行符。另一方面,fgets()存储输入中的换行符,而fputs()也不为输出添加换行符。

10.3.3printf()函数

10.4自定义字符串输入/输出函数

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日09:49:23
//put1.c--不添加换行符打印一个字符串
#include <stdio.h>
void put1(const char* string)  //不会改变这个字符串
{
	while(*string != '\0')
		putchar(*string++);
}

**说明**

  为什么上面的程序用const char *string而不用const char string[]作为形式参数?从技术上来说,二者等价,因此它们都有效。用方括号符号的一个庸医是提醒用户这个函数处理的是数组。但在使用字符串时,实际的参数可以是数组名、引起来的字符串,或被声明为char *类型的变量。使用const char *string可以提醒您实际的参数不一定是一个数组。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日09:59:24
//put_put.c--用户自定义的输出函数
#include <stdio.h>
void put1(const char *);
int put2(const char *);

int main(void)
{
	put1("If I'd as much money");
	put1(" as I could spend,\n");
	printf("I could %d characters.\n", put2("I never would cry old chairs to mend. "));

	return 0;
}

void put1(const char * string)
{
	while(*string)  //等同于*string != '\0'
		putchar(*string++);
}

int put2(const char * string)
{
	int count = 0;
	while(*string)
	{
		putchar(*string++);
		count++;
	}
	putchar('\n');

	return(count);
}

结果:

If I'd as much money as I could spend,
I never would cry old chairs to mend.
I could 38 characters.

10.5字符串函数

10.5.1strlen()函数

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月15日10:21:12
//test.c--试用缩短字符串的函数
#include <stdio.h>
#include <string.h>  //该头文件中包含字符串函数的原型
void fit(char *, unsigned int);

int main(void)
{
	char mesg[] = "Hold on to your hats, hackers. ";

	puts(mesg);
	fit(mesg, 7);
	puts(mesg);
	puts("Let's look at some more of the string. ");
	puts(mesg + 8);

	return 0;
}

void fit(char* string, unsigned int size)
{
	if(strlen(string) > size)
		*(string +size) = '\0';
}

结果:

Hold on to your hats, hackers.
Hold on
Let's look at some more of the string.
to your hats, hackers.

10.5.2strcat()函数

#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,‘\0’也会追加过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
返回值:
	成功:返回dest字符串的首地址
	失败:NULL
	char str[20] = "123";
	char *src = "hello world";
	printf("%s\n", strcat(str, src));

  strcat()函数接受两个字符串参数。它将第二个字符串的一份拷贝添加到第一个字符串结尾,从而第一个字符串成为一个新的组合字符串,第二个字符串并没有变。strcat()函数是char *(指向char的指针)类型。这个函数返回它的第一个参数的值,即其后添加了第二个字符串的那个字符串中第一个字符的地址。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日07:25:53
#include <stdio.h>
#include <string.h>  //声明strcat()函数
#define SIZE 80

int main(void)
{
	char flower[SIZE];
	char addon[] = "s smell like old shoes.";

	puts("What is your favorate flower?");
	gets(flower);
	strcat(flower,addon);
	puts(flower);
	puts(addon);

	return 0;
}

结果:

What is your favorate flower?
Rose
Roses smell like old shoes.
s smell like old shoes.

10.5.3stancat()函数

#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
功能:将src字符串前n个字符连接到dest的尾部,‘\0’也会追加过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
	n:指定需要追加字符串个数
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

	char str[20] = "123";
	char *src = "hello world";
	printf("%s\n", strncat(str, src, 5));

  strcat()函数并不检查第一个数组是否能够容纳第二个字符串。如果没有为第一个数组从分配足够大的空间,多出来的字符溢出到相邻存储单元时就会出现问题。为第一个数组分配足够大的空间后再使用strlen()函数。应该给组合串的长度加1以用来存放空字符。也可以使用strnact(),这个函数把addon字符串中的内容添加到bugs上,直到加到13个字符或遇到空字符为止,由二者中先符合的那一个来终止添加过程。因此,把空字符算在内(两种情况下都要添加空字符),bugs()数组应该足够大,以存放原始字符串(不包括空字符)、增加的最多13个字符和结束的空字符。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日07:44:03
//join_chk.c--连接两个字符串,并检查第一个字符串的大小
#include <stdio.h>
#include <string.h>
#define SIZE 30
#define BUGSIZE 13

int main(void)
{
	char flower[SIZE];
	char addon[] = "s smell like old shoes.";
	char bug[BUGSIZE];
	int available;

	puts("What is your favorate flower?");
	gets(flower);
	if((strlen(addon) + strlen(flower) + 1) <= SIZE)
		strcat(flower, addon);
	puts(flower);
	puts("What is yours favorate bug?");
	gets(bug);
	available = BUGSIZE - strlen(bug) - 1;
	strncat(bug, addon, available);
	puts(bug);

	return 0;
}

结果:

What is your favorate flower?
Rose
Roses smell like old shoes.
What is yours favorate bug?
Aphid
Aphids smell

10.5.4strcmp()函数

#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
参数:
	s1:字符串1首地址
	s2:字符串2首地址
返回值:
	相等:0
	大于:>0 在不同操作系统strcmp结果会不同   返回ASCII差值
	小于:<0
	

    char *str1 = "hello world";
	char *str2 = "hello mike";

	if (strcmp(str1, str2) == 0)
	{
		printf("str1==str2\n");
	}
	else if (strcmp(str1, str2) > 0)
	{
		printf("str1>str2\n");
	}	
	else
	{
		printf("str1<str2\n");
	}

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日07:56:44
//nogo.c--这个程序能满足要求吗
#include <stdio.h>
#define ANSWER "Grant"

int main(void)
{
	char try[40];

	puts("Who is burid in Grant's tomb?");
	gets(try);
	while (try != ANSWER)
	{
		puts("No, that's wrong. Try again. ");
		gets(try);
	}
	puts("That's ringht!");

	return 0;
}

  ANSWER和try实际上是指针,因此比较式try!=ANSWER并不检查这两个字符串是否一样,而检查这两个字符串的地址是否一样。用户永远被告知是“wrong”。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日08:24:05
//compare.c--这个程序可以满足要求
#include <stdio.h>
#include <string.h>  //声明strcmp()函数
#define ANSWER "Grant"
#define MAX 40

int main(void)
{
	char try[MAX];

	puts("Who is buried in Grant's tomb?");
	gets(try);
	while (strcmp(try, ANSWER) != 0)
	{
		puts("No, that's wrong. Try again.");
		gets(try);
	}
	puts("That's right!");

	return 0;
}

结果:

Who is buried in Grant's tomb?
Gran
No, that's wrong. Try again.
Grant
That's right!

  strcmp()函数的一个优点是它比较的是字符串,而不是数组。尽管数组try占用40个内存单元,而字符串“Grant”只占用6个内存单元(一个用来存放空字符),但是函数在比较时只看try的第一个空字符之前的部分。因此,strcmp()可以用来比较存放在不同大小的数组里的字符串。

●strcmp()返回值

  如果第一个字符串在字母表中的顺序先于第二个字符串,则strcmp()函数返回的是负数;相反,返回的是正数。

strcmp("A","C")为-2;

strcmp("C","A")为2;

  如果字符串中的初始字符相同,一般来说,strcmp()函数一直往后查找,直到找到第一对不一致的字符。然后它就返回相应的值。以上的比较表明strcmp()比较所有的字符,而不仅仅是字母;因此我们不应称比较是字母表顺序,而应称strcmp()是按照机器编码顺序进行比较的。这意味着字符的比较是根据它们的数字表示法,一般是ASCLL值。

  ●说明

  strcmp()函数用于比较字符串,而不是字符。因此可以使用诸如“apples”和“A”之类的参数;但是不能使用字符参数,如'A'。考虑到char类型是整数类型,因此可以使用关系运算符来对字符进行比较。嘉定word是一个存储在char数组里的字符串,ch是一个char变量。那么下面的语句是合法的:

if(strcmp(word, "quit") == 0)
puts("Bye!");
if(ch == 'q')
puts("Bye!");
#define _CRT_SECURE_NO_WARNINGS 1
//使用strcmp()函数来判断一个程序是否应该停止读取输入。
//quit_chk.c -- 某程序的开始
#include <stdio.h>
#include <string.h>
#define SIZE 81
#define LIM 100
#define STOP "quit"

int main(void)
{
	char input[LIM][SIZE];
	int ct = 0;
	printf("Enter up to %d lines(type quit to quit): \n", LIM);
	while (ct < LIM && gets(input[ct]) != NULL && strcmp(input[ct], STOP) != 0)
	{
		ct++;
	}
	printf("%d strings entered\n", ct);

	return 0;
}

  有时候输入一个空行来终止输入更方便,也就是说,在一个新行中不输入任何字符就按下Enter键或Return键。要这样做,您可以对while循环的控制语句做如下的修改:

​ while (ct < LIM && gets(input[ct]) != NULL && input[ct][0] != '\0')

10.5.5strncmp()变种

#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较 s1 和 s2 前n个字符的大小,比较的是字符ASCII码大小。
参数:
	s1:字符串1首地址
	s2:字符串2首地址
	n:指定比较字符串的数量
返回值:
	相等:0
	大于: > 0
	小于: < 0
	
    char *str1 = "hello world";
	char *str2 = "hello mike";

	if (strncmp(str1, str2, 5) == 0)
	{
		printf("str1==str2\n");
	}
	else if (strcmp(str1, "hello world") > 0)
	{
		printf("str1>str2\n");
	}
	else
	{
		printf("str1<str2\n");
	} 

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日09:17:47
//starsrch.c -- 使用strncmp()函数
#include <stdio.h>
#include <string.h>
#define LISTSIZE 5

int main(void)
{
	char * list[LISTSIZE] = 
	{
		"astronomy", "astounding", "astrophysics", "ostracise", "asterism"
	};
	int count = 0;
	int i;

	for (i = 0; i < LISTSIZE; i++)
	{
		if (strncmp(list[i], "astro", 5) == 0)
		{
			printf("Found: %s\n", list[i]);
			count++;
		}
	}
	printf("The list contained %d words begining with astro.\n", count);

	return 0;
}

结果:

Found: astronomy
Found: astrophysics
The list contained 2 words begining with astro.

10.5.6strcpy()和strncpy()函数

#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
参数:
	dest:目的字符串首地址
	src:源字符首地址
返回值:
	成功:返回dest字符串的首地址
	失败:NULL

注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

char dest[20] = "123456789";
	char src[] = "hello world";
	strcpy(dest, src);
	printf("%s\n", dest);

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日09:29:24
//copy1.c -- strcpy()示例程序
#include <stdio.h>
#include <string.h>  //声明strcpy()函数
#define SIZE 40
#define LIM 5

int main(void)
{
	char qwords[LIM][SIZE];
	char temp[SIZE];
	int i = 0;

	printf("Enter %d words begning with q: \n", LIM);
	while (i < LIM && gets(temp))
	{
		if(temp[0] != 'q')
			printf("%s doesn't begin with q!\n", temp);
		else
		{
			strcpy(qwords[i], temp);
			i++;
		}
	}
	puts("Here are the words accepted: ");
	for(i = 0; i < LIM; i++)
		puts(qwords[i]);

	return 0;
}

结果:

Enter 5 words begning with q:
quackery
quasar
quilt
quotient
no more
no more doesn't begin with q!
quiz
Here are the words accepted:
quackery
quasar
quilt
quotient
quiz

  strcpy()接受两个字符串指针参数。指向最初字符串的第二个指针可以是一个已声明的指针、数组名或字符串常量。

一、strcpy()的高级属性

  strcpy()函数还有两个有用的属性。首先,它是char *类型,它返回的是第一个参数的值,即一个字符的地址;其次,第一个参数不需要指向数组的开始,这样就可以只复制数组的一部分。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日10:09:29
//copy2.c -- strcpy()示例程序
#include <stdio.h>
#include <string.h>  //声明strcpy()函数
#define WORDS "beast"
#define SIZE 40

int main(void)
{
	char * orig = WORDS;
	char copy[SIZE] = "Be the best that you can be.";
	char * ps;

	puts(orig);
	puts(copy);
	ps = strcpy(copy + 7, orig);
	puts(copy);
	puts(ps);

	return 0;
}

结果:

beast
Be the best that you can be.
Be the beast
beast

二、较为谨慎的选择:strncpy()

  strcpy()和gets()函数同样都有一个问题,那就是都不检查目标字符串是否容纳得下源字符串。复制字符串使用strncpy()比较安全。它需要第三个参数来指明最大可复制的字符数。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//2022年5月16日10:20:38
//copy3.c -- strncpy()示例程序
#include <stdio.h>
#include <string.h>  //声明strncpy()函数
#define SIZE 40
#define TARGSIZE 7
#define LIM 5

int main(void)
{
	char qwords[LIM][TARGSIZE];
	char temp[SIZE];
	int i = 0;

	printf("Enter %d words beginning with q: \n", LIM);
	while (i < LIM && gets(temp))
	{
		if(temp[0] != 'q')
			printf("%s doesn't begin with q!\n", temp);
		else
		{
			strncpy(qwords[i], temp, TARGSIZE - 1);
			qwords[i][TARGSIZE - 1] = '\0';
			i++;
		}
	}
	puts("Here are the words accepted: ");
	for(i = 0; i < LIM; i++)
		puts(qwords[i]);

	return 0;
}

结果:

Enter 5 words beginning with q:
quack
quadratic
quisling
quota
quagga
Here are the words accepted:
quack
quadra
quisli
quota
quagga

  函数调用strncpy(target, source, n)从source把n个字符(或空字符之前的字符,由二者中最先满足的那个决定何时终止)复制到target。

	strncpy(qwords[i], temp, TARGSIZE - 1);
	qwords[i][TARGSIZE - 1] = '\0';

  这就确保已经存储了一个字符串。如果源字符串确实可以容纳得下,和它一起复制的空字符就标志着字符串的真正结束。如果源字符串在目标数组中容纳不下,这个最后的空字符就标志着字符串的结束。

10.5.7sprintf()函数

#include <stdio.h>
int sprintf(char *str, const char *format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 '\0'  为止。
参数:
	str:字符串首地址
	format:字符串格式,用法和printf()一样
返回值:
	成功:实际格式化的字符个数
	失败: - 1

	char dst[100] = { 0 };
	int a = 10;
	char src[] = "hello world";
	printf("a = %d, src = %s", a, src);
	printf("\n");

	int len = sprintf(dst, "a = %d, src = %s", a, src);
	printf("dst = \" %s\"\n", dst);
	printf("len = %d\n", len);  

  sprintf()函数是在stdio.h而不是在,string.h里声明的。它的作用和printf()一样,但是它写到字符串里而不是写到输出显示。因此,它提供了把几个元素组合成一个字符串的一种途径。sprintf()的第一个参数是目标字符串的地址,其余的参数和printf()一样:一个转换说明字符串,接着是要写的项目的列表。

程序:

#define _CRT_SECURE_NO_WARNINGS 1
//format.c -- 格式化一个字符串
//2022年5月16日10:54:29
#include <stdio.h>
#define MAX 20

int main(void)
{
	char first[MAX];
	char last[MAX];
	char formal[2 * MAX + 10];
	double prize;

	puts("Enter your first name: ");
	gets(first);
	puts("Enter your last name: ");
	gets(last);
    puts("Enter your prize money: ");
	scanf("%lf", &prize);
	sprintf(formal, "%s, %-19s: $%6.2f\n", last, first, prize);
	puts(formal);

	return 0;
}

结果:

Enter your first name:
Teddy
Enter your last name:
Behr
Enter your prize money:
2000
Behr, Teddy              : $2000.00

10.5.8其他字符串函数

  ANSI C有20多个处理字符串的函数:

●char * strcpy(char * s1, const char *s2);

该函数把s2指向的字符串(包括空字符)复制到s1 指向的位置,返回值是s1。

●char * strncpy(char * s1, const char * s2, size_t n);

该函数把s2指向的字符串复制到s1指向的位置,复制的字符数不超过n个。返回值是s1.空字符后的字符不被复制。如果源字符串的字符数少于n个,在目标字符串就以空字符填充。如果源字符串的字符数大于或等于n个,空字符就不被复制。返回值是s1。

●char * strcat(char * s1, const char * s2);

s2指向的字符串被复制到s1指向的字符串的结尾。复制过来的s2所指字符串的第一个字符覆盖了s1所指字符串结尾的空字符。返回值是s1。

●char * strncat(char * s1, const char * s2, size_t n);

s2字符串中只有前n个字符被追加到s1字符串,复制过来的s2字符串的第一个字符覆盖了s1字符串结尾的空字符。s2字符串中的空字符及其后的任何字符都不会被复制,并且追加一个空字符到所得结果后面。返回值是s1。

●int strcmp(const chat * s1, const char *s2);

如果s1字符串在机器编码顺序中落后于s2字符串,函数的返回值是一个正数;如果两个字符串相同,返回值是0;如果第一个字符串在机器编码顺序中先于第二个字符串,返回值是一个负数。

●int strncmp(const chat * s1, const char *s2);

该函数的作用和strcmp()一样,只是比较n个字符后或者遇见第一个空字符时会停止比较,由二者中最先满足的那一个条件来终止比较过程。

●char * strchr(const char * s, int c);

该函数返回一个指向字符串s中存放字符c的第一个位置的指针(标志结束的空字符是字符串的一部分,因此也可以搜索到它)。如果没找到该字符,函数就返回空指针。

●char * strpbrk(const char * s1, const char * s2);

该函数返回一个指针,指向字符串s1中存放s2字符串中的任何字符的第一个位置。如果没找到任何字符,函数就返回空指针。

●char * strrchr(const char * s, int c);

该函数返回一个指针,指向字符串s中字符c最后一次出现的地方(标志结束的空字符是字符串的一部分,因此也可以搜索到它)。如果没找到该字符,函数就返回空指针。

●char * strstr(const char * s1, const char * s2);

该函数返回一个指针,指向s1字符串中第一次出现s2字符串的地方。如果在s1中没找到s2字符串,函数就返回空指针。

●size_t strlen(const char * s);

该函数返回s字符串中的字符个数,其中不包括标志结束的空字符。

  size_t类型是sizeof运算符返回的任何类型。C规定sizeof运算符返回一个整数类型,但是没有指定是哪种整数类型。因此size_t在一个系统上可以是unsigned int类型;在另一个系统上,又可以使unsigned long类型。string.h文件为您的特定系统定了size_t,或者可以参考其他有该定义的头文件。

posted @   CodeMagicianT  阅读(84)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示