最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網(wǎng) 會員登陸 & 注冊

小甲魚C語言《帶你學C帶你飛》學習筆記(2)

2020-03-15 09:18 作者:康康Loong  | 我要投稿

視頻鏈接https://www.bilibili.com/video/av27744141

由于篇幅限制,本筆記分兩部分:

小甲魚C語言《帶你學C帶你飛》學習筆記(1)-傳送門

?小甲魚C語言《帶你學C帶你飛》學習筆記(2)-本文

本筆記是本人根據(jù)小甲魚視頻講課內(nèi)容記錄,一些程序做了小修改。根據(jù)自身補充了一部分拓展內(nèi)容。如有錯誤,歡迎一起討論????????——正能量的康sir? ? ??

?如果喜歡我的文章,不妨點個關(guān)注、點個贊、投個幣,謝謝:)

P45單鏈表1

單鏈表

信息域 指針域

信息域存儲數(shù)據(jù),指針域指向下一個地址


最后一個指針域指向NULL

?


單鏈表插入元素(頭插法)

#include<stdio.h>

#include<stdlib.h>

struct Book {

???????? char title[128];

???????? char author[40];

???????? struct Book *next;

};

void getInput(struct Book *book){

???????? printf("請輸入書名:");

???????? scanf("%s",book->title);

???????? printf("請輸入作者:");

???????? scanf("%s",book->author);

}

void addBook(struct Book **library){//**library指向book結(jié)構(gòu)指針的指針

???????? struct Book *book,*temp;

???????? book=(struct Book *)malloc(sizeof(struct Book));

???????? if(book==NULL){

????????????????? printf("內(nèi)存分配失敗!\n");

????????????????? exit(1);//需要stdlib.h

???????? }

???????? getInput(book);

???????? if(*library!=NULL){//有書的情況,頭指針 *library指向新插入書位置

????????????????? temp=*library;//頭指針原來書的位置

????????????????? *library=book;//指向新插入書位置

????????????????? book->next=temp;//next指向下一個節(jié)點地址,即原來書的位置

???????? }

???????? else{//開始沒有書的情況 *library=NULL的情況下添加書

????????????????? *library=book;//第一個節(jié)點指針不是 NULL了是book節(jié)點的

????????????????? book->next=NULL; //next指向下一個節(jié)點地址,即NULL

???????? }

}

?

void printLibrary(struct Book *library){

???????? struct Book *book;

???????? int count = 1;

???????? book=library;

???????? while(book!=NULL){

????????????????? printf("-----------Book%d-----------\n",count);

????????????????? printf("書名:%s\n",book->title);

????????????????? printf("作者:%s\n",book->author);

????????????????? book=book->next;

????????????????? count++;

???????? }

}

void releseLibrary(struct Book *library){//釋放資源

???????? struct Book *temp;

???????? while(library!=NULL){

????????????????? temp=library->next;

????????????????? free(library);

????????????????? library=temp;???

???????? }

}

int main(){

???????? struct Book *library=NULL;

???????? int ch;

???????? while(1){

????????????????? do{

????????????????????????? printf("是否錄入書籍信息(Y/N):");

????????????????????????? ch=getchar();

????????????????? } while(ch!='Y'&&ch!='N');

????????????????? if(ch=='Y')

????????????????? {

????????????????????????? addBook(&library);

????????????????? }

????????????????? else

????????????????? {

????????????????????????? break ;

????????????????? }

???????? }

???????? do{???????????

????????????????? printf("是否打印書籍信息(Y/N):");

????????????????? ch=getchar();

???????? } while(ch!='Y'&&ch!='N');

???????? if(ch=='Y')

???????? {

????????????????? printLibrary(library);

???????? }

???????? releseLibrary(library);

???????? return 0;

}

P46單鏈表2


單鏈表插入元素(尾插法)

只需修改addBook函數(shù)的有書情況下的插法

???????? struct Book *temp;

。。。

???????? if(*library!=NULL){//有書的情況

????????????????? temp=*library;

????????????????? while(temp->next!=NULL){//定位 單鏈表尾部位置

????????????????????????? temp=temp->next;

????????????????? }

????????????????? //插入數(shù)據(jù)

????????????????? temp->next=book;

????????????????? book->next=NULL;

???????? }

優(yōu)化 定義一個指針始終指向尾部,提高效率

static struct Book *tail;

。。。

???????? if(*library!=NULL){//有書的情況

????????????????? tail->next=book;

????????????????? book->next=NULL;

???????? }

???????? else{//開始沒有書的情況 *library=NULL的情況下添加書

????????????????? *library=book;//第一個節(jié)點指針不是 NULL了是book節(jié)點的

????????????????? book->next=NULL; //next指向下一個節(jié)點地址,即NULL

???????? }

???????? tail=book;

?

搜索單鏈表

struct Book *searchBook(struct Book *library,char *target){

???????? struct Book *book;

???????? book=library;

???????? while(book!=NULL){

????????????????? if(!strcmp(book->title,target)||!strcmp(book->author,target)){//strcmp相等返回0 。需要string.h

????????????????????????? break;

????????????????? }

????????????????? book=book->next;

???????? }

???????? return book;

}

void printBook(struct Book *book){

???????? printf("書名:%s",book->title);

???????? printf("作者:%s",book->author);

}

int main(){

。。。

???????? char input[128];

???????? struct Book *book;

。。。

???????? printf("請輸入查找的書名或作者") ;

???????? scanf("%s",input);

???????? book=searchBook(library,input);

???????? if(book==NULL)

???????? {

????????????????? printf("很抱歉,未找到");

???????? }

???????? else{

????????????????? do{

????????????????????????? printf("已找到符合條件的圖書...");

????????????????????????? printBook(book);

????????????????? }while((book =searchBook(book->next,input))!=NULL);//多本圖書都匹配的話可以重復(fù)找

???????? }

。。。

}

P47單鏈表3


單鏈表插入節(jié)點(中間插入)

#include<stdio.h>

#include<stdlib.h>

