☆大綱:1)const;2)定義多維數(shù)組;3)結(jié)構(gòu)體的初始化;4)字符串的初始化;5)指針與數(shù)組;6)字符串常量與指針; 7)宏定義;8)條件編譯;9)枚舉類型;10)typedef語句(別名);11)static/extern;12)常用的指針類型;13)文件操作;14)預(yù)定義符號;15)sizeof;16)malloc與calloc區(qū)別;17)內(nèi)存函數(shù);
☆const top
const表明程序運行期間的值都不會變化,const變量是存放在只讀內(nèi)存中(一般說來,我們的程序指令也被放置在只讀內(nèi)存中)。
例如定義一個常量∏:const double pi=3.1415926;如果后面去修改pi的值會報錯,比如pi=pi+1;這樣是編譯通不過的。
深入討論一下
1)const int a=10;和int const a=10;是等價的。
2)const int * p;//(*p)是一個常量,但是p的值是可以變化的;
比如:int a=10,b=20;p=&a;……;p=&b;這樣是對的,但是*p=20;這樣是不行的。
int a=10;int * const p=&a;*p=20;//p是一個常量,而*p的值是可以變化的,這時必須在聲明p的時候就給p賦值,因為以后不能改變p的值。
用途:
const的用途比較明顯,聲明一些自然數(shù)據(jù),比如圓周率的值,因為這些值是一個定值。另外還可以放置程序誤修改變量的值,比如把某個值傳入某個函數(shù),為了放置值被修改,可以把參數(shù)用const聲明。
例如定義一個常量∏:const double pi=3.1415926;如果后面去修改pi的值會報錯,比如pi=pi+1;這樣是編譯通不過的。
深入討論一下
1)const int a=10;和int const a=10;是等價的。
2)const int * p;//(*p)是一個常量,但是p的值是可以變化的;
比如:int a=10,b=20;p=&a;……;p=&b;這樣是對的,但是*p=20;這樣是不行的。
int a=10;int * const p=&a;*p=20;//p是一個常量,而*p的值是可以變化的,這時必須在聲明p的時候就給p賦值,因為以后不能改變p的值。
用途:
const的用途比較明顯,聲明一些自然數(shù)據(jù),比如圓周率的值,因為這些值是一個定值。另外還可以放置程序誤修改變量的值,比如把某個值傳入某個函數(shù),為了放置值被修改,可以把參數(shù)用const聲明。
☆定義多維數(shù)組 top
int a[3][3]={{1,2,3},{4,5,6},{7,8,9}};
int a[3][3]={1,2,3,4,5,6,7,8,9};//和上面的等價
另外c允許我們只申明部分值,未申明的值都初始化為0,例如:
int a[3][3]={{1,2,3},{4},{5,6}};
int a[3][3]={1,2,3,4,0,0,5,6};
int b[3][3]={[0,0]=1,[0,1]=2,[0,2]=3,[1,0]=4,[2,0]=5,[2,1]=6};
//上面三句和int a[3][3]]={{1,2,3},{4,0,0},{5,6,0}};等價。
求數(shù)組長度:
#define ArraySize(ARR) (sizeof(ARR) / sizeof(ARR[0]))
int a[3][3]={1,2,3,4,5,6,7,8,9};//和上面的等價
另外c允許我們只申明部分值,未申明的值都初始化為0,例如:
int a[3][3]={{1,2,3},{4},{5,6}};
int a[3][3]={1,2,3,4,0,0,5,6};
int b[3][3]={[0,0]=1,[0,1]=2,[0,2]=3,[1,0]=4,[2,0]=5,[2,1]=6};
//上面三句和int a[3][3]]={{1,2,3},{4,0,0},{5,6,0}};等價。
求數(shù)組長度:
#define ArraySize(ARR) (sizeof(ARR) / sizeof(ARR[0]))
☆結(jié)構(gòu)體的初始化 top
1)結(jié)構(gòu)體變量的初始化和數(shù)組的初始化很類似,可以用逗號將結(jié)構(gòu)體成員的值分割,并用一對大括號將其包圍起來。例如:
struct mydate {int year;int month;int day;};
struct mydate date1 ={2007,7,23};
2)也可以只申明部分值,未申明的值可以為0,也可能為其他值(由編譯器決定),例如:
struct mydate date2={2007,7};//相當(dāng)于{2007,7,0};
3)初始化結(jié)構(gòu)體數(shù)組,例如:
struct mydate dates[2]={{2007,7,23},{2007,7,24}};
struct mydate {int year;int month;int day;};
struct mydate date1 ={2007,7,23};
2)也可以只申明部分值,未申明的值可以為0,也可能為其他值(由編譯器決定),例如:
struct mydate date2={2007,7};//相當(dāng)于{2007,7,0};
3)初始化結(jié)構(gòu)體數(shù)組,例如:
struct mydate dates[2]={{2007,7,23},{2007,7,24}};
☆字符串的初始化 top
char a[]="hello";
char b[]={"hello"};
char c[]={'h','e','l','l','o','\0'};
char d[6]="hello";
char e[]="he""llo";//編譯器會自動將相鄰的字符串連接起來
char f[]="he\
llo";//如果要跨行的時候,可以使用反斜線,但是注意,續(xù)行一定要從開始寫,不能有空格等。
以上六種方法等價,但是如果寫成char d[5]="hello";這樣程序不報錯,但是明顯可能導(dǎo)致災(zāi)難。
char b[]={"hello"};
char c[]={'h','e','l','l','o','\0'};
char d[6]="hello";
char e[]="he""llo";//編譯器會自動將相鄰的字符串連接起來
char f[]="he\
llo";//如果要跨行的時候,可以使用反斜線,但是注意,續(xù)行一定要從開始寫,不能有空格等。
以上六種方法等價,但是如果寫成char d[5]="hello";這樣程序不報錯,但是明顯可能導(dǎo)致災(zāi)難。
☆指針與數(shù)組 top
數(shù)組名對應(yīng)著(而不是指向)一塊內(nèi)存,其地址與容量在生命期內(nèi)保持不變,只有數(shù)組的內(nèi)容可以改變。指針可以隨時指向任意類型的內(nèi)存塊,它的特征是“可變”,所以我們常用指針來操作動態(tài)內(nèi)存。當(dāng)數(shù)組作為函數(shù)的參數(shù)進(jìn)行傳遞時,該數(shù)組自動退化為同類型的指針。
int *p;int a[10];
p=a;等價于p=&a[0];
*(p+1);等價于a[1];
使用指針訪問數(shù)組比使用下標(biāo)的速度快。
int *p;int a[10];
p=a;等價于p=&a[0];
*(p+1);等價于a[1];
使用指針訪問數(shù)組比使用下標(biāo)的速度快。
☆字符串常量與指針 top
char a[10];
a="hello";//這樣是錯誤的,因為在申明數(shù)組a時,a已經(jīng)分配了空間,a指向一個固定地址,而"hello"是一個常量,地址也是固定了的,常量不能賦值給常量。但是下面那樣是可以的:
char *a;a="hello";
a="hello";//這樣是錯誤的,因為在申明數(shù)組a時,a已經(jīng)分配了空間,a指向一個固定地址,而"hello"是一個常量,地址也是固定了的,常量不能賦值給常量。但是下面那樣是可以的:
char *a;a="hello";
☆宏定義 top
一般定義,后面不要加分號
定義:#define pt1(s) printf(s)
調(diào)用:pt1("woxingwosu");
結(jié)果:woxingwosu
接受可變參數(shù)個數(shù)的宏(C99規(guī)范中新增的,目前似乎只有g(shù)cc支持)
定義:#define pt1(
) printf(s)
調(diào)用:char * p="2007-07-24";pt1("woxingwosu %s",p);
結(jié)果:woxingwosu 2007-07-24
#操作符(將參數(shù)轉(zhuǎn)變成字符串)
定義:#define str(s) #s
調(diào)用:printf(str(woxingwosu));
結(jié)果:woxingwosu
##操作符(連接參數(shù))
定義:#define myvar(s) woxingwosu##s
調(diào)用:int woxingwosu10=100;printf("value=%d",myvar(10));
結(jié)果:100
定義:#define pt1(s) printf(s)
調(diào)用:pt1("woxingwosu");
結(jié)果:woxingwosu
接受可變參數(shù)個數(shù)的宏(C99規(guī)范中新增的,目前似乎只有g(shù)cc支持)
定義:#define pt1(

