C语言字符串的输出与输入学习笔记

字符串的输入与输入

1.字符串初始化

1⃣️:用足够的空间的数组存储字符串:

const char word[15] ="Hello world ";

这里用const,表示该字符串不会被更改。

注意⚠️:在指定数组大小时,要确保数组的元素个数至少比字符长度多1,因为要容纳" \0 "。所有未被使用的元素都被自动初始化为\0。
在这里插入图片描述

2⃣️:省略数组初始化声明中的大小

const char word[] ="Hello world!This is my first program! ";

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

2.数组与指针

字符数组名与其他数组名一样,是该数组首元素的地址。

char word[10] = "Hello!";

以下表达式都为真

word == &word[0]              //字符数组名为是该数组首元素的地址

*word == 'H'                  //数组首元素的地址解引用为首元素

*(word +1 ) == car[1] == 'H'  // 数组首元素的地址+1后解引用为数组第二个元素

1⃣️:指针创建字符串

const char *pt1 = " Something is poiting at me.";

2⃣️:数组与指针的区别

  • 初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针
  • 数组名为常量,指针名为变量

例:

char heart[] = "I love you";
const char *head = "I love you"; //为什么使用const,在下文‘使用指针的优缺点’中说明原因

数组名heart是常量,指针名head为变量

  • heart是地址常量,不能更改heart,如果更改了heart就意味着改变了数组的存储位置(地址)。可以进行类似heart+1这样的操作,标示数组的下一个元素。但不允许++heart,递增运算只用于变量前。
  • head为变量,该变量最初指向该字符串的首字符,但是他的值可以改变。可以递增运算。
  • char *head是一个指针,根本没分配内存,他指向的 “I love you” 是只读的,不能改变,在下面给他赋值肯定是错的。而char heart[]是一个数组,已经分配内存,是将 "I love you"复制到该内存里面,这个内存是可读写的 。
  • 指针是不分配内存的,它指向的是系统的只读的内存,而数组是分配内存的,就是将系统的只读的内存里面的值复制到它的内存里面,因此可读写

3⃣️:使用指针的优缺点

  • 缺点
    -双引号括起来的字符串是字符串字面量(string literal),是静态对象,因此从语义上来说,指针指向它之后字符串内容自然不可修改,推荐使用const修饰。

  • 优点const char *pt[5] , char arrays[5][8] pt数组是一个内含5个指针的数组,占用40字节。arrays是一个内含5个数组的数组,每个数组内含40个char类型值,占用200字节。所以指针效率高。

3.scanf()与printf()

#include <stdio.h>
int main(void){
    
    char word[8];
    scanf("%s",word);
    printf("%s##\n",word);
    
}

定义一个char类型的长度为8的字符串,然后接收输入并打印输出,用##可以清晰的看出字符串结尾的边界

解析:
当我们输入hello world!后回车,发现控制台只有输出了hello##,这说明scanf读一个单词时到空格就结束。
在这里插入图片描述
为了再次证明整个原理,下面我们使用两次scanf读入输入的字符然后打印。

#include <stdio.h>
int main(void){
    
    char word[8];
    char word2[8];
    scanf("%s",word);
    scanf("%s",word2);
    printf("%s##%s##\n",word,word2);
    
}

我们可以从下图中看出,第一次scanf发现hello后面有空格就读取完毕,并打印时添加了##。第二次scanf继续读取,发现world!后面有回车也同样读取完毕,并在打印时添加了##。
在这里插入图片描述

所以,scanf读取一个单词(到空格,tab或者回车为止),但这也意味着scanf不安全,因为不知道要读入内容的长度。

但是我们可以通过以下的方法,来控制scanf读取的长度。

#include <stdio.h>
int main(void){
    
    char word[8];
    char word2[8];
    scanf("%7s",word);
    scanf("%7s",word2);
    printf("%s##%s##\n",word,word2);
    
}

我们在scanf中的%后面加入一个具体的数字,表示scanf最多读取的长度,在这里我们限制scanf最多读取7个字符。注意:这里是最多读取的长度,如果停止读取时字符串长度不满足,则直接取字符串。

如下图所示,我们第一行输入123,不满足7个字符串长度,prinf打印出来的就只有123.
第二行输入12345678超过7个,则scanf截断第8个字符串,prinf只打印出来1234567.
在这里插入图片描述
这就相当于,scanf不是以回车,空格或tab来读取,而是以你所需要的字符串长度来读取,从而实现了安全输入。

4.gets()与puts()

  • gets()函数简单易用,它读取整行输入,直到遇到换行符,然后丢弃换行符,储存其他字符,并在这些字符末尾添加一个空字符使其成为一个C字符串。
  • puts()函数,只需把字符串的地址作为参数传递给他即可,注意puts函数会在字符串后面加上换行符。