struct Node{

???????? int value;

???????? struct Node *next;

};

void insertNode(struct Node **head,int value){

???????? struct Node *previous;//上一個

???????? struct Node *current;//當前

???????? struct Node *it;//new是關(guān)鍵字 就用it吧

???????? current= *head;

???????? previous=NULL;

???????? while(current!=NULL&&current->value<value){

????????????????? previous=current;

????????????????? current=current->next;

???????? }

???????? it=(struct Node *)malloc(sizeof(struct Node));

???????? if(it==NULL){

????????????????? printf("內(nèi)存分配失敗!\n");

????????????????? exit(1);

???????? }

???????? it->value=value;

???????? it->next=current;

???????? if(previous==NULL){//空鏈表,current為NULL即*head為NULL的情況下,不執(zhí)行循環(huán)導(dǎo)致 previous為NULL

????????????????? *head=it;

???????? }

???????? else//不是空鏈表

???????? {

????????????????? previous->next= it;

???????? }

}

void printNode(struct Node *head){

???????? struct Node *current;

???????? current=head;

???????? while(current!=NULL){

????????????????? printf("%d ",current->value);

????????????????? current=current->next;

???????? }

???????? printf("\n");

}

int main(){

???????? struct Node *head =NULL;

???????? int input;

???????? while(1){

????????????????? printf("請輸入一個整數(shù)(輸入-1表示結(jié)束):");

????????????????? scanf("%d",&input);

????????????????? if(input==-1){

????????????????????????? break;

????????????????? }

????????????????? insertNode(&head,input);

????????????????? printNode(head);

???????? }?

}

?

單鏈表刪除節(jié)點

void deleteNode(struct Node **head,int value){

???????? struct Node *previous;

???????? struct Node *current;

???????? current = *head;

???????? previous=NULL;

???????? while(current!=NULL&&current->value!=value){

????????????????? previous=current;

????????????????? current=current->next;

???????? }

???????? if(current==NULL){

????????????????? printf("找不到匹配的節(jié)點");

????????????????? return;

???????? }

???????? else{

????????????????? if(previous==NULL){

????????????????????????? *head=current->next;

????????????????? }

????????????????? else{

????????????????????????? previous->next=current->next;

????????????????? }

????????????????? free(current);

???????? }

}

main函數(shù)修改

???????? printf("開始測試刪除整數(shù)。。。\n");

???????? while(1){

????????????????? printf("請輸入一個整數(shù)(輸入-1表示結(jié)束):");

????????????????? scanf("%d",&input);

????????????????? if(input==-1){

????????????????????????? break;

????????????????? }

????????????????? deleteNode(&head,input);

????????????????? printNode(head);

???????? }?

P48內(nèi)存池

內(nèi)存碎片

?

時間上消耗 應(yīng)用層——內(nèi)核層——應(yīng)用層

?

解決方法 內(nèi)存池 讓程序額外維護的一個緩存區(qū)域

當申請內(nèi)存時檢查內(nèi)存池有沒有適合的垃圾內(nèi)存塊,重新使用

想申請內(nèi)存時

如果內(nèi)存池非空,則直接從里面獲取空間

如果內(nèi)存池為空,則重新申請內(nèi)存空間

想釋放內(nèi)存時

如果內(nèi)存池有空位(小于內(nèi)存池容量),那么將即將廢棄的內(nèi)存塊插入內(nèi)存池

如果內(nèi)存池沒有空位,那么就用free等釋放掉

?

P49基礎(chǔ)typedef

typedef基本功能 給數(shù)據(jù)類型(包括結(jié)構(gòu)體)起別名

typedef A B;//給A取別名B

typedef A B,C;//給A取別名B/C??梢匀《鄠€別名

?

define也可以 #define B A

區(qū)別

define是直接替換

typedef是對類型的封裝。真正的起別名??梢詫χ羔橆愋腿e名。

?

?

課外知識 fortran世界上第一門高級編程語言

?

給結(jié)構(gòu)體取別名

struct Date{

int year;

int month;

int day

};

typedef struct Date DATE;//如果需要,可以同時定義指針typedef struct Date DATE,*PDATE;

或者

typedef struct Date{

int year;

int month;

int day

} DATE;//如果需要,可以同時定義指針

?

P50進階typedef

使用typedef目的一般有兩個

給變量起容易記住且意義明確的別名;

簡化一些比較復(fù)雜的類型聲明。

?

一些比較復(fù)雜的聲明語句

int (*ptr)[3]數(shù)組指針 ptr是一個指針,指向三個整型元素的數(shù)組(數(shù)組起始地址)

哪個是數(shù)組指針,哪個是指針數(shù)組呢:

A)???? int *p1[10];

B)????? int (*p2)[10];

這里需要明白一個符號之間的優(yōu)先級問題。

“[]”的優(yōu)先級比“*”要高。p1 先與“[]”結(jié)合,構(gòu)成一個數(shù)組的定義,數(shù)組名為p1,int *修飾的是數(shù)組的內(nèi)容,即數(shù)組的每個元素。那現(xiàn)在我們清楚,這是一個數(shù)組,其包含10 個指向int 類型數(shù)據(jù)的指針,即指針數(shù)組。

至于p2 就更好理解了,在這里“()”的優(yōu)先級比“[]”高,“*”號和p2 構(gòu)成一個指針的定義,指針變量名為p2,int 修飾的是數(shù)組的內(nèi)容,即數(shù)組的每個元素。數(shù)組在這里并沒有名字,是個匿名數(shù)組。那現(xiàn)在我們清楚p2 是一個指針,它指向一個包含10 個int 類型數(shù)據(jù)的數(shù)組,即數(shù)組指針

#include<stdio.h>

typedef int (*PTR_TO_ARRAY)[3];

int main(){

???????? int array[3]={1,2,3};

???????? PTR_TO_ARRAY ptr_to_array=&array;

???????? int i;

???????? for(i=0;i<3;i++){

????????????????? printf("%d\n",(*ptr_to_array)[i]);

???????? }

???????? return 0;

}