調(diào)用:char * p="2007-07-24";pt1("woxingwosu %s",p);
結(jié)果:woxingwosu 2007-07-24
#操作符(將參數(shù)轉(zhuǎn)變成字符串)
定義:#define str(s) #s
調(diào)用:printf(str(woxingwosu));
結(jié)果:woxingwosu
##操作符(連接參數(shù))
定義:#define myvar(s) woxingwosu##s
調(diào)用:int woxingwosu10=100;printf("value=%d",myvar(10));
結(jié)果:100
☆條件編譯 top
(#ifdef,#endif,#else,#ifndef,#if,#elif,#undef)
例如:
//考慮平臺的移植性
#ifdef UNIX
#define PATH "/home/root"
#else
#define PATH "c:\windows"
#endif
//避免多次宏定義(一般會報錯)
#ifndef PATH
#define PATH "c:\windows"
#endif
//加入判斷條件
#define OS 1
……
#if OS==1
#define PATH "c:\windows"
#elif OS==0
#define PATH "/homw/root"
#endif
//取消定義的宏定義
#undef PATH
#ifdef UNIX
#define PATH "/home/root"
#else
#define PATH "c:\windows"
#endif
//避免多次宏定義(一般會報錯)
#ifndef PATH
#define PATH "c:\windows"
#endif
//加入判斷條件
#define OS 1
……
#if OS==1
#define PATH "c:\windows"
#elif OS==0
#define PATH "/homw/root"
#endif
//取消定義的宏定義
#undef PATH
☆枚舉類型 top
enum Week {sun,mon,tue,wed,thu,fri,sat};//定義一個枚舉類型,其實sun~sat的值分別是0~6;
enum Week week1=mon;//賦值
//如果指定其中一個值,后面的值會遞增
enum Week {sun,mon=5,tue,wed,thu,fri,sat};
//sun=0,mon~sat=5~10
enum Week week1=mon;//賦值
//如果指定其中一個值,后面的值會遞增
enum Week {sun,mon=5,tue,wed,thu,fri,sat};
//sun=0,mon~sat=5~10
☆typedef語句(別名) top
typedef int count//等價于#define count int
count i=5;
_______________________________________________
typedef char Name[20];
Name name="hello";//等價于char name[20]="hello";
_______________________________________________
typedef char * P;
P p="hello";
_______________________________________________
typedef struct{int year;int month} Date;
Date date={2007,7};
count i=5;
_______________________________________________
typedef char Name[20];
Name name="hello";//等價于char name[20]="hello";
_______________________________________________
typedef char * P;
P p="hello";
_______________________________________________
typedef struct{int year;int month} Date;
Date date={2007,7};
☆static/extern top
(1)靜態(tài)局部變量在函數(shù)內(nèi)定義,但不象自動變量那樣,當(dāng)調(diào)用時就存在,退出函數(shù)時就消失。靜態(tài)局部變量始終存在著,也就是說它的生存期為整個源程序。
(2)靜態(tài)局部變量的生存期雖然為整個源程序,但是其作用域仍與自動變量相同,即只能在定義該變量的函數(shù)內(nèi)使用該變量。退出該函數(shù)后, 盡管該變量還繼續(xù)存在,但不能使用它。
(3)允許對構(gòu)造類靜態(tài)局部量賦初值。若未賦以初值,則由系統(tǒng)自動賦以0值。(而對自動變量不賦初值,則其值是不定的。)
(4) 根據(jù)靜態(tài)局部變量的特點, 可以看出它是一種生存期為整個源程序的量。雖然離開定義它的函數(shù)后不能使用,但如再次調(diào)用定義它的函數(shù)時,它又可繼續(xù)使用, 而且保存了前次被調(diào)用后留下的值。 因此,當(dāng)多次調(diào)用一個函數(shù)且要求在調(diào)用之間保留某些變量的值時,可考慮采用靜態(tài)局部變量。雖然用全局變量也可以達(dá)到上述目的,但全局變量有時會造成意外的副作用(不可重入問題),因此仍以采用局部靜態(tài)變量為宜。
(5)若全局變量僅在單個C文件中訪問,則可以將這個變量修改為靜態(tài)全局變量,以降低模塊間的耦合度。
(6)extern,申明某個變量在另外的一個文件中定義。extern int value;value這個變量在其他文件中定義,value是一個外部全局變量。
(2)靜態(tài)局部變量的生存期雖然為整個源程序,但是其作用域仍與自動變量相同,即只能在定義該變量的函數(shù)內(nèi)使用該變量。退出該函數(shù)后, 盡管該變量還繼續(xù)存在,但不能使用它。
(3)允許對構(gòu)造類靜態(tài)局部量賦初值。若未賦以初值,則由系統(tǒng)自動賦以0值。(而對自動變量不賦初值,則其值是不定的。)
(4) 根據(jù)靜態(tài)局部變量的特點, 可以看出它是一種生存期為整個源程序的量。雖然離開定義它的函數(shù)后不能使用,但如再次調(diào)用定義它的函數(shù)時,它又可繼續(xù)使用, 而且保存了前次被調(diào)用后留下的值。 因此,當(dāng)多次調(diào)用一個函數(shù)且要求在調(diào)用之間保留某些變量的值時,可考慮采用靜態(tài)局部變量。雖然用全局變量也可以達(dá)到上述目的,但全局變量有時會造成意外的副作用(不可重入問題),因此仍以采用局部靜態(tài)變量為宜。
(5)若全局變量僅在單個C文件中訪問,則可以將這個變量修改為靜態(tài)全局變量,以降低模塊間的耦合度。
(6)extern,申明某個變量在另外的一個文件中定義。extern int value;value這個變量在其他文件中定義,value是一個外部全局變量。
☆常用的指針類型 top
1)int *p;//指向整型數(shù)據(jù)的指針
普遍用法:(把指針與數(shù)組聯(lián)系起來)
int a[3];
p=a;
for(int i=0;i<3;i++)
*(p+i)=i;
2)char * p;//指向字符的指針
普遍用法:(方便操作字符數(shù)組或者字符串)
char * p="hello";
3)int **p;//指向指針的指針(非常靈活)
普遍用法:(尤其在函數(shù)參數(shù)時,相當(dāng)傳引用/傳地址)
int a[3];
p=&a;
int i=0;
for(;i<3;i++)
*(*p+i)=i;
也可以實現(xiàn)動態(tài)數(shù)組,例如
#define N 2
……
char **p;
int i=0;
p=(char**)malloc(sizeof(char *)*N);
*(p+0)=(char *)malloc(sizeof(char)*21);
*(p+1)=(char *)malloc(sizeof(char)*11);
sprintf(*(p+0),"The first One");
sprintf(*(p+1),"Second One");
4)int *p[3];//指針數(shù)組
普遍用法:
int a[5],b[5],c[5];
p[0]=a;p[1]=b;p[2]=c;
int i=0,j=0;
for(i=0;i<3;i++)
for(j=0;j<5;j++)
*(p[i]+j)=i+j;
5)int (*p)[3];//指向數(shù)組的指針
int a[2][3]={{0,1,2},{3,4,5}};
int (*p)[3];
int i=0,j=0;
p=&a[0];
for(i=0;i<2;i++)
for(j=0;j<3;j++)
printf("a[%d,%d]=%d",i,j,(*(p+i))[j]);
6)int (*p)(void);//指向函數(shù)的指針
int show(int arg){
printf("hello\n");
return arg+1;
}
int main(void){
int (*p)(int i);
int result;
p=show;
result=p(10);
printf("result=%d",result);
}
普遍用法:(把指針與數(shù)組聯(lián)系起來)
int a[3];
p=a;
for(int i=0;i<3;i++)
*(p+i)=i;
2)char * p;//指向字符的指針
普遍用法:(方便操作字符數(shù)組或者字符串)
char * p="hello";
3)int **p;//指向指針的指針(非常靈活)
普遍用法:(尤其在函數(shù)參數(shù)時,相當(dāng)傳引用/傳地址)
int a[3];
p=&a;
int i=0;
for(;i<3;i++)
*(*p+i)=i;
也可以實現(xiàn)動態(tài)數(shù)組,例如
#define N 2
……
char **p;
int i=0;
p=(char**)malloc(sizeof(char *)*N);
*(p+0)=(char *)malloc(sizeof(char)*21);
*(p+1)=(char *)malloc(sizeof(char)*11);
sprintf(*(p+0),"The first One");
sprintf(*(p+1),"Second One");
4)int *p[3];//指針數(shù)組
普遍用法:
int a[5],b[5],c[5];
p[0]=a;p[1]=b;p[2]=c;
int i=0,j=0;
for(i=0;i<3;i++)
for(j=0;j<5;j++)
*(p[i]+j)=i+j;
5)int (*p)[3];//指向數(shù)組的指針
int a[2][3]={{0,1,2},{3,4,5}};
int (*p)[3];
int i=0,j=0;
p=&a[0];
for(i=0;i<2;i++)
for(j=0;j<3;j++)
printf("a[%d,%d]=%d",i,j,(*(p+i))[j]);
6)int (*p)(void);//指向函數(shù)的指針
int show(int arg){
printf("hello\n");
return arg+1;
}
int main(void){
int (*p)(int i);
int result;
p=show;
result=p(10);
printf("result=%d",result);
}
☆操作文件 top
1)打開文件
語法: FILE *fopen(char *filename, *type);
返回值: 指針地址,如果找不到文件或者失敗的話返回空
注意:在打開文件后,需要判斷文件打開是否成功,否則NULL指針用于文件操作將產(chǎn)生不可預(yù)測的結(jié)果。
type的含義如下
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
字符 含義
────────────────────────────
"r" 打開文字文件只讀
"w" 創(chuàng)建文字文件只寫
"a" 增補, 如果文件不存在則創(chuàng)建一個
"r+" 打開一個文字文件讀/寫
"w+" 創(chuàng)建一個文字文件讀/寫
"a+" 打開或創(chuàng)建一個文件增補
"b" 二進(jìn)制文件(可以和上面每一項合用)
"t" 文這文件(默認(rèn)項)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2)關(guān)閉文件
語法: int fclose(FILE *stream);
返回值: 該函數(shù)返回一個整型數(shù)。當(dāng)文件關(guān)閉成功時, 返回0, 否則返回一個非零值。
注意:一般在程序運行結(jié)束的時候,操作系統(tǒng)會自動關(guān)閉打開的文件。然而這樣卻可能導(dǎo)致嚴(yán)重的后果——丟失數(shù)據(jù)。因為向文件寫數(shù)據(jù)時,是先將數(shù)據(jù)輸出到緩沖區(qū)(在內(nèi)存中),待數(shù)據(jù)充滿緩沖區(qū)后才將整個緩沖區(qū)的數(shù)據(jù)正式輸出到磁盤上的文件中,這樣做的目的是減少頻繁的磁盤讀寫,提高效率。所以,如果緩沖區(qū)內(nèi)的數(shù)據(jù)未滿就結(jié)束程序的運行,就會造成最后留在緩沖區(qū)內(nèi)的數(shù)據(jù)丟失。而使用fclose函數(shù)就可以把緩沖區(qū)內(nèi)最后剩余的數(shù)據(jù)輸出到磁盤文件中,并釋放文件指針和有關(guān)的緩沖區(qū)。
3)常用的幾個文件操作函數(shù):
int feof(FILE filePtr) 如果指針已經(jīng)指向結(jié)尾,返回1,否則返回0
int ferror(FILE filePtr) 如果filePtr指向的文件在I/O操作中發(fā)生了錯誤,返回1,否則返回0
int fflush(FILE filePtr) 將內(nèi)部緩沖區(qū)寫到文件中,如果成功返回0,否則返回EOF
int fgetc(FILE filePtr) 讀取文件中的下一個字符,如果到了結(jié)尾,返回EOF
int fputc(int c,FILE filePtr) 寫入一個字符c,如果成功返回c,否則返回EOF
char * fgets(char * buffer,int n ,FILE filePtr) 從filePtr指向的文件中讀取,直到讀入n-1個字符或者遇到換行符,讀到的數(shù)據(jù)存儲在字符數(shù)組buffer中,如果讀取發(fā)生錯誤或者到了文件結(jié)尾,返回NULL,否則返回buffer
int fprintf(FILE filePtr,char * format[,argument,]) 和printf差不多,區(qū)別在于printf輸出到stdout,而fprintf輸出到filePtr指向的文件
int fscanf(File filePtr,char * format[,argument,]) 和scanf差不多,和fprintf相對應(yīng)
size_t fread(void *ptr, size_t size, size_t n, FILE *stream) 參數(shù)ptr是保存讀取的數(shù)據(jù),void*的指針可用任何類型的指針來替換,如char*、int *等等來替換;size是每塊的字節(jié)數(shù);n是讀取的塊數(shù),如果成功,返回實際讀取的塊數(shù)(不是字節(jié)數(shù)),一般用于二進(jìn)制模式打開的文件中。
FILE * freopen(char * filename,char * accessMode,FILE * filePtr) 關(guān)閉filePtr對應(yīng)的文件,以accessMode中的模式打開filename對應(yīng)的文件,并將該文件與filePtr指向的文件關(guān)聯(lián)起來。一般用于將標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤重定向其他文件。如果操作成功,返回filePtr,否則返回NULL。例如將stderr重定向到文件error.logs,例如freopen("error.logs","w",stderr);
int fseek(FILE * filePtr,int offset,int mode) 重新設(shè)置文件指針的位置,offset是移動的字符數(shù),負(fù)值表示往首部移動,mode有三種取值:SEEK_SET(從文件開始計算),SEEK_CUR(從當(dāng)前位置計算),SEEK_END(從文件結(jié)束計算)。如果操作成功返回0,否則返回非0值。
void rewind(FILE * filePtr) 將文件指針重新定向到文件的開始
int remove(char * filename) 刪除文件名為filename的文件,如果成功返回0,否則返回非0值
int rename(char * oldname,char * newname) 文件重命名,如果成功返回0,否則返回非0值
FILE * tmpfile(void) 創(chuàng)建并以寫更新模式打開一個二進(jìn)制形式的臨時文件。如果成功返回指向的文件指針,否則返回NULL。當(dāng)程序結(jié)束時,該臨時文件自動刪除。
更詳細(xì)可以參考:http://hi.baidu.com/bhqiang/blog/item/b4caba51a3512f19377abe7f.html
4)另外文本文件和二進(jìn)制文件的區(qū)別
按二進(jìn)制寫文件指的是直接按照數(shù)據(jù)在內(nèi)存中的表現(xiàn)形式寫入文件。例如,如果int型數(shù)據(jù)在內(nèi)存中用4個字節(jié)表示,則寫這個int數(shù)據(jù)的時候直接把對應(yīng)的內(nèi)存中4個字節(jié)的內(nèi)容寫入文件。在此過程中數(shù)據(jù)不需要做任何轉(zhuǎn)換,所以效率較高。數(shù)據(jù)有字符型和非字符型(數(shù))兩種。按文本方式寫文件指的是將數(shù)據(jù)轉(zhuǎn)換為對應(yīng)的字符型數(shù)據(jù)之后再寫入文件。對于字符型數(shù)據(jù),由于其本身就是ASCII碼字符,一般不必轉(zhuǎn)換,直接寫入文件。但是,由于不同的系統(tǒng)對于換行符('\n')有不同的處理(轉(zhuǎn)換)方式,在有的系統(tǒng)(如Windows)下也會對'\n'作適當(dāng)?shù)霓D(zhuǎn)換。
對于非字符型數(shù)據(jù),都要進(jìn)行轉(zhuǎn)換處理。例如:int k=11; 如果是文本文件則存儲為'1','1'。如果二進(jìn)制文件,存儲為0000000 0000000 0000000 00001011。
5)一個簡單操作文件的例子:
#include <stdio.h>
void showContentByChar(FILE *file);
void showContentByLine(FILE *file);
int main(){
FILE * inputFile;
int c;
inputFile=fopen("woxingwosu.data","rb");
if(inputFile==NULL){
printf("找不到文件");
return -1;
}
printf("-----按字符讀:-------\n");
showContentByChar(inputFile);
rewind(inputFile);//指針回到文件首
printf("\n-----按行讀:-------\n");
showContentByLine(inputFile);
fclose(inputFile);
return 0;
}
//按字符讀文件
void showContentByChar(FILE *file){
int c;//最好定義成int,不要定義為char
if(file==NULL){
printf("文件指針為空!");
return ;
}
while((c=getc(file))!=EOF)
putchar(c);
}
//按行讀文件
void showContentByLine(FILE *file){
int N=512;
char buff[N+1];
if(file==NULL){
printf("文件指針為空!");
return ;
}
while((fgets(buff,N,file))!=NULL)
printf("%s",buff);
}
語法: FILE *fopen(char *filename, *type);
返回值: 指針地址,如果找不到文件或者失敗的話返回空
注意:在打開文件后,需要判斷文件打開是否成功,否則NULL指針用于文件操作將產(chǎn)生不可預(yù)測的結(jié)果。
type的含義如下
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
字符 含義
────────────────────────────
"r" 打開文字文件只讀
"w" 創(chuàng)建文字文件只寫
"a" 增補, 如果文件不存在則創(chuàng)建一個
"r+" 打開一個文字文件讀/寫
"w+" 創(chuàng)建一個文字文件讀/寫
"a+" 打開或創(chuàng)建一個文件增補
"b" 二進(jìn)制文件(可以和上面每一項合用)
"t" 文這文件(默認(rèn)項)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━
2)關(guān)閉文件
語法: int fclose(FILE *stream);
返回值: 該函數(shù)返回一個整型數(shù)。當(dāng)文件關(guān)閉成功時, 返回0, 否則返回一個非零值。
注意:一般在程序運行結(jié)束的時候,操作系統(tǒng)會自動關(guān)閉打開的文件。然而這樣卻可能導(dǎo)致嚴(yán)重的后果——丟失數(shù)據(jù)。因為向文件寫數(shù)據(jù)時,是先將數(shù)據(jù)輸出到緩沖區(qū)(在內(nèi)存中),待數(shù)據(jù)充滿緩沖區(qū)后才將整個緩沖區(qū)的數(shù)據(jù)正式輸出到磁盤上的文件中,這樣做的目的是減少頻繁的磁盤讀寫,提高效率。所以,如果緩沖區(qū)內(nèi)的數(shù)據(jù)未滿就結(jié)束程序的運行,就會造成最后留在緩沖區(qū)內(nèi)的數(shù)據(jù)丟失。而使用fclose函數(shù)就可以把緩沖區(qū)內(nèi)最后剩余的數(shù)據(jù)輸出到磁盤文件中,并釋放文件指針和有關(guān)的緩沖區(qū)。
3)常用的幾個文件操作函數(shù):
int feof(FILE filePtr) 如果指針已經(jīng)指向結(jié)尾,返回1,否則返回0
int ferror(FILE filePtr) 如果filePtr指向的文件在I/O操作中發(fā)生了錯誤,返回1,否則返回0
int fflush(FILE filePtr) 將內(nèi)部緩沖區(qū)寫到文件中,如果成功返回0,否則返回EOF
int fgetc(FILE filePtr) 讀取文件中的下一個字符,如果到了結(jié)尾,返回EOF
int fputc(int c,FILE filePtr) 寫入一個字符c,如果成功返回c,否則返回EOF
char * fgets(char * buffer,int n ,FILE filePtr) 從filePtr指向的文件中讀取,直到讀入n-1個字符或者遇到換行符,讀到的數(shù)據(jù)存儲在字符數(shù)組buffer中,如果讀取發(fā)生錯誤或者到了文件結(jié)尾,返回NULL,否則返回buffer
int fprintf(FILE filePtr,char * format[,argument,]) 和printf差不多,區(qū)別在于printf輸出到stdout,而fprintf輸出到filePtr指向的文件
int fscanf(File filePtr,char * format[,argument,]) 和scanf差不多,和fprintf相對應(yīng)
size_t fread(void *ptr, size_t size, size_t n, FILE *stream) 參數(shù)ptr是保存讀取的數(shù)據(jù),void*的指針可用任何類型的指針來替換,如char*、int *等等來替換;size是每塊的字節(jié)數(shù);n是讀取的塊數(shù),如果成功,返回實際讀取的塊數(shù)(不是字節(jié)數(shù)),一般用于二進(jìn)制模式打開的文件中。
FILE * freopen(char * filename,char * accessMode,FILE * filePtr) 關(guān)閉filePtr對應(yīng)的文件,以accessMode中的模式打開filename對應(yīng)的文件,并將該文件與filePtr指向的文件關(guān)聯(lián)起來。一般用于將標(biāo)準(zhǔn)輸入,標(biāo)準(zhǔn)輸出和標(biāo)準(zhǔn)錯誤重定向其他文件。如果操作成功,返回filePtr,否則返回NULL。例如將stderr重定向到文件error.logs,例如freopen("error.logs","w",stderr);
int fseek(FILE * filePtr,int offset,int mode) 重新設(shè)置文件指針的位置,offset是移動的字符數(shù),負(fù)值表示往首部移動,mode有三種取值:SEEK_SET(從文件開始計算),SEEK_CUR(從當(dāng)前位置計算),SEEK_END(從文件結(jié)束計算)。如果操作成功返回0,否則返回非0值。
void rewind(FILE * filePtr) 將文件指針重新定向到文件的開始
int remove(char * filename) 刪除文件名為filename的文件,如果成功返回0,否則返回非0值
int rename(char * oldname,char * newname) 文件重命名,如果成功返回0,否則返回非0值
FILE * tmpfile(void) 創(chuàng)建并以寫更新模式打開一個二進(jìn)制形式的臨時文件。如果成功返回指向的文件指針,否則返回NULL。當(dāng)程序結(jié)束時,該臨時文件自動刪除。
更詳細(xì)可以參考:http://hi.baidu.com/bhqiang/blog/item/b4caba51a3512f19377abe7f.html
4)另外文本文件和二進(jìn)制文件的區(qū)別
按二進(jìn)制寫文件指的是直接按照數(shù)據(jù)在內(nèi)存中的表現(xiàn)形式寫入文件。例如,如果int型數(shù)據(jù)在內(nèi)存中用4個字節(jié)表示,則寫這個int數(shù)據(jù)的時候直接把對應(yīng)的內(nèi)存中4個字節(jié)的內(nèi)容寫入文件。在此過程中數(shù)據(jù)不需要做任何轉(zhuǎn)換,所以效率較高。數(shù)據(jù)有字符型和非字符型(數(shù))兩種。按文本方式寫文件指的是將數(shù)據(jù)轉(zhuǎn)換為對應(yīng)的字符型數(shù)據(jù)之后再寫入文件。對于字符型數(shù)據(jù),由于其本身就是ASCII碼字符,一般不必轉(zhuǎn)換,直接寫入文件。但是,由于不同的系統(tǒng)對于換行符('\n')有不同的處理(轉(zhuǎn)換)方式,在有的系統(tǒng)(如Windows)下也會對'\n'作適當(dāng)?shù)霓D(zhuǎn)換。
對于非字符型數(shù)據(jù),都要進(jìn)行轉(zhuǎn)換處理。例如:int k=11; 如果是文本文件則存儲為'1','1'。如果二進(jìn)制文件,存儲為0000000 0000000 0000000 00001011。
5)一個簡單操作文件的例子:
#include <stdio.h>
void showContentByChar(FILE *file);
void showContentByLine(FILE *file);
int main(){
FILE * inputFile;
int c;
inputFile=fopen("woxingwosu.data","rb");
if(inputFile==NULL){
printf("找不到文件");
return -1;
}
printf("-----按字符讀:-------\n");
showContentByChar(inputFile);
rewind(inputFile);//指針回到文件首
printf("\n-----按行讀:-------\n");
showContentByLine(inputFile);
fclose(inputFile);
return 0;
}
//按字符讀文件
void showContentByChar(FILE *file){
int c;//最好定義成int,不要定義為char
if(file==NULL){
printf("文件指針為空!");
return ;
}
while((c=getc(file))!=EOF)
putchar(c);
}
//按行讀文件
void showContentByLine(FILE *file){
int N=512;
char buff[N+1];
if(file==NULL){
printf("文件指針為空!");
return ;
}
while((fgets(buff,N,file))!=NULL)
printf("%s",buff);
}
☆系統(tǒng)預(yù)定義符號 top
有些符號是系統(tǒng)自定義了的
————————————————————————————————————————————————
__FILE__ 當(dāng)前文件名
__LINE__ 當(dāng)前行號
__DATE__ 當(dāng)前日期(月日年)
__TIME__ 當(dāng)前時間(時分秒)
__func__ 當(dāng)前函數(shù)(注意是小寫)
————————————————————————————————————————————————
我們可以利用這些來記錄日志,例如:
#include <stdio.h>
#ifndef ERROR_LOG
#define ERROR_LOG printf("[%s %s][%s %s line:%d] ",__DATE__,__TIME__,__FILE__,__func__,__LINE__)
#endif
int main(){
printf("begin\n");
ERROR_LOG;
printf("end");
getchar();
}
————————————————————————————————————————————————
__FILE__ 當(dāng)前文件名
__LINE__ 當(dāng)前行號
__DATE__ 當(dāng)前日期(月日年)
__TIME__ 當(dāng)前時間(時分秒)
__func__ 當(dāng)前函數(shù)(注意是小寫)
————————————————————————————————————————————————
我們可以利用這些來記錄日志,例如:
#include <stdio.h>
#ifndef ERROR_LOG
#define ERROR_LOG printf("[%s %s][%s %s line:%d] ",__DATE__,__TIME__,__FILE__,__func__,__LINE__)
#endif
int main(){
printf("begin\n");
ERROR_LOG;
printf("end");
getchar();
}
☆ sizeof top
sizeof不是一個函數(shù),而是一個一元運算符。
sizeof看似簡單,實際上有時讓人頭痛。先看下面的程序,猜猜程序的結(jié)果
#include <stdio.h>
int main(){
char a='a';
char * b="hello";
char c[10]="hello";
struct Data1{char m;int n};
struct Data2{char m;struct Data1 data1;int n};
printf("sizeof(a)=%ld\n",sizeof(a));
printf("sizeof(b)=%ld\n",sizeof(b));
printf("sizeof(* b)=%ld\n",sizeof(*b));
printf("sizeof(c)=%ld\n",sizeof(c));
printf("sizeof(Data1)=%ld\n",sizeof(struct Data1));
printf("sizeof(Data2)=%ld\n",sizeof(struct Data2));
}
結(jié)果如下:
sizeof(a)=1
sizeof(b)=4
sizeof(* b)=1
sizeof(c)=10
sizeof(Data1)=8
sizeof(Data2)=16
解釋一下,sizeof(b),b是一個指針,長度為4字節(jié);sizeof(* b),*b是一個字符,長度為1個字節(jié);sizeof(c),c被分配了10個字節(jié)的空間;sizeof(struct Data1),結(jié)構(gòu)體會字節(jié)對齊,即按照最長的基本數(shù)據(jù)類型來遞增空間,這樣每個基本數(shù)據(jù)數(shù)據(jù)類型的偏移地址都是最長基本數(shù)據(jù)類型的n倍,這樣可以加快計算機(jī)的取數(shù)速度,減少指令周期。對于長度比最長基本數(shù)據(jù)類型短的類型,在后面填充字節(jié)。這樣sizeof(struct Date1)就為size(int)*2;sizeof(struct Data2),其實Data2會把Data1打算,最后還是按int填充,所有為sizeof(int)*4。
sizeof看似簡單,實際上有時讓人頭痛。先看下面的程序,猜猜程序的結(jié)果
#include <stdio.h>
int main(){
char a='a';
char * b="hello";
char c[10]="hello";
struct Data1{char m;int n};
struct Data2{char m;struct Data1 data1;int n};
printf("sizeof(a)=%ld\n",sizeof(a));
printf("sizeof(b)=%ld\n",sizeof(b));
printf("sizeof(* b)=%ld\n",sizeof(*b));
printf("sizeof(c)=%ld\n",sizeof(c));
printf("sizeof(Data1)=%ld\n",sizeof(struct Data1));
printf("sizeof(Data2)=%ld\n",sizeof(struct Data2));
}
結(jié)果如下:
sizeof(a)=1
sizeof(b)=4
sizeof(* b)=1
sizeof(c)=10
sizeof(Data1)=8
sizeof(Data2)=16
解釋一下,sizeof(b),b是一個指針,長度為4字節(jié);sizeof(* b),*b是一個字符,長度為1個字節(jié);sizeof(c),c被分配了10個字節(jié)的空間;sizeof(struct Data1),結(jié)構(gòu)體會字節(jié)對齊,即按照最長的基本數(shù)據(jù)類型來遞增空間,這樣每個基本數(shù)據(jù)數(shù)據(jù)類型的偏移地址都是最長基本數(shù)據(jù)類型的n倍,這樣可以加快計算機(jī)的取數(shù)速度,減少指令周期。對于長度比最長基本數(shù)據(jù)類型短的類型,在后面填充字節(jié)。這樣sizeof(struct Date1)就為size(int)*2;sizeof(struct Data2),其實Data2會把Data1打算,最后還是按int填充,所有為sizeof(int)*4。
☆ malloc與calloc的區(qū)別 top
函數(shù)malloc()和calloc()都可以用來動態(tài)分配內(nèi)存空間,但兩者稍有區(qū)別。
語法格式:
void*malloc(size_tsize);
void*calloc(size_tnumElements,size_tsizeOfElement);
malloc()函數(shù)有一個參數(shù),即要分配的內(nèi)存空間的大小:
calloc()函數(shù)有兩個參數(shù),分別為元素的數(shù)目和每個元素的大小,這兩個參數(shù)的乘積就是要分配的內(nèi)存空間的大小。
如果調(diào)用成功,函數(shù)malloc()和函數(shù)calloc()都將返回所分配的內(nèi)存空間的首地址。
函數(shù)malloc()和函數(shù)calloc() 的主要區(qū)別是前者不能初始化所分配的內(nèi)存空間,而后者能。如果由malloc()函數(shù)分配的內(nèi)存空間原來沒有被使用過,則其中的每一位可能都是0;反之, 如果這部分內(nèi)存曾經(jīng)被分配過,則其中可能遺留有各種各樣的數(shù)據(jù)。也就是說,使用malloc()函數(shù)的程序開始時(內(nèi)存空間還沒有被重新分配)能正常進(jìn)行,但經(jīng)過一段時間(內(nèi)存空間還已經(jīng)被重新分配)可能會出現(xiàn)問題。
函數(shù)calloc() 會將所分配的內(nèi)存空間中的每一位都初始化為零,也就是說,如果你是為字符類型或整數(shù)類型的元素分配內(nèi)存,那麼這些元素將保證會被初始化為0;如果你是為指 針類型的元素分配內(nèi)存,那麼這些元素通常會被初始化為空指針;如果你為實型數(shù)據(jù)分配內(nèi)存,則這些元素會被初始化為浮點型的零。
calloc(m, n) 本質(zhì)上等價于p = malloc(m * n); memset(p,0, m * n);顯然calloc的效率要比malloc低,如果沒有初始為0的要求就用malloc。
總之,當(dāng)你在calloc()函數(shù)和malloc()函數(shù)之間作選擇時,你只需考慮是否要初始化所分配的內(nèi)存空間。
另外還有一個重新分配內(nèi)存的函數(shù)realloc
語法格式:void * realloc(void * p,size_t size); 改變指針p分配的空間,重新設(shè)置為size字節(jié)。返回新分配空間的地址,如果失敗返回NULL。原則上是先釋放再重新分配的。但是好像跟系統(tǒng)有關(guān)系。如果縮小空間,可能會復(fù)制空間。如果變大空間,那么返回的指針不一定就是原來的指針了。
語法格式:
void*malloc(size_tsize);
void*calloc(size_tnumElements,size_tsizeOfElement);
malloc()函數(shù)有一個參數(shù),即要分配的內(nèi)存空間的大小:
calloc()函數(shù)有兩個參數(shù),分別為元素的數(shù)目和每個元素的大小,這兩個參數(shù)的乘積就是要分配的內(nèi)存空間的大小。
如果調(diào)用成功,函數(shù)malloc()和函數(shù)calloc()都將返回所分配的內(nèi)存空間的首地址。
函數(shù)malloc()和函數(shù)calloc() 的主要區(qū)別是前者不能初始化所分配的內(nèi)存空間,而后者能。如果由malloc()函數(shù)分配的內(nèi)存空間原來沒有被使用過,則其中的每一位可能都是0;反之, 如果這部分內(nèi)存曾經(jīng)被分配過,則其中可能遺留有各種各樣的數(shù)據(jù)。也就是說,使用malloc()函數(shù)的程序開始時(內(nèi)存空間還沒有被重新分配)能正常進(jìn)行,但經(jīng)過一段時間(內(nèi)存空間還已經(jīng)被重新分配)可能會出現(xiàn)問題。
函數(shù)calloc() 會將所分配的內(nèi)存空間中的每一位都初始化為零,也就是說,如果你是為字符類型或整數(shù)類型的元素分配內(nèi)存,那麼這些元素將保證會被初始化為0;如果你是為指 針類型的元素分配內(nèi)存,那麼這些元素通常會被初始化為空指針;如果你為實型數(shù)據(jù)分配內(nèi)存,則這些元素會被初始化為浮點型的零。
calloc(m, n) 本質(zhì)上等價于p = malloc(m * n); memset(p,0, m * n);顯然calloc的效率要比malloc低,如果沒有初始為0的要求就用malloc。
總之,當(dāng)你在calloc()函數(shù)和malloc()函數(shù)之間作選擇時,你只需考慮是否要初始化所分配的內(nèi)存空間。
另外還有一個重新分配內(nèi)存的函數(shù)realloc
語法格式:void * realloc(void * p,size_t size); 改變指針p分配的空間,重新設(shè)置為size字節(jié)。返回新分配空間的地址,如果失敗返回NULL。原則上是先釋放再重新分配的。但是好像跟系統(tǒng)有關(guān)系。如果縮小空間,可能會復(fù)制空間。如果變大空間,那么返回的指針不一定就是原來的指針了。
☆ 內(nèi)存函數(shù) top
頭文件:<string.h>
void *memchr(void *s, char ch, unsigned n);在數(shù)組的前n個字節(jié)中搜索字符ch。如果找到,返回指向該位置的指針,否則返回NULL
int memcmp (const void *s1,const void *s2,size_t n);比較s1和s2所指的內(nèi)存區(qū)間前n個字符。字符串大小的比較是以ASCII碼表上的順序來決定,次順序亦為字符的值。memcmp()首先將s1第一個字符值減去s2第一個字符的值,若差為0則再繼續(xù)比較下個字符,若差值不為0則將差值返回。若參數(shù)s1和s2所指的內(nèi)存內(nèi)容都完全相同則返回0值。s1若大于s2則返回大于0的值。s1若小于s2則返回小于0的值。
void * memcpy (void * dest ,const void *src, size_t n);拷貝src所指的內(nèi)存內(nèi)容前n個字節(jié)到dest所指的內(nèi)存地址上。與strcpy()不同的是,memcpy()會完整的復(fù)制n個字節(jié),不會因為遇到字符串結(jié)束\0而結(jié)束。返回指向dest的指針。注意:指針src和dest所指的內(nèi)存區(qū)域不可重疊。
void * memmove(void *dest,const void *src,size_t n);memmove()與memcpy()一樣都是用來拷貝src所指的內(nèi)存內(nèi)容前n個字節(jié)到dest所指的地址上。不同的是,當(dāng)src和dest所指的內(nèi)存區(qū)域重疊時,memmove()仍然可以正確的處理,不過執(zhí)行效率上會比使用memcpy()略慢些。返回指向dest的指針。
void * memset (void *s ,int c, size_t n);將參數(shù)s所指的內(nèi)存區(qū)域前n個字節(jié)以參數(shù)c填入,然后返回指向s的指針。在編寫程序時,若需要將某一數(shù)組作初始化,memset()會相當(dāng)方便。返回指向s的指針。注意:參數(shù)c雖聲明為int, 但必須是unsigned char ,所以范圍在0到255之間。
int sprintf(char * buffer,char * format[,argument]); 跟printf差不多,可以參考前面的fprintf(),常用于類型轉(zhuǎn)換。轉(zhuǎn)換結(jié)束后會自動加上空字符\0。返回放置在buffer中的字符數(shù)(不包括結(jié)束的空字符)。頭文件。
int sscanf(char * buffer,char * format[,argument]); 跟scanf()差不多,返回成功轉(zhuǎn)換參數(shù)的個數(shù)。例如:char buffer[]="woxingwosu 666";char username[15];int value;sscanf(buffer,"%s %d",username,&value)頭文件
void *memchr(void *s, char ch, unsigned n);在數(shù)組的前n個字節(jié)中搜索字符ch。如果找到,返回指向該位置的指針,否則返回NULL
int memcmp (const void *s1,const void *s2,size_t n);比較s1和s2所指的內(nèi)存區(qū)間前n個字符。字符串大小的比較是以ASCII碼表上的順序來決定,次順序亦為字符的值。memcmp()首先將s1第一個字符值減去s2第一個字符的值,若差為0則再繼續(xù)比較下個字符,若差值不為0則將差值返回。若參數(shù)s1和s2所指的內(nèi)存內(nèi)容都完全相同則返回0值。s1若大于s2則返回大于0的值。s1若小于s2則返回小于0的值。
void * memcpy (void * dest ,const void *src, size_t n);拷貝src所指的內(nèi)存內(nèi)容前n個字節(jié)到dest所指的內(nèi)存地址上。與strcpy()不同的是,memcpy()會完整的復(fù)制n個字節(jié),不會因為遇到字符串結(jié)束\0而結(jié)束。返回指向dest的指針。注意:指針src和dest所指的內(nèi)存區(qū)域不可重疊。
void * memmove(void *dest,const void *src,size_t n);memmove()與memcpy()一樣都是用來拷貝src所指的內(nèi)存內(nèi)容前n個字節(jié)到dest所指的地址上。不同的是,當(dāng)src和dest所指的內(nèi)存區(qū)域重疊時,memmove()仍然可以正確的處理,不過執(zhí)行效率上會比使用memcpy()略慢些。返回指向dest的指針。
void * memset (void *s ,int c, size_t n);將參數(shù)s所指的內(nèi)存區(qū)域前n個字節(jié)以參數(shù)c填入,然后返回指向s的指針。在編寫程序時,若需要將某一數(shù)組作初始化,memset()會相當(dāng)方便。返回指向s的指針。注意:參數(shù)c雖聲明為int, 但必須是unsigned char ,所以范圍在0到255之間。
int sprintf(char * buffer,char * format[,argument]); 跟printf差不多,可以參考前面的fprintf(),常用于類型轉(zhuǎn)換。轉(zhuǎn)換結(jié)束后會自動加上空字符\0。返回放置在buffer中的字符數(shù)(不包括結(jié)束的空字符)。頭文件
int sscanf(char * buffer,char * format[,argument]); 跟scanf()差不多,返回成功轉(zhuǎn)換參數(shù)的個數(shù)。例如:char buffer[]="woxingwosu 666";char username[15];int value;sscanf(buffer,"%s %d",username,&value)頭文件
☆符號重載
static:
在函數(shù)內(nèi)部,表示該變量的值在各個調(diào)用間都一直保持延續(xù)性(注意不可重入的問題)
用于函數(shù),表示該函數(shù)只對本文件可見
extern
用于變量定義,表示該變量在其他地方定義
用于函數(shù)定義,表示全局可見(屬于多余)
void
作為函數(shù)的返回類型,表示不返回任何值
在指針聲明中,表示通用指針的類型
位于參數(shù)列表中,表示沒有參數(shù)
在函數(shù)內(nèi)部,表示該變量的值在各個調(diào)用間都一直保持延續(xù)性(注意不可重入的問題)
用于函數(shù),表示該函數(shù)只對本文件可見
extern
用于變量定義,表示該變量在其他地方定義
用于函數(shù)定義,表示全局可見(屬于多余)
void
作為函數(shù)的返回類型,表示不返回任何值
在指針聲明中,表示通用指針的類型
位于參數(shù)列表中,表示沒有參數(shù)
☆volatile
volatile的本意是“易變的”
由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優(yōu)化。比如:
static int i=0;
int main(void)
{

while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用dosomething函數(shù),但是,由于編譯器判斷在main函數(shù)里面沒有修改過i,因此可能只執(zhí)行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導(dǎo)致dosomething永遠(yuǎn)也不會被調(diào)用。如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優(yōu)化(肯定執(zhí)行)。此例中i也應(yīng)該如此說明。
一般說來,volatile用在如下的幾個地方:
1、中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;
3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能有不同意義;
【http://www.laogu.com/wz_692.htm】
由于訪問寄存器的速度要快過RAM,所以編譯器一般都會作減少存取外部RAM的優(yōu)化。比如:
static int i=0;
int main(void)
{

while (1)
{
if (i) dosomething();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
程序的本意是希望ISR_2中斷產(chǎn)生時,在main當(dāng)中調(diào)用dosomething函數(shù),但是,由于編譯器判斷在main函數(shù)里面沒有修改過i,因此可能只執(zhí)行一次對從i到某寄存器的讀操作,然后每次if判斷都只使用這個寄存器里面的“i副本”,導(dǎo)致dosomething永遠(yuǎn)也不會被調(diào)用。如果將將變量加上volatile修飾,則編譯器保證對此變量的讀寫操作都不會被優(yōu)化(肯定執(zhí)行)。此例中i也應(yīng)該如此說明。
一般說來,volatile用在如下的幾個地方:
1、中斷服務(wù)程序中修改的供其它程序檢測的變量需要加volatile;
2、多任務(wù)環(huán)境下各任務(wù)間共享的標(biāo)志應(yīng)該加volatile;
3、存儲器映射的硬件寄存器通常也要加volatile說明,因為每次對它的讀寫都可能有不同意義;
【http://www.laogu.com/wz_692.htm】