C | 字符串和字符串函数

目录

一、表示字符串和字符串I/O

1.1 在程序中定义字符串

1.字符串字面量(字符串常量)

2.字符串数组和初始化

3.数组和指针

4.数组和指针的区别

5.字符串数组

1.2 指针和字符串

二、字符串输入

2.1 分配空间

2.2 不幸的gets()函数

2.3 gets()的替代品

1.fgets()函数(和fputs())

2.gets_s()函数

3.s_gets()函数

2.4 scanf()函数

三、字符串输出

3.1 puts()函数

3.2 fputs()函数

3.3 printf()函数

四、自定义输入/输出函数

五、字符串函数

5.1 strlen()函数

 5.2 strcat()函数

5.3 strncat()函数

5.4 strcmp()函数

1.strcmp()的返回值

2.strncmp()函数

5.5 strcpy()和strncpy()函数

1.strcpy()的其他属性

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

5.6 sprintf()函数

5.7 其他字符串函数

1、char *strcpy(char * restrict s1, const char * restrict s2);

2、char *strncpy(char * restrict s1, const char * restrict s2, size_t n);

3、char *strcat(char * restrict s1, const char * restrict s2);

4、char *strncat(char * restrict s1, const char * restrict s2, size_t n);

5、int strcmp(const char * s1, const char * s2);

6、int strncmp(const char * s1, const char * s2, size_t n);

7、char *strchr(const char * s, int c);

8、char *strpbrk(const char * s1, const char * s2);

9、char *strrchr(const char * s, int c);

10、char *strstr(const char * s1, const char * s2);

11、size_t strlen(const char * s);

六、字符串示例:字符串排序

七、ctype.h字符函数和字符串

八、命令行参数

九、把字符串转换为数字


一、表示字符串和字符串I/O

#include 
#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(pt1);
    words[8] = 'p';
    puts(words);
    getchar();
    return 0;
}

运行结果:

Here are some strings:
I am a symbolic string constant.
I am a string in an array.
Something is pointing at me.
I am a spring in an array.

和printf()函数一样,puts()函数也属于stdio.h系列的输入/输出函数。但是,puts()函数只显示字符串,而且自动在显示的字符串末尾加上换行符

1.1 在程序中定义字符串

定义字符串的方法:字符串常量、char类型数组、指向char的指针

1.字符串字面量(字符串常量)

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

从ANSI C标准起,如果字符串字面量之间没有间隔,或者用空白字符分隔,C会将其视为串联起来的字符串字面量。

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

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

printf("\"Run, Spot, run!\" exclaimed Dick.\n");

字符串常量属于静态存储类别(static storage class),用双引号括起来的内容被视为指向该字符串储存位置的指针

/* strptr.c -- 把字符串看作指针 */
#include 
int main(void)
{
    printf("%s, %p, %c\n", "We", "are", *"space farers");
    getchar();
    return 0;
}

运行结果:

We, 00405064, s

2.字符串数组和初始化

定义字符串数组时,必须让编译器知道需要多少空间。一种方法是用足够空间的数组储存字符串。另一种方法是让编译器确定初始化字符数组的大小

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