?

int(*fun)(void);函數(shù)指針 指向一個參數(shù)為void,返回值為int的函數(shù)

#include<stdio.h>

typedef int (*PTR_TO_FUN)(void);

int fun(){

???????? return 520;

}

int main(){

???????? PTR_TO_FUN ptr_to_fun=&fun;//直接寫fun也可,函數(shù)名即是地址

???????? printf("%d\n",(*ptr_to_fun)());

???????? return 0;

}

?

int *(*array[3])(int);//*array[3]是指針數(shù)組,int *(*array[3])(int);是指針函數(shù),返回值int參數(shù)int

#include<stdio.h>

typedef int *(*PTR_TO_FUN)(int);

int *funA(int num){

????????????????????????????????????????????????????? printf("%d\t",num);

????????????????????????????????????????????????????? return &num;//返回無意義,只是測試

}

int *funB(int num){

????????????????????????????????????????????????????? printf("%d\t",num);

????????????????????????????????????????????????????? return &num;//返回無意義,只是測試

}

int *funC(int num){

????????????????????????????????????????????????????? printf("%d\t",num);

????????????????????????????????????????????????????? return &num;//返回無意義,只是測試

}

int main(){

????????????????????????????????????????????????????? PTR_TO_FUN array[3]={&funA,&funB,&funC};

????????????????????????????????????????????????????? int i;

????????????????????????????????????????????????????? for(i=0;i<3;i++){

???????????????????????????????????????????????????????????? printf("addr of num:%p\n",(*array[i])(i));

????????????????????????????????????????????????????? }

????????????????????????????????????????????????????? return 0;

}

?

void (*funA(int,void (*funB)(int)))(int);函數(shù)指針

P51共用體

union共用體名稱

{

???????? 共用體成員1;

???????? 共用體成員2;

???????? 共用體成員3;

???????? 。。。

};

?

共用體所有成員共享同一個內(nèi)存地址。地址長度足以容納最大成員的長度。也會受內(nèi)存對齊影響

#include<stdio.h>

#include<string.h>

typedef int *(*PTR_TO_FUN)(int);

union Test{

???????? int i;

???????? double pi;

???????? char str[10];

};

int main(){

???????? union Test test;

???????? test.i=520;

???????? test.pi=3.14;

???????? strcpy(test.str,"FishC.com");

???????? printf("addr of test.i:%p\n",&test.i);

???????? printf("addr of test.pi:%p\n",&test.pi);

???????? printf("addr of test.str:%p\n",&test.str); //輸出結(jié)果幾個地址%p相同

???????? printf("test.i:%d\n",test.i);

???????? printf("test.pi:%.2f\n",test.pi);

???????? printf("test.str:%s\n",test.str); //輸出結(jié)果只有str正確 ,因為前兩個被覆蓋了

???????? printf("size of int:%d\n",sizeof(int));

???????? printf("size of double:%d\n",sizeof(double));

???????? printf("size of test.str:%d\n",sizeof(test.str));

???????? printf("size of test:%d\n",sizeof(test));

???????? return 0;

}

?

定義共用體類型變量,和結(jié)構(gòu)體兩種方式基本相似。

另外,共用體的名字不是必須的

union {

???????? int a;

???????? char b;

} a,b,c;

?

初始化共用體

union data a={520};//初始化第一個成員

union data b=a;//使用一個共用體初始化另一個

union data c={.ch='c'};//C99新特性,指定初始化成員

?

拓展

共用體常用來節(jié)省內(nèi)存,特別是一些嵌入式編程,內(nèi)存是非常寶貴的!

共用體也常用于操作系統(tǒng)數(shù)據(jù)結(jié)構(gòu)或硬件數(shù)據(jù)結(jié)構(gòu)!

union 在操作系統(tǒng)底層的代碼中用的比較多,因為它在內(nèi)存共享布局上方便且直觀。所以網(wǎng)絡(luò)編程,協(xié)議分析,內(nèi)核代碼上有一些用到 union 都比較好懂,簡化了設(shè)計。

共用體(union)是一種數(shù)據(jù)格式,它能夠存儲不同類型的數(shù)據(jù),但是只能同時存儲其中的一種類型。

————————————————

版權(quán)聲明:本文為CSDN博主「狂奔的烏龜」的原創(chuàng)文章,遵循 CC 4.0 BY-SA 版權(quán)協(xié)議,轉(zhuǎn)載請附上原文出處鏈接及本聲明。

原文鏈接:https://blog.csdn.net/xy010902100449/article/details/48292527

P52枚舉類型

一一列舉

如果一個變量只有幾種可能的值,那么就可以將其定義為枚舉(enumeration)類型

?

聲明枚舉類型

enum枚舉類型名稱 {枚舉值名稱,枚舉值名稱。。。};

定義枚舉變量

enum枚舉類型名稱 枚舉變量1,枚舉變量2;

#include<stdio.h>

#include<time.h>

int main(){

???????? enum Week {sun,mon,tue,wed,thu,fri,sat};//默認第一個是0,此時sun=0,mon=1。。。 。也可以指定值,后面的會依次自動加1。enum Week {sun=1,mon,tue,wed,thu,fri,sat};

???????? enum Week today;

???????? struct tm *p;//tm結(jié)構(gòu)體 包含了當?shù)貢r間和日期,其中成員變量int tm_wday 表示星期幾范圍0-6

???????? time_t t;

???????? time(&t);//time函數(shù)返回表示當前時間的time_t

???????? p=localtime(&t);//localtime函數(shù)將time_t類型的值轉(zhuǎn)化為具體的本地時間和日期

???????? today = (enum Week)p->tm_wday;

???????? switch(today){

????????????????? case mon:

????????????????? case tue:

????????????????? case wed:

????????????????? case thu:

????????????????? case fri:

????????????????????????? printf("工作日\n");break;

????????????????? case sat:

????????????????? case sun:

????????????????????????? printf("休息日\n");break;

????????????????? default:printf("Error\n");

???????? }

???????? return 0;

}

