《C Primer Plus》 学习笔记系列之(二)

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

下面的程序使用了gets()和puts(), 以及字符指针的用法:

#include<stdio.h>
#define LIM 5
#define LINELEN 81   //最大字符串长度
#define MSG "You must have many talents.Tell me more."  //定义字符串常量
int main()
{
    char name[LINELEN];
    char talents[LINELEN];
    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] = {  //mytal[1] mytal[2] mytal[3] mytal[4] mytal[5] 分别指向不同的字符串
        "Multiplying accurately", "stashing data", "Adding numbers swiftly", "Following instructions to the letter",
        "Understanding the c language"
    };  

    int i;
    for (i = 0; i < LIM; i++)
    {   
        puts(mytal[i]);    //使用puts()输出字符串
    } 
    puts(MSG);  
    puts("Your name:");
    gets(name);
    puts("Yours more talents:");
    gets(talents);
    printf("%s\n%s\n", name, talents);
    return 0;
}

#include<stdio.h>
int main()
{
    char title[30];
    while (gets(title) != NULL && title[0] != '\0')
    {   
        puts(title);
    }   
    return 0;
}

gets()试图超过文件结尾读取字符,这个表达式的值为NULL。如果用户在输入行的开始就按了回车键,将输入空字符串,以结束循环。

字符串常量属于静态存储(static storage)类。静态存储是指如果在一个函数中使用字符串常量,即使是多次调用了这个函数,该字符串在程序的整个运行过程中只存储一份。

整个引号中的内容作为指向该字符串存储位置的指针。这点与把数组名作为指向数组存储位置的指针类似。下面的程序可以证明这一点:

#include<stdio.h>
int main()
{
    //整个字符串的内容,"Jack Chou"是作为指向该字符串存储位置的指针
    printf("%s, %p, %c\n", "Hello", "Jack Chou", *"Jack Chou");
    return 0;
}
//运行后的结果:
Hello, 0x80484fc, J


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

通常,被引用的的字符串存储在可执行文件的数据段部分;当程序被加载到内存中时,字符串也被加载到内存中。被引用的字符串被称为位于静态存储区。

但是在程序开始运行后才为数组分配存储空间。这时候,把被引用的字符串复制到数组中。

char m3[] = "Enough about me - what's your name?"
char *m3 = "Enough about me - what's your name?"
在数组形式中,m3是一个地址常量,不允许使用++m3,增量运算符只能用在变量名前,而不能用在常量前。
指针形式(*m3),这个变量初始时指向字符串的第一个字符,++m3则指向第二个字符。

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

只有指针才可以使用增量运算符:
char * head = "I Love Dog!";
while ( *(head) != '\0')
{
    putchar(*(head++));
}

字符串的输入:
gets()函数,如果输入顺利,它返回的是读入字符串的地址;如果出错或gets()遇到文件结尾,它就返回一个空(或0)地址。
这个地址被称为空指针, 并用stdio.h里定义的常量NULL来表示。所以:
while (gets(name) != NULL){}
对于getchar()
while ((ch = getchar()) != EOF){}

gets()的一个不足是它不检查预留存储区能够容纳实际输入的数据。多出来的字符简单的溢出到相邻的内存区。
fgets()函数改进了这个问题,它需要三个参数:
#include<stdio.h>
#define MAX 20
int main()
{
    char name[MAX];
    char * ptr;
    ptr = fgets(name, MAX, stdin);   //做多读取MAX-1个字符,或读完一个换行符为止,stdin说明读取的是键盘数据。
    printf("%s, %s hello", name, ptr);
    return 0;
}

fgets()读取到换行符,会把它存到字符串里,不会像gets()函数那样把它丢掉。
对于scanf()和gets(),scanf()使用两种方法决定输入结束:
输入的字符串都是以遇到的第一个非空白字符开始。如果使用%s格式,字符串读到(但不包括)下一个空白字符(比如空格、制表符或换行符)。
如果指定了字段宽度,比如%10s,scanf()会读入10个字符或直到遇到第一个空白字符。scanf()返回一个成功读取项目的一个数值,或当遇到文件结束时返回一个EOF。
#include<stdio.h>
int main()
{
    float cost;
    scanf("%f", &cost);
    return 0;
}
/*
     运行这段代码,在心显示器的终端输入
     12.50[enter]
     会传送下面的字符序列:
     12.50\n
     scanf()函数读入了1 2 . 5 0,把\n留在了输入流中,等待下一个读入语句处理。
     按下回车键后,12.50\n在缓冲区中,scanf()函数将缓冲区中的12.50给读走了,剩下'\n'
     于是可以使用下面这段代码来清空缓冲区:
     while (getchar() != '\n')
     {
         continue;
     }
 */