const char m1[40] = { 'L','i', 'm', 'i', 't', ' ', 'y', 'o', 'u', 'r', 's', 'e', 'l',
'f', ' ', 't', 'o', ' ', 'o', 'n', 'e', ' ','l', 'i', 'n', 'e',
'\", 's', ' ', 'w', 'o', 'r','t', 'h', '.', '\0'};、//标准的数组初始化形式,更复杂

注意:注意最后的空字符。没有这个空字符,这就不是一个字符串,而是一个字符数组。

所有未被使用的元素都被自动初始化为0(这里的0指的是char形式的空字符,不是数字字符0)

C | 字符串和字符串函数_第1张图片

 const char m2[] = "If you can't think of anything, fake it.";//编译器会自动计算数组的大小

让编译器计算数组的大小只能用在初始化数组时。如果创建一个稍后再填充的数组,就必须在声明时指定大小。声明数组时,数组大小必须是可求值的整数。

一些数组初始化的例子:

int n = 8;

char cookies[1]; // 有效

char cakes[2 + 5];// 有效,数组大小是整型常量表达式

char pies[2*sizeof(long double) + 1]; // 有效

char crumbs[n]; // 在C99标准之前无效,C99标准之后这种数组是变长数组

字符数组名和其他数组名一样,是该数组首元素的地址。因此:

char car[10] = "Tata";

//一下表达式都为真

car == &car[0]、*car == 'T'、*(car+1) == car[1] == 'a'。

3.数组和指针

还可以使用指针表示法创建字符串。

const char * pt1 = "Something is pointing at me.";
const char ar1[] = "Something is pointing at me.";

在这两种情况下,带双引号的字符串本身决定了预留给字符串的存储空间。尽管如此,这两种形式并不完全相同。

数组形式(ar1)

当把程序载入内存时,也载入了程序中的字符串。字符串储存在静态存储区(static memory)中。但是,程序在开始运行时才会为该数组分配内存。此时,才将字符串拷贝到数组中。此时字符串有两个副本:一个是在静态内存中的字符串字面量,另一个是储存在ar1数组中的字符串。

不能更改ar1,如果改变了ar1,则意味着改变了数组的存储位置(即地址)。可以进行类似ar1+1这样的操作,标识数组的下一个元素。但是不允许进行++ar1这样的操作。递增运算符只能用于变量名前(或概括地说,只能用于可修改的左值),不能用于常量

指针形式(*pt1)
一旦开始执行程序,它会为指针变量pt1留出一个储存位置,并把字符串的地址储存在指针变量中。该变量最初指向该字符串的首字符,但是它的值可以改变。因此,可以使用递增运算符。

字符串字面量被视为const数据。应该把pt1声明为指向const数据的指针。不能用pt1改变它所指
向的数据,但是仍然可以改变pt1的值。如果把一个字符串字面量拷贝给一个数组,就可以随意改变数据。

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

// addresses.c -- 字符串的地址
#define MSG "I'm special"
#include 
int main()
{
    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");
    getchar();
    return 0;
}

运行结果:

address of "I'm special": 00405064
address ar: 0061FF10
address pt: 00405064
address of MSG: 00405064
address of "I'm special": 00405064

  • pt和MSG的地址相同,而ar的地址不同
  • 编译器可以把多次使用的相同字面量储存在一处或多处
  • 静态数据使用的内存与ar使用的动态内存不同

4.数组和指针的区别

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

两者主要的区别是:数组名heart是常量,而指针名head是变量。

  • 两者都可以使用数组表示法
  • 两者都能进行指针加法操作
  • 只有指针表示法可以进行递增操作

假设想让head和heart统一,可以这样做:

head = heart; /* head现在指向数组heart */

heart = head; /* 非法构造,不能这样写 */

另外,还可以改变heart数组中元素的信息:

heart[7]= 'M';或者*(heart + 7) = 'M';

数组的元素是变量(除非数组被声明为const),但是数组名不是变量。

建议在把指针初始化为字符串字面量时使用const限定符。如果不修改字符串,不要用指针指向字符串字面量。

5.字符串数组

// arrchar.c -- 指针数组,字符串数组
#include 
#define SLEN 40
#define LIM 5
int main(void)
{
    const char *mytalents[LIM] = {
    "Adding numbers swiftly",
    "Multiplying accurately", "Stashing data",
    "Following instructions 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));
    getchar();
    return 0;
}

运行结果:

Let's compare talents.
My Talents                           Your Talents
Adding numbers swiftly               Walking in a straight line
Multiplying accurately               Sleeping
Stashing data                        Watching television
Following instructions to the letter Mailing letters
Understanding the C language         Reading email

sizeof mytalents: 20, sizeof yourtalents: 200

mytalents中的指针指向初始化时所用的字符串字面量的位置,这些字符串字面量被储存在静态内存中;而yourtalents 中的数组则储存着字符串字面量的副本。为字符串数组分配内存的使用率较低

C | 字符串和字符串函数_第2张图片

 (实际上,mytalents 数组的指针元素所指向的字符串不必储存在连续的内存中,图中所示只是为了强调两种数组的不同)

综上所述,如果要用数组表示一系列待显示的字符串,请使用指针数组,因为它比二维字符数组的效率高。如果要改变字符串或为字符串输入预留空间,不要使用指向字符串字面量的指针

1.2 指针和字符串

/* p_and_s.c -- 指针和字符串 */
#include 
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);
    getchar();
    return 0;
}

运行结果:

Don't be a fool!
mesg = Don't be a fool!; &mesg = 0061FF1C; value = 00405064
copy = Don't be a fool!; © = 0061FF18; value = 00405064

第1项,mesg和copy都以字符串形式输出(%s转换说明)。第2项,打印两个指针的地址。最后一项,显示两个指针的值。程序并未拷贝字符串。

二、字符串输入

2.1 分配空间

char *name;
scanf("%s", name);

此时该参数是个未初始化的指针,name可能会指向任何地方。最简单的方法是,在声明时显式指明数组的大小。

