文件操作:
知识点: 持久化 1.文本文件的读写 2.二进制文件的读写 3.缓冲文件系统 1.打开文件 2.读写文件 3.保存 4.关闭文件 ====================================== 文本文件的读写 1.文件的打开 1)打开模式 r(read) 读文件,不存在,打开失败 w(write) 写文件,如果文件不存在,创建文件,存在则清空文件内容 a(append) 以追加方式写文件 a+ 读写方式打开,以追加方式写文件 r+ 读写方式打开,文件存在打开 w+ 读写方式打开,文件不存在创建,存在打开并清空 2)函数原型 FILE *fopen(const char *path, const char *mode) 思考:如果打开的文件不存在会如何 2.文件的关闭 1)函数原型 int fclose(FILE *file); 3.读写文件内容 1)按字符读文本文件,从文件读取一个字符 1>函数原型 int fgetc(FILE *file); 函数返回值:读取成功返回该字符的ascii码,失败返回-1 1.如果读到文件末尾,返回-1 2.如果打开方式不对,返回-1 思考:如何判断读到文件末尾 2)按字符写文本文件,向文件写入一个字符 int fputc(int c, FILE *fp); 函数返回值:写入成功返回写入字符对应的ascii码,失败返回-1 1.打开方式不对,返回-1 2.磁盘空间不足,返回-1 3.对文件没有写权限,返回-1 练习: 1.通过循环将字符数组中的内容写入到文件中 2.读取文件中的文件内容 3)按行读文本文件,从文本读取一行字符 1>函数原型 char *fgets(char *buf, int size, FILE *fp); stdin stdout stderr fgets(buf, 100, stdin); 函数返回值:读取成功返回读取和参数buf相同的地址,失败返回NULL 4)按行写文本文件,向文本写入一行字符 1>函数原型 int fputs(char *s, FILE *fp); 函数返回值:返回成功写如的字符数,失败返回-1 5)格式化读写文本文件 1>格式化输出函数原型,按照指定格式将内容写入文本文件 %s|%d|%d|%d|%d|%c %s 角色名 char *name %d 级别 int level %d 血 int boold %d 攻 int act %d 防 int def %c y/n char boss int fprintf(FILE *fp, const char *fmt, …); 函数返回值:成功返回写入的字符数,失败返回-1 2>格式化输入函数原型,按照指定格式从文本文件读取内容 int fscanf(FILE *fp, const char *fmt, …); %[^'|'] 函数返回值:成功返回对应读取的变量个数,失败返回-1 练习:1.按照指定格式保存自身的基本信息 2.按照指定格式读取自身的基本信息并且输出 ============================================= 二进制文件的读写 需求: 1.现在需要保存班级中的所有学生的身高信息 int height[10] %s|%d|%d a|9|100 a|100|9 double score[10] 2.保存学生的完整信息 1.文件的打开 1)读写模式 同上 2.读写文件内容 1)读取函数,从文件读取数据到内存中 size_t fread(void *ptr, size_t size, size_t n, FILE *stream); 参数:1.要保存数据的首地址 2.每次读取的数据大小 3.读取次数,每次读取都会在首地址后+对应的数据大小 4.读取的文件 返回值:成功返回读取的次数,失败返回0 2)写入函数,把内存的一块数据写入到文件 size_t fwrite(void *ptr, size_t size, size_t n, FILE *stream); 参数:1.要写入数据的首地址 2.每次写入的数据大小 3.写入次数 4.写入的文件 返回值:成功返回写入的次数,失败返回0 3)文件定位函数,文件随机读写 1>ftell,读取文件当前读写位置 long ftell(FILE *stream); 2>设置当前文件读写位置 int fseek(FILE *stream, long offset, int whence); whence: SEEK_SET 相对与文件开头 SEEK_CUR 相对于当前位置 SEEK_END 相对于文件末尾 练习:将文件位置指针向后移动n个位置 然后写入数据,查看最后的结果============================================= 缓冲文件系统 思考:printf的内容一定会马上输出到屏幕吗 1.缓冲系统的作用 2.缓冲的过程 printf->fprintf->缓冲区->write 行缓冲 全缓冲 fflush(fp) 无缓冲 stderr 3.手动刷新缓冲 fflush 4.系统的标准输输入,输出,错误输出 stdin, stdout, stderr ============================================= 作业: 1.文件十六进制显示工具 fp1 %c —> fp2 %x fprintf(fp2, “%x”, var);
链表:
知识点: 1.链表基础 2.节点的创建和添加 llist_append_node 3.链表的遍历 llist_print_each 4.链表的查找与修改 5.链表的插入与删除 6.链表的销毁 7.链表逆序 ========================== 回顾数组 1.数组的常用操作 1)插入 2)修改 3)遍历 4)删除 5)逆序 2.数组操作的问题 1)插入和删除的效率低 1 2 3 5 6 0 0 1 2 3 4 9 5 6 1 2 3 4 9 5 6 0 2)当数组空间不足时需要重新申请内存空间 3)但是遍历速度快 ========================== 链表基础 1.什么是链表 链表(Linked list)是一种常见的基础数据结构,是一种线性表 2.链表的作用 一种数据结构,保存数据 3.如何创建链表 ========================== 链表节点的创建和添加 1.如何创建和添加一个节点 2.如何插入一个节点 4.处理链表的框架 void llist_append_node (struct node *head, struct node *new) void llist_change_node(struct node *head, int id, char *name) append * insert * search * change delete destory print_all * print_node 5.添加节点模块append 练习:添加一个节点到头结点后面 ========================== 链表的遍历 1. llist_print_each函数 void llist_print_each(struct node *head); 练习: 1.遍历输出链表内容 2.向链表添中加多个节点 ========================== 链表的查找与修改 1. llist_search_node函数 struct node *llist_search_node(struct node *head, int id) void llist_print_node(struct node *nd) 1)遍历链表 2)比较要搜索的内容是否和节点数据内容匹配 3)返回节点地址 练习: 1.查找指定id的学生信息,并输出该信息 2.change函数 1)搜索要修改的节点空间 2)修改节点内的数据 1.修改指定id的学生性别 ========================== 链表的插入和删除 1. llist_insert_node函数 void llist_insert_node(struct node *head, struct node *nd, int id) 1)创建一个新节点 2)插入到指定位值 练习: 链表包含10个节点 id=3,id=4 <== tmp id=1 1.创建一个新节点并且插入到第一个节点的前面 2.创建一个节点并且插入到第三和第四个节点之间 2.delete函数 void llist_delete_node(struct node *head, int id); 1)修改该节点上一个节点的指向 2)释放当前节点 练习: 1.删除id为1节点 2.删除id为10节点 3.删除id为5节点 ========================== 链表销毁 1.destory函数 int llist_destory(struct node *head); 1)销毁和清空是两种不同的操作 例如:倒空杯子的水和砸碎杯子两个操作 练习: 销毁上面创建的链表 ========================== 链表逆序 H->A->B->C->D->E ^ ^ ^ p pp t 1.inverse函数 void llist_inverse(struct node *head); 练习: 1.按照上述顺序实现一个链表的逆序,并输出逆序的结果 ========================== 多文件封装 1.头文件 2.实现文件 ========================== 创建一个班级链表,该链表包含10个学生信息 1.每个学生包含信息有 姓名,年龄,身高,性别,语数英三门成绩 2.实现: 1)添加学生信息 2)输出所有的学生信息 3)搜索指定学生的信息,并且输出 4)将指定学生插入到指定学生的前面 5)删除指定学生信息 6)销毁班级链表 ======================== enume menu{ EXIT, ADD, SONE, SALL, INSERT, DEL, DELALL}; 1.添加学生 2.查询指定学生信息 3.查询所有学生信息 4.学生插队 5.删除指定学生信息 6.删除班级 0.退出系统
预处理:
知识点: 1.宏的定义 #define #undef 2.宏与函数的区别 3.文件包含和多文件编译 #include <> “” #import 3.条件编译 #if #elif #else #endif #ifdef #ifndef 4.typedef关键字 > gcc -E 源代码文件.c -o 预处理结果文件.i //预处理 > gcc -S 预处理结果文件.i -o 汇编结果文件.s //编译 翻译为汇编 > gcc -c 汇编结果文件.s -o 目标文件.o //汇编 > gcc 目标文件.o -o a.out //链接 ld ================================= 宏定义 0.什么是宏定义 1)宏定义是C提供的三种预处理功能中的一种,预处理包括宏定义、文件包含、条件编译 1.宏定义的作用 1)使用标识符来替换一些常用的代码 for (i = 0; i < 100; ++i) LOOP100 { printf(“i”); } WALK 小明---nickname 别名 2.宏定义的本质 1)一种占位符,用来代替一些常用代码 2)一切以转换为前提,做任何事情之前先要替换 2.基本的宏定义 0)#define指令语法!!!! 宏指令 宏名 宏体 1)标识符 2)大写,不加分号结束 3)使用 示例:定义常量PI的宏 练习: 定义数组大小的宏 定义重力加速度宏 9.8 G 3.带有参数的宏定义 1)示例:计算两数最大值的宏 练习: 1.计算两数最小值的宏 ?: 2.计算两数之和的宏,两数之积 思考:ADD(1,2)*ADD(1,2)结果 4.宏替换的问题 1)宏替换作用的演示,gcc编译参数 2)思考:能否嵌套定义或调用 5.常用宏 2)字母大小写转换 TOLOWER ch + 32 TOUPPER ch - 32 4)设置指定数某位为1或0 SET1, SET0 5)获取某一位的值 GETBITS 6)取得一个short的高1个字节和低1个字节 SLOW SHIGH 7)将short的高字节和低字节互换 8) 交换两数宏 9)安全释放内存宏 SAFTY_FREE 6.宏和函数的区别 1)思考:宏的本质和函数的本质 2)思考:宏和函数调用的速度,安全性和空间 3)回忆函数调用的过程 函数调用需要开辟栈空间,保存参数和局部变量 4)本质:宏只是替换,并不会产生函数调用 5)速度:宏不占运行时间,只占编译时间,函数调用占运行时间 6)安全:宏不会检查参数类型,函数会检查参数类型 7)空间:宏的参数不占用空间,函数的用空间 8)能否调用自身:宏不能进行递归调用,函数可以 思考:如何结合宏与函数的优势 1.代码非常简单而又十分常用的时候使用宏 2.代码非常复杂,一般使用函数 ================================== 文件包含和多文件编译 1.思考:如何实现多人合作项目 接口---->函数原型 .h //head 实现---->函数定义 .c cvs git svn 2.回忆函数定义和声明 3.头文件和源文件 4.#include指令语法!!!! 1)本质:头文件包含实际是替换文件内容 2)作用:实现声明和实现相分离,进一步使程序更加的模块化 #include <stdio.h> 尖括号表示系统标准目录 双引号表示自定义路径 add, sub, mut, div 5.示例:链表的实现的声明相分离 练习: 1.实现加减乘除四则运算,并将函数的声明与实现相分离 并编写测试函数测试该些函数 A B C D 200 100 =================================== 条件预编译 需求:现在用户只购买系统中的某些模块 1.思考:如何可以简单快速的在编译的过程中满足用户的需求 2.#ifdef 条件编译语法!!!! #ifndef 1)作用:模块选择 示例:判断是否定义过PI宏 练习:判断是否有定义上述的常用宏 思考: 1.如何可以安全,快速的注释代码 2.// 和 /**/注释是否可以嵌套使用 3.#if 条件编译语法!!!! 1)作用 快速安全的注释代码 4.安全注释 思考:如果重复包含头文件会如何 5.防止头文件重复包含 =================================== 调试宏 NSLog 思考:如何使用printf调试代码,是否方便移出 1.调试宏定义 INFO, WARN, ERROR, DEBUG 2.调试开关 3.预定义宏 printf("line = %d, file = %s, DATA = %s, TIME = %s", __LINE__, __FILE__, __DATE__, __TIME__); __LINE__:行号 __FILE__:文件名 __DATE__:日期 %s __TIME__:时间 __FUNCTION__, __func__:函数名 =================================== typedef关键字 int4 int32 int64 思考:程序的跨平台性 64 32 16 8 int int int2 long long long size_t long long int64 long 1.typedef作用 1)typedef常用来定义一个标识符和及关键字的别名,并不分配实际的内存 2)typedef可以增强程序的可读性 3)typedef可以增强标识符的灵活性 2.typedef本质 typedef本质为类型的别名 3.typedef语法使用!!!! 4.重定义结构体 5.重定义类型 6.typedef与define的区别 1)typedef是为了程序的可读性而为标识符另起的别名,而 define在c语言中是为了定义常量,常用代码替换 2)define可以使用#ifdef和#ifndef来进行逻辑判断
动态内存分配:
知识点: 动态内存管理 1.数据在内存中的存储 2.内存分配malloc和realloc和calloc函数使用和注意事项 3.free函数使用 4.堆和栈的区别 .heap .stack .bss .data .rodata 5.常用内存操作函数 ============================== 内存使用 1.回顾函数在使用时候所开辟的内存栈 2.变量所在的内存空间 3.栈空间的限制 stack 1)栈大小:一般在2M左右 4.栈数据的特点 1)回顾局部变量的生命周期 2)每个变量的内存大小均在编译时候确定,空间由操作系统分配 3)数据由系统自动释放 4)数据分配在栈段 思考: int var; return &var; 20000 / 1000 == 20 0.是否可以返回函数执行过程中局部变量的内存地址 1.如果需要的内存栈无法提供足够的内存容纳我们的数据怎么办 2.如果需要将函数内的数据保存到程序结束再销毁,是否可以 5.堆空间的使用 heap 1)堆空间的特点 1>大小由程序员决定,空间由程序员分配 2>大小在运行时候确定 3>由程序员手动释放 4>数据分配在堆段 ============================== 动态内存分配函数 1.malloc 内存分配 1)函数原型 void *malloc(size_t size) void *calloc(int n, size_t size) 申请size个字节的内存 2)函数使用 void *p = malloc(4096 * 1024 * 1024); 3)分配内存注意 1)函数执行完后如果成功,返回该size大小内存的首地址 2)函数执行失败返回NULL 3)malloc会在堆上寻找一块size个字节的内存返回给用户 4)使用之前一定要判断是否分配成功,因为malloc函数执行有可能会失败(内存不足) 1.内存不足 2.没有足够大的连续内存空间 练习: 1.分配一能足够保存double 数据的内存空间,并且赋值输出 2.分配一个足够保存n个double类型数据 的内存空间,并且赋值输出 思考:如果现在所使用的内存空间不足,想要重新分配一块内存,但是又不想丢失原来的数据,么办 2.realloc 重新分配内存 1)函数原型 void *realloc(void *ptr, size_t size) 在ptr指针后再分配size个字节的内存空间 2)函数使用 realloc(ptr, 40); 3)函数使用注意 1)思考:如果ptr执行NULL会如何 2)思考:如果在ptr后没有size个字节的内存会如何 3)思考:函数返回值是什么 练习:使用realloc函数为malloc分配的内存向后扩展40个字节的空间 0.比较返回地址是否为原理的内存地址 1.输出新空间中的所有内容 2.对新的内存空间进行赋值 ================================ 内存释放函数 思考:申请的内存是否需要释放,不释放是否可以,会造成怎样的结果 1.free 内存释放 1)函数原型 void free(void *ptr) 2)函数使用 free(ptr) 3)函数使用注意 思考:释放后的内存是否可以继续使用 1>只能对有效分配的内存进行释放 思考: 被释放的内存还可以引用吗 2>思考:如果对一个指针连续释放两次会如何 3>不能释放栈上的空间 3>如何避免上述情况 练习:释放malloc和realloc申请的内存空间 ================================ 堆和栈的区别 1.分配大小 栈 不能超过2M 堆 可以任意分配,只要系统有内存 2.分配速度 栈 快 堆 慢 =============================== 数据在内存中的存储与分布 1.数据区:栈,堆,数据段 2.栈 1)进入函数时分配空间,函数结束时释放空间 2)局部变量,函数参数均在此段中 3)此段中的初始值均为随机数 3.堆 1)malloc申请,free释放 2)初始值 4.数据段 1)全局变量 bss 没有初始化的全局变量 2)static静态变量 3)字符串常量(只读静态存储取) rodata ================================ 常用内存处理函数 1.memset 将指定地址指定大小设置为指定内容 1)函数原型 void *memset(void *ptr, int c, size_t len) 2)函数使用 3)函数使用注意 练习:分配10个字节的内存空间 1.输出该空间中的内容 2.将该内存空间上的内容全部设置为1 2.memmove 内存拷贝,从s2拷贝len的字节内容到s1中 1)函数原型 void *memmove(void *dst, const void *src, size_t len) void *memcpy(void *dst, const void *src, size_t len) 2)函数使用 3)函数使用注意 ===========================================