高级c语言(五)

输出缓冲区:

当我们使用标准库的输出系列函数打印数据到屏幕,数据并不会立即显示到屏幕上,而先存储到一块内存中,我们把这块内存称为输出缓冲区,等满足相关条件后,再从缓冲区中显示到屏幕,相关条件有:

1、从输出状态切换到输入状态。
2、缓冲区满了,1k=1024个字节,系统会把缓冲区中所有数据一起显示到屏幕了。
3、程序正常结束时,系统会把缓冲区中所有数据一起显示到屏幕了。
4、遇到'\n'时,'\n'前面的数据会立即显示到屏幕上。
5、调用fflush(stdout) 强制刷新,会把立即输出缓冲区中所有数据一起显示到屏幕了。
总结:缓冲区机制的目的是为了提高输入输出效率
练习1:实现一个动态时钟,在屏幕上动态显示出 hh:mm:ss
复制代码
#include <unistd.h>
#include <stdlib.h>
int main(int argc,const char* argv[])
{
    int sec = time(NULL);   
​
    for(;;)
    {   
        system("clear");
        printf("%d:%d:%d",(sec/3600+8)%24,sec/60%60,sec%60);    
        fflush(stdout);
        sleep(1);
        sec++;
    }
}
​
输入缓冲区:

当我们从终端输入数据给程序时,系统并没有立即把数据交给程序读取,而先存储到了一块内存中,我们这块内存称为输入缓冲区,直到我们按下Enter键时,系统才会把缓冲区中的数据给程序读取。

当我们输入的数据过多,或者类型不匹配,标准的输入系列函数就会读取失败,或只读取一部分,剩余的数据就会残留缓冲区中,影响后续数据的输入,当我们发现这情况情况后,应先清理输入缓冲区,后续的数据才能正常输入

清理输入缓冲区的方式:

复制代码
方法1:
while('\n' != getch()); //  清空缓冲区,直到按下回车结束
​
方法2:正则表达式
scanf("%*[^\n]");   //  从缓冲区中读取任意类型数据并丢弃,直到遇到'\n'
scanf("%*c");       //  从换乘区中读取一个字符并丢弃
​
方法3:
stdin->_IO_read_ptr = stdin->_IO_read_end;  
//  设置输入缓冲区的位置指针到缓冲区末尾,此时缓冲区会被操作系统自动清空
​
注意:
    1、方法3只能在Linux系统中使用
    2、如果输入缓冲区中本来就没有垃圾数据,使用方法1和方法2就需要你手动多输入一个'\n'作为垃圾数据,程序才能往下走
字符:
什么是字符:

字符就是符号或图案,但在计算机中以整数形式存在,当需要显示时,会根据ASCII表中的对应关系显示出相应的符号或图案。

在C语言中使用char类型的变量存储字符的ASCII码值,也就是使用整数进行模拟字符,标准的ASCII码表的范围是:0~127,共128个字符,其他的语种,使用-128~-1进行设计字符编码,比如中文的汉字,使用的是2~3字节存储一个汉字。

重要的字符:
复制代码
'\0' ASCII值是 0  空字符  字符串的结束标志
'0' ASCII值是 48
'A' ASCII值是 65
'a' ASCII值是 97
输出:
复制代码
printf(“%c”,ASCII值);
putchar(ASCII值);
字符的输入:
复制代码
char ch;
scanf("%c",&ch);
ch = getchar();
注意:

当先输入数值型数据(整数形、浮点型),再输入字符型数据时,前一次的输入会残留一个'\n'或空格,影响字符型数据的输入,是缓冲区在影响字符的输入

有以下方法解决:
复制代码
方法1:增加一个空白字符的接收函数
scanf("%*c");
getch();
getchar();
​
方法2:在%c前面增加一个空格
scanf(" %c");
​
方法3:全部清空输入缓冲区
stdin->_IO_read_ptr = stdin->_IO_read_end;
练习2:先输入n的值,再连续输入n个字符,统计出每个字符出现的次数
复制代码
#include <stdio.h>
​
int main(int argc,const char* argv[])
{
    int n;
    printf("请输入n的值:");
    scanf("%d",&n);
​
    char arr[128] = {},ch;
    for(int i=0; i<n; i++)
    {
        scanf(" %c",&ch);                                                                        
        arr[ch]++;
    }
    for(int i=0; i<128; i++)
    {
        if(arr[i])
        {
            printf("%c 出现了%hhd次\n",i,arr[i]);
        }
    }
}
​