char name[81];

2.2 不幸的gets()函数

gets()函数,读取整行输入,直至遇到换行符,然后丢弃换行符,储存其余字符,并在这些字符的末尾添加一个空字符使其成为一个 C 字符串。它经常和 puts()函数配对使用,该函数用于显示字符串,并在末尾添加换行符。

/* getsputs.c -- 使用 gets() 和 puts() */
#include 
#define STLEN 5
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.");
    getchar();
    return 0;
}

运行结果:

Enter a string, please.
no
Your string twice:
no
no
Done.

gets()唯一的参数是 words,它无法检查数组是否装得下输入行。如果输入的字符串过长,会导致缓冲区溢出(buffer overflow)

C11标准委员会直接从标准中废除了gets()函数。

2.3 gets()的替代品

1.fgets()函数(和fputs())

如果fgets()读到一个换行符,会把它储存在字符串中。这点与gets()不同,gets()会丢弃换行符。fgets()函数的第3 个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin(标准输入)作为参数,该标识符定义在stdio.h中。fgets()函数把换行符放在字符串的末尾(假设输入行不溢
出),通常要与 fputs()函数(和puts()类似)配对使用。

/* fgets1.c -- 使用 fgets() 和 fputs() */
#include 
#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.");
    system("pause");
    return 0;
}

运行结果:

Enter a string, please.
apple pie
Your string twice (puts(), then fputs()):
apple pie

apple pie
Enter another string, please.
strawberry shortcake
Your string twice (puts(), then fputs()):
strawberry sh
strawberry shDone.

puts()函数会在待输出字符串末尾添加一个换行符,而fputs()不会这样做

fputs()函数返回指向 char的指针。如果一切进行顺利,该函数返回的地址与传入的第 1 个参数相同。但是,如果函数读到文件结尾,它将返回一个特殊的指针:空指针(null pointer)。如果在读入数据时出现某些错误,该函数也返回NULL。

/* fgets2.c -- 使用 fgets() 和 fputs() */
#include 
#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.");
    system("pause");
    return 0;
}

运行结果:

Enter strings (empty line to quit):
By the way, the gets() function
By the way, the gets() function
also returns a null pointer if it
also returns a null pointer if it
encounters end-of-file.
encounters end-of-file.

Done.

统使用缓冲的I/O。这意味着用户在按下Enter键之前,输入都被储存在临时存储区(即,缓冲区)中。按下Enter键就在输入中增加了一个换行符,并把整行输入发送给fgets()。对于输出,fputs()把字符发送给另一个缓冲区,当发送换行符时,缓冲区中的内容被发送至屏幕上

以下程序读取输入行,删除储存在字符串中的换行符,如果没有换行符,则丢弃数组装不下的字符。

/* fgets3.c -- 使用 fgets() */
#include 
#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 // 如果word[i] == '\0'则执行这部分代码
            while (getchar() != '\n')
            continue;
        puts(words);
        }
    puts("done");
    system("pause");
    return 0;
}

运行结果:

Enter strings (empty line to quit):
This
This
program seems
program s
unwilling to accept long lines.
unwilling
But it doesn't get stuck on long
But it do
lines either.
lines eit

done

空字符和空指针

空字符(或'\0')是用于标记C字符串末尾的字符,其对应字符编码是0。由于其他字符的编码不可能是 0,所以不可能是字符串的一部分。空指针(或NULL)有一个值,该值不会与任何数据的有效地址对应。通常,函数使用它返回一个有效地址表示某些特殊情况发生,例如遇到文件结尾或未能按预期执行。

空字符是整数类型,而空指针是指针类型。

空字符是一个字符,占1字节;而空指针是一个地址,通常占4字节。

2.gets_s()函数

C11新增的gets_s()函数(可选)

gets_s(words, STLEN);

gets_s()与fgets()的区别如下:

  • gets_s()只从标准输入中读取数据,所以不需要第3个参数。
  • 如果gets_s()读到换行符,会丢弃它而不是储存它。
  • 如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针。

3.s_gets()函数

编写函数:如果字符串中出现换行符,就用空字符替换它;如果字符串中出现空字符,就丢弃该输入行的其余字符,然后返回与fgets()相同的值。

#include 

char * s_gets(char * st, int n)
{
char * ret_val;
int i = 0;ret_val = fgets(st, n, stdin);
if (ret_val) // 即,ret_val != NULL
    {
    while (st[i] != '\n' && st[i] != '\0')
        i++;
    if (st[i] == '\n')
        st[i] = '\0';
    else
        while (getchar() != '\n')
        continue;
    }
return ret_val;
}

