主頁 > 後端開發 > C基礎筆記

C基礎筆記

2020-09-14 20:28:03 後端開發

C概述
    1,c特點:
        優點:代碼量小    速度快    功能強大
        缺點:危險性高    開發周期長    可移植性不強

    2,c的應用領域
        系統軟體開發:
            作業系統:Windows,Linux,Unix
            驅動程式:主板驅動,顯卡驅動,攝像頭驅動
            資料庫:DB2    Oracle    Sql Server
        應用領域:辦公(wps)    影像處理(ps)    嵌入式軟體開發(智能手機,掌上電腦)    游戲開發(2D,3D)
    3,C的32個關鍵字:auto    break    case    char    const    continue    default    do    double    else
            enum    extern    flaot    for    goto    if    int    long    register    return
            short    signed    sizeof    static    struct    switch    typedef    unsigned    union    void
            volatile    while


c編程預備知識
    1,cpu,記憶體條,硬碟,顯卡,主板,顯示幕主機間的關系
        作業系統將硬碟資料復制到記憶體條由cpu處理(cpu不能直接處理硬碟資料)處理后有聲,有影像則經過聲卡,顯卡顯示
    2,hello world運行原理
        程式通過編譯器編譯生成.exe檔案,編譯器請求作業系統呼叫cpu運行
    3,什么是資料型別
        基本資料型別
            整數
                整形    int    4
                短整型    short int    2
                長整型    long int    8
            浮點型
                單精度浮點數    float    4
                雙精度浮點數    double    8
            字符
                char    1
        復合資料型別
            結構體
            列舉
            共用體
    4,什么是變數
        變數的本質就是記憶體一段存盤空間
    5,變數為什么必須初始化
        初始化就是賦值
    6,如何定義變數
        資料型別    變數名    =        要賦的值;
        int    i   =3;    等價于    int   i;    i  =3  ;
        int    i=3,   j=5;        等價于    int   i;        int  j;   i=3;    j=5;
    7,什么是進制
        逢幾進幾個,一次累加,
        print用法:
            %d表示十進制輸出        //進制轉換都可通過十進制    例:(47)10  轉十六進制    47  /  16  =  2...15   就是2f,
            %x或%X表示十六進制輸出    //反之,十六轉十進制     2=16  f=15   2*16+15=47
            %0表示八進制輸出        //十進制轉八進制   47  /  8  =  5...7    就是57   反之  5*8+7=47
    8,常量在c中的表示
        整數:
            十進制:    傳統寫法
            八進制:    前面加0x或0X
            十六進制:前面加0
        浮點數:
            傳統寫法:
                float   x  =  3.3;    //x值為3200
            科學計數法:
                float   x=123.45e-2    //x值為1.2345
        字符
            單個字符單引號括起來    'a'
            字串雙引號括起來        "abc"
    9,常量以什么樣的二進制代碼存盤在計算機中
        整數:以補碼形式轉化為二進制代碼存盤于計算機中
        實數:以TEEE754標準轉化為二進制代碼存盤于計算機中
        字符:本質與整除存盤方式相同
    10,代碼規范
        參考林銳《高質量c/c++》
    11,什么是位元組
        存盤資料單位    應件所能訪問的最小單位
    12,不同資料型別之間的相互賦值問題
        大轉小   小轉大容易資料丟失
    13,基本輸入輸出函式
        print()輸出
            1,print("字串");
            2,print("輸出控制符",輸出引數);
            3,print("輸出控制符1  輸出控制符2.......",輸出引數1,輸出引數2......);
            4.print("輸出控制符   非輸出控制符",輸出引數);    //例:print("i = %d,j = %d" ,  i,  j);
                輸出控制符包含以下:
                    %d    %ld    %c    %f    %lf    %x或%X或%#X
            測驗%x  %X  %#x   %#X的用法
                #   include<stdio.h>
                    {
                        int    i  =  47;        47  /  16  =  2 ....15      15  =  f
                        print("%x\n",x);    //輸出結果:2f
                        print("%X\n",x);    //輸出結果:2F
                        print("%#x\n",x);    //輸出結果:0#2f
                        print("%#X\n",x);    //輸出結果:0#2F
                    }
            為什么需要輸出控制符
                1,01代碼可表示資料也可表示指令
                2,若01代碼表示資料,那么同樣的01組合以不同的輸出格式輸出就會有不同的輸出結果
        scanf()鍵入資料至變數
            兩種用法:
                scanf("輸入控制符",輸入引數);    //例:scanf("%d",&i);    //i表地址,&為取地址符
                                          print("i  =  %d\n",i);
                scanf("非輸入控制符,輸入控制符",輸入引數);    //scanf("m%d",&i);    //輸入前要加m,例:m123    
    14,運算子
        取余  %
        取余的運算物件必須是整數,結果是整數后的余數,其余數符號與輩出數相同,
        //例;
            print("%d %d %d %d %d %d\n",3%3,13%-3,-13%3,-13%-3,-13%-3,-13%23,3%5);
            return;
            結果為:0   1  -1  -1  -13  3
        邏輯運算子
            !    &&(且)    ||()或
            &&    同真為真    一假為假        左邊運算式為假,右邊運算式不執行
            ||    同假為假    一真為真        左邊運算式為真,右邊運算式不執行


    15,if 陳述句
        1,if的簡單用法:
            格式:
                if(運算式)
                    陳述句
            功能:運算式為真則執行,運算式為假則不執行,
        2,if的范圍:
            if(運算式)
                陳述句A;
                陳述句B;
            解釋:只能控制A,不能控制B;加上“{}”才可以全部控制,

    16,for()回圈寫100以內奇數平均值:
        #include<stdio.h>
            main(void){
                int   i,  sum  =  0,  int  =  count;
                float  avg;
                for(i = 0, i<101, i++){
                    if(i % 2 == 1){
                        sum+=i;        //奇數和
                        ++count;        //奇數個數
                        }
                    }
                avg = 1.0 * sum / count ;        //奇數平均值
                print("sum = %d\n", sum);
                print("count = %d\n", count);
                print("avg = %d\n", avg);
                }
        for()執行流程:
            多個for回圈的嵌套使用:
                for(1 ; 2 ; 3 )
                    for( 4 ; 5 ; 6 )    //先執行內回圈,內回圈完成后才執行外回圈,1245A632:執行流程,
                        A;    //屬于內回圈    內回圈5不滿足時A一直執行到滿足,滿足后回到外回圈,如果2不滿足時,內回圈則繼續執行;滿足則跳出到B執行,
                        B;    //不屬于內回圈
        例:斐波拉且數列
            #clude<stdio.h>
                main(void){
                    int n, f1 =2, f2 = 2, f3;
                    print("輸入數列:");
                    scanf("%d", &n);
                    if(1 == n){
                        f3 =1;
                    }else if(2== n){
                        f3 = 2;
                    }else{
                        f3 = f1 + f2;
                        f1 = f2;
                        f2 = f3;
                    }
                }
                    print("%d\n", f3);
                    return 0;
                
            }
        while()回圈:            與for()單回圈一樣,
            int 1;
            while(2){
                A;
                3;
                }
        例:判斷是否是回文,
        #include<stdio.h>
            main(void){    
            int  val, m, sum=0;
            m=val;
            while(m){
                sum=sum * 10 + m%10;
                m /=10;
            }
            if(sum == val){
                print("yes")
                }
            else{
                print("no")
                }
            return 0;
        }
    17 自增/自減
        自增:
            前自增    ++i
            后自增    i--
        異同:
            同:最終i都加1
            異:前自增整體運算式的值是i加1之后的值
                   后自增整體運算式的值是i加1之前的值

    18 do...while()求一元二次方程
        #include<stdio.h>
        #include<manth.h>
        int main(void)
        {
                double a, b, c;
                double delta;
                double x1, x2;
                char ch;
            do{
                
                print("請輸入一元二次方程的系數:")
                print("a=");
                scanf("%lf", &a);
                print("b=");
                scanf("%lf", &b);
                print("c=");
                scanf("%lf", &c);
                delta=b * b - 4 * a * c;
                if ( delta > 0)
                {
                    x1 = (- b +sqrt (delta)) / (2 * a);
                    x1 = (- b -sqrt (delta)) / (2 * a);
                    print("有兩個解,x1 = %lf, x2 = %lf\n", x1, x2);
                }else if (0==dalta){
                    x1 = x2 =(- b) / (2 * a);
                    print("有唯一解,x1 = x2 = %lf\n", x1, x2);
                }else{
                    print("有無實數解);
                }
            print("是否繼續<y/n>");
            scanf(" %c", &ch);    //%c前必須加空格,原因太復雜懶得說,自己百度,
            } while('y' == ch || 'Y' ==ch);
            return 0;
        }
    19 break和continue
        break:
            在多層switch嵌套中break只能終止距離它最近的switch;
            在多層回圈中break只能終止距離它最近的那個回圈;
            在回圈中break用于終止回圈;
            switch中break用于終止switch;
            break不能直接用于if,除非if屬于回圈內部字句;
        continue:
            用于跳過本次回圈余下的陳述句,轉去判斷是否需要執行下次回圈;
            例:
                for(1;2;3){
                    A;
                    B;
                        continue;        //執行完該陳述句后,會執行3,C和D被跳過不執行;
                    C;
                    D;
                }


陣列
    為什么需要陣列

    陣列的分類
        一維陣列
            解決同型別資料的存盤和使用    
            為n個變數連續分配存盤空間
            所有的變數資料型別必須相同
            所有變數所占位元組大小必須相同
            
            初始化
                完全初始化:    int  a[5] = {1 ,2 ,3 ,4 ,5};
                不完全初始化:         int  a[5] = {1 ,2 ,3};
                不初始化:    int  a[5];
                清零:        int  a[5] = {0};
                一維陣列名不代表陣列中的所有元素
                一維陣列名代表陣列第一個元素的地址    
        二維陣列
            int  a[ 3 ] [ 4 ];    看作三行四列
            int  a[ i ] [ j ];    表示第i+1行第j+1列;
            int  a[ m ] [ n ];    該二維陣列右下角位置的元素只能是 a [m-1] [n-1].
            
            二維陣列的輸出:
                int [2][3] = {
                    {1, 2, 3},
                    {4, 5, 6}
                };
                int i, j;
                for(i = 0; i < 2; ++i){
                    for(j = 0; j < 3; ++j)
                        print("%-5d",a[ i ] [ j ]);
                    print("\n")
                }
        多維陣列
            是否存在多維陣列        不存在,因為記憶體是線性一維的1
            n維陣列可以當作每個元素是n-1維陣列的一維陣列
            例;int a[ 3 ] [ 4 ] [ 5 ];
                該陣列是含有3個元素的唯一陣列,只不過每個元素都是4行5列的二維陣列;


函式
    為什么需要函式
        實作代碼復用
        有利于程式模塊化

    什么是函式
        邏輯上:完成特定功能的獨立代碼塊
        物理上:
            可以接收資料(也可以不接收)
            對接收的資料進行處理
            可以將資料處理的結果回傳(也可以不回傳)
        總結:函式是個工具,它是為了解決大量類似問題而設計的,    //就是Java中的方法,函式呼叫 == 方法呼叫
    
    定義函式
        函式的回傳值    函式名(函式的形參串列)
        {
            函式的執行體;
        }

        1,函式定義的本質是詳細描述函式之所以能夠實作某個特定功能的具體方法;
        2,函式回傳值的型別也稱函式的型別,因為如果,函式前的回傳值型別 和 函式執行體中的 return 運算式;中的型別不同的話
            則最終函式回傳值的型別 以函式名前的的回傳值型別為準;
        例:
            int f()
            {
            return  10.5;    //因為函式的回傳值型別是 int,所以f回傳的是10而不是10.5;
            }
        3,return 運算式;的含義:
            1,終止被調函式,向主函式回傳運算式的值;
            2,如果運算式為空,則只終止函式,不向主調函式回傳任何值;
            3,break是用來終止回圈和switch的,return是用來終止函式的;
            例:
                void f(){
                    return;    //終止被調函式,不向主函式回傳值;
                }
                void  f(){
                    return 10;        //終止函式的,向主函式回傳10;
                }

    函式分類
        有參函式 和 無參函式
        有回傳值函式 和 無回傳值函式
        庫函式 和 用戶自定義函式
        值傳遞函式 和 地址傳遞函式
        普通函式 和 主函式(mian函式)
            一個程式有且只能有一個主函式;
            主函式可以呼叫其他函式,而其它函式不能呼叫主方法;
            普通函式可以相互呼叫;
            主函式是程式的入口,也是程式的出口;
        例:求素數
            #include<stdio.h>
            bool IsPrime(int val){
                for(int i = 2; i < val; ++i){
                    if( val % i == 0){    //  輸入值對i能夠整除說明不是素數,跳出,
                        break;
                    }else if (i == val){    //i是從2開始的,i++到與輸入值相等說明,在i++中沒有被整除,所以是素數;
                        return true;    //判斷出 是 回傳ture 否 false
                    }else{
                        return  flase;
                    }
                }
            }
            int main( int m) {
                int m;
                scanf("%d", &m);
                if (IsPrime(m)){
                    print("yes");
                }else{
                    print("no")
                }
            }
    
        宣告函式注意事項
            如果函式呼叫寫在了函式定義前面,則必須加函式前置宣告;
            函式前置宣告:
                1,告訴編譯器即將出現的若干字母代表的是一個函式;
                2,告訴編譯器即將出現的若干字母代表的函式的形參和回傳值的具體情況;
                3,函式宣告是一個陳述句,末尾必須加分號;
                4,對庫函式的宣告是通過 # include<庫函式所在的檔案的名字.h>來實作的;
                例:void f(int);    //帶參函式        void f(void);    //無參函式
            形參和實參
                個數相同    位置一一對應    資料型別必須相互兼容

    合理設計函式思維
        模塊化編程,一個函式的功能盡量單一;
        常用系統函式
            double scrt (double x);    //求x的平方根
            int abs(int x);    //求x的絕對值
            double fabs(double x);    //求x的平方值
        推薦書籍:《TurBoc2.0》實用大全  ==JavaAPI

        變數的作用域和存盤方式
            按作用分:
                全域變數
                    在所有函式外部定義的變數叫全域變數
                    使用范圍:從定義位置開始到整個程式結束
                區域變數
                    在一個函式內部定義的變數或者函式的形參    都統稱為區域變數
                    使用范圍:本函式內部
                    區域函式和全域函式命名沖突
                        函式內部命名變數與全域變數重名時,區域變數會屏蔽全域變數,
            按變數的存盤方式分:
                靜態變數
                自動變數
                暫存器變數

        例1,統計鍵盤輸入數字內有多少是素數,主函式運算:        //代碼復用性不高;不夠簡單明了;
            int main(void){
                int val, i, j;
                scanf("%d\n",&val);
                for( i = 2; i < = val; ++i){        //從2---輸入數的每個數挨個進行判斷是否是素數
                    for(j = 2; j < i; ++j){        //對i++的每一個值進行判斷是否是素數
                        if( 0 == i%j){
                            break;
                        }
                        if(j == i){
                            print("%d\n",i)
                        }
                    }
                }
                return 0;
            }

        例2,統計鍵盤輸入數字內有多少是素數, 函式呼叫來寫;    //代碼復用性提高,但若求多個數字中有哪些素數就不行了;
        bool IsPrime( int m){
            int i;
            for( i =2; i <= m; ++i){
                if(0 == m % i)
                    break;
                }    
            if(m == i)
                return true;
            else
                return false;    
        }
        int main(void){
            int val, i, j;
            for( i = 2; i <= val; ++i){
                if( IsPrime(i) )
                print("%d\n", i );
            }
        return 0;
        }
    
        例3,求多個數字中有哪些素數;
        bool IsPrime( int m){        //判斷是否是素數,是 true     否 false
            int i;
            for( i =2; i <= m; ++i){
                if(0 == m % i)
                    break;
                }    
            if(m == i)
                return true;
            else
                return false;    
        }
        void TraverseVal ( int n){    //輸出1-n之間的素數
            int i;
            for( i = 2; i < n; ++i){
                if( IsPrime(i) )
                    print("%d\n", i);
            }
        }
        int main(void){
            int val;
            char ch;
            do{
                TraverseVal (val);
                print("是否需要繼續輸入:");
                scanf("  %ch\n", &ch);    
            }whie( ' y ' == ch || ' Y ' ==ch );
            return 0;
        }



指標
    指標熱身例子
        int main(void){
            int *  p;        //int *  表示p變數存放的是int 型別的地址,*p 是int型別
            double *  p;    //同上
                    //理解:p是變數名,p變數的資料型別是 int * 型別;int * 型別就是存放int 變數地址的型別
            int    i = 3;
            int  j;

            p = & i;        /*
                        1,p保存了 i 的地址,因此 p 指向 i ;
                        2,p不是 i , i 也不是 p ;雙方修改了值也互不影響;
                        3,如果一個指標變數指向了某個普通變數,則 *指標變數   就等同于  普通變數;
                        例:
                            如果 p 是個指標變數,并且存放了普通變數 i 的地址    則 p 指向了普通變數;
                            *p 就等同于    i
                            或者說:在所有出現 *p 的地方都可以替換成 i;
                    */
            j = *p;            //  *p  就是以 p 的內容為地址的變數;
            print(" i = %d, j = %d\n", i, j );        //結果:i = 3, j = 3;
        }
    
    指標的重要性:
        表示一些復雜的資料結構
        快速的傳遞資料
        使函式回傳一個以上的值
        能直接訪問硬體
        能夠方便處理字串
        是理解面向物件語言中參考的基礎
        C語言的靈魂

    指標的定義
        地址
            記憶體單元編號        
            從零開始的非負整數
            范圍:4G            //cpu到記憶體條有三根總線,分別是控制,資料,地址;cpu連接記憶體條的線是地址線,一根線控制兩個單元,0  1;地址總線有多少就是2的多少次方;
                        一般電腦32位,就是2的32次方,能控制2的32次方單元,能存盤2的32次方位元組,一個單元8位,2的32次方*8就是能控制的位;
                        1k = 2的10次方b;1m = 2的10次方kb = 2的20次方b;1g = 2的10次方mb = 2的30次方b;
                        那么記憶體為:2的32次方 = 2的30次方 + 2的2次方 = 4G;
        指標
             本質是操作受限的非負整數
             指標就是地址,地址就是指標;
                     地址是記憶體單元的編號;
                     指標變數是存放地址的變數;
                    指標和指標變數是兩個不同的概念;
        注意:通常我們敘述是會把指標變數簡稱為指標,實際兩者含義不同;
        
    指標的分類
        1,基本型別指標————————重點!重點!重點!

            例:兩個值互換
            1,函式呼叫錯誤案例:
                #include<>stdio.h
                void swap_1(int i, int j){
                    int t;
                    t = i; i = j;  j = t;
                }
                int main (void){
                    int a = 3;
                    int b = 5;
                    swap_1(a, b );
                    print("a = %d, b = %d\n", a, b);
                    return 0 ;
                }
                輸出結果為:3    5
            原因:首先主函式a,b是賦值了的,a,b 是實參,i,j是形參,是四個變數,就算把 i , j 改為 a , b 也還是四個變數(地址不一樣),
                     形參互換跟實參沒有關系,
            執行流程:主函式開始,檢測到swap_1(int , int );去執行,先是給所有變數分配空間( i, j, t分配靜態空間),將a, b 實參值傳入形參 i , j,
                形參進行互換, 互換結束后子函式釋放空間,主函式運行到print("...");時就只剩下實參 a, b(子函式空間釋放了,沒了) ,原樣輸出,3    5;
                
            2,地址錯誤案例:
                #include<>stdio.h
                void huhuan(int * p, int * q){    //接受實參資料的是 p, q ,而不是 *p, *q ,因為變數名為p,q
                    int * t;        //同型別才可當互換的第三方變數
                    t = p;        //這里換的是 p, q ,是地址,跟a , b沒關系,換的不是值,
                    p = q;        //* p , *q 才是a, b 的值,要換* p , *q 才能實作   3   5  的互換,
                    q = t;
                }
                int main(void){
                    int a = 3, b = 5;
                    huhuan(&a, &b);    //這里必須是 &a, &b, 而不是 a, b  因為 a, b 是int 型別,p,q是int * 型別是地址,所以要用取地址符&a, &b,
                    print("a = %d, b = %d\n", a, b);
                    return 0 ;
                }
                輸出還是    3    5    解答如注釋

            3,正確指標案例:
                #include<>stdio.h
                void huhuan(int *, int *);    //引數可以不寫
                void huhuan(int * p, int * q){    //  p是存放地址的變數,就是放的地址;*p是以p(p地址里面的值)的內容為地址的變數,也就是說*p存放是p地址的值;
                    int t;    //  注意:p是int *型別, *p是int 型別;要互換*p,t (第三存盤變數)就必須是int 型別
                    t = *p;        //p->&a; q->&b;    *p = a;  *q = b;    互換的是a,b;所以這里是*p和*q的互換;
                    *p = *q;
                    *q = t;
                }
                int main(void){
                    int a = 3, b = 5;
                    huhuan(&a, &b);
                    print("a = %d, b = %d\n", a, b);
                    return 0 ;
                }

            關于函式和指標的見解(彈幕友軍很nice)
                函式作用:因為子函式并不是把主函式的值調過來,而是相當于復制了一份主函式的值進行運算最后回傳結果,
                    因此子函式復制來的值和主函式的值并不是同一個值,在子函式復制來的值進行互換就不會影響到主函式值(本體);
                指標作用:而指標在子函式中呼叫主函式的值(通過地址找到主函式本值)就是將主函式本值拿過來放在子函式中進行互換,
                    int * 型別變數p——>&a,就是變數p存盤了a的地址及值,但是p只能表示地址不可表示a的值,
                    而*p就是以 int * p型別的內容(內容:地址的存放的值,例如p->&a,那么*p==a)為地址的變數
                疑問:函式篇章時,判斷素數也有函式呼叫,我疑問也是子函式呼叫了主函式變數,但是回傳結果是正確的,按照上述不應該子函式運算后釋放空間了,主函式也拿不到結果嗎,
                    解答:主函式定義的變數是沒有賦值的,所以不存在兩個不同的變數值,

            附注:
                * 的含義
                    1,乘法
                    2,定義指標變數
                        int  *  p ;    //定義了名為 p 的指標變數,int *  表示p只能存放一個int 變數的地址;        2,指標和陣列
                    3,指標運算子
                        該運算子放在已經定義好的指標變數的前面
                        如果p是一個已經定義好的指標變數
                        則 *p 表示   以p的內容(內容:地址的存放的值,例如p->&a,那么*p==a) 為地址的變數
            如何通過被調函式修改主函式普通變數的值
                1,實參必須為該普通變數的地址
                2,形參必須為指標變數
                3,在被調函式中通過
                    *  形參名  =  ......
                     的方式來修改主函式相關變數的值
        2,指標和陣列
            1,指標和一維陣列
                一維陣列名:一維陣列名是一個指標常量,它存放的是一維陣列第一個元素的地址;
            下表和指標的關系
                如果p是個指標變數,則 p[ i ] 永遠等價于  *(p+i)
            確定一個一維陣列需要幾個引數
                兩個:陣列第一個元素的地址
                          陣列長度
            例:
                #include<stdio.h>
                void f (int * pArr , int len ){
                    pArr[ 3 ] = 88;            //通過指標進行修改陣列值
                //    for(int i ;i < len ; ++i)
                //        print("%d\n", pArr[ 3 ]);    //pArr[ 3 ] == * pArr[ i + 1];遍歷輸出陣列值
                }
                int main(void){
                    int a[ 6 ] = {1, 2, 3, 4, 5, 6};
                    print("%d\n",a[ 3 ]);
                    f( a, 6);            //a本身就是一個陣列地址,a == a[ i ];a發給 pArr 這兩者是一樣的(地址和內容)
                                   a[ i ]  == * a[ i + i]  == pArr[ i ] ==* pArr[ i +1 ]
                    print("%d\n", a[ 3 ]);
                    return 0 ;
                }
            指標變數的運算
                指標變數不能  相加 相乘  相除
                如果兩個指標變數指向的是同一塊連續空間的不同存盤單元,則這兩個指標變數才可以相減
                例:
                    int main(void){
                        int i = 5;
                        int j = 10;
                        int * p = &i, *q = &j;
                    //    q - p;            //無意義,無法相減,不在同一個連續的存盤空間
                        int a [ 5 ];
                        p = &a [ 1 ];
                        q = &a [ 4 ];
                        print("p和q所指向的單元相隔%d個單元\n", q - p);
                        return 0 ;
                    }
                輸出為    3    個單元

        3,指標和函式

        4,指標和結構體

        5,多級指標
            int i = 10;
            int * p = &i;
            int ** q = &p;
            int *** r = &q:
            int **型別是存盤 int *地址的型別,int ***型別是存盤 int **地址的型別,以此推論,
            例1:靜態變數不能跨函式使用
                void f (int ** q){
                    int i = 5;
                    //*q 等價于p  p和**q都不等價于p    因為**q存放*p的地址,*q==p
                    *q = &i;        //p = &i
                }
                
                int main(void){
                    int * p ;            
                    f(&p);            //p是int * 型別 ,&p是int **
                    print("%d\n",*p);        //語法正確,邏輯有問題;
                    return 0 ;
                }
                輸出    5
                f函式終止后,為f分配的所有靜態變數會全部釋放(i,q是靜態分配),p指向f函式中的i變數就已經不存在了(或者說i的訪問權限已經回傳給作業系統了,不能被使用),p沒有權限訪問i的記憶體空間,所以錯了;    
    
            例2:動態記憶體可以跨函式使用
                void f (int ** q){
                    *q / p(表示這里p也可以) = (int *)malloc(sizeof(int));    //sizeof回傳該資料型別所占字符,p =*q
                //    q = 5;    //error 這個p是地址不可以賦值
                //    *q = 5;    //error    *q =p
                    **q = 5;    //true      **q =*p   
                }
                int main (void){
                    int * p;
                    f(&p);
                    printf("%d\n",*p);        //*p 這里沒有問題,*p指向的是上面動態分配的,動態分配是在堆里分配的,堆里分配的函式終止時記憶體不會被釋放;
                                //函式終止本質是出堆疊,靜態空間是在堆疊分配的,出堆疊空間就沒了,f函式也沒有free(q),所以f函式終止后*q仍然屬于本程式,*p就可以訪問;
                    return 0;
                }

    專題:動態記憶體分配
        傳統陣列的缺點:
            1,陣列長度必須事先制定,且只能是整數,不能是變數;
                例:
                    int a [ 5 ];        //ok
                    int len = 5;  int a [ len ];    //error

            2,傳統形式定義的陣列,該陣列的記憶體程式員無法手動釋放,在一個函式運行期間,系統為該函式中的陣列所分配的空間
                 會一直存在,直到該函式運行完畢時,陣列空間才會釋放;
                 
            3,陣列的長度一旦定義,其長度就不能再更改,陣列的長度不能再函式運行的程序中動態的擴充或縮小;

            4,A函式定義的陣列,在A函式運行期間可以被其他函式使用,但A函式運行完畢后(函式運行完畢,空間釋放,沒東西了),
                 A函式中的陣列將無法再被其他函式使用;
            傳統方式定義的陣列不能跨函式使用;

        為什么要動態分配記憶體
            動態陣列解決了以上四個問題;
            傳統陣列也叫靜態陣列;

        動態記憶體和靜態記憶體的比較
            靜態記憶體是由系統自動分配,由系統自動釋放
            靜態記憶體是堆疊分配

            動態記憶體是由程式員手動分配,手動釋放
            動態記憶體是堆分配的

        動態記憶體分配舉例——動態一陣列的構造    malloc是 memory(記憶體)allocate(分配)
            例1:
                #include<stdio.h>
                #include<malloc.h>
                int main(void){
                    int i = 5;
                    int * p = (int * )malloc(4);        /*
                                        1,使用malloc函式,必須加 malloc.h 頭檔案
                                        2,malloc函式只有一個形參,且引數是整形
                                        3,4表示請求系統為本程式分配4個位元組
                                        4,malloc函式只能回傳首位元組地址,但要加上型別強制轉換,表示首位元組地址的型別;
                                        5,p本身所占的記憶體是靜態分配的,p所指向的記憶體是動態分配的;
                                    */
                    *p = 5;                //*p也是代表int變數,只是跟上面的 i 變數的記憶體分配方式不一樣
                    free(p);                //free(p)表示把p所指向的動態記憶體給釋放掉,p本身記憶體是靜態,程式員無法釋放,只能有函式終止時系統釋放;
                }
        
            例2:
                #include<stdio.h>
                #include<malloc.h>
                int main(void){
                    int a[ 5 ];        //int 4個位元組,本陣列20個位元組
                    int len;
                    int * pArr;
                    int i;
        
                    //動態的構造一維陣列
                    printf("請輸入要輸入的元素個數");
                    scanf("%d", &len);
                    pArr = (int *) malloc (4 * len);        //動態陣列,其格式用法與上靜態陣列一致,長度為len

                    //動態陣列賦值
                    for(i = 0; i < len; ++i)
                        scanf("%d",&pArr[ i ]);
                    
                    //陣列輸出
                    fo(i = 0; i <len; ++i)
                        print("%d", &pArr[ i ]);
                    return 0;
                }



結構體
    為什么需要結構體
        為表示一些復雜的事物,而普通的基本型別無法滿足實際需求;
    什么叫結構體
        把一些基本資料型別組合在一起的一個新的復合資料型別,叫結構體;
    如何讓定義結構體
        struct Student{                struct Student2{                      struct {    
            int id;                    int id;                    int id;
            char name;                char name;                char name;
            char pwd;                    char pwd;                    char pwd;
        };    定義資料型別而非定義變數;        }st2;                           }st3;
        推薦第一種,就類似Java的物體類,這里是結構體變數
    怎么使用結構體變數
        賦值和初始化
            定義的同時可以整體賦初值:        struct  Student st={1,"張三","123"};    //類似于Java的new物件;
            如果定義完后,則只能單個賦初值    struct  Student st2;    st2.id = 1;   st2.name = "張三";  st2.pwd  = "123"
        如何取出結構體變數中的每一個成員
            1,結構體變數名.成員名
            2,指標變數名—>成員名    在計算機會被轉化成(*指標變數名). 成員名  的方式來執行(常用)
                例:
                    struct Student{    
                        int aage;                
                        float score;            
                        char sex;                
                    };
                    int main(void){
                        struct  Student st={10,66.6,'F'};    //初始化的同時定義賦值
                        struct  Student  * pst = &st;        
                        
                        pst -> age =88;    //(常用)
                        st . score = 66.6f;    //66.6在C中默認double型別,如果希望一個實數是float型別,則必須在末尾加一個f或F,因此66.6是double,66.6f或66.6F是float型別;
                        printf("%d %f\n",st .age, pst ->score);
                        return 0;
                    };
                    pst ->sge 在計算機內部會轉化成  ( *pst ).age;    //pst => age  ==  (*pst) .age   ==   st.sge;
        通過函式完成對結構體變數的輸入和輸出
            #include<stdio.h>
            #include<string.h>
            void IputStudent(struct Student stu);
            void OutputStudent(struct Student ss);
            struct Student{
                int age;
                char sex;
                chae name[100];
            };
            int main(void){
                struct Student st;

                InputStudent(&st);        //結構體變數輸入    有修改,必須發送st的地址
                printf("%d %c %s\n",st . age, st . sex, st . name);
                OutputStudent(st);        //結構體變數的輸出    可以發送st的地址,也可以發送st的內容,但是用指標可以減少記憶體耗費,提高執行速度;推薦指標
                return 0;
            }
            void OutputStudent(struct Student ss){        //輸出不修改,ss和st是一樣的,這里的可以不用指標
                printf("%d %c %s\n",ss . age, ss . sex, ss . name);
            }
            void IputStudent(struct Student *pstu){        //指標變數無論它指向的地址占幾個位元組,但它本身只占4個(存放首位元組地址4個),一個變數不管多大但是只用他的首位元組地址表示
                                //什么樣的資料型別意味著 (*指標變數) 指向這個變數占多少位元組
                                // 例:double *p, x = 66.6;    p = &x;    p就指向x變數占8個位元組;以此推理,現在的 Student *pstu 型別就是指向的這個占105個位元組,int *p 指向的4個;
                    (*pstu).age = 10;
                    strcpy(pstu ->name, "張三");
                    pstu ->sex = 'F';
                };
            輸出:10    F    張三
            /*
            *    //此函式無法修改stu,的值,原因是之前的函式呼叫問題;
            *    void IputStudent(struct Student stu){        //資料變數名定義的,非malloc動態分配的,所以是靜態的
            *        stu.age = 10;
            *        strcpy(stu.name, ""張三);    //表示將“張三”拷貝到name字符陣列里;而stu.name = "張三" ;    error
            *        stu.sex = 'F';
            *    };
            *    原因:這個函式的變stu與上面的st是兩個變數,函式終止后會將給此函式分配的靜態變數都釋放掉,所以這里的stu進行操作對st無影響;
            */
        結構體變數的運算
            結構體變數不能先相加,相減,相互乘除
            可以相互賦值
        結構體變數和結構體指標變數作為函式引數傳遞的問題
            推薦結構體指標變數作為函式引數的傳遞
        冒泡排序
            void sort (int * a, int len) {
                int i , j , t ;
                for( i = 0; i < len -1; ++i){
                    for( j = 0; j < len-1-i; ++j){
                        if(a[ j ] > a[ j+1]){
                            t = a[ j ];
                            a[ j ] = a[ j+1 ];
                            a[ a+1 ] = t;
                        }
                    }
                }
            }
            int main(void){
                int a[ 6 ] = {3,4,1,8,6,2};
                int i;
                sort(a, 6);
                for(i = 0; i< 6; ++i){
                    printf("%d",a[ i ]);
                }
                printf("\n");
                return 0;
            }
        舉例:
            動態構造存放學生資訊的結構體陣列
                #include<stdio.h>
                #include<malloc.h>        
                struct Student{
                    int age;
                    float score;
                    char name[100];
                }
                int main(void){
                    int len;
                    struct Student * pArr;
                    int i;
                    //構建一維動態陣列
                    printf("輸入學生個數:\n");
                    printf("len = ");
                    scanf("%d", &len );
                    pArr = (struct Student *)malloc (len * size(struct Student ));

                    //輸入
                    for( i = 0;i < len;++i){
                        printf("輸入第%d個學生",i+1);
                        printf("age = ");
                        scanf("%d", &pArr[i].age);

                        printf("name = ");
                        scanf("%d", pArr[i].name);    //本身就是陣列,name就已經是地址了,所以不用加&

                        printf("score = ");
                        scanf("%d", &pArr[i].score);
                    }

                    //根據成績排序
                    for(i = 0;i< len-1;++i){        
                        for( j = 0;j <len - 1 - i; ++j){
                            if(pArr[ j ].score < pArr[j+1].score){    //降序   >  升序
                                t = pArr[ j ];
                                pArr[ j ] = pArr[ j + 1];
                                pArr[ j+ 1] = t;
                            }
                        }
                    }
                    //輸出
                    for( i = 0;i < len;++i){
                        printf("輸入第%d個學生",i+1);
                        printf("age =%d \n", pArr[i].age);
                        printf("name =%s \n", pArr[i].name);
                        printf("score =%f \n", pArr[i].score);
                    }
                    return 0;
                }        


補碼
    原碼
        也叫 符號-絕對值
        最高位0表示正  1表示負  ,其余二進制是該數字的絕對值二進制位

        簡單易懂,加減復雜,存在加減乘除,增加了cpu復雜度,0的表示不唯一

    反碼
        反碼運算不便也沒有在計算機中應用

    移碼
        表示數值平均平移n位,n稱為移碼量
        主要用于浮點數的階碼的存盤

    補碼
        十進制轉二進制
            正整數二進制
                除2取余,直至商為零,余數倒排
            負整數而今制
                先求與該負整數相對應的正整數的二進制碼,然后所有位取反,末尾加一,不夠位數時,左邊補1(補都少個取決于什么型別)
                例如:-3        正整數二進制碼:011  所有位取反:100    末尾加一:101    不夠位左加一(int 型別32位):左補29個1(省略29個1) 101
            0轉二進制
                全是0

        二進制轉十進制
            如果首位是0,則表示整數,按普通方法求;例 (0110)2

            如果首位是1,則表示負整數
                所有位取反,末尾加一,所得數字就是該負數的絕對值
                注意:這里的所有位必須補滿才能取反,例如:int 型別 1011取反,就要補全前面的29個1,(省略28個1),因為不補滿取反的話,系統會自動給你補滿,但是補的不是1,是0,那就成正整數了,結果就錯了;
                
鏈表
    陣列和鏈表的優劣
        陣列:
            優點:存取速度快
            缺點:需要一個連續的很大的記憶體;插入和洗掉元素效率低;
        鏈表:
            優點:
                插入洗掉元素效率高
                不需要一個連續的很大的記憶體            
            缺點:查找某位置元素效率低

鏈表很難,首先要學好指標,這是屬于資料結構的內容,當時還沒學好指標呢(特么的老師講沒講我都不知道),學的稀里糊涂的,
但是,世上無難事,只要肯放棄;所以鏈表不弄了,c基礎完結了就,后來也學過資料結構,堆疊,隊,鏈表,二叉樹等等,但是忘了,難得看了頭疼,
https://www.bilibili.com/video/av12907870        //資料結構教程鏈接
https://www.bilibili.com/video/BV1os411h77o?p=180    //c基礎教程鏈接































轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/38539.html

標籤:C

上一篇:(52)指標 (53)陣列的增刪改查

下一篇:C語言 俄羅斯方塊demo

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 【C++】Microsoft C++、C 和匯編程式檔案

    ......

    uj5u.com 2020-09-10 00:57:23 more
  • 例外宣告

    相比于斷言適用于排除邏輯上不可能存在的狀態,例外通常是用于邏輯上可能發生的錯誤。 例外宣告 Item 1:當函式不可能拋出例外或不能接受拋出例外時,使用noexcept 理由 如果不打算拋出例外的話,程式就會認為無法處理這種錯誤,并且應當盡早終止,如此可以有效地阻止例外的傳播與擴散。 示例 //不可 ......

    uj5u.com 2020-09-10 00:57:27 more
  • Codeforces 1400E Clear the Multiset(貪心 + 分治)

    鏈接:https://codeforces.com/problemset/problem/1400/E 來源:Codeforces 思路:給你一個陣列,現在你可以進行兩種操作,操作1:將一段沒有 0 的區間進行減一的操作,操作2:將 i 位置上的元素歸零。最終問:將這個陣列的全部元素歸零后操作的最少 ......

    uj5u.com 2020-09-10 00:57:30 more
  • UVA11610 【Reverse Prime】

    本人看到此題沒有翻譯,就附帶了一個自己的翻譯版本 思考 這一題,它的第一個要求是找出所有 $7$ 位反向質數及其質因數的個數。 我們應該需要質數篩篩選1~$10^{7}$的所有數,這里就不慢慢介紹了。但是,重讀題,我們突然發現反向質數都是 $7$ 位,而將它反過來后的數字卻是 $6$ 位數,這就說明 ......

    uj5u.com 2020-09-10 00:57:36 more
  • 統計區間素數數量

    1 #pragma GCC optimize(2) 2 #include <bits/stdc++.h> 3 using namespace std; 4 bool isprime[1000000010]; 5 vector<int> prime; 6 inline int getlist(int ......

    uj5u.com 2020-09-10 00:57:47 more
  • C/C++編程筆記:C++中的 const 變數詳解,教你正確認識const用法

    1、C中的const 1、區域const變數存放在堆疊區中,會分配記憶體(也就是說可以通過地址間接修改變數的值)。測驗代碼如下: 運行結果: 2、全域const變數存放在只讀資料段(不能通過地址修改,會發生寫入錯誤), 默認為外部聯編,可以給其他源檔案使用(需要用extern關鍵字修飾) 運行結果: ......

    uj5u.com 2020-09-10 00:58:04 more
  • 【C++犯錯記錄】VS2019 MFC添加資源不懂如何修改資源宏ID

    1. 首先在資源視圖中,添加資源 2. 點擊新添加的資源,復制自動生成的ID 3. 在解決方案資源管理器中找到Resource.h檔案,編輯,使用整個專案搜索和替換的方式快速替換 宏宣告 4. Ctrl+Shift+F 全域搜索,點擊查找全部,然后逐個替換 5. 為什么使用搜索替換而不使用屬性視窗直 ......

    uj5u.com 2020-09-10 00:59:11 more
  • 【C++犯錯記錄】VS2019 MFC不懂的批量添加資源

    1. 打開資源頭檔案Resource.h,在其中預先定義好宏 ID(不清楚其實ID值應該設定多少,可以先新建一個相同的資源項,再在這個資源的ID值的基礎上遞增即可) 2. 在資源視圖中選中專案資源,按F7編輯資源檔案,按 ID 型別 相對路徑的形式添加 資源。(別忘了先把檔案拷貝到專案中的res檔案 ......

    uj5u.com 2020-09-10 01:00:19 more
  • C/C++編程筆記:關于C++的參考型別,專供新手入門使用

    今天要講的是C++中我最喜歡的一個用法——參考,也叫別名。 參考就是給一個變數名取一個變數名,方便我們間接地使用這個變數。我們可以給一個變數創建N個參考,這N + 1個變數共享了同一塊記憶體區域。(參考型別的變數會占用記憶體空間,占用的記憶體空間的大小和指標型別的大小是相同的。雖然參考是一個物件的別名,但 ......

    uj5u.com 2020-09-10 01:00:22 more
  • 【C/C++編程筆記】從頭開始學習C ++:初學者完整指南

    眾所周知,C ++的學習曲線陡峭,但是花時間學習這種語言將為您的職業帶來奇跡,并使您與其他開發人員區分開。您會更輕松地學習新語言,形成真正的解決問題的技能,并在編程的基礎上打下堅實的基礎。 C ++將幫助您養成良好的編程習慣(即清晰一致的編碼風格,在撰寫代碼時注釋代碼,并限制類內部的可見性),并且由 ......

    uj5u.com 2020-09-10 01:00:41 more
最新发布
  • Rust中的智能指標:Box<T> Rc<T> Arc<T> Cell<T> RefCell<T> Weak

    Rust中的智能指標是什么 智能指標(smart pointers)是一類資料結構,是擁有資料所有權和額外功能的指標。是指標的進一步發展 指標(pointer)是一個包含記憶體地址的變數的通用概念。這個地址參考,或 ” 指向”(points at)一些其 他資料 。參考以 & 符號為標志并借用了他們所 ......

    uj5u.com 2023-04-20 07:24:10 more
  • Java的值傳遞和參考傳遞

    值傳遞不會改變本身,參考傳遞(如果傳遞的值需要實體化到堆里)如果發生修改了會改變本身。 1.基本資料型別都是值傳遞 package com.example.basic; public class Test { public static void main(String[] args) { int ......

    uj5u.com 2023-04-20 07:24:04 more
  • [2]SpinalHDL教程——Scala簡單入門

    第一個 Scala 程式 shell里面輸入 $ scala scala> 1 + 1 res0: Int = 2 scala> println("Hello World!") Hello World! 檔案形式 object HelloWorld { /* 這是我的第一個 Scala 程式 * 以 ......

    uj5u.com 2023-04-20 07:23:58 more
  • 理解函式指標和回呼函式

    理解 函式指標 指向函式的指標。比如: 理解函式指標的偽代碼 void (*p)(int type, char *data); // 定義一個函式指標p void func(int type, char *data); // 宣告一個函式func p = func; // 將指標p指向函式func ......

    uj5u.com 2023-04-20 07:23:52 more
  • Django筆記二十五之資料庫函式之日期函式

    本文首發于公眾號:Hunter后端 原文鏈接:Django筆記二十五之資料庫函式之日期函式 日期函式主要介紹兩個大類,Extract() 和 Trunc() Extract() 函式作用是提取日期,比如我們可以提取一個日期欄位的年份,月份,日等資料 Trunc() 的作用則是截取,比如 2022-0 ......

    uj5u.com 2023-04-20 07:23:45 more
  • 一天吃透JVM面試八股文

    什么是JVM? JVM,全稱Java Virtual Machine(Java虛擬機),是通過在實際的計算機上仿真模擬各種計算機功能來實作的。由一套位元組碼指令集、一組暫存器、一個堆疊、一個垃圾回收堆和一個存盤方法域等組成。JVM屏蔽了與作業系統平臺相關的資訊,使得Java程式只需要生成在Java虛擬機 ......

    uj5u.com 2023-04-20 07:23:31 more
  • 使用Java接入小程式訂閱訊息!

    更新完微信服務號的模板訊息之后,我又趕緊把微信小程式的訂閱訊息給實作了!之前我一直以為微信小程式也是要企業才能申請,沒想到小程式個人就能申請。 訊息推送平臺🔥推送下發【郵件】【短信】【微信服務號】【微信小程式】【企業微信】【釘釘】等訊息型別。 https://gitee.com/zhongfuch ......

    uj5u.com 2023-04-20 07:22:59 more
  • java -- 緩沖流、轉換流、序列化流

    緩沖流 緩沖流, 也叫高效流, 按照資料型別分類: 位元組緩沖流:BufferedInputStream,BufferedOutputStream 字符緩沖流:BufferedReader,BufferedWriter 緩沖流的基本原理,是在創建流物件時,會創建一個內置的默認大小的緩沖區陣列,通過緩沖 ......

    uj5u.com 2023-04-20 07:22:49 more
  • Java-SpringBoot-Range請求頭設定實作視頻分段傳輸

    老實說,人太懶了,現在基本都不喜歡寫筆記了,但是網上有關Range請求頭的文章都太水了 下面是抄的一段StackOverflow的代碼...自己大修改過的,寫的注釋挺全的,應該直接看得懂,就不解釋了 寫的不好...只是希望能給視頻網站開發的新手一點點幫助吧. 業務場景:視頻分段傳輸、視頻多段傳輸(理 ......

    uj5u.com 2023-04-20 07:22:42 more
  • Windows 10開發教程_編程入門自學教程_菜鳥教程-免費教程分享

    教程簡介 Windows 10開發入門教程 - 從簡單的步驟了解Windows 10開發,從基本到高級概念,包括簡介,UWP,第一個應用程式,商店,XAML控制元件,資料系結,XAML性能,自適應設計,自適應UI,自適應代碼,檔案管理,SQLite資料庫,應用程式到應用程式通信,應用程式本地化,應用程式 ......

    uj5u.com 2023-04-20 07:22:35 more