aaabbdce

a 3 b 2 d 1 c1 e1

判断字符类型的函数:
函数名 函数功能
isalnum() 当字母或数字字符时, 返回真值
isalpha() 当字母字符时, 返回真值
iscntrl() 当控制字符时, 返回真值
isdigit() 当数字字符时, 返回真值
isgraph() 当非空格可打印字符时, 返回真值
islower() 当小写字母字符时, 返回真值
isprint() 当可打印字符时, 返回真值
ispunct() 当标点字符时, 返回真值
isspace() 当空格字符时, 返回真值
isupper() 当大写字母字符时, 返回真值
isxdigit() 当十六进制字符时, 返回真值
串型结构:

由若干个相同类型的数据组成顺序表(数组),在数据的末尾有一个结束标志,在使用这种数组时,可以不关心数组的长度。并且串型结构的处理都是批量性的

复制代码
#include <stdio.h>
​
void show_string(int arr[])
{
    for(int i=0; arr[i] != ~0; i++)
    {   
        printf("%d ",arr[i]);
    }   
}
​
int main(int argc,const char* argv[])
{
    int arr[] = {33,5,0,63,34,23,5,~0,32,23,56};             
    show_string(arr);
}
字符串:
什么是字符串:

由字符类型组成的串型结构,它的结束标志是'\0',使用它可以存储单词、句子、文章、汉字等更丰富的信息,一般使用char类型的数组存储。

复制代码
// 定义字符串时,要为'\0'预留位置
char arr1[] = {'H','e','l','l','o','\0'};
char arr2[10] = {'H','e','l','l','o'};
字符串字面值:

1、"由双引号包括着的若干个字符"

2、它是以常量字符数组的形式存在,末尾隐藏着一个'\0'。

3、它们会被存储在text内存段,一旦强行修改就会出现段错误。

4、使用指针指向字符串字面值时,一定要用const加以保护,防止出现段错误,宁可出现编译时的错误,也不要出现运行时的错误。

5、编译器会优化它的存储,相同的字符串字面值,只会存储一份在text内存段中。

6、最常用的是用它给字符数组初始化,char arr[] = "hello" 编译器会自动拷贝字符串到数组的内存中(包括'\0'),完成初始化就有了两份字符串存储在内存中,一份存储在stack\data,另一份还存储在text。

**注意:**使用字符串字面值给字符数组赋值,只能在定义字符数组时使用,这是编译器帮忙完成拷贝的,在完成字符数组的定义后,只能使用strcpy函数对字符串进行赋值。memcpy

复制代码
#include <string.h>

int main(int argc,const char* argv[])
{
    char str[] = {'a','b','c','1','2',0};
    char str1[5] = {'A','B','c','1','2'};
    str[0] = 'A';
    printf("%s\n",str);
    printf("%s\n","hehehedijfdio");
    printf("%s\n","hehehedijfdio");

    const char* p = "hehe";//   text内存段
    const char* p1 = "hehe";//  text内存段
    printf("%p %p %s\n",p,p1,p);
//  p[0] = 'H';

    char str2[] = "hello world hehexiaixijefikdfm";
    str2[1] = 'E';                                                                               
//  str2 = "hehe";
    strcpy(str2,"xixihaha");
    printf("%s\n",&str2[9]);

}
字符串的输出:
复制代码
printf("%s",字符串的首地址);
puts(字符串的首地址); // 输出完字符串后会再输出一个\n
字符串的输入:
复制代码
scanf("%s",存储字符串的首地址);
缺点:不能输入带有空格的字符串

char *gets(char *s);
返回值:就是s,为了链式调用
缺点:直接从终端中接收字符数据,遇到'\n',可以接收空格字符,但是它不检查数据的长度跟存储空间的关系,所以很容易接收过长产生段错误、脏数据,官方编译器不建议使用该函数,会产生警告