其缺陷是遇到不合适的输入时毫无反应。它丢弃多余的字符时,既不通知程序也不告知用户。

2.4 scanf()函数

scanf()函数有两种方法确定输入结束。无论哪种方法,都从第1个非空白字符作为字符串的开始。如果使用%s转换说明,以下一个空白字符(空行、空格、制表符或换行符)作为字符串的结束(字符串不包括空白字符)。如果指定了字段宽度,如%10s,那么scanf()将读取10 个字符或读到第1个空白字符停止(先满足的条件即是结束输入的条件)

C | 字符串和字符串函数_第3张图片

scanf()函数返回一个整数值,该值等于scanf()成功读取的项数或EOF(读到文件结尾时返回EOF)

/* scan_str.c -- 使用 scanf() */
#include 
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);
    system("pause");
    return 0;
}

 运行结果:

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

或者

Please enter 2 names.
Portensia Callowit
I read the 2 names Porte and nsia.

根据输入数据的性质,用fgets()读取从键盘输入的数据更合适。例如,scanf()无法完整读取书名或歌曲名,除非这些名称是一个单词。scanf()的典型用法是读取并转换混合数据类型为某种标准形式。

scanf()和gets()类似,也存在一些潜在的缺点。如果输入行的内容过长,scanf()也会导致数据溢出

三、字符串输出

3.1 puts()函数

/* put_out.c -- 使用 puts() */
#include 
#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);
    system("pause");
    return 0;
}

运行结果:

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

puts()在显示字符串时会自动在其末尾添加一个换行符

用双引号括起来的内容是字符串常量,且被视为该字符串的地址。储存字符串的数组名也被看作是地址。

该函数在遇到空字符时就停止输出,所以必须确保有空字符。

/* nono.c -- 千万不要模仿! */
#include 
int main(void)
{
    char side_a[] = "Side A";
    char dont[] = { 'W', 'O', 'W', '!' };
    char side_b[] = "Side B";
    puts(dont); /* dont 不是一个字符串 */
    system("pause");
    return 0;
}

运行结果:

WOW!Side A

由于dont缺少一个表示结束的空字符,所以它不是一个字符串,因此puts()不知道在何处停止。它会一直打印dont后面内存中的内容,直到发现一个空字符为止。

3.2 fputs()函数

fputs()函数是puts()针对文件定制的版本。它们的区别如下:

fputs()函数的第 2 个参数指明要写入数据的文件。如果要打印在显示器上,可以用定义在stdio.h中的stdout(标准输出)作为该参数。

fputs()不会在输出的末尾添加换行符。

gets()丢弃输入中的换行符,但是puts()在输出中添加换行符。另一方面,fgets()保留输入中的换行符,fputs()不在输出中添加换行符。

puts()应与gets()配对使用,fputs()应与fgets()配对使用。

3.3 printf()函数

和puts()一样,printf()也把字符串的地址作为参数。它可以格式化不同的数据类型。与puts()不同的是,printf()不会自动在每个字符串末尾加上一个换行符。

printf()的形式更复杂些,需要输入更多代码,而且计算机执行的时间也更长。然而,使用 printf()打印多个字符串更加简单。

四、自定义输入/输出函数

在getchar()和putchar()的基础上自定义所需的函数。

一个类似puts()但是不会自动添加换行符的函数:

/* put1.c -- 打印字符串,不添加\n */
#include 
void put1(const char * string)/* 不会改变字符串 */
{
    while (*string != '\0')
        putchar(*string++);
//数组表示法
    //int i = 0;
    //while (string[i]!= '\0')
    或者while (*string),当string指向空字符时,*string的值是0
        //putchar(string[i++]);
}

++的优先级高于*

一个类似puts()的函数,该函数还给出待打印字符的个数:

/* put2.c -- 打印一个字符串,并统计打印的字符数 */
#include 
int put2(const char * string)
{
    int count = 0;
    while (*string) /* 常规用法 */
    {
        putchar(*string++);
        count++;
    }
    putchar('\n'); /* 不统计换行符 */
    return(count);
}

使用一个简单的驱动程序测试put1()和put2(),并演示嵌套函数的调用.

//put_put.c -- 用户自定义输出函数
#include 
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."));
    system("pause");
    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 count 37 characters.

为了获得 put2()的返回值,计算机必须先执行put2(),因此在打印字符数之前先打印了传递给该函数的字符串。

五、字符串函数

C库提供了多个处理字符串的函数,ANSI C把这些函数的原型放在string.h头文件中。

