1.源碼文件如何變成可執(zhí)行文件(*)
需要以下4個步驟:
(1)預(yù)處理階段:預(yù)處理器根據(jù)以#開頭的指令,修改主要包括#include、#define和條件編譯三個方面,修改源碼內(nèi)容,比如如果源碼中有 #include<stdio.h> 則預(yù)處理器會讀取文件 stdio.h 文件中的內(nèi)容校仑,并將其直接插入到原來的源碼文件中,通常另存為以 .i 為擴(kuò)展名的文件传惠。(gcc -E hello.c -o hello.i)
(2)編譯階段:編譯器讀取 .i 文件中的內(nèi)容迄沫,并將其翻譯為以 .s 為擴(kuò)展名的匯編語言文件。(gcc -S hello.c -o hello.s)
(3)匯編階段:匯編器將 .s 文件翻譯成機器碼卦方,并保存為 .o為擴(kuò)展名的文件羊瘩。
(gcc -c hello.s -o hello.o)
(4)鏈接階段:鏈接器將不同的 .o 文件合并到一起,組成最終的可執(zhí)行文件盼砍;比如我們的程序里調(diào)用了 printf 函數(shù)困后,作為一個C標(biāo)準(zhǔn)函數(shù),printf 單獨存在于一個 printf.o 的文件中衬廷,那么鏈接器將會找到這個 printf.o 文件,將其中的內(nèi)容合并到我們自己的 .o 文件中汽绢,生成可以被加載到內(nèi)存中執(zhí)行的文件吗跋。
C語言主要分為幾個版本:Old Style C、C89宁昭、C99和C11跌宛。其中,C89积仗、C99和C11是標(biāo)準(zhǔn)語言規(guī)范疆拘,現(xiàn)在廣泛使用的是C99。
2.面向過程和面向?qū)ο蟮膮^(qū)別(*)
(1)面向過程就是分析出解決問題所需要的步驟寂曹,然后用函數(shù)把這些步驟一步一步實現(xiàn)哎迄,使用的時候一個一個依次調(diào)用就可以了。
(2)面向?qū)ο笫前褬?gòu)成問題事務(wù)分解成各個對象隆圆,建立對象的目的不是為了完成一個步驟漱挚,而是為了描敘某個事物在整個解決問題的步驟中的行為。
比如:五子棋
面向過程:1.開始游戲渺氧,2旨涝、黑子先走,3侣背、繪制畫面白华,4慨默、判斷輸贏,5弧腥、輪到白子厦取,6、繪制畫面鸟赫,7蒜胖、判斷輸贏,8抛蚤、返回步驟2台谢,9、輸出最后結(jié)果岁经。
面向?qū)ο螅?.黑白雙方朋沮,2、棋盤系統(tǒng)缀壤,負(fù)責(zé)繪制畫面樊拓,3、規(guī)則系統(tǒng)塘慕,負(fù)責(zé)判定諸如犯規(guī)筋夏、輸贏等。
特點
- 面向過程(蛋炒飯)
優(yōu)點:性能比面向?qū)ο蟾咄寄兀驗轭愓{(diào)用時需要實例化条篷,開銷比較大,比較消耗資源;比如單片機蛤织、嵌入式開發(fā)赴叹、 Linux/Unix等一般采用面向過程開發(fā),性能是最重要的因素指蚜。
缺點:沒有面向?qū)ο笠拙S護(hù)乞巧、易復(fù)用、易擴(kuò)展 - 面向?qū)ο螅ㄉw澆飯)
優(yōu)點:易維護(hù)摊鸡、易復(fù)用绽媒、易擴(kuò)展,由于面向?qū)ο笥蟹庋b免猾、繼承些椒、多態(tài)性的特性,可以設(shè)計出低耦合的系統(tǒng)掸刊,使系統(tǒng) 更加靈活免糕、更加易于維護(hù)
缺點:性能比面向過程低
3.基本數(shù)據(jù)類型
(1)數(shù)值類型
數(shù)值類型分為整型(整數(shù))和浮點型(小數(shù))。按照表示數(shù)字范圍的從大到小。
<1>整型
整數(shù)分為五類:字符型(char)石窑、短整型(short)牌芋、整型(int)、長整型(long)和長長整型(long long)
<2>浮點型
浮點型分三類:單精度型(float)松逊、雙精度型(double)和長雙精度型(long double)
(2)獲取類型的大小
關(guān)鍵字sizeof:查看變量或者類型大小躺屁。
(3)字節(jié)
sizeof獲得數(shù)據(jù)的單位是Byte(字節(jié))。Byte(字節(jié))是計量存儲容量的一種計量單位经宏,一個字節(jié)是8位二進(jìn)制犀暑,可容納256個數(shù)字。一個ASCII字符就是一個字節(jié)烁兰。
用B表示Byte(字節(jié))耐亏,用b表示bit(比特/位).
(4)輸入輸出格式化
double的輸入占位符必須是%lf,輸出占位符可以是%f沪斟。
(5)整數(shù)類型
<1>表示范圍
類型的表示范圍與類型大小存在如下關(guān)系:
n表示的是bit位广辰,比如:char是一個字節(jié),8位主之,int是4個字節(jié)择吊,32個二進(jìn)制位,由于存在負(fù)數(shù)槽奕,所以數(shù)的大小會減半几睛。
<2>無符號整型
在一些特殊情況下,數(shù)據(jù)只用0和整數(shù)粤攒,不存在負(fù)數(shù)所森,這時可以使用無符號整型unsigned。無符號整型只是在原來的整型前加上關(guān)鍵字unsigned琼讽。因為沒有負(fù)數(shù),所以數(shù)值的表示范圍擴(kuò)大了一倍洪唐。
總之钻蹬,類型的表示范圍與類型大小存在如下關(guān)系:
<3>整數(shù)類型的選擇
- 大多數(shù)情況下使用int。
- 如果int范圍不夠凭需,使用long long问欠。
- 避免使用long。
- 謹(jǐn)慎使用unsigned粒蜈。
(5)浮點類型
<1>浮點數(shù)的范圍
示例 | 輸出 | 含義 |
---|---|---|
1.0/0.0 | inf | 表示正無窮大 |
-1.0/0.0 | -inf | 表示負(fù)無窮大 |
0.0/0.0 | nan | 不存在 |
<2>浮點數(shù)的精度
注意:浮點類型沒有無符號unsigned類型顺献。
如何比較浮點數(shù)?使用最小誤差枯怖。
float a = 10.2;
float b = 9;
float c = a - b;
if(fabs(c - 1.2) < 0.000001){
printf("%f == 1.2\n",c);//1.200000 == 1.2
}
在誤差范圍內(nèi)認(rèn)為相等注整。即絕對值差小于精度最小值。
float
浮點數(shù)誤差通常為1-6(6位有效數(shù)字)。
double
浮點數(shù)誤差通常為1-15(15位有效數(shù)字)肿轨。
<3>浮點類型選擇
- 大多數(shù)情況下使用double寿冕。
- 盡量不要使用float。
- 過程運算可以使用long double椒袍。
既然浮點數(shù)這么不準(zhǔn)確驼唱,為什么還需要?
浮點數(shù)通過損失精度驹暑,獲取更大的表示范圍玫恳。
(6)字符類型
字符類型是一個特殊類型,是整型的一種优俘。使用單引號表示字符字面量京办,例如:字母'a'、數(shù)字'1'兼吓、空字符''臂港、轉(zhuǎn)義字符\n。
- 通常使用%c作為格式化占位符輸入輸出视搏,有時也可以使用%d輸出字符對應(yīng)ASCII編碼审孽。(C語言中字符即數(shù)字。)
<1>ASCII編碼
ASCII編碼使用7 位二進(jìn)制數(shù)(剩下的1位二進(jìn)制為0)來表示所有的大寫和小寫字母浑娜,數(shù)字0 到9佑力、標(biāo)點符號, 以及在美式英語中使用的特殊控制字符筋遭。
- ASCII表的特點:
1.字母在ASCII表中是順序排列的打颤。
2.大寫字母和小寫字母是分開排列的。
<2>運算
- 字符類型可以像整型一樣參與運算漓滔。
1.一個字符加上一個數(shù)字得到ASCII表中對應(yīng)新字符编饺。
2.兩個字符相減,得到這兩個字符在表中的距離响驴。
<3>轉(zhuǎn)義字符/逃逸字符
在ASCII表中透且,除了常見的字符(如:大小寫字母、數(shù)字等)豁鲤,還包含一些無法打印出來的控制字符或特殊字符秽誊。這些字符以反斜線\開頭,后面接著一個字符琳骡,這種字符被稱作轉(zhuǎn)義字符/逃逸字符锅论。
(7)布爾類型
在C99中新增bool類型表示邏輯值,它只有兩種值(true和false)
楣号。使用前需要加上頭文件stdbool.h
最易。
(8)數(shù)據(jù)類型轉(zhuǎn)換
<1>自動類型轉(zhuǎn)換
當(dāng)運算符左右兩邊操作數(shù)的類型不一致時怒坯,會自動轉(zhuǎn)換成較大類型。
- 整型:
char
→short
→int
→long
→long long
- 浮點型:
int
→float
→double
→long double
<2>強制類型轉(zhuǎn)換
當(dāng)把一個較大的類型轉(zhuǎn)換成較小的類型耘纱,需要強制轉(zhuǎn)換敬肚。
強制轉(zhuǎn)換語法:
(轉(zhuǎn)換后的類型)值
-
浮點數(shù)轉(zhuǎn)整數(shù)采用的是截斷方式。
printf("%d\n",(int)3.14);//3
-
整型轉(zhuǎn)浮點型也需要強制轉(zhuǎn)換束析。
printf("%f\n",(double)2);//2.000000
4.運算符
(1)算術(shù)運算符
+ 艳馒、-、*员寇、 /(取整)弄慰、 %(取余)
(2)關(guān)系運算符
==、蝶锋!=陆爽、>、 <扳缕、 >=慌闭、 <=
(3)邏輯運算符
&& 、 || 躯舔、 驴剔!
閏年判斷:year%4==0&&year%100!=0 || year%400==0(year能被4整除 and 不能被100整除 or year能被400整除 )
(4)復(fù)合賦值運算符
+=、-=粥庄、*=丧失、/=、%=
(5)自增惜互、自減運算符
自增運算符與自減運算符優(yōu)先級高于算術(shù)運算符布讹。
<1>前綴自增/自減
++a、--a
<2>后綴自增/自減
(6)運算的優(yōu)先級順序
- 自增/自減的優(yōu)先級大于算數(shù)運算符的優(yōu)先級训堆;
- 自增/自減的優(yōu)先級大于解引用
*
的優(yōu)先級 - 中括號[ ]的優(yōu)先級大于解引用
*
的優(yōu)先級 - 結(jié)構(gòu)體中用點
.
的優(yōu)先級大于取地址&
的優(yōu)先級 - 結(jié)構(gòu)體中用點
.
的優(yōu)先級大于接引用*
5.變量
初始化和賦值:
初始化時在生成變量時放入數(shù)值描验,賦值是在已經(jīng)生成變量時放入數(shù)值。
6.控制語句
(1)條件判斷
<1>if-else
代碼塊與if之間使用空格或者Tab縮進(jìn)坑鱼,不影響編譯和執(zhí)行膘流,只是為了提高代碼可讀性。
if(condition1){
}else if(condition2){
}else{
}
<2>switch case
switch(表達(dá)式){
case 整型常量1:
/* 表達(dá)式等于整型常量1執(zhí)行的代碼 */
break; /* 可選的 */
case 整型常量2:
/* 表達(dá)式等于整型常量2執(zhí)行的代碼 */
break; /* 可選的 */
default : /* 可選的 */
/* 表達(dá)式不等于上面所有情況執(zhí)行的代碼 */
}
(2)循環(huán)
<1>while語句
while(條件){
/* 如果條件為真將重復(fù)執(zhí)行的語句 */
}
<2>do-while
do {
/* 如果表達(dá)式為真將重復(fù)執(zhí)行的語句 */
}while(條件);//注意while()后的分號;姑躲。
do-while循環(huán)是先循環(huán)后判斷睡扬,循環(huán)體至少執(zhí)行一次盟蚣;while循環(huán)是先判斷后循環(huán)黍析,循環(huán)體可能一次也不執(zhí)行。
<3>for
for (初始值;條件;遞增或遞減){
/* 如果條件為真將重復(fù)執(zhí)行的語句 */
}
在while和for循環(huán)中屎开,break是結(jié)束一個循環(huán)體阐枣;continue是結(jié)束單次循環(huán)。
(3)簡化
<1>省略大括弧
如果if語句、while語句蔼两、for語句中只有一個執(zhí)行語句甩鳄,可以省略大括弧。
<2>三元運算符:?
如果if-else語句只有單個執(zhí)行語句额划,可以使用三元運算符:?妙啃。
7.進(jìn)制
(1)轉(zhuǎn)換
<1>十進(jìn)制轉(zhuǎn)R進(jìn)制
十進(jìn)制轉(zhuǎn)任何進(jìn)制用短除法。從下往上來統(tǒng)計俊戳。
<2>R進(jìn)制轉(zhuǎn)十進(jìn)制
加權(quán)和揖赴。
0x2A=2*16^1+A*16^0=32+10=42
代碼:
2進(jìn)制數(shù)1011轉(zhuǎn)10進(jìn)制:1
(1*2)+0)*2+1)*2+1)=11
int res=0;
res=res*2+每一位
(2)C語言中的進(jìn)制
<1>進(jìn)制常量表示
C語言不能直接表示二進(jìn)制常量。八進(jìn)制數(shù)字以0開頭抑胎,十六進(jìn)制數(shù)字以0x或0X開頭燥滑。
<2>進(jìn)制打印(輸出)
進(jìn)制的輸出其實與字符輸出是一樣的,根據(jù)占位符的不同輸出不同阿逃。
- 十進(jìn)制:%d
- 八進(jìn)制:%#o
- 十六進(jìn)制:%#x
char a = 'a';
printf("%c\t%d\t%#o\t%#x\n",a,a,a);//a,97,0141,0x61
<3>進(jìn)制輸入
- 10進(jìn)制:%d
- 8進(jìn)制:%o
- 16進(jìn)制:%x
scanf("%o",&n);//010
printf("%d\n",n);//8
scanf("%x",&n);//0xa
printf("%d\n",n);//10
<4>%i
%i 可以匹配八進(jìn)制铭拧、十進(jìn)制、十六進(jìn)制表示的整數(shù)恃锉。
例如: 如果輸入的數(shù)字有前綴 0(018)搀菩,%i將會把它當(dāng)作八進(jìn)制數(shù)來處理,如果有前綴0x (0x54),它將以十六進(jìn)制來處理淡喜。
scanf("%i",&n);//0xa
printf("%d\n",n);//10
8.文件操作
(1)文件輸入輸出
- 使用
printf()
和命令行重定向>
實現(xiàn)文件輸出秕磷; - 使用
scanf()
和命令行重定向<
實現(xiàn)文件輸入。
<1>實例
hello.c
char name[256];
scanf("%s",name);
printf("Hello %s\n",name);
-
編譯
gcc hllo.c -o hello
執(zhí)行
echo zhangsan > namefile //把張三重定向到namefile文件中
./hello < namefile > output //把輸入定向到./hello 中炼团,然后把執(zhí)行結(jié)果定向到output中澎嚣。
(2)文件的打開和關(guān)閉fopen和fclose
- 頭文件
<stdio.h>
<1>打開文件fopen
FILE *fopen(const char *pathname, const char *mode);
- 參數(shù)
No. | 參數(shù) | 作用 |
---|---|---|
1 | filename | 需要打開的文件 |
2 | mode | 文件打開方式 |
- 返回值
No. | 類型 | 說明 |
---|---|---|
1 | 成功 | 返回值是指向這個文件流的文件指針 |
2 | 失敗 | NULL |
- 打開方式
No. | 打開方式 | 含義 |
---|---|---|
1 | r(read) | 讀 |
2 | w(write) | 寫,不存在創(chuàng)建,存在清空寫入(w),追加(a) |
3 | a(append) | 追加 |
4 | +(plus) | 讀或?qū)懳林ィ饕桥浜蟫易桃、w、a使用 |
5 | t(text) | 文本文件(默認(rèn)) |
6 | b(binary) | 二進(jìn)制文件 |
<2>關(guān)閉文件fclose
int flcose(FILE* stream);
- 參數(shù)
stream
文件指針锌俱。 - 返回值
如果成功釋放晤郑,返回0
; 否則返回EOF(-1)
.
(3)文本讀寫fprintf()
和fscanf()
<1>函數(shù)原型
int printf(const char *format, ...);
int fprintf(FILE *stream, char *format, argument...);
int fscanf(FILE *stream, char *format, argument... );
fprintf()
/fscanf()
與printf()
/scanf()
使用非常相似,區(qū)別在于fprintf()/fscanf()
第一個參數(shù)stream
是文件描述符贸宏。
<2>用法示例
- 從文件中讀出數(shù)據(jù)
int i ;
float f ;
char c;
char str[10];
fscanf(fp, "%d %f %c %s\n", &i, &f, &c, str); //字符串是不需要加&
- 將數(shù)據(jù)寫入文件
int i = 10;
float f = 3.14;
char c = 'C';
char str[10] = "haha";
fprintf(fp, "%d %f %c %s\n", i, f, c, str);
如果不需要從文件里面寫入字符串造寝,那么就可以用逗號或者其他符號來分隔;如果文件里需要寫入字符串,那么字符串與其他數(shù)據(jù)之間只能用空格和回車來分隔吭练。
<3>實例
- 將多個學(xué)生信息寫入文件并讀出诫龙。
struct Student {
char name[32]; //姓名
int age; //年齡
float score; //成績
};
int main() {
//打開文件
FILE* fp =fopen("./info.txt","r");
if(NULL==fp) {
perror("文件fp打開失敗");
return 1;
}
//從文件中讀取數(shù)據(jù)
int n;
fscanf(fp,"%d",&n);
struct Student student[n];
for(int i=0; i<n; ++i) {
fscanf(fp,"%s %d %f",student[i].name,&student[i].age,&student[i].score);
}
//將數(shù)據(jù)寫入文件
FILE* fq=fopen("./res.txt","w");
if(NULL==fq) {
perror("fq打開失敗");
return 1;
}
for(int i=0; i<n; ++i) {
fprintf(fq,"%s\t%d\t%f\n",student[i].name,student[i].age,student[i].score);
}
//關(guān)閉文件
fclose(fq);
fclose(fp);
}
(4)二進(jìn)制讀寫:fread()和fwrite()
對于二進(jìn)制文件的數(shù)據(jù)讀取和寫入是一對,只有以二進(jìn)制的方式寫入到文件才能讀出來鲫咽,不能從文本文件中讀取數(shù)據(jù)签赃。
<1>函數(shù)原型
size_t fread(void *ptr, size_t size, size_t count, FILE* stream);
size_t fwrite(void *ptr, size_t size, size_t count, FILE* stream);
- 參數(shù)
No. | 參數(shù) | 作用 |
---|---|---|
1 | ptr | 一個指針谷异,在fread()中是從文件里讀入的數(shù)據(jù)存放的地址;在fwrite()中是寫入到文件里的數(shù)據(jù)存放 的地址。 |
2 | size | 每次要讀寫的字節(jié)數(shù) |
3 | count | 讀寫的次數(shù) |
4 | stream | 文件指針 |
- 返回值
成功讀取/寫入的字節(jié)數(shù)锦聊。
<2>用法示例
- 從文件中讀出字符串
char str[100];
fread(str, sizeof(str), 1, fp);//每次讀取sizeof(str)個字節(jié)歹嘹,讀一次
或者:
fread(str,1,sizeof(str),fp);//每次讀取1個字節(jié),讀取sizeof(str)次孔庭,和上面一樣尺上。
- 將字符串寫入文件
char str[] = "Hello World";
fwrite(str, sizeof(str), 1, fp);
或者:
fwite(str,1,sizeof(str),fp);
<3>實例
- 往文件中寫數(shù)據(jù)(結(jié)構(gòu)體)
typedef struct {
char name[32]; //姓名
int age; //年齡
float score; //成績
}Student;
int main(){
FILE* fp = fopen("./1.txt","wb");
if(NULL==fp){
perror("open failed");
return 1;
}
Student student[2];
strcpy(student[0].name,"張三");//結(jié)構(gòu)體中的字符串賦值要使用strcpy
student[0].age=20;
student[0].score=92.8;
strcpy(student[1].name,"李四");
student[1].age=22;
student[1].score=76.2;
for(int i=0;i<2;++i){
fwrite(&student[i],sizeof(Student),1,fp);
}
fclose(fp);
}
- 從文件中讀數(shù)據(jù)(結(jié)構(gòu)體)
int main() {
//打開文件
FILE* fp =fopen("./1.txt","rb");
if(NULL==fp) {
perror("文件fp打開失敗");
return 1;
}
Student student;//由于不知道讀取的個數(shù),所以使用while循環(huán)
while(fread(&student,sizeof(Student),1,fp)){//讀到數(shù)據(jù)就顯示圆到,否則就退出
printf("%s\t%d\t%f\n",student.name,student.age,student.score);
}
fclose(fp);
}
(1)寫操作fwrite()后必須關(guān)閉流fclose()尖昏。
(2)不關(guān)閉流的情況下,每次讀或?qū)憯?shù)據(jù)后构资,文件指針都會指向下一個待寫或者讀數(shù)據(jù)位置的指針抽诉。
(3)sizeof和strlen的區(qū)別,否則就會讀不到數(shù)據(jù)(在讀數(shù)據(jù)時吐绵,使用strlen(buf))或者讀到一半數(shù)據(jù)(寫數(shù)據(jù)時迹淌,使用sizeof(msg)=8)。
(4)對于二進(jìn)制文件的寫入和讀取是同時存在的己单,不能從普通文件中讀取數(shù)據(jù)唉窃。
(5)其他類型數(shù)據(jù)的讀取:https://www.cnblogs.com/xudong-bupt/p/3478297.html
<4>二級制文件和文本文件
比較 | 文本 | 二進(jìn)制 |
---|---|---|
優(yōu)勢 | 便于人類讀寫纹笼,跨平臺 | 文件較小纹份,機器讀寫比較快 |
劣勢 | 文件較大,機器讀寫比較慢 | 不便人類讀寫廷痘,不跨平臺 |
配置 | Unix用文件 | Windows用注冊表 |
- 說明:
(1)Unix喜歡用文本文件來做數(shù)據(jù)存儲和程序配置蔓涧。
(2)windows喜歡用二進(jìn)制文件。
(3)數(shù)據(jù)量較多使用數(shù)據(jù)庫
(4)多媒體使用二進(jìn)制
(5)通常使用第三方庫讀寫文件笋额,很少直接讀寫二進(jìn)制文件元暴。
(5)文件定位:ftell()
和fseek()
<1>函數(shù)原型
// 獲取位置
long ftell(FILE* stream);
// 設(shè)置位置
int fseek(FILE* stream,long offset,int whence);
- 參數(shù)
No. | 參數(shù) | 含義 |
---|---|---|
1 | stream | 文件指針 |
2 | offset | 偏移量,基于起始點偏移了offset個字節(jié) |
3 | whence | 起始點 |
No. | whence |
數(shù)值 | 含義 |
---|---|---|---|
1 | SEEK_SET | 0 | 從頭開始 |
2 | SEEK_CUR | 1 | 從當(dāng)前開始 |
3 | SEEK_END | 2 | 從結(jié)束開始 |
- 返回值
ftell()
返回文件指針當(dāng)前位置兄猩,基于文件開頭的偏移字節(jié)數(shù)茉盏。
<2>示例:
fseek(stream, 0, SEEK_END);
// 將文件指針指向文件結(jié)尾,并偏移了 0 個字節(jié)枢冤,也就是直接將文件指針指向文件結(jié)尾
fseek(stream, -10, SEEK_CUR);
// 將文件指針指向當(dāng)前位置鸠姨,并偏移了 -10 個字節(jié),也就是將文件指針往前移動10個字節(jié)
應(yīng)用
獲取文件大小淹真。
char path[1024];
scanf("%s",path);
FILE* fp = fopen(path,"r");
if(fp){
fseek(fp,0,SEEK_END);
long size = ftell(fp);
printf("%ldB\n",size);
}
(6)文件結(jié)尾判斷feof()
<1>函數(shù)原型
int feof(FILE* stream);
- 返回值
一旦文件指針指向文件結(jié)尾讶迁,就返回一個真值;否則返回非真值趟咆。
(7)返回開頭rewind()
<1>函數(shù)原型
void rewind(FILE* stream);
<2>舉例
FILE *fp = fopen("./text.txt", "r+");
fseek(fp, 0, SEEK_END); // 將文件指針指向文件結(jié)尾
long len = ftell(fp); // 獲取文件指針位置添瓷,得到文件的大小(Byte)
rewind(fp); // 將文件指針重新指向文件開頭
(8) 清空數(shù)據(jù)流fflush()
<1>函數(shù)原型
void fflush(FILE* stream);
<2>使用
在使用多個輸出函數(shù)連續(xù)進(jìn)行多次輸出時,有可能發(fā)現(xiàn)輸出錯誤值纱。因為下一個數(shù)據(jù)再上一個數(shù)據(jù)還沒輸出完畢鳞贷,還在輸出緩沖區(qū)中時,下一個printf就把另一個數(shù)據(jù)加入輸出緩沖區(qū)虐唠,結(jié)果沖掉了原來的數(shù)據(jù)搀愧,出現(xiàn)輸出錯誤。 在 prinf()疆偿;后加上fflush(stdout); 強制馬上輸出咱筛,避免錯誤。
(9)文件重名名rename
int rename(const char *old_filename, const char *new_filename);
(10)文件刪除remove
int remove(char * filename);
(11)putc
和getc
<1>函數(shù)原型
int getc ( FILE * stream );//從stream中獲取一個字符
int putc ( int character, FILE * stream );//將字符寫入到stream中
<2>實現(xiàn)cp命令
int main(int argc,char* argv[]){
if(3!=argc){
perror("argc error");
return 1;
}
FILE* srcfp=fopen(argv[1],"rb");
if(NULL==srcfp){
perror("srcfile error");
return 1;
}
FILE* dstfp=fopen(argv[2],"wb");
if(NULL==dstfp){
perror("dstfile error");
return 1;
}
while(!feof(srcfp)){//到達(dá)文件末尾返回true,否則返回flase
putc(getc(srcfp),dstfp);
}
fclose(srcfp);
fclose(dstfp);
}
9.宏定義
(1) 宏定義是什么杆故?
宏是用來表示一段代碼的標(biāo)識符迅箩。
宏也是標(biāo)識符,也要滿足標(biāo)識符的規(guī)則处铛。但通常習(xí)慣使用大寫字母和下劃線命名饲趋。
(2)宏定義怎么用?
<1>宏定義通常有三種用法:
- 當(dāng)作常量使用撤蟆。
- 當(dāng)作函數(shù)使用奕塑。
- 編譯預(yù)處理。
<2>宏定義常量
- 預(yù)定義宏
ANSI C標(biāo)準(zhǔn)有定義好的宏定義家肯,稱為預(yù)定義宏龄砰。這些宏定義以雙下劃線__
開頭結(jié)尾。
printf("%s:%d",__FILE__,__LINE__);//源文件名:當(dāng)前語句所在的行號
printf("%s:%s",__DATE__,__TIME__);//月 日 年 時間
- 自定義宏
除了使用標(biāo)準(zhǔn)定義的宏讨衣,可以使用#define
指令用來定義一個宏换棚。
(1)語法
#define 標(biāo)識符 值
#define PI 3.1415926
(2)說明
注意沒有結(jié)尾的分號,因為不是C的語句反镇。
名字必須是一個單詞圃泡,值可以是各種東西。
在C語言的編譯器開始之前愿险,編譯預(yù)處理程序會把程序中的名字換成值颇蜡,是完全的文本替換。
-
如果一個宏的值有其他宏的名字辆亏,也會被替換
#define PI_2 2*PI
-
如果一個宏的值超過一行风秤,最后一行之前行末需要加
\
#define PI_2 2 \ * \ PI
-
宏的值后面出現(xiàn)的注釋不會被當(dāng)做宏的值的一部分。
#define PI_2 2*PI // 二倍的PI
<3>帶參數(shù)的宏
宏可以帶參數(shù)扮叨,使用上有些像函數(shù)缤弦。這種宏稱為帶參數(shù)的宏盹兢。
- 語法
#define 標(biāo)識符(參數(shù)...) 代碼
示例:
#define square(x) ((x)*(x))
#define cube(x) ((x)*(x)*(x))
- 錯誤示范
#define square(x) x*x
square(10);//100
square(10+1);//10+1*10+1=21
- 說明
上面因為缺少括號導(dǎo)致錯誤茂浮,稱為宏定義邊際效應(yīng)岁钓,所以帶參數(shù)的宏需要在以下兩個位置加上括號:
(1)參數(shù)出現(xiàn)的每個地方都要加括號激涤。
(2)整個值要加括號 - 參數(shù)的宏也可以有多個參數(shù)
#define MIN(a,b) ((a)<(b)?(a):(b))
swap函數(shù):
#define SWAP(m,n) {\
int t=m;\
m=n;\
n=t;\
}
盡量避免使用宏定義。
<4>編譯預(yù)處理
有時我們會使用沒有值的宏累提,這種宏用于條件編譯的尘喝,#ifdef #ifndef
用于檢查宏是否被定義過≌悖控制代碼的編譯朽褪。
#define TEST
#ifdef TEST
printf("Test\n");//如果前面定義了:#define TEST,則執(zhí)行這個,否則執(zhí)行后面的无虚。
#else
printf("No Test\n");
#endif
(3)宏展開
宏的本質(zhì)是指編譯前(編譯預(yù)處理階段)缔赠,用定義中的值或者代碼完全替換宏的標(biāo)識符。
只替換條件編譯中的宏友题。使用下面指令來查看宏的展開嗤堰。
gcc -E hello.c -o hello.i
(4)編譯預(yù)處理指令
以#
開頭的都是編譯預(yù)處理指令。除了宏定義度宦,還有文件包含#include
和條件編譯指令#if梁棠、#ifdef #ifndef、#else斗埂、#elif符糊、#endif
,一般寫在.h
文件中呛凶。
- 文件包含
#include
,把文件內(nèi)容包含到代碼中男娄。 - 條件編譯指令,根據(jù)編譯條件漾稀,選擇編譯或者編譯某段代碼模闲。
- 格式
#ifndef __HELLO_H
#define __HELLO_H
函數(shù)聲明(函數(shù)原型)
#endif //__HELLO_H
10.頭文件
(1)經(jīng)驗
編寫小的程序可以把代碼寫在一個文件中,當(dāng)編寫大程序中崭捍,需要把代碼分在多個文件中尸折。
- 多個源代碼文件
(1)main()里面代碼太長適當(dāng)分成幾個函數(shù)。
(2)一個源代碼文件太長適當(dāng)分成幾個文件殷蛇。
(3)兩個獨立的源代碼文件不能編譯成可執(zhí)行文件实夹。
(2)頭文件概念
<1>#include
指令
把函數(shù)原型放到一個頭文件.h
中,在需要調(diào)用這個函數(shù)的源代碼文件.c
時粒梦,使用#include
指令包含這個頭文件亮航,使編譯器在編譯的時候知道函數(shù)的原型。
<2>頭文件作用
頭文件主要用于編譯器的編譯階段匀们,告訴編譯器在代碼中使用了這么一個函數(shù)缴淋。只是告訴編譯器函數(shù)的原型,保證調(diào)用時參數(shù)類型和個數(shù)正確。
(3)頭文件的使用
在使用和定義函數(shù)的地方都要#include
頭文件重抖,#include
指令不一定要放在.c
文件的最前面露氮,但是通常習(xí)慣這樣做。
#include
指令分類
#include
指令有兩種形式:
(1)#include <>
:編譯器到指定目錄查找钟沛,主要用于查找標(biāo)準(zhǔn)庫的頭文件畔规。
(2)#include ""
:編譯器先到當(dāng)前目錄查找(.c文件所在目錄),如果沒有再到指定目錄查找讹剔。
#include
指令是一個編譯預(yù)處理指令,和宏一樣详民,在編譯之前就處理了延欠。它會把指定的文件原封不動的插入到它所在的地方。
(4)頭文件怎么寫
頭文件通常用來存放所有對外公開的函數(shù)的原型和全局變量的聲明沈跨。
通常任何.c文件都有對應(yīng)同名的.h文件
<1>聲明
- 常見的聲明
- 函數(shù)聲明
- 變量聲明
- 結(jié)構(gòu)體聲明
- 宏聲明
- 枚舉聲明
- 類型聲明
通常聲明只能可以放在頭文件中由捎,否則,編譯器連接會出現(xiàn)重名函數(shù)錯誤饿凛。
- 重復(fù)聲明
在一個編譯單元中狞玛,不允許重復(fù)聲明,尤其是結(jié)構(gòu)體聲明涧窒。為了防止頭文件不被多次#include導(dǎo)致重復(fù)聲明心肪。 -
定義與聲明
聲明是不產(chǎn)生代碼的語句。定義是產(chǎn)生代碼的語句纠吴。
int i;// 變量的定義,在.c文件中
extern int i; // 變量的聲明硬鞍,在.h文件中
<2>標(biāo)準(zhǔn)頭文件結(jié)構(gòu)
避免頭文件多次包含,必須使用標(biāo)準(zhǔn)頭文件結(jié)構(gòu)戴已。
#ifndef _文件名_H__
#define _文件名_H__
// 聲明
#endif
使用條件編譯和宏固该,保證頭文件在一個編譯單元中只會#include
一次。
#pragma once指令也起到相同作用糖儡,但是并不是所有編譯器支持伐坏。
-
分析
在cord.cpp 中第一次遇到coordin.h的文件的時候,它的內(nèi)容會被讀取握联,找到#ifndef COORDIN_H_桦沉,并且會給COORDIN_H_設(shè)定一個值,之后再次看到coordin.h頭文件時金闽,COORDIN_H_就已經(jīng)被定義了永部,coordin.h的內(nèi)容就不會再次被讀取了。
- 誤區(qū):
(1)#include
不是用來引入庫的呐矾,是告訴編譯器函數(shù)聲明苔埋,
(2)頭文件只有函數(shù)原型,函數(shù)實現(xiàn)在.a(Unix)或者.lib(Windows)中蜒犯。
(3)現(xiàn)代的C語言編譯器會默認(rèn)引入所有的標(biāo)準(zhǔn)庫组橄。
11. 變量的作用域和生存周期
(1)作用域
<1>作用域是什么?
在什么范圍內(nèi)可以訪問這個變量荞膘。
<2>作用域怎么用?
局部變量的作用域在變量定義的大括號以內(nèi)。
(2)生存周期
<1>生存周期是什么玉工?
變量什么時候出現(xiàn)到什么時候滅亡羽资。對于局部變量,生存期與作用域一致遵班。
<2>使用
不要返回局部變量的地址(注意是地址屠升,不是值),返回局部變量的值是可以的狭郑。
- 認(rèn)識
string& test_str(){
string str = "test";
return str;
}
int main(){
string& str_ref = test_str();
cout << str_ref << endl;
return 0;
}
A.編譯警告
B.返回局部變量的引用腹暖,運行時出現(xiàn)未知錯誤
C.正常編譯且運行
D.把代碼里的&都去掉之后,程序可以正常運行
- 分析:
ABD翰萨。
(1)在C語言中,局部變量是分配在椩啻穑空間上的, 當(dāng)函數(shù)調(diào)用結(jié)束后,由編譯器釋放.
(2)通過調(diào)用test_str得到了他的局部變量的內(nèi)存地址, 然而在main函數(shù)中調(diào)用函數(shù)時,這個內(nèi)存地址被”破壞”了亩鬼,類似于野指針殖告。在c語言中,一種典型的錯誤就是將一個指向局部變量的指針作為函數(shù)的返回值
(3) 如果返回指針(變量地址),應(yīng)該返回堆區(qū)或者全局區(qū)的地址(全局變量或者靜態(tài)變量)
(3)同名隱藏
在相同作用域中雳锋,同名變量會報錯黄绩;在不同的作用域中,內(nèi)部變量會隱藏外部變量玷过,
int main() {
int n = 1;
{
printf("n = %d\n",n);//1
int n=10;
printf("n = %d\n",n);//10
n = 20;
}
printf("n = %d\n",n);//1
}
12.變量分類
(1)本地變量/局部變量
<1>概念
在大括號內(nèi)定義的變量就是本地變量/局部變量宝与。
<2>特點
- 1.本地變量是定義在代碼塊內(nèi)的,可以定義在函數(shù)的塊內(nèi)冶匹,可以定義在語句的塊內(nèi)(for),可以定義在一個隨意的大括弧里面习劫。
- 2.程序進(jìn)入塊前,內(nèi)部變量不存在嚼隘,離開時失效诽里。
- 3.塊外定義的變量,塊內(nèi)仍然有效飞蛹。(反過來不行)
函數(shù)的每次運行谤狡,都會產(chǎn)生一個獨立的變量空間,在這個空間中的變量卧檐,是函數(shù)這次運行獨有的墓懂。
1.定義在函數(shù)內(nèi)部的變量就是本地變量;2.參數(shù)也是本地變量
<3>初始化
- 1.本地變量不會默認(rèn)初始化
- 2.參數(shù)在進(jìn)入函數(shù)時被初始化霉囚。
本地變量/局部變量的生存期和作用域都是在大括號內(nèi)捕仔。
(2)全局變量
<1>定義
定義在函數(shù)外面的變量稱為全局變量。
int n;//全局變量
int main(){
int m;//局部變量
}
<2>特點
全局變量有全局的生存周期和作用域。
- 1.不屬于任何函數(shù)榜跌。
- 2.所有函數(shù)內(nèi)部都可以使用闪唆。
<3>初始化
- 沒有初始化的全局變量會自動初始化為0。
- 只能用編譯時刻已知的值初始化全局變量钓葫。(只能用常量來初始化全局變量)
- 初始化發(fā)生在main()前悄蕾。
<4>同名隱藏
如果函數(shù)內(nèi)部存在與全局變量同名的變量,則全局變量被隱藏础浮。
全局變量有全局的生存周期和作用域帆调。
(3)局部靜態(tài)變量
<1>定義
在本地變量定義時加上static變成靜態(tài)本地變量。(只初始化1次)
<2>特點
當(dāng)函數(shù)離開時豆同,靜態(tài)局部變量會繼續(xù)存在并保存其值番刊。
int inc(){
static int n = 1;
n = n + 1;
return n;
}
int main(){
printf("%d\n",inc());//2
printf("%d\n",inc());//3
printf("%d\n",inc());//4
}
(1)靜態(tài)本地變量的初始化在第一次進(jìn)入函數(shù)時執(zhí)行,以后進(jìn)入函數(shù)會保持離開的值诱告。
(2)靜態(tài)本地變量是特殊的全局變量撵枢,具有全局生存周期和局部作用域民晒。
(4)全局靜態(tài)變量
<1>定義
在全局變量前加上關(guān)鍵字static精居。
<2>全局變量與全局靜態(tài)變量區(qū)別
- 1.若程序由一個源文件構(gòu)成時,全局變量與全局靜態(tài)變量沒有區(qū)別潜必。
- 2.若程序由多個源文件構(gòu)成時:
非靜態(tài)的全局變量的作用域是整個源程序靴姿,非靜態(tài)的全局變量在各個源文件中都是有效的,而靜態(tài)全局變量則限制了其作用域磁滚, 即只在定義該變量的源文件內(nèi)有效佛吓, 在同一源程序的其它源文件中不能使用它。 - 3.非靜態(tài)全局變量具有外部鏈接的靜態(tài)垂攘,可以在所有源文件里調(diào)用维雇,除了本文件,其他文件可以通過extern的方式引用晒他。
extern int a,b;//在.h文件中全局變量的聲明
(5)變量的作用域和生命期總結(jié)
- 作用域
變量或函數(shù)在運行時候的有效作用范圍 吱型。 -
生命期
變量或函數(shù)在運行時候的沒被銷毀回收的存活時間。
(6)static
關(guān)鍵字小結(jié)
static
在C語言里面既可以修飾變量陨仅,也可以修飾函數(shù)津滞。
<1>static
變量
- 靜態(tài)局部變量:在函數(shù)中定義的,生命周期是整個源程序灼伤,但是作用域和局部變量沒區(qū)別触徐。只能在定義這個變量的函數(shù)范圍內(nèi)使用,而且只在第一次進(jìn)入這個函數(shù)時候被初始化狐赡,之后的初始化會跳過撞鹉,并保留原來的值。退出這個函數(shù)后,盡管這個變量還在孔祸,但是已經(jīng)不能使用了隆敢。
- 靜態(tài)全局變量:全局變量本身就是靜態(tài)存儲的,但是靜態(tài)全局變量和非靜態(tài)全局變量又有區(qū)別:
1.全局變量:變量的作用域是整個源程序崔慧,其他源文件也可以使用拂蝎,生命周期整個源程序。
2.靜態(tài)全局變量:變量的作用域范圍被限制在當(dāng)前文件內(nèi)惶室,其他源文件不可使用温自,生命周期整個源程序。
靜態(tài)變量的生命周期是整個源程序皇钞,而且只能被初始化一次悼泌,之后的初始化會被忽略。(如果不初始化夹界,數(shù)值數(shù)據(jù)將被默認(rèn)初始化為0, 字符型數(shù)據(jù)默認(rèn)初始化為NULL)馆里。
<2>static
函數(shù)(內(nèi)部函數(shù))
只能被當(dāng)前文件內(nèi)的其他函數(shù)調(diào)用,不能被其他文件內(nèi)的函數(shù)調(diào)用可柿,主要是區(qū)別非靜態(tài)函數(shù)(外部函數(shù))鸠踪。
1.在函數(shù)前面加上
static
就使它成為只能所在編譯文件中使用的函數(shù)。
2.在全局變量前加上static
使它成為只能所在編譯文件中使用的全局變量
(7)實踐經(jīng)驗
<1>不要返回本地變量的指針
- 返回本地變量的地址是危險的复斥。
- 返回全局變量或靜態(tài)本地變量的地址是安全的营密。
- 返回函數(shù)內(nèi)的動態(tài)內(nèi)存是安全的,但注意要記得釋放目锭。
- 最好的做法是返回傳入的指針评汰。(常用)
<2>慎用靜態(tài)變量
- 不要使用全局變量在函數(shù)間傳遞參數(shù)和結(jié)果。
- 盡量避免使用全局變量痢虹。
- 使用全局變量和靜態(tài)本地變量的函數(shù)是不可重入的被去,是線程不安全的。
13.內(nèi)存
(1) 結(jié)構(gòu)體字節(jié)對齊
在C語言里奖唯,結(jié)構(gòu)體所占的內(nèi)存是連續(xù)的惨缆,但是各個成員之間的地址不一定是連續(xù)的。所以就出現(xiàn)了"字節(jié)對齊"臭埋。
字節(jié)對齊默認(rèn)原則
- 結(jié)構(gòu)體變量的大小踪央,一定是其最大的數(shù)據(jù)類型的大小的整數(shù)倍,如果某個數(shù)據(jù)類型大小不夠瓢阴,就填充字節(jié)畅蹂。
- 結(jié)構(gòu)體變量的地址,一定和其第一個成員的地址是相同的荣恐。
struct Box{
int height;
char a[10];
double width;
char type;
};
int main(void) {
struct Box box;
printf("box = %p\n", &box);
printf("box.height = %p\n", &box.height);
printf("box.a = %p\n", box.a);
printf("box.width = %p\n", &box.width);
printf("box.type = %p\n", &box.type);
printf("box = %ld\n", sizeof(box));
}
(2)內(nèi)存四區(qū)
<1>棧區(qū)(stack)
由編譯器自動分配和釋放液斜,主要是存放函數(shù)參數(shù)的值累贤,局部變量的值。
比如:int a; int *p; 這兒的a和p都存放在棧中
<2>堆區(qū)(heap)
由程序員自己申請分配和釋放少漆,需要malloc()臼膏、calloc()、realloc()
函數(shù)來申請示损,用free()函數(shù)來釋放如果不釋放渗磅,可能出現(xiàn)指針懸空/野指針。
函數(shù)不能返回指向棧區(qū)的指針检访,但是可以返回指向堆區(qū)的指針始鱼。
<3> 數(shù)據(jù)區(qū)(data)
** 數(shù)據(jù)區(qū)在程序結(jié)束后由操作系統(tǒng)釋放**
- 存放常量。
包含字符串常量和其他常量脆贵。 char *p = "I love u"; 指針p指向的這塊內(nèi)存屬于常量區(qū)医清。 - 存放全局變量和靜態(tài)變量。
初始化的全局變量和靜態(tài)變量在一塊區(qū)域(data段)卖氨;未初始化的全局變量和未初始化的靜態(tài)變量在相鄰的另一塊區(qū)域会烙,稱作BSS(Block Started by Symbol:以符號開始的塊);
<4>代碼區(qū)(code)
用于存放編譯后的可執(zhí)行代碼筒捺,二進(jìn)制碼柏腻,機器碼。
int a; //申請棧區(qū)內(nèi)存
a = 4; //指向的代碼焙矛,放在代碼區(qū)葫盼。
(3)堆和棧的區(qū)別(重要)
No. | 比較方面 | 棧 | 堆 |
---|---|---|---|
1 | 管理方式 | 由系統(tǒng)自動管理残腌,以執(zhí)行函數(shù)為單位 | 由程序員手動控制 |
2 | 空間大小 | 空間大小編譯時確定(參數(shù)+局部變量) | 具有全局性村斟,總體無大小限制。 |
3 | 分配方式 | 函數(shù)執(zhí)行抛猫,系統(tǒng)自動分配蟆盹;函數(shù)結(jié)束,系統(tǒng)立即自動回收闺金。 | 使用new/malloc()手動申請逾滥;使用delete/free()手動釋放 |
4 | 優(yōu)點 | 使用方便,不需要關(guān)心內(nèi)存申請釋放败匹。 | 可以跨函數(shù)使用寨昙。(可以返回指向堆區(qū)的指針) |
5 | 缺點 | 只能在函數(shù)內(nèi)部使用。 | 容易造成內(nèi)存泄露掀亩。 |
(4)顯示目標(biāo)文件區(qū)段大小
- size命令:
dec與hex是前面三個區(qū)域的和舔哪,dec是十進(jìn)制,hex是十六進(jìn)制槽棍。
- 各區(qū)段的含義
No. | 區(qū)段 | 名稱 | 含義 |
---|---|---|---|
1 | text | 代碼段(code segment/text segment) | 存放程序執(zhí)行代碼的內(nèi)存區(qū)域捉蚤。該區(qū)域的大小在運行前已確定抬驴,且通常屬于只讀±虑桑可能包含一些只讀的常數(shù)變量布持,例如字符串常量等。 |
2 | data | 數(shù)據(jù)段(data segment) | 存放程序中已初始化的全局變量的內(nèi)存區(qū)域陕悬。數(shù)據(jù)段屬于靜態(tài)內(nèi)存分配题暖。 |
3 | bss | BSS段(bss segment) | 存放程序中未初始化的全局變量的內(nèi)存區(qū)域。BSS是英文Block Started by Symbol的簡稱捉超。BSS段屬于靜態(tài)內(nèi)存分配芙委。 |
- 沒有顯示的區(qū)段
No. | 區(qū)段 | 含義 |
---|---|---|
1 | 棧(stack) | 存放程序臨時創(chuàng)建的局部變量,也就是函數(shù)括弧{}中定義的變量(不包括static聲明的變量)狂秦。在函數(shù)被調(diào)用時灌侣,參數(shù)也會被壓入發(fā)起調(diào)用的進(jìn)程棧中,并且待到調(diào)用結(jié)束后裂问,函數(shù)的返回值也會被存放回棧中侧啼。 |
2 | 堆(heap) | 存放程序動態(tài)分配的內(nèi)存段,大小并不固定堪簿,可動態(tài)擴(kuò)張或縮減痊乾。當(dāng)調(diào)用malloc()等函數(shù)分配內(nèi)存時,堆被擴(kuò)張椭更;當(dāng)調(diào)用free()等函數(shù)釋放內(nèi)存時哪审,堆被縮減。 |
14.二進(jìn)制
(1)位運算
位運算說穿了虑瀑,就是直接對整數(shù)在內(nèi)存中的二進(jìn)制位進(jìn)行操作.
<1>按位運算
No. | 操作符 | 功能 |
---|---|---|
1 | & | 按位與 |
2 | | | 按位或 |
3 | ~ | 按位取反 |
4 | ^ | 按位異或(相異為真) |
<2>運算規(guī)則
<3>按位與
1.讓某一位或某些位為0(清零)
int n = 0xFFFF;
n = n & 0x0010;//0x10
2.取一個數(shù)中某些指定位:
比如a=23湿滓,我想取a的二進(jìn)制的后面4位數(shù),那么可以找一個后4位是1其余位是0的數(shù)b舌狗,即b=0x0f(十六進(jìn)制叽奥,轉(zhuǎn)換為二進(jìn)制為00001111),a&b就得到了a的后四位痛侍。
a:00010111
b:00001111
a&b:00000111
3.保留指定位:
比如a=23(用8bit表示)朝氓,我想保留其二進(jìn)制的第4和第6位(最左邊為第1位),其余位置0主届。那么可以找一個第4和第6位是1其余位是0的數(shù)b與a進(jìn)行按位與運算.
a:00010111
b:00010100
a&b:00010100
4.應(yīng)用:
- 判斷某一位是否為1赵哲;
設(shè)置一個只有某一位是1的數(shù),其余為是0君丁,然后和判斷的數(shù)進(jìn)行位與枫夺。 - 判斷一個數(shù)是否是偶數(shù);
與1進(jìn)行位與谈截,如果其二進(jìn)制的最末尾是0表示偶數(shù)筷屡,為1表示奇數(shù)涧偷。
結(jié)論:任何二進(jìn)制位與0能實現(xiàn)置0;與1保持原值不變.
<4>按位或
1.讓某一位或某些位為1,其余位不變毙死。
int n = 0x0000;
n = n | 0x0010;
2.拼接兩個二進(jìn)制數(shù)燎潮。
int a = 0xab00;
int b = 0x0012;
int c = a|b;//0xab12
<5>按位取反
1,得到全部為1的數(shù)字~0
int n = ~0;// 等同于0xFFFF
2.使數(shù)字的部分清零x& ~7。
int n = 0xFFFF;
n = n & ~7;
<6>按位異或
1.兩個相等數(shù)異或結(jié)果為0扼倘。
int n = 0x1234;
n = n^n;
2.對同一個變量兩次異或相同值确封,變回原值。
int a = 0x1234;
int b = 0x1357;
a = a^b;//0x163
a = a^b;//0x1234
3.0和任何數(shù)字(或字符)異或都為任何數(shù)(或字符)
int n=21;
n = n^0;//21
4.應(yīng)用:
- 把一個數(shù)據(jù)的某些位翻轉(zhuǎn)再菊,即1變?yōu)?爪喘,0變?yōu)?
如要把a的奇數(shù)位翻轉(zhuǎn),可以對a和b進(jìn)行“按位異或”運算纠拔,其中b的奇數(shù)位置為1秉剑,偶數(shù)位置為0。 - 交換兩個值稠诲,不用臨時變量(***)
x ^= y;
y ^= x;
x ^= y;
- 加密解密
加密程序(a^b)侦鹏,解密程序是加密程序的逆過程,這里的加密和解密程序是完全相同的臀叙,原因是(a^b)^b=a略水。
5.例題
[136.只出現(xiàn)一次的數(shù)字]:
思路:每個數(shù)字全部異或,相同的會為0劝萤,直到最后一個數(shù)字渊涝。
[389.找不同]:
思路:字符也可以異或,相同字符異或為0.
邏輯運算與按位運算
1.邏輯運算結(jié)果只有0和1兩種值床嫌,按位運算有多種值跨释。
2.邏輯運算相當(dāng)于把所有的非零值都變成1,再按位運算既鞠。
(2)移位運算
在移動過程中相當(dāng)于操作二進(jìn)制數(shù)
No. | 操作符 | 功能 |
---|---|---|
1 | << | 左移 |
2 | >> | 右移 |
<1>左移
i<<j
表示i
中所有位向左移動j
個位置煤傍,右邊填入0
盖文。
左移一位相當(dāng)于乘以2嘱蛋,兩位乘以4。
1<<1;//2
1<<2;//4
1<<3;//8
<2>右移
i>>j
表示i
中所有位向右移動j
個位置五续,對于unsigned
類型洒敏,左邊填入0
;對于signed
類型,左邊填入符號位疙驾。
右移一位相當(dāng)于除以2凶伙,再取整。
14>>1;//7
14>>2;//3
14>>3;//1
14>>4;//0
1.應(yīng)用:
-
循環(huán)移位的實現(xiàn):
將一個無符號整數(shù)x的各位進(jìn)行循環(huán)左移n位的運算它碎,即把移出的高位填補在空出的低位處函荣。b=(a<<n) | (a>>(16-n)) 显押;
求x的絕對值
y = x >> 31 ;//二進(jìn)制最高位
return (x^y)-y ; //or: (x+y)^y
<3>位移運算與乘除運算
<4>綜合
求一個無符號數(shù)的二進(jìn)制中1的個數(shù)。
//就是判斷最低位是否為1(最右邊)
int numof1(int n){
int count=0;
while(n){
if(n & 1){
++count;
}
n = n >> 1;
}
return count;
}
(3)位域
<1>定義
位域是又稱作位段傻挂,是把一個字節(jié)中的二進(jìn)位劃分為幾個不同的區(qū)域乘碑。
<2>作用
節(jié)省空間,有些信息不需要占用一個完整的字節(jié)金拒。
<3>使用
- 1.定義位域
定義位域與結(jié)構(gòu)定義相仿兽肤。
struct 位域結(jié)構(gòu)名{
類型 位域名:位域長度;
};
為了保證位域的可以移植性,成員類型通常為unsigned int
和int
绪抛,C99可以使用bool
资铡。
示例:
struct Byte{
unsigned int b1:1;
unsigned int b2:1;
unsigned int b3:1;
unsigned int b4:1;
unsigned int b5:1;
unsigned int b6:1;
unsigned int b7:1;
unsigned int b8:1;
};
-
2.位域變量
定義和使用位域變量與結(jié)構(gòu)體相同。每個域有一個域名幢码,允許在程序中按域名進(jìn)行操作笤休。struct Byte a;
3.位域大小
整個結(jié)構(gòu)體的總大小為最寬基本類型成員大小的整數(shù)倍。