拓展

https://blog.csdn.net/L202132061/article/details/79383855

https://blog.csdn.net/L202132061/article/details/79383855

P53位域

單片機 集成電路芯片,把CPU、RAM、ROM、I/O等集成到一塊硅片上構(gòu)成小而完善的微型計算機系統(tǒng)。

節(jié)約空間

位域,或稱位段、位字段。

例如,對一個字節(jié)劃分為幾個部分并命名,這幾部分就是位域

使用位域的做法是 在結(jié)構(gòu)體定義時,在結(jié)構(gòu)體或成員后面使用冒號和數(shù)字來表示該成員所占的位byte數(shù)。

#include<stdio.h>

int main(){

???????? struct Test{

????????????????? unsigned int a:1;

????????????????? unsigned int b:1;

????????????????? unsigned int c:2;

???????? };

???????? struct Test test;

???????? test.a=0;

???????? test.b=1;

???????? test.c=2;

???????? printf("a=%d,b=%d,c=%d\n",test.a,test.b,test.c);

???????? printf("size of test=%d",sizeof(test));

???????? return 0;

}

//a=0,b=1,c=2

//size of test=4

//如果改為 unsigned int c:1;則c=0因為2二進制為10,不夠兩位,只能存后面的0

?

無名位域

位域成員可以沒有名稱,只要給出數(shù)據(jù)類型和寬度即可。

struct Test{

???????? unsigned int x:100;

???????? unsigned int :100//位域成員可以沒有名稱,只要給出數(shù)據(jù)類型和寬度即可。

}

?

?

P54位操作

c語言并沒有規(guī)定一個字節(jié)有幾位(一般是8位),只是規(guī)定“可尋址的數(shù)據(jù)存儲單位,其尺寸必須可以容納運行環(huán)境的基本字符集的任何成員”。一般是由編譯器規(guī)定在limits.h中

?

邏輯位運算符



只作用于整型數(shù)據(jù)


~按位取反

1變0,0變1

?

&按位與

不是邏輯與(&&)。同時為1才是1

?

^按位異或

兩個不同為1,相同為0

?

|按位或

不是邏輯或(||)。有1則1,全0才0

?

和賦值號=結(jié)合

除了按位取反都可以和賦值號結(jié)合

#include<stdio.h>

int main(){

???????? int mask = 0xFF;//0x表示是16進制? FF是15 15即0000 0000 0000 0000 1111 1111?

???????? int v1= 0xABCDEF; //10 11 12 13 14 15 即? ?1010 1011 1100 1101 1110 1111

???????? int v2= 0xABCDEF;

???????? int v3= 0xABCDEF;

???????? v1 &=mask;//即v1=v1&mask;下面兩句也是

???????? v2 |=mask;

???????? v3 ^=mask;

???????? printf("v1=0x%X\nv2=0x%X\nv3=0x%X\n",v1,v2,v3);

???????? return 0;

}

//v1=0xEF????????????????? 0000 0000 0000 0000 1110 1111????????

//v2=0xABCDFF???????? 1010 1011 1100 1101 1110 1111

//v3=0xABCD10???????? 1010 1011 1100 1101 0001 0000

P55移位和位操作的應(yīng)用

移位運算符:

左移位運算符<<

右移位運算符>>

?

左移位運算符<<


左邊操作數(shù)是即將被移位的數(shù)據(jù),右邊是要移動的位數(shù)。移出的數(shù)據(jù)扔掉,移動后空位用0填充

?

右移位運算符<<


左邊操作數(shù)是即將被移位的數(shù)據(jù),右邊是要移動的位數(shù)。移出的數(shù)據(jù)扔掉,移動后空位用0填充

?

和賦值號結(jié)合

#include<stdio.h>

int main(){

???????? int value=1;

???????? printf("-----左移---------\n");

???????? while(value<1024){

????????????????? value<<=1;//value=value<<1;

????????????????? printf("value=%d\n",value);

???????? }

???????? printf("-----右移---------\n");

???????? value=1024;

???????? while(value>1){

????????????????? value>>=1;

????????????????? printf("value=%d\n",value);

???????? }

???????? return 0;

}

?

一些未定義行為

移位運算符右操作數(shù)如果是負數(shù),或右操作數(shù)大于左操作數(shù)支持的最大寬度,那么表達式結(jié)果均屬于“未定義行為”。不同編譯器結(jié)果不同。

有符號和無符號也對移位運算符有不同的影響。有符號數(shù)移動后是否覆蓋符號位決定權(quán)還是在編譯器。

?

位運算符的應(yīng)用

掩碼

配電箱

只開主臥 掩碼10001000 相"&"結(jié)果10001000

打開位 開主臥 掩碼10001000 相"|"結(jié)果11001000

關(guān)閉位 關(guān)閉客廳 掩碼01000000 "~"掩碼10111111 相"&"結(jié)果10001000

轉(zhuǎn)置位 掩碼01001000 "^"結(jié)果10001000

P56打開和關(guān)閉文件

萬物皆文件Everything is a file

KISS原則Keep it simple,stupid.

?

文本文件和二進制文件

文本文件?? 由一些字符的序列組成的文件

二進制文件 通常指除了文本文件以外的

嚴格來說,文本文件也屬于二進制文件。打開方式要區(qū)分開主要是因為換行符。C語言換行符\n,unix系統(tǒng)\n,windows用\r\n,mac用\r。如果在windows系統(tǒng)以文本文件打開,讀會將\r\n自動轉(zhuǎn)換為\n,寫會\n轉(zhuǎn)換為\r\n,但是以二進制模式打開則不會做轉(zhuǎn)換。如果在unix系統(tǒng)二者是一樣的。

?

打開和關(guān)閉文件

讀 從文件中獲取數(shù)據(jù)

寫 將數(shù)據(jù)寫入到文件里

在完成讀寫操作后,必須將其關(guān)閉

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

??????? FILE *fp;

??????? int ch;

?