char *fgets(char *s, int size, FILE *stream);
功能:可以从指定文件stream中读取不超过size-1个字符会自动在末尾添加'\0',并存储到s中,返回值也是s,为了链式调用
stream: 数据的来源,写stdin即可 stdout 一切皆文件
size:最多只能读取size-1个字符,必定会为'\0'预留位置
缺点1:如果输入的字符个数不足size-1个时,会把最后输入的'\n'一起接收
缺点2:如果输入的字符个数超过size-1个时,超出部分的字符数据会继续残留在输入缓冲区中,会继续影响后序的输入

解决方法:
	char usr[6] = {};
    printf("请输入字符串:");
	fgets(usr,6,stdin);

    int len = -1;
    //  计算出'\0'下标为len
    while(usr[++len]);

    //  检查'\0'前面是否是'\n'
    if('\n' == usr[len-1])
    {
        //  证明输入不足size-1
        usr[len-1] = '\0';
    }
    else
    {
        //  证明输入超过size-1个,\n在缓冲区中,有残留
        //  清理输入缓冲区
        //while('\n' != getch());
        stdin->_IO_read_ptr = stdin->_IO_read_end;
    }

#include <stdio.h>                                                                                      

char* my_fgets(char *s, int size)
{
    if(NULL == s)
        return NULL;

    fgets(s,size,stdin);

    int len = -1;
    while(s[++len]);

    if('\n' != s[len-1])
        stdin->_IO_read_ptr = stdin->_IO_read_end;
    else
        s[len-1] = '\0';

    return s;
}

int main(int argc,const char* argv[])
{
    char str[10] = {};
    my_fgets(str,10);
    printf("---%s---\n",str);
}
操作字符串的常用函数:
复制代码
size_t strlen(const char *s);
功能:计算字符串的长度,不包括'\0'
 
 	char str[] = "he\0he \ni";
    printf("strlen:%d\n",strlen(str));	2
    printf("sizeof:%d\n",sizeof(str));  9
    printf("sizeof:%d\n",sizeof("xixixx")); 7
    printf("strlen:%d\n",strlen("xixixx")); 6
    char* p = "xixixx";                     
	printf("sizeof:%d\n",sizeof(p));	指针 4\8
 	printf("strlen:%d\n",strlen(p));	6
    简答题:sizeof(运算符)跟strlen (函数)的区别?

        
char *strcpy(char *dest, const char *src);
功能:把字符串src拷贝到dest处,相当于 = 运算符
注意:会把src末尾的'\0'一起拷贝过来

char *strcat(char *dest, const char *src);
功能:把src字符串追加到dest的末尾  相当于 += 运算符
注意:从dest的\0开始追加src,并且会把src的\0一起追加过来

int strcmp(const char *s1, const char *s2);
功能:按字典序比较两个字符串
    s1 > s2 返回正数
    s1 < s2 返回负数
    s1 == s2 返回0
    逐个字符进行比较,一旦出结果立即结束,后面的不再比较
重点笔试题:
练习:自己实现 strlen、strcpy、strcat、strcmp四个函数的功能(不能调用str系列函数)
复制代码
#include <stdio.h>

size_t my_strlen(const char* s)
{
    const char* temp = s;
    while(*temp) temp++;
    return temp - s;
}

char* my_strcpy(char* dest,const char* src)
{
    char* temp = dest;                             
    //  必须把src末尾的'\0'拷贝给dest
    while(*temp++ = *src++);
    return dest;
}

char* my_strcat(char* dest,const char* src)
{
    char* temp = dest;
    //  temp移动到dest的'\0'位置
    while(*temp) temp++;
    //  把src逐个字符拷贝到dest末尾
    while(*temp++ = *src++);
    return dest;
}

int my_strcmp(const char* s1,const char* s2)
{
    while(*s1 == *s2 && *s1) s1++,s2++;
    return *s1 - *s2;
    /*
    if(*s1 > *s2) return 1;
    if(*s1 < *s2) return -1;
    return 0;
    */
}

