本文最后更新于117 天前,其中的信息可能已经过时,如有错误请发送邮件到shikeAB@outlook.com。
【转载】C语言入门到精通,这一篇就够了(13万字笔记)6
const关键字
- const是一个类型修饰符
- 使用const修饰变量则可以让变量的值不能改变
##const有什么主要的作用?
- 使用const修饰变量则可以让变量的值不能改变
- (1)可以定义const常量,具有不可变性
const int Max=100;
int Array[Max];
- (2)便于进行类型检查,使编译器对处理内容有更多了解,消除了一些隐患。
void f(const int i) { .........}
+ 编译器就会知道i是一个常量,不允许修改;
- (3)可以避免意义模糊的数字出现,同样可以很方便地进行参数的调整和修改。 同宏定义一样,可以做到不变则已,一变都变!如(1)中,如果想修改Max的内容,只需要:const int Max=you want;即可!
-
(4)可以保护被修饰的东西,防止意外的修改,增强程序的健壮性。 还是上面的例子,如果在 函数体内修改了i,编译器就会报错;
void f(const int i) { i=10;//error! }
- (5) 可以节省空间,避免不必要的内存分配。
#define PI 3.14159 //常量宏
const doulbe Pi=3.14159; //此时并未将Pi放入ROM中 ...... double i=Pi; //此时为Pi分配内存,以后不再分配!
double I=PI; //编译期间进行宏替换,分配内存
double j=Pi; //没有内存分配
double J=PI; //再进行宏替换,又一次分配内存! const定义常量从汇编的角度来看,只是给出了对应的内存地址,而不是象#define一样给出的是立即数,所以,const定义的常量在程序运行过程中只有一份拷贝,而#define定义的常量在内存 中有若干个拷贝。
- (6) 高了效率。编译器通常不为普通const常量分配存储空间,而是将它们保存在符号表 中,这使得它成为一个编译期间的常量,没有了存储与读内存的操作,使得它的效率也很高。
如何使用const?
- (1)修饰一般常量一般常量是指简单类型的常量。这种常量在定义时,修饰符const可以用在类型说明符前,也可以用在类型说明符后
int const x=2; 或 const int x=2;
- (当然,我们可以偷梁换柱进行更新: 通过强制类型转换,将地址赋给变量,再作修改即可以改变const常量值。)
// const对于基本数据类型, 无论写在左边还是右边, 变量中的值不能改变
const int a = 5;
// a = 666; // 直接修改会报错
// 偷梁换柱, 利用指针指向变量
int *p;
p = &a;
// 利用指针间接修改变量中的值
*p = 10;
printf("%d\n", a);
printf("%d\n", *p);
- (2)修饰常数组(值不能够再改变了)定义或说明一个常数组可采用如下格式:
int const a[5]={1, 2, 3, 4, 5};
const int a[5]={1, 2, 3, 4, 5};
const int a[5]={1, 2, 3, 4, 5};
a[1] = 55; // 错误
-
(3)修饰函数的常参数const修饰符也可以修饰函数的传递参数,格式如下:void Fun(const int Var); 告诉编译器Var在函数体中的无法改变,从而防止了使用者的一些无 意的或错误的修改。
-
(4)修饰函数的返回值: const修饰符也可以修饰函数的返回值,是返回值不可被改变,格式如 下:
const int Fun1();
const MyClass Fun2();
-
(5)修饰常指针
- const int *A; //const修饰指针,A可变,A指向的值不能被修改
- int const *A; //const修饰指向的对象,A可变,A指向的对象不可变
- int *const A; //const修饰指针A, A不可变,A指向的对象可变
- const int *const A;//指针A和A指向的对象都不可变
-
技巧
先看“*”的位置
如果const 在 *的左侧 表示值不能修改,但是指向可以改。
如果const 在 *的右侧 表示指向不能改,但是值可以改
如果在“*”的两侧都有const 标识指向和值都不能改。
内存管理
进程空间
- 程序,是经源码编译后的可执行文件,可执行文件可以多次被执行,比如我们可以多次打开 office。
- 而进程,是程序加载到内存后开始执行,至执行结束,这样一段时间概念,多次打开的wps,每打开一次都是一个进程,当我们每关闭一个 office,则表示该进程结束。
- 程序是静态概念,而进程动态/时间概念。
###进程空间图示
有了进程和程序的概念以后,我们再来看一下,程序被加载到内存以后内存空间布局是什么样的
栈内存(Stack)
- 栈中存放任意类型的变量,但必须是 auto 类型修饰的,即自动类型的局部变量, 随用随开,用完即消。
- 内存的分配和销毁系统自动完成,不需要人工干预
- 栈的最大尺寸固定,超出则引起栈溢出
- 局部变量过多,过大 或 递归层数太多等就会导致栈溢出
int ages[10240*10240]; // 程序会崩溃, 栈溢出
#include
int main()
{
// 存储在栈中, 内存地址从大到小
int a = 10;
int b = 20;
printf("&a = %p\n", &a); // &a = 0060FEAC
printf("&b = %p\n", &b); // &b = 0060FEA8
return 0;
}
堆内存(Heap)
- 堆内存可以存放任意类型的数据,但需要自己申请与释放
- 堆大小,想像中的无穷大,但实际使用中,受限于实际内存的大小和内存是否连续性
int *p = (int *)malloc(10240 * 1024); // 不一定会崩溃
#include
#include
int main()
{
// 存储在栈中, 内存地址从小到大
int *p1 = malloc(4);
*p1 = 10;
int *p2 = malloc(4);
*p2 = 20;
printf("p1 = %p\n", p1); // p1 = 00762F48
printf("p2 = %p\n", p2); // p2 = 00762F58
return 0;
}
malloc函数
函数声明 | void * malloc(size_t _Size); |
---|---|
所在文件 | stdlib.h |
函数功能 | 申请堆内存空间并返回,所申请的空间并未初始化。 |
常见的初始化方法是 | memset 字节初始化。 |
参数及返回解析 | |
参数 | size_t _size 表示要申请的字符数 |
返回值 | void * 成功返回非空指针指向申请的空间 ,失败返回 NULL |
#include
#include
#include
int main()
{
/*
* malloc
* 第一个参数: 需要申请多少个字节空间
* 返回值类型: void *
*/
int *p = (int *)malloc(sizeof(int));
printf("p = %i\n", *p); // 保存垃圾数据
/*
* 第一个参数: 需要初始化的内存地址
* 第二个初始: 需要初始化的值
* 第三个参数: 需要初始化对少个字节
*/
memset(p, 0, sizeof(int)); // 对申请的内存空间进行初始化
printf("p = %i\n", *p); // 初始化为0
return 0;
}
free函数
- 注意: 通过malloc申请的存储空间一定要释放, 所以malloc和free函数总是成对出现
函数声明 | void free(void *p); |
---|---|
所在文件 | stdlib.h |
函数功能 | 释放申请的堆内存 |
参数及返回解析 | |
参数 | void* p 指向手动申请的空间 |
返回值 | void 无返回 |
#include
#include
#include
int main()
{
// 1.申请4个字节存储空间
int *p = (int *)malloc(sizeof(int));
// 2.初始化4个字节存储空间为0
memset(p, 0, sizeof(int));
// 3.释放申请的存储空间
free(p);
return 0;
}
calloc函数
函数声明 | void *calloc(size_t nmemb, size_t size); |
---|---|
所在文件 | stdlib.h |
函数功能 | 申请堆内存空间并返回,所申请的空间,自动清零 |
参数及返回解析 | |
参数 | size_t nmemb 所需内存单元数量 |
参数 | size_t size 内存单元字节数量 |
返回值 | void * 成功返回非空指针指向申请的空间 ,失败返回 NULL |
#include
#include
#include
int main()
{
/*
// 1.申请3块4个字节存储空间
int *p = (int *)malloc(sizeof(int) * 3);
// 2.使用申请好的3块存储空间
p[0] = 1;
p[1] = 3;
p[2] = 5;
printf("p[0] = %i\n", p[0]);
printf("p[1] = %i\n", p[1]);
printf("p[2] = %i\n", p[2]);
// 3.释放空间
free(p);
*/
// 1.申请3块4个字节存储空间
int *p = calloc(3, sizeof(int));
// 2.使用申请好的3块存储空间
p[0] = 1;
p[1] = 3;
p[2] = 5;
printf("p[0] = %i\n", p[0]);
printf("p[1] = %i\n", p[1]);
printf("p[2] = %i\n", p[2]);
// 3.释放空间
free(p);
return 0;
}
realloc函数
函数声明 | void *realloc(void *ptr, size_t size); |
---|---|
所在文件 | stdlib.h |
函数功能 | 扩容(缩小)原有内存的大小。通常用于扩容,缩小会会导致内存缩去的部分数据丢失。 |
参数及返回解析 | |
参数 | void * ptr 表示待扩容(缩小)的指针, ptr 为之前用 malloc 或者 calloc 分配的内存地址。 |
参数 | size_t size 表示扩容(缩小)后内存的大小。 |
返回值 | void* 成功返回非空指针指向申请的空间 ,失败返回 NULL。 |
- 注意点:
- 若参数ptr==NULL,则该函数等同于 malloc
- 返回的指针,可能与 ptr 的值相同,也有可能不同。若相同,则说明在原空间后面申请,否则,则可能后续空间不足,重新申请的新的连续空间,原数据拷贝到新空间, 原有空间自动释放
#include
#include
#include
int main()
{
// 1.申请4个字节存储空间
int *p = NULL;
p = realloc(p, sizeof(int)); // 此时等同于malloc
// 2.使用申请好的空间
*p = 666;
printf("*p = %i\n", *p);
// 3.释放空间
free(p);
return 0;
}
#include
#include
#include
int main()
{
// 1.申请4个字节存储空间
int *p = malloc(sizeof(int));
printf("p = %p\n", p);
// 如果能在传入存储空间地址后面扩容, 返回传入存储空间地址
// 如果不能在传入存储空间地址后面扩容, 返回一个新的存储空间地址
p = realloc(p, sizeof(int) * 2);
printf("p = %p\n", p);
// 2.使用申请好的空间
*p = 666;
printf("*p = %i\n", *p);
// 3.释放空间
free(p);
return 0;
}
链表
- 链表实现了,内存零碎数据的有效组织。比如,当我们用 malloc 来进行内存申请的时候,当内存足够,但是由于碎片太多,没有连续内存时,只能以申请失败而告终,而用链表这种数据结构来组织数据,就可以解决上类问题。
静态链表
#include
#include
#include
// 1.定义链表节点
typedef struct node{
int data;
struct node *next;
}Node;
int main()
{
// 2.创建链表节点
Node a;
Node b;
Node c;
// 3.初始化节点数据
a.data = 1;
b.data = 3;
c.data = 5;
// 4.链接节点
a.next = &b;
b.next = &c;
c.next = NULL;
// 5.创建链表头
Node *head = &a;
// 6.使用链表
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
return 0;
}
动态链表
- 静态链表的意义不是很大,主要原因,数据存储在栈上,栈的存储空间有限,不能动态分配。所以链表要实现存储的自由,要动态的申请堆里的空间。
-
有一个点要说清楚,我们的实现的链表是带头节点。至于,为什么带头节点,需等大家对链表有个整体的的认知以后,再来体会,会更有意义。
-
空链表
- 头指针带了一个空链表节点, 空链表节点中的next指向NULL
- 头指针带了一个空链表节点, 空链表节点中的next指向NULL
#include
#include
// 1.定义链表节点
typedef struct node{
int data;
struct node *next;
}Node;
int main()
{
Node *head = createList();
return 0;
}
// 创建空链表
Node *createList(){
// 1.创建一个节点
Node *node = (Node *)malloc(sizeof(Node));
if(node == NULL){
exit(-1);
}
// 2.设置下一个节点为NULL
node->next = NULL;
// 3.返回创建好的节点
return node;
}
- 非空链表
- 头指针带了一个非空节点, 最后一个节点中的next指向NULL
- 头指针带了一个非空节点, 最后一个节点中的next指向NULL
动态链表头插法
- 1.让新节点的下一个节点等于头结点的下一个节点
- 2.让头节点的下一个节点等于新节点
#include
#include
// 1.定义链表节点
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
int main()
{
Node *head = createList();
printNodeList(head);
return 0;
}
/**
* @brief createList 创建链表
* @return 创建好的链表
*/
Node *createList(){
// 1.创建头节点
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 2.接收用户输入数据
int num = -1;
printf("请输入节点数据\n");
scanf("%i", &num);
// 3.通过循环创建其它节点
while(num != -1){
// 3.1创建一个新的节点
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = num;
// 3.2让新节点的下一个节点指向头节点的下一个节点
cur->next = head->next;
// 3.3让头节点的下一个节点指向新节点
head->next = cur;
// 3.4再次接收用户输入数据
scanf("%i", &num);
}
// 3.返回创建好的节点
return head;
}
/**
* @brief printNodeList 遍历链表
* @param node 链表指针头
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
动态链表尾插法
- 1.定义变量记录新节点的上一个节点
- 2.将新节点添加到上一个节点后面
- 3.让新节点成为下一个节点的上一个节点
#include
#include
// 1.定义链表节点
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
int main()
{
Node *head = createList();
printNodeList(head);
return 0;
}
/**
* @brief createList 创建链表
* @return 创建好的链表
*/
Node *createList(){
// 1.创建头节点
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 2.接收用户输入数据
int num = -1;
printf("请输入节点数据\n");
scanf("%i", &num);
// 3.通过循环创建其它节点
// 定义变量记录上一个节点
Node *pre = head;
while(num != -1){
// 3.1创建一个新的节点
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = num;
// 3.2让新节点链接到上一个节点后面
pre->next = cur;
// 3.3当前节点下一个节点等于NULL
cur->next = NULL;
// 3.4让当前节点编程下一个节点的上一个节点
pre = cur;
// 3.5再次接收用户输入数据
scanf("%i", &num);
}
// 3.返回创建好的节点
return head;
}
/**
* @brief printNodeList 遍历链表
* @param node 链表指针头
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
动态链优化
#include
#include
// 1.定义链表节点
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
void insertNode1(Node *head, int data);
void insertNode2(Node *head, int data);
int main()
{
// 1.创建一个空链表
Node *head = createList();
// 2.往空链表中插入数据
insertNode1(head, 1);
insertNode1(head, 3);
insertNode1(head, 5);
printNodeList(head);
return 0;
}
/**
* @brief createList 创建空链表
* @return 创建好的空链表
*/
Node *createList(){
// 1.创建头节点
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 3.返回创建好的节点
return head;
}
/**
* @brief insertNode1 尾插法插入节点
* @param head 需要插入的头指针
* @param data 需要插入的数据
* @return 插入之后的链表
*/
void insertNode1(Node *head, int data){
// 1.定义变量记录最后一个节点
Node *pre = head;
while(pre != NULL && pre->next != NULL){
pre = pre->next;
}
// 2.创建一个新的节点
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = data;
// 3.让新节点链接到上一个节点后面
pre->next = cur;
// 4.当前节点下一个节点等于NULL
cur->next = NULL;
// 5.让当前节点编程下一个节点的上一个节点
pre = cur;
}
/**
* @brief insertNode1 头插法插入节点
* @param head 需要插入的头指针
* @param data 需要插入的数据
* @return 插入之后的链表
*/
void insertNode2(Node *head, int data){
// 1.创建一个新的节点
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = data;
// 2.让新节点的下一个节点指向头节点的下一个节点
cur->next = head->next;
// 3.让头节点的下一个节点指向新节点
head->next = cur;
}
/**
* @brief printNodeList 遍历链表
* @param node 链表指针头
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
链表销毁
/**
* @brief destroyList 销毁链表
* @param head 链表头指针
*/
void destroyList(Node *head){
Node *cur = NULL;
while(head != NULL){
cur = head->next;
free(head);
head = cur;
}
}
链表长度计算
/**
* @brief listLength 计算链表长度
* @param head 链表头指针
* @return 链表长度
*/
int listLength(Node *head){
int count = 0;
head = head->next;
while(head){
count++;
head = head->next;
}
return count;
}
链表查找
/**
* @brief searchList 查找指定节点
* @param head 链表头指针
* @param key 需要查找的值
* @return
*/
Node *searchList(Node *head, int key){
head = head->next;
while(head){
if(head->data == key){
break;
}else{
head = head->next;
}
}
return head;
}
链表删除
void deleteNodeList(Node *head, Node *find){
while(head->next != find){
head = head->next;
}
head->next = find->next;
free(find);
}
作业
- 给链表排序
/**
* @brief bubbleSort 对链表进行排序
* @param head 链表头指针
*/
void bubbleSort(Node *head){
// 1.计算链表长度
int len = listLength(head);
// 2.定义变量记录前后节点
Node *cur = NULL;
// 3.相邻元素进行比较, 进行冒泡排序
for(int i = 0; i < len - 1; i++){
cur = head->next;
for(int j = 0; j < len - 1 - i; j++){
printf("%i, %i\n", cur->data, cur->next->data);
if((cur->data) > (cur->next->data)){
int temp = cur->data;
cur->data = cur->next->data;
cur->next->data = temp;
}
cur = cur->next;
}
}
}
/**
* @brief sortList 对链表进行排序
* @param head 链表头指针
*/
void sortList(Node *head){
// 0.计算链表长度
int len = listLength(head);
// 1.定义变量保存前后两个节点
Node *sh, *pre, *cur;
for(int i = 0; i < len - 1; i ++){
sh = head; // 头节点
pre = sh->next; // 第一个节点
cur = pre->next; // 第二个节点
for(int j = 0; j < len - 1 - i; j++){
if(pre->data > cur->data){
// 交换节点位置
sh->next = cur;
pre->next = cur->next;
cur->next = pre;
// 恢复节点名称
Node *temp = pre;
pre = cur;
cur = temp;
}
// 让所有节点往后移动
sh = sh->next;
pre = pre->next;
cur = cur->next;
}
}
}
- 链表反转
/**
* @brief reverseList 反转链表
* @param head 链表头指针
*/
void reverseList(Node *head){
// 1.将链表一分为二
Node *pre, *cur;
pre = head->next;
head->next = NULL;
// 2.重新插入节点
while(pre){
cur = pre->next;
pre->next = head->next;
head->next = pre;
pre = cur;
}
}
文件基本概念
- 文件流:
- C 语言把文件看作是一个字符的序列,即文件是由一个一个字符组成的字符流,因此 c 语言将文件也称之为文件流。
- 文件分类
-
文本文件
- 以 ASCII 码格式存放,一个字节存放一个字符。
文本文件的每一个字节存放一个 ASCII 码,代表一个字符
。这便于对字符的逐个处理,但占用存储空间
较多,而且要花费时间转换。 - .c文件就是以文本文件形式存放的
- 以 ASCII 码格式存放,一个字节存放一个字符。
-
二进制文件
- 以补码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间较少。
数据按其内存中的存储形式原样存放
- .exe文件就是以二进制文件形式存放的
- 以补码格式存放。二进制文件是把数据以二进制数的格式存放在文件中的,其占用存储空间较少。
-
- 文本文件和二进制文件示例
- 下列代码暂时不要求看懂, 主要理解什么是文本文件什么是二进制文件
#include
int main()
{
/*
* 以文本形式存储
* 会将每个字符先转换为对应的ASCII,
* 然后再将ASCII码的二进制存储到计算机中
*/
int num = 666;
FILE *fa = fopen("ascii.txt", "w");
fprintf(fa, "%d", num);
fclose(fa);
/*
* 以二进制形式存储
* 会将666的二进制直接存储到文件中
*/
FILE *fb = fopen("bin.txt", "w");
fwrite(&num, 4, 1, fb);
fclose(fb);
return 0;
}
-
内存示意图
-
通过文本工具打开示意图
- 文本工具默认会按照ASCII码逐个直接解码文件, 由于文本文件存储的就是ASCII码, 所以可以正常解析显示, 由于二进制文件存储的不是ASCII码, 所以解析出来之后是乱码
文件的打开和关闭
- FILE 结构体
- FILE 结构体是对缓冲区和文件读写状态的记录者,所有对文件的操作,都是通过FILE 结构体完成的。
struct _iobuf {
char *_ptr; //文件输入的下一个位置
int _cnt; //当前缓冲区的相对位置
char *_base; //文件的起始位置)
int _flag; //文件标志
int _file; //文件的有效性验证
int _charbuf; //检查缓冲区状况,如果无缓冲区则不读取
int _bufsiz; // 缓冲区大小
char *_tmpfname; //临时文件名
};
typedef struct _iobuf FILE;
- fileopen函数
函数声明 | FILE * fopen ( const char * filename, const char * mode ); |
---|---|
所在文件 | stdio.h |
函数功能 | 以 mode 的方式,打开一个 filename 命名的文件,返回一个指向该文件缓冲的 FILE 结构体指针。 |
参数及返回解析 | |
参数 | char*filaname :要打开,或是创建文件的路径。 |
参数 | char*mode :打开文件的方式。 |
返回值 | FILE* 返回指向文件缓冲区的指针,该指针是后序操作文件的句柄。 |
mode | 处理方式 | 当文件不存在时 | 当文件存在时 | 向文件输入 | 从文件输出 |
---|---|---|---|---|---|
r | 读取 | 出错 | 打开文件 | 不能 | 可以 |
w | 写入 | 建立新文件 | 覆盖原有文件 | 可以 | 不能 |
a | 追加 | 建立新文件 | 在原有文件后追加 | 可以 | 不能 |
r+ | 读取/写入 | 出错 | 打开文件 | 可以 | 可以 |
w+ | 写入/读取 | 建立新文件 | 覆盖原有文件 | 可以 | 可以 |
a+ | 读取/追加 | 建立新文件 | 在原有文件后追加 | 可以 | 可以 |
注意点:
- Windows如果读写的是二进制文件,则还要加 b,比如 rb, r+b 等。 unix/linux 不区分文本和二进制文件
- fclose函数
函数声明 | int fclose ( FILE * stream ); |
---|---|
所在文件 | stdio.h |
函数功能 | fclose()用来关闭先前 fopen()打开的文件. |
函数功能 | 此动作会让缓冲区内的数据写入文件中, 并释放系统所提供的文件资源 |
参数及返回解析 | |
参数 | FILE* stream :指向文件缓冲的指针。 |
返回值 | int 成功返回 0 ,失败返回 EOF(-1)。 |
#include
int main()
{
FILE *fp = fopen("test.txt", "w+");
fclose(fp);
return 0;
}
–
一次读写一个字符
- 写入
函数声明 | int fputc (int ch, FILE * stream ); |
---|---|
所在文件 | stdio.h |
函数功能 | 将 ch 字符,写入文件。 |
参数及返回解析 | |
参数 | FILE* stream :指向文件缓冲的指针。 |
参数 | int : 需要写入的字符。 |
返回值 | int 写入成功,返回写入成功字符,如果失败,返回 EOF。 |
#include
int main()
{
// 1.打开一个文件
FILE *fp = fopen("test.txt", "w+");
// 2.往文件中写入内容
for(char ch = 'a'; ch <= 'z'; ch++){
// 一次写入一个字符
char res = fputc(ch, fp);
printf("res = %c\n", res);
}
// 3.关闭打开的文件
fclose(fp);
return 0;
}
- 读取
函数声明 | int fgetc ( FILE * stream ); |
---|---|
所在文件 | stdio.h |
函数功能 | 从文件流中读取一个字符并返回。 |
参数及返回解析 | |
参数 | FILE* stream :指向文件缓冲的指针。 |
返回值 | int 正常,返回读取的字符;读到文件尾或出错时,为 EOF。 |
#include
int main()
{
// 1.打开一个文件
FILE *fp = fopen("test.txt", "r+");
// 2.从文件中读取内容
char res = EOF;
while((res = fgetc(fp)) != EOF){
printf("res = %c\n", res);
}
// 3.关闭打开的文件
fclose(fp);
return 0;
}
- 判断文件末尾
- feof函数
函数声明 | int feof( FILE * stream ); |
---|---|
所在文件 | stdio.h |
函数功能 | 判断文件是否读到文件结尾 |
参数及返回解析 | |
参数 | FILE* stream :指向文件缓冲的指针。 |
返回值 | int 0 未读到文件结尾,非零 读到文件结尾。 |
#include
int main()
{
// 1.打开一个文件
FILE *fp = fopen("test.txt", "r+");
// 2.从文件中读取内容
char res = EOF;
// 注意: 由于只有先读了才会修改标志位,
// 所以通过feof判断是否到达文件末尾, 一定要先读再判断, 不能先判断再读
while((res = fgetc(fp)) && (!feof(fp))){
printf("res = %c\n", res);
}
// 3.关闭打开的文件
fclose(fp);
return 0;
}
- 注意点:
- feof 这个函数,是去读标志位判断文件是否结束的。
- 而标志位只有读完了才会被修改, 所以如果先判断再读标志位会出现多打一次的的现象
- 所以企业开发中使用feof函数一定要先读后判断, 而不能先判断后读
- 作业
- 实现文件的简单加密和解密
#include
#include
void encode(char *name, char *newName, int code);
void decode(char *name, char *newName, int code);
int main()
{
encode("main.c", "encode.c", 666);
decode("encode.c", "decode.c", 666);
return 0;
}
/**
* @brief encode 加密文件
* @param name 需要加密的文件名称
* @param newName 加密之后的文件名称
* @param code 秘钥
*/
void encode(char *name, char *newName, int code){
FILE *fw = fopen(newName, "w+");
FILE *fr = fopen(name, "r+");
char ch = EOF;
while((ch = fgetc(fr)) && (!feof(fr))){
fputc(ch ^ code, fw);
}
fclose(fw);
fclose(fr);
}
/**
* @brief encode 解密文件
* @param name 需要解密的文件名称
* @param newName 解密之后的文件名称
* @param code 秘钥
*/
void decode(char *name, char *newName, int code){
FILE *fw = fopen(newName, "w+");
FILE *fr = fopen(name, "r+");
char ch = EOF;
while((ch = fgetc(fr)) && (!feof(fr))){
fputc(ch ^ code, fw);
}
fclose(fw);
fclose(fr);
}
来自陕西