??????? if ((fp = fopen("hello.txt", "r")) == NULL)

??????? {

??????????????? printf("打開文件失??!\n");

??????????????? exit(EXIT_FAILURE);

??????? }

??????? while ((ch = getc(fp)) != EOF)//EOF通常是-1

??????? {

??????????????? putchar(ch);

??????? }

??????? fclose(fp);//關(guān)閉,釋放緩沖區(qū)

??????? return 0;

}

fopen用于打開文件并返回函數(shù)指針

打開成功,返回指向FILE結(jié)構(gòu)的文件指針;打開失敗,返回NULL并設(shè)置errno為指定的錯誤

fopen(路徑,模式)

路徑參數(shù)可以是相對路徑(./a.txt),也可以是絕對路徑(/home/user1/a.txt)。如果只給出文件名(a.txt)則表示該文件在當前文件夾。

模式參數(shù)

模式

描述

"r"

1. 以只讀的模式打開一個文本文件,從文件頭開始讀取
? 2. 該文本文件必須存在

"w"

1. 以只寫的模式打開一個文本文件,從文件頭開始寫入
? 2. 如果文件不存在則創(chuàng)建一個新的文件
? 3. 如果文件已存在則將文件的長度截斷為 0(重新寫入的內(nèi)容將覆蓋原有的所有內(nèi)容)

"a"

1. 以追加的模式打開一個文本文件,從文件末尾追加內(nèi)容
? 2. 如果文件不存在則創(chuàng)建一個新的文件

"r+"

1. 以讀和寫的模式打開一個文本文件,從文件頭開始讀取和寫入
? 2. 該文件必須存在
? 3. 該模式不會將文件的長度截斷為 0(只覆蓋重新寫入的內(nèi)容,原有的內(nèi)容保留)

"w+"

1. 以讀和寫的模式打開一個文本文件,從文件頭開始讀取和寫入
? 2. 如果文件不存在則創(chuàng)建一個新的文件
? 3. 如果文件已存在則將文件的長度截斷為 0(重新寫入的內(nèi)容將覆蓋原有的所有內(nèi)容)

"a+"

1. 以讀和追加的模式打開一個文本文件
? 2. 如果文件不存在則創(chuàng)建一個新的文件
? 3. 讀取是從文件頭開始,而寫入則是在文件末尾追加

"b"

1. 與上面 6 中模式均可結(jié)合("rb", ? "wb", "ab", "r+b", "w+b", ? "a+b")
? 2. 其描述的含義一樣,只不過操作的對象是二進制文件

?

P57讀寫文件1

順序讀寫和隨機讀寫

?

讀單個字符

fgetc、getc

#include <stdio.h>

? int fgetc( FILE *stream );

fgetc()函數(shù)返回來自stream(流)中的下一個字符

stream參數(shù)是FILE對象的指針,指定一個待讀取的文件流

返回值:

將讀取到的unsigned char類型轉(zhuǎn)換為int并返回

如果到達文件尾或者發(fā)生錯誤時返回EOF.

?

fgetc 函數(shù)和 getc 函數(shù)兩個的功能和描述基本上是一模一樣的,它們的區(qū)別主要在于實現(xiàn)上:fgetc 是一個函數(shù);而 getc 則是一個宏的實現(xiàn)。

一般來說宏產(chǎn)生較大的代碼,但是避免了函數(shù)調(diào)用的堆棧操作,所以速度會比較快。

由于 getc 是由宏實現(xiàn)的,對其參數(shù)可能有不止一次的調(diào)用,所以不能使用帶有副作用(side effects)的參數(shù)。所謂帶有副作用的參數(shù)就是指 getc(fp++) 這類型的參數(shù),因為參數(shù)在宏的實現(xiàn)中可能會被調(diào)用多次,所以你的想法是 fp++,而副作用下產(chǎn)生的結(jié)果可能是 fp++++++。

?

?

寫單個字符fputc,putc

#include <stdio.h>

int fputc(int c, FILE *stream);

c?????? 指定待寫入的字符

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待寫入的文件流

返回值:

如果函數(shù)沒有錯誤,返回值是寫入的字符;

如果函數(shù)發(fā)生錯誤,返回值是EOF

fputc 是一個函數(shù);而 putc 則是一個宏的實現(xiàn)

?

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

??????? FILE *fp1,*fp2;

??????? int ch;

??????? if ((fp1 = fopen("hello.txt", "r")) == NULL)

