第11章 字符串和字符串函数(4 字符串函数)

发布于 14 天前  50 次阅读


11.5 字符串函数

C库提供了多个处理字符串的函数,ANSI C把这些函数的原型放在string.h的头文件中。其中常用的函数有strlen()\strcat()\strcmp()\strncmp()\strcpy和strncpy()。另外还有sprintf()函数放在stdio.h头文件中

11.5.1 strlen()函数

strlen()函数用于统计字符串长度。下面的函数可以缩短字符串的长度。
void fit(char *string,usigned int size)
{
    if(strlen(string)>size)
        string[size] = '\0';
}
该函数要改变字符串,所以函数头在声明形式参数string时没有使用const限定符。
//  test_fit.c      --使用缩短字符串长度的函数
#include<stdio.h>
#include<string.h>
void fit(char *string,unsigned int size);
int main(void)
{
    char mesg[] ="Things should be as simple as possible,"
                            " but not simple.";
    puts(mesg);
    fit(mesg,38);
    puts(mesg);
    puts("Let's look at some more of string.");
    puts(mesg + 39);

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

file

fit()函数把第39个元素的逗号替换成了'\0'字符。puts()函数在空字符处停止输出,并忽略其余字符,然而这些字符还在缓冲区中。下面的函数调用把这些字符打印出来了。
puts(mesg + 8)
表达式mesg+39是mesg[39]的地址。该地址上存储的是空字符。所以puts()显示该字符并继续输出直至遇到原来字符串中的空字符

file

11.5.2 strcat()函数

strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char *(即指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。
//  str_cat.c       --拼接两个字符
#include<stdio.h>
#include<string.h>
#define SIZE 80
char *s_gets(char *string,int n);
int main(void)
{
    char flower[SIZE];
    char addon[]="s smell like 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 encountered!");
    puts("bye");

    return 0;
}
char *s_gets(char *string,int n)
{
    char *ret_val;
    int i = 0;

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

file

11.5.3 strncat()函数

strcat()函数无法检查第一个数组是否能容纳第2个字符串。如果分配给第1个数组的空间不够大,多出来的字符溢出到相邻存储单元时就会出现问题。当然可以像第一个strlen()查看第一个数组的长度。注意,要给拼接后的字符串长度加1才够空间存放末尾的空字符,或者,用strncat(),该函数的第3个参数制定了最大添加字符数,例如,strncat(bugs,addon,13)将把addon字符串的内容添加附加给bugs,在加到第13个字符或遇到空字符时停止,因此,算上空字符(无论哪种情况都要添加空字符),bugs数组应该足够大,以容纳原始字符(不包含空字符)、添加上原始字符串在后面的13个字符和末尾的空字符。
//Q:计算avaiavle变量的值,用于表示允许添加的最大的字符数。
//join_chk.c    --拼接两个字符串,检查第一个数组的大小
#include<stdio.h>
#include<string.h>
#define SIZE 30
#define BUGSIZE 13
char *s_gets(char *string,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 bug?");
    s_gets(bug,BUGSIZE);
    available = BUGSIZE - strlen(bug) - 1;
    strncat(bug,addon,available);
    puts(bug);

    return 0;   
}
char *s_gets(char *string,int n)
{
    char *ret_val;
    int i = 0;

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

file

gets()造成的安全隐患来自使用该程序的人,而strcat()暴露的问题是那些粗心的程序猿造成的。无法控制用户会进行什么操作,但是可以控制你的程序做什么。C语言相信程序员,因此程序员有责任确保strcat()的使用安全。

11.5.4 strcmp()函数

假设要把用户的响应与已存储的字符串作比较.
//nogo.c    --该程序是否能正常运行?
#include<stdio.h>
#define ANSWER "Grant"
#define SIZE 40
char *s_gets(char *string,int n);
int main(void)
{
    char try1[SIZE];

    puts("Who is buried in Grant's tomb?");
    s_gets(try1,SIZE);
    while(try1!=ANSWER)
    {
        puts("No,that's wrong.Try again.");
        s_gets(try1,SIZE);
    }
    puts("That's wrong!");

    return 0;
}
char *s_gets(char *string,int n)
{
    char *ret_val;
    int i = 0;

    ret_val = fgets(string,n,stdin);
    if(ret_val)
    {
        while(string[i]!='\n'&&string[i]!='\0')
            i++;
        if(string[i]=='\n')
            string[i] = '\0';
        else
            while(getchar()!='\n')
                continue;
    }
    return ret_val;
}
这个程序看上去没问题,但是运行后却不对劲,ANSWER和try都是指针,所以try!=ANSWER检查的不是两个字符串是否相等,而是这两个字符串的地址是否相同。因为ANSWER和try储存在不同的位置,所以这两个地址不可能相同,因此,无论用户输入什么,程序都提示输入不正确。
该函数要比较的是字符串的内容,不是字符串的地址,读者可以自己设计一个函数,也可使用C标准库中的strcmp()函数(用于字符串比较)。该函数通过比较运算符来比较字符串,就像比较数字一样。如果两个字符串参数相等,该函数就返回0,否则就返回非零值。
//nogo.c    --该程序是否能正常运行?
#include<stdio.h>
#include<string.h>
#define ANSWER "Grant"
#define SIZE 40
char *s_gets(char *string,int n);
int main(void)
{
    char try1[SIZE];

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

    return 0;
}
char *s_gets(char *string,int n)
{
    char *ret_val;
    int i = 0;

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

file

由于非零值都为“真”,所以许多经验丰富的C程序员会把该例中main()中的while循环写出
while(strcmp(try,ANSWER))
strcmp()函数比较的是字符串,不是整个数组,这是非常好的功能。虽然数组try占用40个字节,而存储在其中的“Grant”只占用了6个字节(还有一个用来存放空字符),strcmp()只会比较try中的第1个空字符前面的部分,所以,可以用strcmp()比较储存在不同大小数组中的字符串。
如果用户输入GRANT、grant或者Ulysses S. Grant会怎么样?程序会告诉用户输入错误。希望程序更友好,必须把所有正确答案的可能性包含其中。这里可以用一些小技巧。例如,可以用#define定义类GRANT这样的答案,并编写一个函数把输入的内容都转换成大写,就解决了大小写的问题。
//nogo.c    --该程序是否能正常运行?
#include<stdio.h>
#include<string.h>
#define ANSWER "Grant"
#define SIZE 40
char *s_gets(char *string,int n);
void daxiaoxie(char *string,int n);
int main(void)
{
    char try1[SIZE];

    puts("Who is buried in Grant's tomb?");
    s_gets(try1,SIZE);
    daxiaoxie(try1,SIZE);
    while(strcmp(try1,ANSWER))
    {
        puts("No,that's wrong.Try again.");
        s_gets(try1,SIZE);
        daxiaoxie(try1,SIZE);
    }
    puts("That's wrong!");

    return 0;
}
char *s_gets(char *string,int n)
{
    char *ret_val;
    int i = 0;

    ret_val = fgets(string,n,stdin);
    if(ret_val)
    {
        while(string[i]!='\n'&&string[i]!='\0')
            i++;
        if(string[i]=='\n')
            string[i] = '\0';
        else
            while(getchar()!='\n')
                continue;
    }
    return ret_val;
}
void daxiaoxie(char *string,int n)
{
    if(string[0]>='a'&&string[0]<='z')
        string[0] = string[0] - 32;
}

1.strcmp()的返回值

如果strcmp()比较的字符串不同,他会返回什么值?
//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(\"C\",\"A\") is ");
    printf("%d\n",strcmp("C","A"));

    printf("strcmp(\"Z\",\"a\") is ");
    printf("%d\n",strcmp("Z","a"));

    printf("strcmp(\"apples\",\"apple\") is ");
    printf("%d\n",strcmp("apples","apple"));

    return 0;
 }

file

strcmp()比较“A”本身,返回0;
比较“A”和“B”,返回-1;
比较“B”和“A”,返回1;
这说明,如果在字母中第1个字符串位于第2个字符串前面,strcmp()中就返回负数:反之,strcmp()则返回整数。所以,strcmp()比较"C"和"A",返回1.其他系统可能返回2,即两者的ASCII码之差。ASCII标准规定,在字母表中,如果第1个字符串在第二个字符串前面,strcmp返回一个负数;如果两个字符串相同,strcmp()返回0;如果第1个字符串在第二个字符串后面,strcmp()返回正数。然而,返回的具体值取决于实现。例如,下面给出在不同实现中的输出,该实现返回两个字符的差值:

file

如果两个字符串开始的几个字符都相同会怎样?
一般而言,strcmp()会依次比较每个字符,直到发现第1对不同的字符为止。然后返回相应的值。例如:在上面的最后一个例子中”apples“和”apple“只有最后一对字符不相同(”apples“的s和”apple“的空字符)。由于空字符在ASICC中排第1.字符s一定在它后面,所以strcmp()返回一个正数。
最后一个例子表明。strcmp()比较所有的字符,不只是字母。所以,与其说该函数按字母顺序进行比较,不如说是按照机器排序序列(machin collating sequence)进行比较,即根据字符的数值进行比较(通常使用ASCII值)。在ASCII中,大写字母在小写字母前面,所以strcmp("Z","a")返回值是负值。
大多数情况下,strcmp()返回的具体值并不重要,我们只在意该值是0还是非0(即,比较的两个字符串是否相等)。或者按字母排序字符串,在这种情况下,需要直到比较的结果是为正、为负还是为0。
strcmp()函数比较的是字符串,不是字符,所以其参数应该是字符串而不是字符。但是,char类型实际上是整数类型,所以可以使用关系运算符来比较字符。假设word是储存在char类型数组中的字符串,ch是char类型的变量。下面语句都有效:
if(strcmp(word,"quit")==0)    //使用strcmp比较字符串
    puts("Bye!");
if(ch == 'q')         //使用==比较字符
    puts("Bye!");
尽管如此,不要使用ch或’q‘作为strcmp()的参数
//Q用strcmp()函数检测程序是否要要停止读取输入。
//quit_chk.c    --某程序的开始部分
//Q用strcmp()函数检测程序是否要要停止读取输入。
//quit_chk.c    --某程序的开始部分
#include<stdio.h>
#include<string.h>
#define SIZE 80
#define LIM 10
#define STOP "quit"
char *s_gets(char *string,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 *string,int n)
{
    char *ret_val;
    int i = 0;

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

file

该程序在读到EOF字符(这种情况下s_gets()返回NULL)、用户输入quit或输入项达到LIM时退出。
顺带一提,有时输入空行(即,只按下Enter或Return键)表示结束输入更方便。为实现这一功能,只需要修改一下while循环的条件即可:
while(ct <LIM && s_gets(input[ct],SIZE)!=NULL&&input[ct][0]!='\0')
这里input[ct]是刚输入的字符串,input[ct][0]是该字符的第一个字符。如果用户输入空行,s_gets()便会把行第1个字符(换行符)替换成空字符。所以,下面的表达式用于检测空行:
input[ct][0]!='\0'

2.strncmp()函数

strcmp()函数比较字符串中的字符,直到发现不同的字符为止,这一过程可能会持续到字符串的末尾。而strncmp()函数在比较两个字符串时,可以比较到字符不同的地方,也可以只比较第3个参数指定的字符数。例如,要查找以“astro”开头的字符串,可以限定函数只查找这5个字符。如下程序所示:
//starsrch.c --使用strncmp()
#include<stdio.h>
#include<string.h>
#define LISTSIZE 6
int main(void)
{
    const char *list[LISTSIZE] = 
    {
        "astronomt","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 word beginning"" with astro.\n",count);

    return 0;
}

file

11.5.5 strcpy()和strncpy()函数

前面提到过,如果pts1和pts2都是指向字符串的指针,那么下面语句拷贝的是字符串的地址而不是字符串本身:
pst2 = pst1;
如果希望拷贝整个字符串,要使用strcpy().下述程序要求用户输入以q开头的单词。该程序把输入拷贝至一个临时一个临时数组中,如果第1个字母是q,程序调用strcpy()把整个字符串从临时数组拷贝至目标数组。strcpy()函数相当于字符串赋值运算符。
//copy1.c   --演示strcpy() 
#include<stdio.h>
#include<string.h>    //strcpy()
#define SIZE 40
#define LIM 5
char *s_gets(char *string,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 does'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 *string,int n)
{
    char *ret_val;
    int i = 0;

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

file

注意,只有在输入以q开头的单词后才会递增计算器i,而且该程序通过比较字符进行判断:
if(temp[0]!='q')
这行代码的意思是:temp中的第1个字符是否是q?当然也可以通过比较字符串进行判断:
if(srtncmp(temp,"q",1)!=0)
这行代码的意思是:temp字符串和“q”的第1个元素是否相等?
请注意,strcpy()第2个参数(temp)指向的字符串被拷贝至第1个参数(qword[i])指向的数组中,拷贝出来的字符串被称为目标字符串,最初的字符串被称为源字符串。参考赋值表达式语句,很容易记住strcpy()参数的顺序,即第1个是目标字符串,第2个是源字符串。
char target[20];
int x;
strcpy(target,"Hi ho!");
target = "So long";   //语法错误
程序员有责任确保目标数组有足够的空间容纳源字符串的副本。下面的代码有点问题
char *str;
strcpy(str,"The C of Tranquility");   //有问题
strcpy()把"The C of Tranquility"拷贝到str指向的地址上,但是str未被初始化,所以,所以该字符串可能被拷贝到任意的地方!
总之,strcpy()接受两个字符串指针作为参数,可以把指向源字符的第2个指针声明为指针、数组名或字符串常量;而指向源字符串副本的第1个指针应指向一个数据对象,且该对象有足够的空间存储源字符串的副本,记住,声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间。

1.strcpy()的属性

strcpy()函数还有两个有用的属性。第一,strcpy()的返回类型是char *,该函数返回的是第1个参数的值,即是第一个字符的地址。第二,第1个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分。
//copy.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);
    ps = strcpy(copy+7,orig);
    puts(copy);
    puts(ps);

    return 0;
}

file

注意,strcpy()把源字符串中的空白字符也拷贝在内。该例中,空字符串覆盖了copy数组中that的第1个t。由于第一个参数是copy+7,所以ps指向copy中的第8个元素,puts(ps)从该处开始打印。

file

2.更谨慎的选择:strncpy()

strcpy()和strncpy()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。
拷贝字符串用strncpy更安全。该函数的第3个参数指明了可以拷贝的最大字符数。
为了演示目标空间装不下源字符串的副本会发生什么情况,该程序使用了一个相当小的目标字符串(共7个元素,包含6个字符);
//cope2.c       --strncpy()
#include<stdio.h>
#include<string.h>
#define SIZE 40
#define TARGSIZE 7
#define LIM 5
char *s_gets(char *st,int n);
int main(void)
{
    char qwords[LIM][SIZE];
    char temp[SIZE];
    int i;

    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
        {
            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;
}
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;
}

file

strnpy(target,source,n),把source中的n个字符或空字符之前的字符(先满足哪个条件就拷贝到何处)拷贝至target中,因此,如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是strncpy()拷贝长度不会超过n,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝副本中不一定有空字符。鉴于此,该程序把n设置为比目标数组大小少1(TARGSIZE-1),然后把数组最后一个元素设置为空字符:
strncpy(qwords[i],temp,TARGSIZE - 1);
qwords[i][TARGSIZE-1] = '\0';
这样做确保存储的是一个字符串,如果目标空间能容纳字符串的副本,那么从源字符串拷贝的空字符便是该副本的结尾;如果目标空间装不下副本,则把最后一个元素设置为空字符。

11.5.6 sprintf()函数

sprintf()函数声明在stdio.h中,而不是在string.h中。该函数和printf()类似,但是它是把数据写入字符串,而不是打印在显示器上。因此,该函数可以把多个元素组合成一个字符串。sprintf()的第1个参数是目标字符串的地址。其余参数个printf()相同,即格式字符串和待写入项的列表。
下述程序用sprintf()把3个项(两个字符串和一个数字)组合成一个字符串。注意,sprintf()的用法和printf()相同,只不过sprintf()把组合后的字符串储存在数组formal中二不是显示在屏幕上。
//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]!='\n'&&st[i]!='\0')
            i++;
        if(st[i]=='\n')
            st[i]='\0';
        else
            while(getchar()!='\n')
                continue;
     }
     return ret_val;
 }

file

11.5.7 其他字符串函数

ANSIC库有20多个用于处理字符串的函数。
- char *strcpy(char *restrict s1,cpnst char *restric s2);
该函数把s2指向的字符串(包括空白符)拷贝至s1指向的位置,返回值是s1。
- char *strncpy(char *restrict s1,cpnst char *restric s2,size_t n)
该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符串后面的字符,如果源字符串少于n个,目标字符串就可以拷贝的空字符结尾;如果原子符串有n个或超过n个字符,就不会拷贝空字符
- char *strcat(char *restrict,const char *restrict s2);
该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符串将覆盖s1字符串末尾的空字符。该函数返回s1.
- char *strncat(char *restrict,const char *restrict s2,size_t n);
该函数把s2字符串中的n个字符拷贝至s1字符串末尾,s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1.
- int strcmp(const char *s1,const char *s2);
如果s1字符在机器排序序列中位于s2字符的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数。
- int strncmp(const char *s1,const char *s2,size_t n);
 该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符或遇到第一个空字符时停止比较。
- char *strchr(const char *s,int c);
如果s字符串中包含c字符,该函数返回指定s字符串首次出现的c字符的指针(末尾的空字符也是字符串的一部分,所以在查找范围之内);如果字符串s中未找到c字符,该函数返回空指针。
- char *strpbrk(const char *s1,const char *s2);
如果s1字符包含s2字符串中的任意字符,该函数返回指向s1字符串首位置的指针:如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。
- char *strrchr(const char *s1,int c)
该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以也在查找范围之内)。如果未找到c字符,则返回空指针。
- char *strstr(const char *s1,const char *s2);
该函数返回s1字符串中s2字符串出现的受位置。如果在s1中没有找到s2,则返回空指针。
- size_t strlen(const char *s);
该函数返回s字符串中的字符数,不包括末尾的空字符。
请注意,那些使用const关键词的函数原型表明,函数不会更改字符串。例如下面的函数原型:
char *strcpy(char *restrict s1,const char *restrict s2);
表明不能更改s2指向的字符串,至少不能在strcpy()中更改字符串。但是可以更改s1指向的字符串,这样做很合理,因为s1是目标字符串,要改变,而s2是源字符串,不能更改。
关键字restrict该关键字限制了函数参数的用法,例如,不能把字符串拷贝给函数本身。
第5章中讨论过,size_t类型是sizeof运算符返回的类型,C规定了sizeof运算符返回一个整数类型,但未指定是哪种整数类型,所以size_t在一个系统中可以是unsigned int,而在另一个系统中可以是unsigned long。string.h头文件针对系统定义了size_t,或者参与其他有size_t定义的头文件。
其中一个函数的简单用法,前面学过fgets()读入一行数据时,在目标字符串的末尾添加换行符。我们自定义的s_gets()函数通过while()循环检测换行符。其实,可以用strchr()代替s_gets()。首先使用strchr()查找换行符(如果有的话)。如果该函数发现了换行符,将返回换行符的地址,然后便可以用空字符代替该位置上的换行符。
char line[80];
char *find;

fgets(line,80,stdin);
find = strchr(line,'\n'); //查找换行符
if(find)    //如果没有找到返回NULL
{
    *find = '\0'; //在该处的字符替换成'\0'
}
如果strchr()未找到换行符,fgets()在达到行末尾之前就达到了它能读取的最大字符数,可以像s_gets()中那样,给if添加一个else来处理这种情况。

擦肩而过的概率