但是gets()无法检查数组是否能装得下输入行,gets函数并不知道数组中有多少元素,如果输入的字符串过长,会导致缓冲区溢出。

因此gets函数也是不安全的,不建议使用gets()。

5.fgets()与fputs()

  • fgets()函数的第二个参数指明了读入字符的最大数量。如果该参数为n,那么fgets函数将读入n-1个字符。如果fgets()函数读到一个换行符,会把它储存在字符串中。这点与gets不同,gets会丢弃换行符。fgets()函数的第三个参数指明要读入的文件。如果读入从键盘输入的数据,则以stdin作为参数。
  • fputs()函数的第二个参数指明他要写入的文件。如果要在计算机显示器上打印,则使用stdout作为参数。与puts()函数不同,fputs()函数不会在待输出字符串末尾添加一个换行符。

例:

#include <stdio.h>
#define LEN 14
int main(void){
    
    char words[LEN];
   
    puts("Enter a String");
    fgets(words,LEN, stdin);
    puts(words);  //puts()函数会添加换行符\n
    fputs(words, stdout);
    
    return 0;
    
}

在这里插入图片描述
输入apple后,apple\n\0 被存储在数组中。
我们看到控制台输出的内容发现输出的两个apple之间有一行空白,因为puts()函数会添加换行符\n。

1⃣️fgets()优缺点:

fgets()储存换行符有好有坏

  1. 缺点是你可能并不想把换行符储存在字符串中,这样的换行符会带来一些麻烦。
  2. 优点是对于储存的字符串而言,检查末尾是否有换行符可以判读是否读取了一整行。如果不是一整行,要处理好一行中剩下的字符。

2⃣️fgets()返回值:

读取成功,返回读取到的字符串,即string;失败或读到文件结尾返回NULL。

下面的程序验证读到文件结尾返回NULL。读入并显示用户输入的内容,直到fgets()读到文件结尾或空行(即,首字符为换行符)。

3⃣️fgets()操作实例:

#include <stdio.h>
#define LEN 10
int main(void){
    
    char words[LEN];
   
    puts("Enter Strings (empty line to quit): ");
    while(fgets(words, LEN, stdin) != NULL && words[0] != '\n'){
        fputs(words, stdout);
    }
    
    puts("Done!");
    
    return 0;
    
}

在这里插入图片描述
LEN设置的为10,所以fgets()一次读取9个字符(剩余一个字符留给’\0’),第一次读取到" I’m Kevin ",并存储为I’m Kevin\0,接着fputs()打印出来,并且没有换行。然后while进入下一轮迭代,fgets()继续读取,第二次读取到“ ,from Chi ",并存储为,from Chi\0,接着fputs()打印出来,并且没有换行。直到读取完所有字符为止。最后一次输入时,直接键入回车,所以跳出while循环,程序结束并打印Done!

3⃣️fgets()操作进阶:

如果说我们想第一次的输入 “I’m Kevin,from China.” 只读取前9个字符,然后丢弃掉其他的字符,第二次输入也是如此,那该怎么办???
换句话说该想法就是:按照设定的大小读取输入行,并删除储存在字符串中的换行符,如果没有换行符,则丢弃数组装不下的字符。
再拆分成小的问题则就是以下的两个问题:

  1. 那如果不希望把换行符储存在字符串中,如何处理掉换行符呢??
    我们可以在已储存的字符串中查找换行符,并将其替换为空字符:
while(words[i] != 'n' ) {
	i++;
	}
words[i] = '\0\;
  1. 如果仍然有字符串留在输入行怎么办??
    丢弃掉其余的字符串即可。
    详细原理可以查看主页文章《C语言 getchar()原理及易错点解析》
while(getchar() != '\n')
	continue;

我们把这两个问题整合到上面的代码中:

#include <stdio.h>
#define LEN 10
int main(void){
    
    char words[LEN];
   
    puts("Enter Strings (empty line to quit): ");
    while(fgets(words, LEN, stdin) != NULL && words[0] != '\n'){
       
        int 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;
    
}

在这里插入图片描述

   while(words[i] != '\n' && words[i] != '\0'){
                i++;
            }

这段代码说的是,遍历字符串,直至遇到换行符或者空字符。如果先遇到换行符,下面的if语句将其换成空字符;如何先遇到空字符,else就将输入行舍弃。

注意⚠️:空字符与空格不一样!!!!空字符是’ \0 ',ASCII码为0;空格的ASCII为32. 下面程序可以验证:

#include <stdio.h>
void main()
{
    char a=' ';
    char b='\0';
    printf ("%d\n",a);
    printf ("%d\n",b);
    
}

Output:

32
0
  • 0
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值