5.1 strlen()函数

strlen()函数用于统计字符串的长度。

/* test_fit.c -- 使用缩短字符串长度的函数 */
#include 
#include  /* 内含字符串函数原型 */
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(mesg);
    puts("Let's look at some more of the string.");
    puts(mesg + 39);
    system("pause");
    return 0;
}
void fit(char *string, unsigned int size)
{
    if (strlen(string) > size)
        string[size] = '\0';
}

运行结果:

Things should be as simple as possible, but not simpler.
Things should be as simple as possible
Let's look at some more of the string.
 but not simpler.

fit()函数把第39个元素的逗号替换成'\0'字符。puts()函数在空字符处停止输出,并忽略其余字符。然而,这些字符还在缓冲区中。

C | 字符串和字符串函数_第4张图片

 5.2 strcat()函数

strcat()(用于拼接字符串)函数接受两个字符串作为参数。该函数把第2个字符串的备份附加在第1个字符串末尾,并把拼接后形成的新字符串作为第1个字符串,第2个字符串不变。strcat()函数的类型是char*(即,指向char的指针)。strcat()函数返回第1个参数,即拼接第2个字符串后的第1个字符串的地址。

/* str_cat.c -- 拼接两个字符串 */
#include 
#include  /* strcat()函数的原型在该头文件中 */
#define SIZE 80
char * s_gets(char * st, 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");
    system("pause");
    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;
}

运行结果:

What is your favorite flower?
rose
roses smell like old shoes.
s smell like old shoes.
bye

flower改变了,而addon保持不变

5.3 strncat()函数

strncat()函数的第3 个参数指定了最大添加字符数

/* join_chk.c -- 拼接两个字符串,检查第1个数组的大小 */
#include 
#include 
#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 bug?");
    s_gets(bug, BUGSIZE);
    available = BUGSIZE - strlen(bug) - 1;
    strncat(bug, addon, available);
    puts(bug);
    system("pause");
    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;
}

运行结果:

What is your favorite flower?
rose
roses smell like old shoes.
What is your favorite bug?
nonono
nononos smel

C语言相信程序员,程序员有责任确保strcat()的使用安全。

5.4 strcmp()函数

strcmp()函数通过比较运算符来比较字符串,就像比较数字一样。如果两个字符串参数相同,该函数就返回0,否则返回非零值。

/* compare.c -- 该程序可以正常运行 */
#include 
#include  // strcmp()函数的原型在该头文件中
#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))
    {
        puts("No, that's wrong. Try again.");
        s_gets(try, SIZE);
    }
    puts("That's right!");
    system("pause");
    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;
}

运行结果:

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

strcmp()函数比较的是字符串,不是整个数组,这是非常好的功能。虽然数组try占用了40字节,而储存在其中的"Grant"只占用了6字节(还有一个用来放空字符),strcmp()函数只会比较try中第1个空字符前面的部分。所以,可以用strcmp()比较储存在不同大小数组中的字符串。

1.strcmp()的返回值

/* compback.c -- strcmp()的返回值 */
#include 
#include 
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"));
    system("pause");
    return 0;
}

运行结果:

strcmp("A", "A") is 0
strcmp("A", "B") is -1
strcmp("B", "A") is 1
strcmp("C", "A") is 1
strcmp("Z", "a") is -1
strcmp("apples", "apple") is 1

strcmp()比较所有的字符,不只是字母。所以,与其说该函数按字母顺序进行比较,不如说是按机器排序序列(machine collating sequence)进行比较。

当两个参数为降序时,返回1,当两个参数为升序时,返回-1。

strcmp()函数比较的是字符串,不是字符,所以其参数应该是字符串(如"apples"和"A"),而不是字符(如'A')。下面语句都有效:

if (strcmp(word, "quit") == 0) // 使用strcmp()比较字符串
puts("Bye!");
if (ch == 'q') // 使用 == 比较字符
puts("Bye!");

//不要使用ch或'q'作为strcmp()的参数。

/* quit_chk.c -- 某程序的开始部分 */
#include 
#include 
#define SIZE 80
#define LIM 10
#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);
    system("pause");
    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;
}

运行结果:

Enter up to 10 lines (type quit to quit):
GOGOGO
second
that's all
quit
3 strings entered

input[ct][0] != '\0'可用来检测空行

2.strncmp()函数

strncmp()函数在比较两个字符串时,可以比较到字符不同的地方,也可以只比较第3个参数指定的字符数

/* starsrch.c -- 使用 strncmp() */
#include 
#include 
#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);
    system("pause");
    return 0;
}

运行结果:

