在程序中定义字符串
字符串的定义:字符串是以空字符(\0)结尾的char类型数组
如何表示字符串
字符串的表示方式:
a.使用字符串常量表示(字符串字面量)
Eg:” i am a string in a array”
双引号中的字符和编译器自动加入末尾的\0字符,都作为字符串储存在内存中
从ANSI标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,c会将其视为串联起来的字符串字面量
字符串常量属于静态储存类别*,这说明如果在函数中使用字符串常量,该字符串只会被储存一次,在整个程序的生命期存在,即使函数被调用多次.用双引号扩起来的内容将被视为指向该字符串储存位置的指针,这类似于把数组名作为指向该数组位置的指针
/*strptr.c—把字符串看作指针*/
#include<stdio.h>
int main(void)
{
Printf(“%s,%p,%c\n”,”We”,”are”,*”space farers”);
Return 0;
}
b.使用char类型数组表示
Eg.
Const char ml[40]=“limit yourself to one line’s worth.”
const char ml[40]={‘l’,’i’,’m’,’i’,’t’,’\0’} (注意最后的空字符,没有空字符,这就是一个字符数组而不是字符串)
Const char m2[]=‘if you can’t think of anything, fake it’(省略数组大小函数将在最后遇到空字符时知道停止)
在指定数组大小时,要确保数组大小至少比字符串大一,用于储存空字符,所有未被使用的元素都被自动初始化为0(这里的0指的是char形式的空字符,不是数字字符0)
在初始化数组时,可以参略数组大小,但如创建一个需要稍后再初始化的数组时,需要用整形常量指明数组大小,否则使用整形变量的话,将被视为变长数组
c.使用指向char类型数据的指针表示
Eg. const char * pt1=“something is pointing at me”;(指针名即该字符串首地址)
Const char ar1[]=“something is pointing at me”;
如果将字符串赋予数组,数组将预留比字符串长度多一位的储存单元。字符串储存在可执行文件里,在程序载入内存时,才会为数组分配内存,所以此时字符串有两个副本,一个在静态储存区中,一个在数组中。而此时的数组名,是地址常量,(字符串储存地址不可变)所以不可用递增递减符,ar1++ etc.但是作为地址常量,可以让其指向下一个元素,ar1+1 etc.
如果将字符串赋予执政指针,编译器将在静态储存区预留相应的储存空间给字符串,在程序开始执行后,留一个位置给指针,储存字符串的地址。将字符串储存其中,因为字符串是不可更改的,所以应将指针设为const。因为指针是变量(有别于字符串的自己的储存单元)所以可以使用递增递减。(而数组是将字符串的值赋予数组,而数组地址改变会引起字符串地址的改变,字符串是常量不可改变,所以数组名是常量)
总之,初始化数组将静态储存区的字符串拷贝至数组,初始化指针把字符串的地址拷贝给指针
/*adresses.c*/
Define MSG “I’m special”
#include<stdio.h>
Int main()
{
Char ar[]==MSG;
Const char *pt==MSG;
printf(“adress of \”I’m special\”:%p\n”,”Im special’);
printf(“ address ar: %p\n”,ar);
printf(“ address pt:%p\n”,pt );
printf(“ address of MSG:%p\n”,MSG);
printf(“address of \”I’special\”:%p \n”,” “I’m special ”);
return 0;
}
pt 和MSG位置相同,因为pt储存着的正是字符串的位置,ar与MSG位置不同,因为数组地址上储存的是字符串的另一个副本,而不是储存在静态储存区的字符串,所以被打印的地址其实是数组的地址。两次打印“I’m special”地址都相同,说明编译器可以把多次使用的相同字面量储存在一处或多处,其他编译器可能储存在不同的位置。静态内存和ar使用的动态内存值不同,位数也不同
字符串数组(将字符串赋予数组与将字符串赋予指针的区别)
Char heart[]=“i love apple”;
Char const *head “i love orange”;
两者都可以用数组法或者指针法表示
for(i=0;i<6;i++)
putchar(heart[i]);
for(i=0;i<6;i++)
putchar(head[i]);
for(i=0;i<6;i++)
putchar(*(heart+i));
for(i=0;i<6;i++)
putchar(*(head+i));
但是,因为数组名是地址常量,(字符串地址不可更改),所以只有指针可以使用递增递减运算
for(i=0;i<6;i++)
putchar(*(heart++));
如果想要使指针和数组一致,可以让指针指向数组
head=heart;
但不可以使用
heart=head;
因为不可将一个变量赋予一个常量;但在改变指针中储存的地址后(即改变指针的指向后),指针之前所指向的字符串不会消失,但如果未保存字符串的位置,则之后将无法访问
另外,可以改变数组的元素,因为尽管数组名是常量,但数组的本身是变量,除非在开始加上const限定,
例如,heart[7]==M;
是否可以改变没有const限定的指针指向的字符串呢,例如;
Char *word==“frame”;
word[1]=‘1’;
这种行为是未定义的,编译器可以使用内存中的一个副本来表示完全相同的字符串常量,
char*p1=“klingon”;
p1[0]=‘F’;
printf(“klingon”);
printf(“beware the %ss!\n”,“klingon”);
编译器可以使用相同的地址替换每个klingon实例,如果编译器使用这种单次副本表示法,,且允许p1[0]修改’F’那将影响所有使用该字符串的代码(修改的是位于静态储存区的字符串,所以修改后之后从原字符串拷贝的所有副本都会受到影响)
把非const数组初始化为字符串常量是可以的,因为数组中的字符串是原字符串的副本,修改只会影响到当前数组而不会影响到原字符串
总之,如果不修改字符串,不要用指针指向字符串常量
如果想要同时储存多个字符串,可以使用字符串数组,这样可以通过数组的不同下标访问多个不同的字符串,例如,指向字符串的指针数组和char类型数组的数组;
/*arrchar.c指针数组,字符串数组*/
#include<stdio.h>
#define SLEN 40
#define LIM 5
int main(void)
{
Const char *mytalents[LIM]=
{
“adding number swiftly”,
“multiplying accurately”,“stashing data”,
“following instruction to the letter”,
“understanding the c language”
};
Char yourtalents[LIM][SLEN]={
“walking in a straight line”,
“sleeping”,”watching television”,
“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;
}
指向字符串的指针数组和char类型数组初始化方式相同,区别在于两种类型数组所占内存不同,
指针数组有5个指针,每个char指针占8个字节所以大小为40个字节,而char类型有5个数组,每个数组分配了40个字节的储存空间,所以大小为200节。从此看出,用char类型多维数组来储存字符串,内存使用率较低。其次,使用指针数组储存字符串,指针储存的是静态内存中字符串的地址,不可使用解引用符号对其值进行更改。而char类型数组中字符串,为原字符串的副本,其值是可以更改的。最后,指针数组指向的字符串,不必存在于连续的储存单元中,但给char类型数组分配的储存空间需是连续的。
指针和字符串
/*p_and_s.c —指针和字符串*/
#include<stdio.h>
int main(void)
{
Const char *mesg = “don’t be a fool!”;
Const char *copy;
copy=mesg;
printf(“%s\n”,copy);
printf(“mesg=%s;&mesg=%p;value=%p\n”,mesg,&mesg,mesg);
printf(“copy=%s;©=%p;value=%p\n”,copy,©,copy);
return 0;
}
此举并未将字符串的值拷贝到copy中,而是将mesg中储存着的字符串的地址拷贝到copy中,所以copy现在也指向字符串。打印出的mesg和copy的两个地址,一个是指针本身的地址,一个是所储存的字符串的地址,因为指向同一个字符串,所以地址相同。这种方式在不需要改变字符串值的前提下比拷贝整个字符串更有效率
字符串的输入
字符串的输入步骤为-预留合适的储存空间-使用输入函数获取字符串
先分配空间意味着,不可以使用未初始化指针和未初始化且省略了长度的数组来为字符串分配空间
这可能导致稍后程序异常终止,或者覆盖掉程序中其他代码或数据
gets() and puts()函数
scanf()函数和转换说明%只能读取一个单词。gets()函数就用于处理这种情况。它读取整行输入,然后求弃掉换行符,在输入末尾加上空字符,使其成为字符串。gets()函数常和puts()函数配对使用,puts()函数输出字符串,且在结尾添加换行符
getsputs.c—使用gets()
#include<stdio.h>
#define STLEN 81
Int main(void)
{
char words[STLEN];
puts(“enter a string ,please.”);
gets(words);
printf(“your string twice”);
printf(“%s\n”,words);
puts(words);
puts(“done.”);
return 0;
}
有些编译器会出现警告segementation fault:11(分段错误),gets(words)只知道words数组的首地址,(因为words作为数组名代表words字符串的首地址),但是不知道输入字符串长度是否在数组储存范围内,所以在被送出缓冲区传入目标储存空间后,有可能会占用未分配空间,抹除其他数据导致程序终止。所以gets()函数在c11标准后被废除
fgets() and fputs()函数
fgets()函数有三个参数,它用第二个参数来限制输入长度,解决了输入可能会溢出目标储存空间的问题,因为它是设计被用来输入文件的函数,所以第三个参数需要时指定的输入文件,如果是标准输入,用stdin代替。
不论是遇见换行符,还是达到了n-1个字符(假设第二个参数为n,需要留一位添加空字符),只要满足其中之一的条件,fgets()对输入的读入就会停止;与gets()函数不一样,fgets()函数在遇见换行符后,会自动保留换行符,并在末尾加上空字符,所以它经常和fput()函数配对。和puts()函数不同,fputs()不会在字符串后面加上空字符,并且,它需要第二个参数来指明输出至什么对象文件,如果是标准输出,需使用stdout
//fgetsl.c—使用fgets()和fputs()
#include<stdio.h>
#define STLEN 14
int main(void)
{
char words[STLEN];
puts(“enter a string,please.”)
fgets(words,STLEN,stdin);
printf(“your string twice, (puts(),then fputs()):\n”);
puts(words);
fputs(words,stdout);
puts(“enter another string please”);
fgets(words,STLEN,stdin);
printf(“your string twice, (puts(),then fputs()):\n”);
puts(words);
fputs(words,stdout);
puts(“done”);
return 0;
}
注意在使用fgets()时,如果输入先达到溢出条件,fgets()还未碰到换行符就停止读入了,所以并未将换行符储存其中
fputs()函数返回指向char数据类型的指针,指针指向第一个参数的地址,如果读到文件结尾或者读入错误,将返回空指针(null),该指针不会指向任何有效数据,在代码中,可用数字0代替,c语言中用宏null代替更常见
//fgets2.c—使用fgets()和fputs()
#include<stdio.h>
#define STLEN 10
int mian (void)
{
char words[STLEN];
puts(“enter strings (empty line to quit):”);
while(fgets(words,STLEN,stdin)!=NULL&&word[0]!=‘\n’)
fputs(words,stdout);
puts(“done”);
return 0;
}
虽然数组长度被设为10,但程序仍旧在读取超过数组长度的输入时正常运行,这是因为在遇到空行或eof时,fgets()会不断读取之前未能完整读入的数据。直到遇到换行符,将输入转入储存区。fputs()将输入传递到另一缓冲区,在遇到换行符后,将输入打印出来
现在来讨论,对于fgets()会自动保存字符串结尾的换行符的行为的利弊。
好处是,我们可以通过观察结尾是否有换行符来判断fgets()是否读取了整行输入,让fgets()停止读入输入的条件有两个,一个是输入超出分配空间大小,一个是遇到换行符,所以,当字符串结尾仅仅只有空字符时,说明函数为了避免分段错误而未读取整行输入,在输入行会有剩余字符待处理。反之,函数读取了整行输入
那么,如何处理输入行的剩余字符,可以丢弃,
while(getchar()!=‘\n’)
continue;
如何处理掉换行符,采取将其替换为空字符的方法:
while(words[i]!==‘\n’)
i++;
words[i]=‘\0’;
//fgets3.c—使用fgets()
#include<stdio.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&&word[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;
}
空字符和空指针
空字符是整形数据,其ascll值为0,区别于其他任何字符,用于标记字符串结尾,占1字节
空指针为指针数据,储存一个有别于其他数据对应的有效地址的有效地址来标记文件结尾或者输入错误,占4字节(随系统大小变化而变化)
空字符和空指针都可以用数值0表示
gets_s()函数
gets_s()函数与fgets()函数类似,可以限定读入的输入的最大长度
不同的是,gets_s()函数
只从标准输入中读入数据,所以省掉了第三个参数
读到换行符,gets_s()函数会丢弃它而不会储存它
在读入的输入超出最大输入限定时仍为读到换行符,gets_s()函数会把字符串数组中的首字符变为空字符,然后读取并丢弃之后的输入直到换行符或者文件结尾,返回空指针。接着调用依赖实现的“处理函数”,可能终止或退出程序
总结gets() fgets() gets_s()函数用法,在输入在目标数组大小可以容纳输入的条件下,三者都没问题,fgets()会保留换行符,需要编写函数将其替换成空字符。
在输入在目标数组大小不能容纳输入的条件下,fgets()最方便,gets()会造成buffer overflow,造成异常,使用gets_s()需要直到如何编写处理函数才不会导致程序终止,并且它会丢弃输入行剩余字符不论我们需要与否,而fgets()则可以按照需求保留剩余字符或者丢弃
s_gets()函数
s_gets()函数是自定义函数,使用fgets()将输入中的换行符替换成空字符,将空字符之后的字符串省略,保持键盘与输入行的一致性,避免输入行中有多余的字符,在下次输入时如果与期望数据类型不匹配可能导致程序崩溃。但其缺点是输入不匹配的数据类型会无反应,且丢弃多余字符串没有提示
char * s_gets(char * st, int n)
{
char ret_val;
int i=0;
ret_val=fget(st,n,stdin);
if(ret_val)
{
while(st[i]!=‘\n’&&st[i]!=‘\0’)
i++;
if(st[i]==‘\n’)
s[i]==‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
}
scanf()函数
scanf()配合转换符%s使用也可以输入字符串,但与fgets()不同的是,它停止读入的条件不同,一是超过转换符%s指定宽度,它会舍弃剩余输入,二是如果遇到空白字符(空格 换行 制表 空行)scanf()就会停止读入,不像gets(),fgets(),gets_s(),遇见换行符才停止读入,且读取换行符之前所有的字符,scanf从第一个非空白字符开始读取。不论先满足一或二哪种条件,scanf()都会停止读入。除此之外,scanf()返回成功读取的项数,或者读到文件结尾或读取失败时返回eof;fgets,gets,gets_s返回字符串首地址
//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”,count,name1,name2);
return 0;
}
注意,因为指定字面宽度而未能全部读取的字符将留在输入列中等待下次读取
因为scanf的读入停止条件,所以scanf一般不能完整读入书名或者歌名歌单,scanf擅长读取混合数据类型例如scanf(“%d %s %c,a,b,c,);
字符串的输出
puts()函数
参数为字符串首地址,puts()函数会在结尾自动添加换行符,该函数遇到空字符时停止输出
//put_out.c使用puts()
#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=“ a 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;
}
第一个puts函数的输出,双引号扩起来的字符串常量,且被视为该字符串的地址
但是下面程序
char dont[]={‘w’,’o’,’w’,!’};
puts(dont);这样的语句会导致puts()函数不知道从哪里开始停止输出,因为dont是个char类型数组结尾没有空字符做标记,所以会一直输出直到在其他储存单元内遇见一个空字符
fputs()函数
fputs()相比于puts()来说,是针对文件的定制版本,它有两个参数,第一个参数为待打印的字符串,第二个参数为输出至的文件,与gets()函数不同,它不在字符串末尾加换行符
注意,gets fgets puts fputs之间如非有意为之不可混用,因为混用可能造成打印两个换行符或者没打印出任何换行符,最好匹配使用,例如
char line[81];
while(gets(line))
puts(line);
char line[81];
while(fgets(line))
fputs(line);
printf()函数
printf()和puts()或fputs()相比,形式更复杂,执行时间更长,将字符串地址作为参数,它不会自动在结尾加换行符,在但它和scanf()一样,更适用于多类型数据输出,例如,printf(“hey %d %c %s”,a,b,c);
自定义输入/输出函数
如果c库标准函数无法满足输入/输出要求,可以在getchar()和putchar()基础上自定义函数,例如,想实现puts()函数功能但不想要最后的空字符
//put1.c—打印字符串,不添加\n
#include<stdio.h>
Void put1(const char *string)
{
while(*string!=‘\0’)//or while(*string)
putchar(*string++);
}//++高于*,是递增的是指针而非指针上的值
同一个程序,使用数组表示法,需要添加一个整型变量
int i=0;
while(string[i]!=‘\0’)
putchar(string[i++]);
//put2.c—打印一个字符串,并统计打印出字符的个数
#include<stdio.h>
int put2(const char *string)
{
int count=0;
while(*string)
{
putchar(*string++)
count++;
}
putchar(‘\n’);
return count;
}
结合put1和put2函数
//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 count %d characters.\n”,put2(“i never would cry old chairs to mend.”))
return 0;
}
void put1(const char *string)
{
while(*string)
putchar(*string++);
}
Int put2(const char * string)
{
int count==0;
while(*string)
{
putchar(*string++);
count++;
}
return (count);
}
字符串函数
c库提供很多字符串函数,其原型都存在string.h中,sprintf()的原型存在stdio.h头文件中
strlen()函数用于计算字符串实际长度,以下函数可缩短字符串长度,用到了strlen()函数
//test_fit.c—使用缩短字符串长度的函数
#include<stdio.h>
#include<string.h>
void fit(char *, unsigned int)
int main(void)
{
char mesg []=“things should be as simple as possible,””but not simpler.”;
puts(mesg);
fit(mesg,38);
puts(“let’s look at some more of the string.”);
puts(mesg+39);
return 0;
}
void fit(char *string, unsigned int size)
{
if(strlen(string)>size)
string[size]=‘\0’;
}
strcat()函数
strcat()是char *类型,它选了两个字符作为参数,它起到字符串之间的拼接作用,它将第二个字符串的备份拼接到第一个字符串末尾,然后将拼接后的字符串作为新字符串,返回新字符串的首地址
//str_cat.c—拼接两个字符串
#include<stdio.h>
#include<string.h>
#define SIZE 80
char * s_gets(char * st, int n);
Int main(void)
{
char flowers[SIZE];
char addon []=“a smell like an old shoes”;
puts(“what is your favorite flower”);
if(s_gets(flower,SIZE))
strcat(flower,addon);
puts(flower);
puts(addon);
}
else
puts(“end of file”);
puts(“bye”);
return 0;
}
char *s_gets(char* st, int n)
{
char * ret_val;
int i=0;
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;
}
strncat()函数
但不是每次第一个数组的大小都能容纳新字符串,如果出现数据溢出,可能会造成程序异常终止和其他隐患。一种方法是使用stren()查看第一个参数长度,先进行人为比较,第二种是使用strncat(),与strcat()不一样的是,strcat()使用第三个参数限制最多可以添加几个字符,这里应该注意添加的字符和原来的字符数相加后,还需预留一字节给空字符,因为strncat()函数会自动在字符串末尾加上一个空字符例如
//join_chk.c—拼接两个字符串,检查第一个数组大小
#include<stdio.h>
#include<string.h>
#define SIZE 30
#define BUGSIZE 13
char * s_gets(char * st, int n);
int main(void)
{
char flower[SIZE];
char addon []=“s smell like old shoes.”;
char bug[BUGSIZE];
int available;
puts(“what is your favorite flower?”);
s_gets(flower,SIZE);
if((strlen(addon)+strlen(flower)+1<=size)
strcat(flower,addon);
puts(flower);
puts(“what is your favorite flower”);
s_gets(bug, BUGSIZE);
available= BUGSIZE-strlen(bug)-1;
strncat(bug,addon,available);
puts(bug);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
if(ret_val)
{
while(st[i]!=‘\n’&&st[i]!=‘\0’)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’
continue;
}
return ret_val;
}
strcmp()函数
比较两个字符串的内容,因为它比较的是字符串而不是数组,所以他不会比较整个数组,因此它可以比较储存在不同大小中的字符串,如果两个字符串内容相同,strnmp()返回值为0,如果两个字符串不相同,返回非0值。例如,
//compare.c—该程序可以正常运行
#include<stdio.h>
#include<string.h>
#define ANSWER “Grant”
#define SIZE 40
char * s_gets(char *st, int n);
Int main(void)
{
char try[SIZE];
puts(“who is buried in Grant’s tomb?”);
s_gets(try, SIZE);
while(strcmp(try, ANSWER)!=0)//while(strcmp(try,ANSWER))
{
puts(“No,that’s wrong. Try again.”);
s_gets(try, SIZE);
}
puts(“that’s right ! ”);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val==fgets(st,n,stdin);
if(ret_val)
{
while(st[i]!=‘\n”&&st[i]!=‘\0’)
st[i]++;
if(st[i]==‘\n’
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
strcmp()返回值
如果比较的两个字符串相同,函数返回0,否则,函数返回第一个两个字符串中第一对不一样的字符的ascll码的值,(函数会一个一个对比完两个字符串中所有的字符,例如,
//compback.c—strcmp()的返回值
#include<stdio.h>
#include<string.h>
int main(void)
{
printf(“strcmp(\”A\”,,\’A\’) is ”);
printf(“%d\n”,strcmp(“A”,“A”));
printf(“strcmp(\”A\”,,\’B\’) is ”);
printf(“%d\n”,strcmp(“A”,“B”));
printf(“strcmp(\”B\”,,\’A\’) is ”);
printf(“%d\n”,strcmp(“B”,“A”));
printf(“strcmp(\”apples\”,,\’apple\’) is ”);
printf(“%d\n”,strcmp(“apples”,“apple”));//函数比较所有字符不单是字母,与其说函数按照字母顺序进行排序,不如说其按照字符所对应的ascll码进行排序
return 0;
}
下列函数决定程序是否停止读取输入
//quit_chk.c—某程序开始的部分
#include<stdio.h>
#include<string.h>
#define SIZE 80
#define STOP “quit”
char * s_gets(char * st, int n);
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&&s_gets(input[ct],SIZE)!=NULL&&strcmp(input[ct],STOP)!=0)
{
ct++;
}
printf(“%d strings entered\n”,ct);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
这个程序利用strcmp()函数比较输入的字符串是否满足停止条件,停止条件一为输入字符串为quit,第二为超过五行输入,第三位读到了文件结尾,满足三个条件中的一个即停止读入。比起EOF来说,输入空行更容易表示结束,因此程序可以替换为
while(ct<LIM&&s_gets(input[ct],SIZE)&&input[ct][0]!=\0)
strncmp()函数
strncmp()与strcmp()类似,比较两个字符串的内容,但是不同的是,它多了一个对所比较字符串中的字符个数的限定,例如strncmp(ar,br,5);表明函数将两字符串中的前五个字符做比较,而不会比较5个字符之后的内容。不论是达到字符限制,还是遇到了不同的字符,都会让函数停止比较。如果内容相同,将返回0,如果内容不同,将返回第一处两个不同字符间的ascll码之差
//starsrch.c—使用strncmp()
#include<stdio.h>
#include<string.h>
#define LISTSIZE 6
int main()
{
const char * list[LISTSIZE]=
{
“astronomy”,”astounding”,“astrophysics”,
“ostracize”,“asterism”,“astrophobia”
};
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 beginning with astro\n”,count);
return 0;
)
strcpy()函数
两个函数都可以拷贝整个字符串(包括末尾的空字符),相当于字符串赋值运算符,将字符串赋予到新的地址上或数组中,通过赋值表达式可以知道目标数组为第一个参数,(不必为数组首地址)源字符串为第二个参数,strcpy()返回值为第一个参数的值,
//copy1.c—演示strcpy()
#include<stdio.h>
#include<string.h>
#define SIZE 40
#define LIM 5
char *s_gets(char * st, int n);
int main(void)
{
char qwords[LIM][SIZE];
char temp[SIZE];
int i=0;
printf(“enter %d words beginning with q:\n”,LIM);
while(i<LIM&&s_gets(temp,SIZE))
{
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;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
//copy2.c—使用strcpy()
#include<stdio.h>
#include<string.h>
#define WORDS “beast”
#define SIZE 40
int main(void)
{
const char * orig= WORDS;
char copy[SIZE]=“be the best that you can be.”;
char * ps;
puts(orig);
puts(copy);
puts(ps);
return 0;
}
strncpy()函数
如果拷贝的源字符串超出目标数组可以储存的大小,会造成安全隐患。strncpy()函数
函数第三个函数限制最多可以拷贝的字符数,strncpy()函数不会拷贝字符串末尾的“\0”
//copy3.c—使用strncpy
#include<stdio.h>
#include<string.h>
#define SIZE 40
#define TARGSIZE 7
#define LIM 5
char * s_gets(char * st, int n);
{
char qwords[LIM][TARGSIZE];
char temp[SIZE];
int i=0;
printf(“enter %d words beginning with q:\n”);
while(i<LIM&&s_gets(temp, SIZE))
{
if(temp[0]!=‘q’)
printf(“%s doesn’t begin with q!\n”)
else
{
strncpy(qwords[i],temp, TARGSIZE-1)//如果不减1则在拷贝大于targsize的字符串时最后一个字符会被空字符覆盖
qwords[i][TARGSIZE-1]=‘\0’;
i++;
}
}
puts(“here are the words accepted”);
fir(i=0;i<LIM;i++)
puts(qwords[i]);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
sprintf()函数
与printf()函数类似,sprintf()将格式转换符和地址列表作为参数,但是不同的是,结果不会输出在屏幕上,而是输入到目标字符串中。不像其他字符串函数,sprintf()函数声明储存在stdio.h中而非string.h中。
//format.c—格式化字符串
#include<stdio.h>
#define MAX 20
char s_gets(char * st, int n);
int main(void)
{
char first[MAX];
char last[MAX];
char formal[2*MAX=10];
double prize;
puts(“enter your first name”);
s_gets(first, MAX);
puts(“enter your last name”);
s_gets(last, MAX);
puts(“enter your prize money”);
scanf(“%lf”,&prize);
sprintf(formal,“%s,%-19s:$%6.2f\n”,last,first,prize);
puts(formal);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
其他字符串(???)
char * strchr(const char * s, int c);
如果字符串s中包含字符c(s末尾空字符也在查找范围内),返回s首位置,否则,返回空指针
char * strpbrk(const char * s1,const char * s2)
如果字符串s1中包含字符串s2中任意字符,返回s1首位置,否则,返回空字符
char * strrchr(const char * s, int n)
函数返回s中n最后出现的位置,如未找到n,返回null
char * strstr(const char *s1, const char *s2);
函数返回s1中s2出现的首位置,如未找到s2,返回null
size_t strlen(const char * s)
size_t是sizeof运算符返回类型,为整数,但并未规定是什么类型整数,不同系统不一样,返回s字符数,不包括空字符
字符串示例:字符串排序
//sort_str.c读入字符串并排序字符串
#include<stdio.h>
#include<string.h>
#define SIZE 81
#define LIM 20
#define HALT “”//whats the point
void stsrt(char *strings [], int num);
char * s_gets(char * st, int n);
int main(void)
{
char input[LIM][SIZE];
char *ptstr[LIM];
int ct=0;
int k;
printf(“input up to %d lines, and I will sort them\n”, LIM);
printf(“to stop, press the enter key at a line’s start.\n”);
while(ct<LIM&&s_gets(inputs[ct]),SIZE)!=NULL&&input[ct][0]!=‘\0’
{
ptstr[ct]=input[ct];
ct++;
}
stsrt(ptstr,ct);
puts(“\nhere’s the sorted list”);
for(k=0;k<ct;k++)
puts(ptstr[k]);
return 0;
}
void stsrt(char *string [], int num)
{
char * temp;
int top, seek;
for(top=0;top<num-1;top++)//selection sort algorithm
for(seek=top+1;seek<num;seek++)
if(strcmp(strings[top],strings[seek])>0)
{
temp=strings[top];//change the value saved on pointers
strings[top]=strings[seek];
strings[seek]=temp;
}//改变指针而非改变实际的字符串
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
ctype.h字符函数和字符串
//mod_str.c—修改字符串
#include<stdio.h>
#include<string.h>
#include<ctype.h>
#define LIMIT 81
void ToUpper(char *);
int PunctCount (const char *);
int main(void)
{
char line[LIMIT];
char * find;
puts(“please enter a line”);
fgets(line,LIMIT,stdin);
find=strchr(line,’\n’);
if(find)
*find=‘\0’;
ToUpper(line);
puts(line);
printf(that line has %d punctuation characters\n”, PunctCount(line));
return 0;
}
void ToUpper(char * str)
{
while(*str)
{
*str=toupper(*str);
str++;
}
}
int PunctCount (const char *str)
{
int ct=0;
while(*str)
{
if (ispunct(*str))
ct++;
str++;
}
return ct;
}
命令行参数
在命令行环境下运行程序的指令称为命令行
//repeat.c—带参数的main()
#include<stdio.h>
int main(int argc, char *argv[])
{
int count;
printf(“the command line has %d arguments”\n”,argc-1);
for(count=1;count<argc;count++)
printf(“%d:%s\n”,count,argv[count]);
printf(“\n”);
return 0;
}
argc; argument count 为命令行中字符串数量
argv: argument value 为所输入字符串的地址
所输入的第一个字符串为程序名
把字符串转换为数字
数字既可以以字符串形式储存,也可以数值形式储存,c要求用数值形式进行数值运算
在屏幕显示上用字符串形式
scanf将字符串转换为数值形式,printf和sprintf将数值转换为字符串
atoi()函数
接受一个字符串,返回相应的整数值,例如
//hello.c—把命令行参数转换为数字
#include<stdio.h>
#include<stdlib.h>
int main(int argc, char * argv[])
{
int i,times;
if (argc<2||(times = atoi(argv[1])) <1)
printf(“usage: %s positive number\n”,argv[0])
else
for(i=0;i<times;i++)
puts(“hello, good looking!”);
return 0;
}
如果字符串只以整数开头,那么atoi()只返回整数的数值部分
如果字符串中没有数字,那么atoi()返回0
stdlib.h头文件
atof()把字符串转换成double类型数值
atol()把字符串转换成long类型数值
atoi()把字符串转换成int类型数值
更智能的函数还有:
以下函数可以识别和报告字符串中的首字符是否是数字
strtol把字符串转换成long类型数值 可指定数字进制(最多转换36进制)
long strtol(const char * restrict nptr, char ** restrict endptr, int base)
nptr是指向带转换字符串的指针,endptr是一个指针的地址,该指针被设置为表识输入数字结束字符的地址,base表示以什么进制写入数字
strtod把字符串转换成double类型数值,只以10进制转换
strtoul把字符串转换成unsigned long类型数值 可指定数字进制
//strcnvt.c—使用strtol()
#include<stdio.h>
#include<stdlib.h>
#define LIM 30
char * s_gets(char * st, int n);
int main()
{
char number[LIM];
char * end;
long value;
puts(“enter a number (empty line to quit):”);
while(s_gets(number,LIM)&&number[0]!=‘\0’)
value=strtol(number,&end,10);
printf(“base 10 input, base 10 output:%1d,stopped at %s (%d)\n”,value,end,*end);
value=strtol(number,&end,16);
printf(“base 16 input, base 10 output:%1d,stopped at %s (%d)\n”,value,end,*end);
puts(“next number”);
}
puts(“bye!\n”);
return 0;
}
char * s_gets(char * st, int n)
{
char * ret_val;
int i=0;
ret_val=fgets(st,n,stdin);
If(ret_val)
{
while(st[i!=‘\0’&&st[i]!=‘\n)
st[i]++;
if(st[i]==‘\n’)
st[i]=‘\0’;
else
while(getchar()!=‘\n’)
continue;
}
return ret_val;
}
ftoa()和itoa()将double和int数值转换为字符串,可用printf和sprint代替