int main(int argc,const char* argv[])
{
    char str1[10] = "abcde";
    char str2[10] = "ccc";
    char str3[10] = "bb";
    printf("my_strlen:%d\n",my_strlen(str1));
    printf("my_strcpy:%s\n",my_strcpy(str1,str2));
    printf("my_strcat:%s\n",my_strcat(str3,str2));
    printf("my_strcmp:%d\n",my_strcmp("aac","abc"));
}
字符串相关函数:
复制代码
int atoi(const char *nptr);
功能:字符串转int类型

long atol(const char *nptr);
功能:字符串转long类型

long long atoll(const char *nptr);
功能:字符串转long long类型

double atof(const char *nptr);"2.4"
功能:字符串转double类型

char *strstr(const char *haystack, const char *needle);
功能:查找haystack中是否存在needle
返回值:needle第一次在haystack出现的位置,如果找不到返回NULL
"abcdefcd"  "cad"

char *strchr(const char *s, int c);
功能:查找字符串s中是否有字符c。
返回值:c在s中第一次出现的位置,如果找不到返回NULL。

int sprintf(char *str, const char *format, ...);
功能:把任意类型的数据输出到str中 把任意类型的数据拼接成字符串
返回值:字符串str的长度

int sscanf(const char *str, const char *format, ...);
功能:从str中读取任意类型数据 从字符串中解析任意类型的数据
返回值:成功读取到的变量个数

	int n = 10; 
    double d = 3.14;
    long long l = 444;
    char str[256] = {}; 

    sprintf(str,"学号:%d 分数:%lf 总分:%lld\n",n,d,l);
    printf("--%s--",str);
    int n1 = 0;
    double d1 = 0;
    long long l1 = 0;
    sscanf(str,"学号:%d 分数:%lf 总分:%lld",&n1,&d1,&l1);
    printf("%d %lf %lld\n",n1+10,d1,l1);         
电子通讯录作业:
联系人信息:

姓名、性别、联系电话

功能:

1、添加联系人

2、删除联系人 (按名字删除)

3、修改联系人信息 (按名字查找)

4、查询联系人 (按名字或者电话查询 选做:支持模糊查询)

5、显示所有联系人

6、该通讯录最多只存储100个联系人

7、选做:考虑数据存储在堆内存中

复制代码
#include <stdio.h>

//	名字
char name[100][20];
//	性别
char sex[100];
//	电话
char tel[100][12];

int main()
{
    for(;;)
    {
        //	功能提示
		puts("1、添加联系人");
        puts("2、删除联系人");
        puts("3、修改联系人");
        puts("4、查询联系人");
        puts("5、列出联系人");
        puts("6、退出系统");
        
        switch(getch())
        {
            case 1:	//	执行添加联系人的函数
                	break;
                ...
        }
    }
}
相关推荐
禁默几秒前
深入浅出:AWT的基本组件及其应用
java·开发语言·界面编程
Code哈哈笑10 分钟前
【Java 学习】深度剖析Java多态:从向上转型到向下转型,解锁动态绑定的奥秘,让代码更优雅灵活
java·开发语言·学习
程序猿进阶13 分钟前
深入解析 Spring WebFlux:原理与应用
java·开发语言·后端·spring·面试·架构·springboot
qq_4336184415 分钟前
shell 编程(二)
开发语言·bash·shell
charlie11451419130 分钟前
C++ STL CookBook
开发语言·c++·stl·c++20
袁袁袁袁满30 分钟前
100天精通Python(爬虫篇)——第113天:‌爬虫基础模块之urllib详细教程大全
开发语言·爬虫·python·网络爬虫·爬虫实战·urllib·urllib模块教程
ELI_He99936 分钟前
PHP中替换某个包或某个类
开发语言·php
小林熬夜学编程41 分钟前
【Linux网络编程】第十四弹---构建功能丰富的HTTP服务器:从状态码处理到服务函数扩展
linux·运维·服务器·c语言·网络·c++·http
m0_7482361144 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
倔强的石头1061 小时前
【C++指南】类和对象(九):内部类
开发语言·c++