Found: astronomy
Found: astrophysics
Found: astrophobia
The list contained 3 words beginning with astro.

5.5 strcpy()和strncpy()函数

如果希望拷贝整个字符串,要使用strcpy()函数。

/* copy1.c -- 演示 strcpy() */
#include 
#include  // strcpy()的原型在该头文件中
#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]);
    system("pause");
    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;
}

运行结果:

Enter 5 words beginning with q:
quit
wer
wer doesn't begin with q!
quio
qjlklk
qjksjdf
qcxvdv
Here are the words accepted:
quit
quio
qjlklk
qjksjdf
qcxvdv

strcpy()第2个参数(temp)指向的字符串被拷贝至第1个参数(qword[i])指向的数组中。拷贝出来的字符串被称为目标字符串,最初的字符串被称为源字符串

char target[20];

int x;

x = 50; /* 数字赋值*/

strcpy(target, "Hi ho!"); /* 字符串赋值*/

target = "So long"; /* 语法错误 */

strcpy()接受两个字符串指针作为参数,可以把指向源字符串的第2个指针声明为指针、数组名或字符串常量;而指向源字符串副本的第1个指针应指向一个数据对象(如,数组),且该对象有足够的空间储存源字符串的副本。记住,声明数组将分配储存数据的空间,而声明指针只分配储存一个地址的空间。

char * str;

strcpy(str, "The C of Tranquility"); // 有问题,str未初始化

1.strcpy()的其他属性

strcpy()函数还有两个有用的属性。第一,strcpy()的返回类型是char *,该函数返回的是第 1个参数的值,即一个字符的地址。第二,第 1 个参数不必指向数组的开始。这个属性可用于拷贝数组的一部分。

/* copy2.c -- 使用 strcpy() */
#include 
#include  // 提供strcpy()的函数原型
#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);
    system("pause");
    return 0;
}

运行结果:beast
Be the best that you can be.
Be the beast
beast

strcpy()把源字符串中的空字符也拷贝在内。

C | 字符串和字符串函数_第5张图片

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

strcpy()和 strcat()都有同样的问题,它们都不能检查目标空间是否能容纳源字符串的副本。拷贝字符串用 strncpy()更安全,该函数的第 3个参数指明可拷贝的最大字符数

/* copy3.c -- 使用strncpy() */
#include 
#include  /* 提供strncpy()的函数原型*/
#define SIZE 40
#define TARGSIZE 7
#define LIM 5
char * s_gets(char * st, int n);
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 && 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]);
    system("pause");
    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;
}

运行结果:

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中。因此,如果source中的字符数小于n,则拷贝整个字符串,包括空字符。但是,strncpy()拷贝字符串的长度不会超过n,如果拷贝到第n个字符时还未拷贝完整个源字符串,就不会拷贝空字符。所以,拷贝的副本中不一定有空字符。鉴于此,该程序把 n 设置为比目标数组大小少1(TARGSIZE-1),然后把数组最后一个元素设置为空字符:

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

5.6 sprintf()函数

sprintf()函数声明在stdio.h中,而不是在string.h中。该函数和printf()类似,但是它是把数据写入字符串,而不是打印在显示器上。sprintf()的第1个参数是目标字符串的地址。其余参数和printf()相同,即格式字符串和待写入项的列表。

/* format.c -- 格式化字符串 */
#include 
#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);
    system("pause");
    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;
}

运行结果:

Enter your first name:
Zhang
Enter your last name:
san
Enter your prize money:
5000
san, Zhang              : $5000.00

sprintf()函数获取输入,并将其格式化为标准形式,然后把格式化后的字符串储存在formal中。

5.7 其他字符串函数

1、char *strcpy(char * restrict s1, const char * restrict s2);

该函数把s2指向的字符串(包括空字符)拷贝至s1指向的位置,返回值是s1

2、char *strncpy(char * restrict s1, const char * restrict s2, size_t n);

该函数把s2指向的字符串拷贝至s1指向的位置,拷贝的字符数不超过n,其返回值是s1。该函数不会拷贝空字符后面的字符,如果源字符串的字符少于n个,目标字符串就以拷贝的空字符结尾;如果源字符串有n个或超过n个字符,就不拷贝空字符

3、char *strcat(char * restrict s1, const char * restrict s2);

该函数把s2指向的字符串拷贝至s1指向的字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。该函数返回s1

4、char *strncat(char * restrict s1, const char * restrict s2, size_t n);

