C語言知識點(上)

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)換成較大類型。

  • 整型:charshortintlonglong long
  • 浮點型:intfloatdoublelong 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)putcgetc

<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ū)

image.png

<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 intint绪抛,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ù)倍。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末症副,一起剝皮案震驚了整個濱河市宛官,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌瓦糕,老刑警劉巖底洗,帶你破解...
    沈念sama閱讀 218,122評論 6 505
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異咕娄,居然都是意外死亡亥揖,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,070評論 3 395
  • 文/潘曉璐 我一進(jìn)店門圣勒,熙熙樓的掌柜王于貴愁眉苦臉地迎上來费变,“玉大人,你說我怎么就攤上這事圣贸≈科纾” “怎么了?”我有些...
    開封第一講書人閱讀 164,491評論 0 354
  • 文/不壞的土叔 我叫張陵吁峻,是天一觀的道長滑负。 經(jīng)常有香客問我,道長用含,這世上最難降的妖魔是什么矮慕? 我笑而不...
    開封第一講書人閱讀 58,636評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮啄骇,結(jié)果婚禮上痴鳄,老公的妹妹穿的比我還像新娘。我一直安慰自己缸夹,他們只是感情好痪寻,可當(dāng)我...
    茶點故事閱讀 67,676評論 6 392
  • 文/花漫 我一把揭開白布螺句。 她就那樣靜靜地躺著,像睡著了一般橡类。 火紅的嫁衣襯著肌膚如雪壹蔓。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,541評論 1 305
  • 那天猫态,我揣著相機與錄音佣蓉,去河邊找鬼。 笑死亲雪,一個胖子當(dāng)著我的面吹牛勇凭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播义辕,決...
    沈念sama閱讀 40,292評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼虾标,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了灌砖?” 一聲冷哼從身側(cè)響起璧函,我...
    開封第一講書人閱讀 39,211評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎基显,沒想到半個月后蘸吓,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,655評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡撩幽,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,846評論 3 336
  • 正文 我和宋清朗相戀三年库继,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片窜醉。...
    茶點故事閱讀 39,965評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡宪萄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出榨惰,到底是詐尸還是另有隱情拜英,我是刑警寧澤,帶...
    沈念sama閱讀 35,684評論 5 347
  • 正文 年R本政府宣布琅催,位于F島的核電站居凶,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏恢暖。R本人自食惡果不足惜排监,卻給世界環(huán)境...
    茶點故事閱讀 41,295評論 3 329
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望杰捂。 院中可真熱鬧,春花似錦棋蚌、人聲如沸嫁佳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,894評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽蒿往。三九已至盛垦,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間瓤漏,已是汗流浹背腾夯。 一陣腳步聲響...
    開封第一講書人閱讀 33,012評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蔬充,地道東北人蝶俱。 一個月前我還...
    沈念sama閱讀 48,126評論 3 370
  • 正文 我出身青樓,卻偏偏與公主長得像饥漫,于是被迫代替她去往敵國和親榨呆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,914評論 2 355