??????? {

??????????????? printf("打開文件失敗!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? if ((fp2 = fopen("fish.txt", "w")) == NULL)

??????? {

??????????????? printf("打開文件失??!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? while ((ch = getc(fp1)) != EOF)//EOF通常是-1

??????? {

??????????????? fputc(ch,fp2);

??????? }

??????? fclose(fp1);//關(guān)閉,釋放緩沖區(qū)

??????? fclose(fp2);//關(guān)閉,釋放緩沖區(qū)

??????? return 0;

}

?

讀寫整個字符串fgets、fputs

fgets 函數(shù)用于從指定文件中讀取字符串。

fgets 函數(shù)最多可以讀取 size - 1 個字符,因為結(jié)尾處會自動添加一個字符串結(jié)束符 '\0'。當讀取到換行符('\n')或文件結(jié)束符(EOF)時,表示結(jié)束讀取('\n' 會被作為一個合法的字符讀取,EOF不會)。

函數(shù)原型:

#include <stdio.h>

char *fgets(char *s, int size, FILE *stream);

s?????? 字符型指針,指向用于存放讀取字符串的位置

size?? 指定讀取的字符數(shù)(包括最后自動添加的 '\0')

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待操作的數(shù)據(jù)流

返回值:

1. 如果函數(shù)調(diào)用成功,返回 s 參數(shù)指向的地址。

2. 如果在讀取字符的過程中遇到 EOF,則 eof 指示器被設(shè)置;如果還沒讀入任何字符就遇到這種 EOF,則 s 參數(shù)指向的位置保持原來的內(nèi)容(s不變),函數(shù)返回 NULL。

3. 如果在讀取的過程中發(fā)生錯誤,則 error 指示器被設(shè)置,函數(shù)返回 NULL,但 s 參數(shù)指向的內(nèi)容可能被改變。

?

fputs 函數(shù)用于將一個字符串寫入到指定的文件中,表示字符串結(jié)尾的 '\0' 不會被一并寫入。

#include <stdio.h>

int fputs(const char *s, FILE *stream);

s?????? 字符型指針,指向用于存放待寫入字符串的位置

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待操作的數(shù)據(jù)流

返回值:

如果函數(shù)調(diào)用成功,返回一個非 0 值;(此處錯誤。API文檔里是成功時返回非負值, 失敗時返回EOF)

如果函數(shù)調(diào)用失敗,返回EOF

?

feof 函數(shù)用于檢測文件的末尾指示器(end-of-file indicator)是否被設(shè)置。

#include <stdio.h>

int feof(FILE *stream);

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待檢測的文件流

返回值:

如果檢測到末尾指示器(end-of-file indicator)被設(shè)置,返回一個非 0 值;

如果檢測不到末尾指示器(end-of-file indicator)被設(shè)置,返回值為 0。

feof 函數(shù)僅檢測末尾指示器的值,它們并不會修改文件的位置指示器。

文件末尾指示器只能使用 clearerr 函數(shù)清除。

?

#include <stdio.h>

#include <stdlib.h>

#define MAX 1024

int main()

{

??????? FILE *fp;

??????? char buffer[MAX];

??????? if ((fp = fopen("hello.txt", "w")) == NULL)

??????? {

??????????????? printf("打開文件失??!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? fputs("hello 1111\n",fp);

??????? fputs("hello 2222\n",fp);

??????? fputs("hello 3333\n",fp);

??????? fclose(fp);//關(guān)閉,寫入文件,釋放緩沖區(qū) 。

????????????????? //需要fclose,不關(guān)的話,文件指示器指向文件末尾,影響后面操作 。而且還在緩沖區(qū),還沒寫入文件。

??????? if ((fp = fopen("hello.txt", "r")) == NULL)

??????? {

??????????????? printf("打開文件失敗!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? while(!feof(fp)) //feof檢測不到末尾,返回0 。所以這里是未到末尾 。

??????? {

??????? ?? fgets(buffer,MAX,fp);//每次最多讀取MAX-1個字符,因為結(jié)尾自動添加\0。 讀取到\n或EOF會結(jié)束這一行,\n也會被作為合法字符讀取,EOF不會 。

????????????????????????? printf("%s",buffer);

????????????????? }

???????? ??? return 0;

}

//hello 1111

//hello 2222

//hello 3333

//hello 3333

//出現(xiàn)問題 第四行打印第三行內(nèi)容,但文件hello中并沒有第四行 ,只有回車。

//原因 fgets如果還沒讀入任何字符就遇到 EOF,則 s 參數(shù)指向的位置保持原來的內(nèi)容(s不變),函數(shù)返回 NULL。

//解決? if(!fgets(buffer,MAX,fp)==NULL)printf("%s",buffer);

P58讀寫文件2

格式化讀寫文件

fscanf、fprintf

和scanf、printf相似,只不過是從文件讀取、輸出到文件

?

拓展 為什么scanf中用&取地址符,而printf不用。因為scanf本來就是一個函數(shù),用取地址后就能將接受的數(shù)據(jù)存在這個地址里,在scanf函數(shù)外也能用。指針在函數(shù)內(nèi)就是通過訪問所指向地址的值來進行改寫,并且能延續(xù)到函數(shù)外。

?

#include <stdio.h>

#include <stdlib.h>

#include <time.h>

int main()

{

??????? FILE *fp;

??????? struct tm *p;

??????? time_t t;

??????? time(&t);

??????? p=localtime(&t);

??????? if ((fp = fopen("date.txt", "w")) == NULL)

??????? {

??????????????? printf("打開文件失敗!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? fprintf(fp,"%d-%d-%d",1900+p->tm_year,1+p->tm_mon,p->tm_mday);

??????? fclose(fp);

??????? int year,month,day;

??????? if ((fp = fopen("date.txt", "r")) == NULL)

??????? {

??????????????? printf("打開文件失??!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

??????? fscanf(fp,"%d-%d-%d",&year,&month,&day);

??????? printf("%d-%d-%d",year,month,day);

??????? fclose(fp);

???????? ??? return 0;

}

?

以二進制方式讀寫文本文件

#include <stdio.h>

#include <stdlib.h>

int main()

{

??????? FILE *fp;

??????? if ((fp = fopen("text.txt", "wb")) == NULL)

??????? {

??????????????? printf("打開文件失??!\n");

??????????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??????? }

????????????????? fputc('5',fp);

????????????????? fputc('2',fp);

????????????????? fputc('0',fp);

????????????????? fputc('\n',fp);

??????? fclose(fp);

???????? ??? return 0;

}

?

二進制讀寫文件fread、fwrite

fread 函數(shù)用于從指定的文件中讀取指定尺寸的數(shù)據(jù)。

#include <stdio.h>

size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr??? 指向存放數(shù)據(jù)的內(nèi)存塊指針,該內(nèi)存塊的尺寸最小應(yīng)該是 size * nmemb 個字節(jié)

size?? 指定要讀取的每個元素的尺寸,最終尺寸等于 size * nmemb

nmemb???? 指定要讀取的元素個數(shù),最終尺寸等于 size * nmemb

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待讀取的文件流

返回值:

1. 返回值是實際讀取到的元素個數(shù)(nmemb);

2. 如果返回值比 nmemb 參數(shù)的值小,表示可能讀取到文件末尾或者有錯誤發(fā)生(可以使用 feof 函數(shù)或 ferror 函數(shù)進一步判斷)。

?

fwrite 函數(shù)用于將指定尺寸的數(shù)據(jù)寫入到指定的文件中。

函數(shù)原型:

#include <stdio.h>

size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr??? 指向存放數(shù)據(jù)的內(nèi)存塊指針,該內(nèi)存塊的尺寸最小應(yīng)該是 size * nmemb 個字節(jié)

size?? 指定要寫入的每個元素的尺寸,最終尺寸等于 size * nmemb

nmemb???? 指定要寫入的元素個數(shù),最終尺寸等于 size * nmemb

stream????? 該參數(shù)是一個 FILE 對象的指針,指定一個待寫入的文件流

返回值:

1. 返回值是實際寫入到文件中的元素個數(shù)(nmemb);

2. 如果返回值與 nmemb 參數(shù)的值不同,則有錯誤發(fā)生。

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

struct Date{

???????? int year;

???????? int month;

???????? int day;

};

struct Book{

???????? char name[40];

???????? char author[40];

???????? char publisher[40];

???????? struct Date date;

};

int main()

{

??? FILE *fp;

?? ?struct Book *book_for_write,*book_for_read;

??? book_for_write=(struct Book *)malloc(sizeof(struct Book));

??? book_for_read=(struct Book *)malloc(sizeof(struct Book));

??? if(book_for_write==NULL||book_for_read==NULL)

??? {

??????? printf("內(nèi)存分配失敗");

????????????????? exit(EXIT_FAILURE);

???????? }

???????? strcpy(book_for_write->name,"帶你學c帶你飛");

???????? strcpy(book_for_write->author,"小甲魚");

???????? strcpy(book_for_write->publisher,"清華大學出版社");

???????? book_for_write->date.year=2017;

???????? book_for_write->date.month=11;

???????? book_for_write->date.day=11;

???????? if ((fp = fopen("file.txt", "w")) == NULL)

??? {

??????????? printf("打開文件失?。n");

??????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??? }

???????? fwrite(book_for_write,sizeof(struct Book),1,fp);

???????? fclose(fp);

???????? if ((fp = fopen("file.txt", "r")) == NULL)

??? {

??????????? printf("打開文件失??!\n");

??????????? exit(EXIT_FAILURE);//exit() 需要stdlib.h

??? }

???????? fread(book_for_read,sizeof(struct Book),1,fp);

???????? printf("書名:%s\n",book_for_read->name);

???????? printf("作者:%s\n",book_for_read->author);

???????? printf("出版社:%s\n",book_for_read->publisher);

???????? printf("出版日期:%d-%d-%d\n",book_for_read->date.year,book_for_read->date.month,book_for_read->date.day);

???????? fclose(fp);?

???????? return 0;

}

P59隨機讀寫文件

ftell返回給定流 stream 的當前文件位置。

#include <stdio.h>

#include <stdlib.h>

int main()

{

??? FILE *fp;

??? if((fp=fopen("hello.txt","w"))==NULL){

??? ?printf("文件打開失敗!\n");

??? ?exit(EXIT_FAILURE);

???????? }

???????? printf("%ld\n",ftell(fp));

???????? fputc('F',fp);

???????? printf("%ld\n",ftell(fp));

???????? fputs("ishC\n",fp);

???????? printf("%ld\n",ftell(fp));

???????? fclose(fp);

???????? return 0;

}

//0開頭

//1

//7因為win系統(tǒng)換行 \r\n 所以1+6

?

rewind移動到文件頭

上面程序添加

rewind(fp);

fputs("Hello",fp);

會把原來的內(nèi)容覆蓋掉

?

fseek用于設(shè)置文件流的位置指示器

#include<stdio.h>

int fseek(FILE *stream,long int offset,int whence);

stream -- 這是指向 FILE 對象的指針,該 FILE 對象標識了流。

offset -- 這是相對 whence 的偏移量,以字節(jié)為單位。

whence -- 這是表示開始添加偏移 offset 的位置。它一般指定為下列常量之一:

SEEK_SET? 文件的開頭

SEEK_CUR 文件指針的當前位置

SEEK_END 文件的末尾

返回值

如果成功,則該函數(shù)返回零,否則返回非零值。

#include <stdio.h>

#include <stdlib.h>

#define N 4

struct Stu{

???????? char name[24];

???????? int num;

???????? float score;

}stu[N],sb;

int main()

{

??? FILE *fp;

??? int i;

??? if((fp=fopen("score.txt","w"))==NULL){

??? ?printf("打開文件失?。n");

??? ?exit(EXIT_FAILURE);

???????? }

???????? for(i=0;i<N;i++){

????????????????? printf("請開始錄入成績(格式:姓名 學號 成績)");

????????????????? scanf("%s %d %f",stu[i].name,&stu[i].num,&stu[i].score);

???????? }

???????? fwrite(stu,sizeof(struct Stu),N,fp);

???????? fclose(fp);

???????? if((fp=fopen("score.txt","rb"))==NULL){

????????????????? printf("打開文件失?。?#34;);

????????????????? exit(EXIT_FAILURE);

???????? }

???????? fseek(fp,sizeof(struct Stu),SEEK_SET);

???????? fread(&sb,sizeof(struct Stu),1,fp);

???????? printf("%s(%d)的成績是:%.2f\n",sb.name,sb.num,sb.score);

???????? fclose(fp);

???????? return 0;

}

?

可移植性問題

對于以二進制模式打開的文件,fseek在某些操作系統(tǒng)中可能不支持SEEK_END位置。

對于以文本模式打開的文件,feek函數(shù)的whence參數(shù)只能取SEEK_SET才是有意義的,并且傳遞給offset參數(shù)的值要么是0,要么是上一次對同一個文件調(diào)用ftell函數(shù)獲取的返回值。

?

P60標準流和錯誤處理

標準流

標準輸入stdin

標準輸出stdout

標準錯誤輸出stderr

?

#include <stdio.h>

#include <stdlib.h>

int main()

{

???????? FILE *fp;

???????? if((fp=fopen("壓根都不存在的文件.txt","r"))==NULL){

????????????????? fputs("打開文件失?。n",stderr);

????????????????? exit(EXIT_FAILURE);

???????? }

???????? fclose(fp);

???????? return 0;

}

?

錯誤處理

錯誤指示器ferror

使用clearerr函數(shù)可以人為的清除文件末尾指示器和錯誤指示器狀態(tài)

ferror函數(shù)只能檢測是否出錯,但無法獲取錯誤原因。不過,大多數(shù)系統(tǒng)函數(shù)在出現(xiàn)錯誤時會將錯誤原因就在errno中。

perror函數(shù)可以直觀的打印出錯誤原因。會自己加"冒號空格錯誤原因"。例如perror("錯誤原因是");輸出為"錯誤原因是: Bad file descriptor"

strerror函數(shù)直接返回錯誤碼對應(yīng)的錯誤消息。

#include <stdio.h>

#include <stdlib.h>

#include <errno.h>

#include<string.h>

int main()

{

???????? FILE *fp;

???????? int ch;

???????? if((fp=fopen("hello.txt","r"))==NULL){

????????????????? fputs("打開文件失??!\n",stderr);

????????????????? exit(EXIT_FAILURE);

???????? }

???????? while(1){

????????????????? ch=fgetc(fp);

????????????????? if(feof(fp)){

????????????????????????? break;

????????????????? }

????????????????? putchar(ch);

???????? }

???????? fputc('c',fp);//因為只讀的,所以會失敗,觸發(fā)ferro

???????? if(ferror(fp)){

????????????????? fputs("出錯了,這條消息輸出到錯誤輸出流\n",stderr);

????????????????? printf("使用errno得到的錯誤代碼:%d\n",errno); //errno.h

????????????????? perror("使用perror得到的錯誤原因是"); //stdio.h

????????????????? //fputs("出錯了,,原因是%s\n",stderr);

????????????????? printf("使用strerror得到的錯誤原因是:%s\n",strerror(errno));//strerror需要<string.h>

???????? }

???????? clearerr(fp);

???????? if(feof(fp)||ferror(fp)){

????????????????? printf("123456");//不打印。末尾指示器和錯誤指示器被clear清除掉,不會觸發(fā)

???????? }???????

???????? fclose(fp);

???????? return 0;

}

輸出結(jié)果:

Hello

出錯了,這條消息輸出到錯誤輸出流

使用errno得到的錯誤代碼:9

使用perror得到的錯誤原因是: Bad file descriptor

使用strerror得到的錯誤原因是:Bad file descriptor

P61 IO緩沖區(qū)


IO緩沖區(qū)

#include<stdio.h>

#include<stdlib.h>

int main(){

???????? FILE *fp;

???????? if((fp=fopen("output.txt","w"))==NULL){

????????????????? perror("打開文件失敗,原因");

????????????????? exit(EXIT_FAILURE);

???????? }

???????? fputs("I Love FishC.com\n",fp);//寫在了緩沖區(qū)里,并沒有寫入文件

???????? getchar();//不輸入字符,這時看output是空的 。輸入后 ,下一步執(zhí)行fclose才寫入文件

???????? fclose(fp);

???????? return 0;

}

?

標準IO提供三種類型的緩沖模式

按塊緩存 也稱為全緩存,在填滿緩沖區(qū)后才進行實際的設(shè)備讀寫操作;

按行緩存 是指在接收到換行符\n之前,數(shù)據(jù)都是先緩存在緩沖區(qū)的;

不緩存 允許直接讀寫設(shè)備上的數(shù)據(jù)

?

setvbuf()定義流 stream 應(yīng)如何緩沖。

#include<stdio.h>

int setvbuf(FILE *stream, char *buffer, int mode, size_t size)

參數(shù)

stream -- 這是指向 FILE 對象的指針,該 FILE 對象標識了一個打開的流。

buffer -- 這是分配給用戶的緩沖。如果設(shè)置為 NULL,該函數(shù)會自動分配一個指定大小的緩沖。

mode -- 這指定了文件緩沖的模式:

模式 描述

_IOFBF?????? 全緩沖:對于輸出,數(shù)據(jù)在緩沖填滿時被一次性寫入。對于輸入,緩沖會在請求輸入且緩沖為空時被填充。

_IOLBF?????? 行緩沖:對于輸出,數(shù)據(jù)在遇到換行符或者在緩沖填滿時被寫入,具體視情況而定。對于輸入,緩沖會在請求輸入且緩沖為空時被填充,直到遇到下一個換行符。

_IONBF????? 無緩沖:不使用緩沖。每個 I/O 操作都被即時寫入。buffer 和 size 參數(shù)被忽略。

size --這是緩沖的大小,以字節(jié)為單位。

返回值

如果成功,則該函數(shù)返回 0,否則返回非零值。

?

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

int main(){

???????? char buff[1024];

???????? memset(buff,'\0',sizeof(buff));//memset使用一個常量字節(jié)填充內(nèi)存空間。需要string.h

???????? setvbuf(stdout,buff,_IOFBF,1024);//修改為_IONBF 后會都輸出

???????? fprintf(stdout,"welcome to fishc.com");

???????? fflush(stdout);// 刷新緩沖區(qū)會立即輸出

???????? fprintf(stdout,"輸入任意字符后才會顯示該行字符\n");

???????? getchar();

???????? return 0;

}

? ? ??


小甲魚C語言《帶你學C帶你飛》學習筆記(2)的評論 (共 條)

分享到微博請遵守國家法律
禄丰县| 通江县| 密山市| 舞阳县| 双柏县| 陵川县| 临猗县| 芮城县| 泊头市| 逊克县| 洛阳市| 舞钢市| 海兴县| 隆回县| 临城县| 公主岭市| 惠水县| 福泉市| 周口市| 花莲县| 兰坪| 屏东市| 宁德市| 白城市| 海淀区| 姜堰市| 东乡族自治县| 梁山县| 改则县| 新乡市| 陆良县| 岳阳县| 广德县| 环江| 扎兰屯市| 台南市| 大埔县| 乌兰察布市| 景洪市| 永和县| 隆化县|