该函数把s2字符串中的n个字符拷贝至s1字符串末尾。s2字符串的第1个字符将覆盖s1字符串末尾的空字符。不会拷贝s2字符串中空字符和其后的字符,并在拷贝字符的末尾添加一个空字符。该函数返回s1

5、int strcmp(const char * s1, const char * s2);

如果s1字符串在机器排序序列中位于s2字符串的后面,该函数返回一个正数;如果两个字符串相等,则返回0;如果s1字符串在机器排序序列中位于s2字符串的前面,则返回一个负数

6、int strncmp(const char * s1, const char * s2, size_t n);

该函数的作用和strcmp()类似,不同的是,该函数在比较n个字符后或遇到第1个空字符时停止比较

7、char *strchr(const char * s, int c);

如果s字符串中包含c字符,该函数返回指向s字符串首位置的指针(末尾的空字符也是字符串的一部分,所以在查找范围内);如果在字符串s中未找到c字符,该函数则返回空指针。

8、char *strpbrk(const char * s1, const char * s2);

如果 s1 字符中包含 s2字符串中的任意字符,该函数返回指向 s1 字符串首位置的指针;如果在s1字符串中未找到任何s2字符串中的字符,则返回空字符。

9、char *strrchr(const char * s, int c);

该函数返回s字符串中c字符的最后一次出现的位置(末尾的空字符也是字符串的一部分,所以在查找范围内)。如果未找到c字符,则返回空指针。

10、char *strstr(const char * s1, const char * s2);

该函数返回指向s1字符串中s2字符串出现的首位置。如果在s1中没有找到s2,则返回空指针

11、size_t strlen(const char * s);

该函数返回s字符串中的字符数,不包括末尾的空字符。

关键字restrict限制了函数参数的用法。例如,不能把字符串拷贝给本身。size_t类型是sizeof运算符返回的类型。

六、字符串示例:字符串排序

/* sort_str.c -- 读入字符串,并排序字符串 */
#include 
#include 
#define SIZE 81 /* 限制字符串长度,包括 \0 */
#define LIM 20 /* 可读入的最多行数 */
#define HALT "" /* 空字符串停止输入 */
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(input[ct], SIZE) != NULL 
    && input[ct][0] != '\0')
    {
        ptstr[ct] = input[ct]; /* 设置指针指向字符串 */
        ct++;
    }
    stsrt(ptstr, ct); /* 字符串排序函数 */
    puts("\nHere's the sorted list:\n");
    for (k = 0; k < ct; k++)
        puts(ptstr[k]); /* 排序后的指针 */
    system("pause");
    return 0;
}

/* 字符串-指针-排序函数 */
void stsrt(char *strings [], int num)
{
char *temp;
int top, seek;
for (top = 0; top < num - 1; top++)
    for (seek = top + 1; seek < num; seek++)
        if (strcmp(strings[top], strings[seek]) > 0)
        {
            temp = strings[top];
            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] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

运行结果:

Input up to 20 lines, and I will sort them.
To stop, press the Enter key at a line's start.
O that I was where I would be,
Then would I be where I am not;
But where I am I must be,
And where I would be I can not.


Here's the sorted list:

And where I would be I can not.
But where I am I must be,
O that I was where I would be,
Then would I be where I am not;

排序的是指向字符串的指针,而不是字符串本身。排序过程把ptrst重新排列,并未改变
input。

C | 字符串和字符串函数_第6张图片

 我们采用选择排序算法(selection sort algorithm)来排序指针。

七、ctype.h字符函数和字符串

ctype.h系列与字符相关的函数虽然不能处理整个字符串,但是可以处理字符串中的字符

/* mod_str.c -- 修改字符串 */
#include 
#include 
#include 
#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) // 如果地址不是 NULL,
        *find = '\0'; // 用空字符替换
    ToUpper(line);
    puts(line);
    printf("That line has %d punctuation characters.\n", PunctCount(line));
    system("pause");
    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;
}

运行结果:

Please enter a line:
Are you OK?
ARE YOU OK?
That line has 1 punctuation characters.

ToUpper()函数,利用toupper()函数处理字符串中的每个字符,把整个字符串转换成大写;PunctCount()函数,利用ispunct()统计字符串中的标点符号个数。另外,使用strchr()处理fgets()读入字符串的换行符(如果有的话)。

while (*str)循环处理str指向的字符串中的每个字符,直至遇到空字符。

ctype.h中的函数通常作为宏(macro)来实现。

八、命令行参数

命令行(command line)是在命令行环境中,用户为运行程序输入命令的行。

命令行参数(command-line argument)是同一行的附加项。