字符串的输出:

c有三个用于输出字符串的标准库函数:puts()、fputs()、printf()
对于puts()函数,只要给出字符串参数的地址,就可以输出字符串。
fput()需要第二个参数说明要写的文件。可以使用stdout作为参数进行输出。
char line[81];
while(fgets(line, 81, stdin))
{
    fputs(line, stdout);
}
puts()和gets()一起使用而设计的,
fputs()和fgets()一起使用而设计。

自定义字符串的输出:

#include<stdio.h>
void put1(const char * string)   //这个函数不用并不改变字符串,所以使用const修饰符
{
    while(*string != '\0')   //while(*string)
    {
        putchar(*string++);     //++比*的优先级高
    }
}

字符串函数

c库提供了许多处理字符串的函数,ANSI C用头文件string.h给出这些函数的原型。

strlen()

得到字符串的长度

strcat()

接收两个字符串参数,它将第二个字符串的一份拷贝添加到第一个字符串的结尾,从而使第一个字符串成为一个新的组合字符串,
而第二个字符串没有改变。
char flower[50] = "Rose";
char addon[] = "s smell like old shoes";
strcat(flower, addon);
puts(flower);   //将输出连接后的字符串

strncat()

strncat(flower, addon, 10);  //把addon字符串中的内容添加到flower上,直到加到10个字符或空字符为止。

strcmp()

接收两个字符串参数,如果两个字符串参数相同,就返回0。
strcmp(flower, addon);

strncmp()

可以比较到字符串的不同处,也可以比较完由第三个参数指定的字符数。
如下面的程序如果想搜索以"astro"开头的字符串,可以限定搜索前5个字符

char * list[5] = {"astronomy", "astrounding", "astrophysics", "ostracize", "asterism"};
int count = 0;
int i;
for (i = 0; i < 5; i++)
{
    if (strncmp(list[i], "astro", 5) == 0)  //指定比较前5个字符
    {
    printf("Found:%s\n", list[i]);
    count++;
    }
}
printf("The list contained %d words beginning with astro.\n", count);

strcpy()

复制字符串,在字符串运算中的作用等价于赋值运算符
char qwords[] = "this is a test.";
char temp[30];
strcpy(temp, qwords);  //strcpy(temp, "Hi, jack");   //根据赋值语句的顺序,左边目标字符串在左边
声明一个数组将为数据分配存储空间,而声明一个指针只为一个地址分配存储空间。
strcpy()是char *类型,它返回的是第一个参数的值,及一个字符的地址。
见下面的程序:

#include<stdio.h>
#include<string.h>
#define WORDS "beast"
#define SIZE 40
int main()
{
    char * orig = WORDS;
    char copy[SIZE] = "Be the best that you can be.";
    char * ps;

    puts(orig);
    puts(copy);
    ps = strcpy(copy + 7, orig);   //ps指向copy的第8个元素,索引为7.orig中的'\0'正好把that中的't'给覆盖
    puts(copy);            //遇到了前面被复制进去的空字符'\0',空字符就标志着字符串的真正结束。所以就不输出copy后面的内容了
    puts(ps);          //ps指向copy的第8个元素
    return 0;
}

strncpy()

需要使用第三个参数来指明最大可复制的字符数

头文件stdio.h支持的sprintf()函数

写到字符串里,而不是输出到屏幕显示。

选择法排序(字符串排序):

使用选择法进行排序,下面是一个程序实例:

#include<stdio.h>
int main()
{
    //选择法排序
    int arr[10] = {10, 9, 3, 5, 8, 1, 2, 4, 0, 6}; 
    int i, j, temp;
    for (i = 0; i < 9; i++)
    {   
        for (j = i + 1; j < 10; j++)
        {   
            if (arr[i] > arr[j])   //以从小到大的顺序进行排序
            {   
                temp = arr[j];
                arr[j] = arr[i];
                arr[i] = temp;
            }   
        }   
    }   

    //输出那个数组
    int k;
    for (k = 0; k < 10; k++)
    {   
        printf("%d\t", arr[k]);
    }   
    putchar('\n');
    return 0;
}

命令行参数

看一个例子:

#include<stdio.h>
int main(int argc, char *argv)
{
    int count;
    printf("The commnd line has %d arguments.\n", argc - 1); 
    printf("The command is:\n");
    for (count = 1; count < argc; count++)
    {   
        printf("%s\n", argv[count]);  //%s需要提供字符串的地址作为参数
    }   
    return 0;
}

argc是整型数,其值是命令行的单词个数;argv是一个指针,指向一个char指针数组。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值