/* repeat.c -- 带参数的 main() */
#include 
int main(int argc, char *argv [])
{
    int count;
    printf("The command line has %d arguments:\n", argc - 1);
    for (count = 0; count < argc; count++)
        printf("%d: %s\n", count, argv[count]);
    printf("\n");
    system("pause");
    return 0;
}

运行结果:

PS C:\Users\***\Desktop\VS_PJ\CPPch11> .\repeat Resistance is futile
The command line has 3 arguments:
0: C:\Users\Hao Guoqing\Desktop\VS_PJ\CPPch11\repeat.exe
1: Resistance
2: is
3: futile

C编译器允许main()没有参数或者有两个参数(一些实现允许main()有更多参数,属于对标准的扩展)。main()有两个参数时,第1个参数是命令行中的字符串数量。过去,这个int类型的参数被称为argc(表示参数计数(argument count))。系统用空格表示一个字符串的结束和下一个字符串的开始。因此,上面的repeat示例中包括命令名共有4个字符串,其中后3个供repeat使用。该程序把命令行字符串储存在内存中,并把每个字符串的地址储存在指针数组中。而该数组的地址则被储存在 main()的第 2 个参数中。按照惯例,这个指向指针的指针称为argv(表示参数值[argument value])。如果系统允许(一些操作系统不允许这样),就把程序本身的名称赋给argv[0],然后把随后的第1个字符串赋给argv[1],以此类推。

char **argv与char *argv[]等价。也就是说,argv是一个指向指针的指针,它所指向的指针指向 char

九、把字符串转换为数字

数字既能以字符串形式储存,也能以数值形式储存。

在屏幕上显示数字则要求字符串形式,因为屏幕显示的是字符。printf()和sprintf()函数,通过%d 和其他转换说明,把数字从数值形式转换为字符串形式,scanf()可以把输入字符串转换为数值形式。

/* hello.c -- 把命令行参数转换为数字 */
#include 
#include 
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!");
    system("puase");
    return 0;
}

运行结果:

PS C:\Users\***\Desktop\VS_PJ\CPPch11> .\hello 3
Hello, good looking!
Hello, good looking!
Hello, good looking!

命令行参数3被储存为字符串3\0。atoi()函数把该字符串转换为整数值3,然后该值被赋给times。该值确定了执行for循环的次数。

如果字符串仅以整数开头,atoi()函数也能处理,它只把开头的整数转换为字符。如果命令行参数不是数字,atoi()函数返回0。

从ANSI C开始,stdlib.h头文件中包含了atoi()函数的原型。除此之外,还包含了 atof()和 atol()函数的原型。atof()函数把字符串转换成 double 类型的值, atol()函数把字符串转换成long类型的值。

ANSI C还提供一套更智能的函数:strtol()把字符串转换成long类型的值,strtoul()把字符串转换成unsigned long类型的值,strtod()把字符串转换成double类型的值。这些函数的智能之处在于识别和报告字符串中的首字符是否是数字。而且,strtol()和strtoul()还可以指定数字的进制

long strtol(const char * restrict nptr, char ** restrict endptr, int base);

nptr是指向待转换字符串的指针,endptr是一个指针的地址,该指针被设置为标识输入数字结束字符的地址,base表示以什么进制写入数字。

/* strcnvt.c -- 使用 strtol() */
#include 
#include 
#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: %ld, stopped at %s (%d)\n",
        value, end, *end);
        value = strtol(number, &end, 16); /* 十六进制 */
        printf("base 16 input, base 10 output: %ld, stopped at %s (%d)\n",
        value, end, *end);
        puts("Next number:");
    }
    puts("Bye!\n");
    system("pause");
    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;
}

运行结果:

10
base 10 input, base 10 output: 10, stopped at  (0)
base 16 input, base 10 output: 16, stopped at  (0)
Next number:
10atom
base 10 input, base 10 output: 10, stopped at atom (97)
base 16 input, base 10 output: 266, stopped at tom (116)
Next number:

Bye!

第1次转换在读到空字符时结束,此时end指向空字符。打印end会显示一个空字符串,以%d转换说明输出*end显示的是空字符的ASCII码。对于第2个输入的字符串,当base为10时,end的值是'a'字符的地址。所以打印end显示的是字符串"atom",打印*end显示的是'a'字符的ASCII码。当base为16时,'a'字符被识别为一个有效的十六进制数,strtol()函数把十六进制数10a转换成十进制数266。

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

使用 strcmp()来代替关系运算符,当比较字符串时,应该使用strcpy()或strncpy()代替赋值运算符把字符串赋给字符数组。

你可能感兴趣的:(c语言,开发语言)