2018-09-07(指針,多級指針,指針數(shù)組,指針數(shù)組字符串,結(jié)構(gòu)體,結(jié)構(gòu)體,枚舉,變量修飾符)

  • 定義一個函數(shù), 要求能夠在函數(shù)中修改傳入變量的值
    int num = 6;
    printf("調(diào)用之前:num = %i\n", num);
//    change(num);
    change(&num);
    printf("調(diào)用之后:num = %i\n", num);
    return 0;
}
//void change(int value){
// 結(jié)論: 如果函數(shù)的形參是數(shù)組或者是指針,
// 那么就可以在函數(shù)中修改外界傳入變量的值
void change(int *value){ // 相當(dāng)于 int *value = #
    *value = 888;
  • 需求: 要定定義一個函數(shù), 在函數(shù)中交換傳入變量的值
void main(){
    int num1=10,num2=20;
    chang(&num1,&num2);
    printf("num1=%i,num2=%i",num1,num2);

}
基本數(shù)據(jù)類型作為形參, 在函數(shù)內(nèi)修改形參, 不會影響到函數(shù)外的實參,所以定義為指針變量,傳進(jìn)來地址
int chang(int *n1,int *n2){

    int mid=*n1;
    *n1=*n2;
    *n2=mid;
}
  • 需求: 要求定義一個函數(shù), 可以同時返回兩個變量的和,差,積,商
     需要大家知道的是: 在C語言中, 默認(rèn)情況下一個函數(shù)只能返回一個值
     如果想讓某一個函數(shù)同時返回多個值, 可以借助指針來實現(xiàn)
#include <stdio.h>
//int sum(int num1, int num2);
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4);
int main()
{
    int a = 10;
    int b = 20;
//    int res = sum(a, b);
//    printf("res = %i\n", res);

    int d, e, f, g;
    test(a, b, &d, &e, &f, &g);
    printf("和 = %i\n", d);
    printf("差 = %i\n", e);
    printf("積 = %i\n", f);
    printf("商 = %i\n", g);
    return 0;
//    printf("return 后面的語句\n");
}

/**先輸出/**,然后按住Ctrl旁邊的Fn+回車鍵,自動生成以下注釋
 * @brief test 可以同時返回兩個變量的和,差,積,商
 * @param num1 參與運(yùn)算的第一個變量
 * @param num2 參與運(yùn)算的第二個變量
 * @param res1 和
 * @param res2 差
 * @param res3 積
 * @param res4 商
 */
void test(int num1, int num2, int *res1, int *res2, int *res3, int *res4){
    *res1 = num1 + num2;
    *res2 = num1 - num2;
    *res3 = num1 * num2;
    *res4 = num1 / num2;
}

注意點:return后面的語句執(zhí)行不到
int sum(int num1, int num2){
    // 注意點:
    // return的作用是用于結(jié)束當(dāng)前函數(shù)
    // 只要函數(shù)執(zhí)行到return, 就會立刻停止往下執(zhí)行
    // 所以return 后面不可以編寫任何語句, 因為執(zhí)行不到
    return num1 + num2;
//    return num1 - num2;
//    return num1 * num2;
//    return num1 / num2;
}
*/
  • 多級指針
前面我們學(xué)習(xí)的指針我們稱之為一級指針
     * 什么是多級指針
     * 指針中保存的又是其它指針的地址, 我們就稱之為多級指針
     * 如何定義多級指針
     * 在要保存的指針變量的基礎(chǔ)上加一顆星即可
     * 例如: int *p;  如果想保持指針變量p的地址, 只需在定義時多加一顆星即可 int **pp;
     *
     * 訪問的規(guī)則:pp
     * 如果定義就如何訪問, 例如: int *p; 訪問 *p;  例如: int **pp; 訪問 **pp;
     *
     * 定義指針的規(guī)律:
     * 1.將要指向的變量的定義拷貝過來
     * 2.再拷貝過來的代碼的類型和變量名稱中間加上一顆星
     * 3.修改變量名稱
int num=6//int num;num=6;
int *p=&num;//int *p;p=&num;定義指針規(guī)律的意思就是把int num;拷貝過來,在int和num之間加*,在中間靠近誰都沒關(guān)系,然后改變num的名稱為p
int **p2=&p;
printf("&num = %p\n", &num); // &num =  0060FEAC
printf("p = %p\n", p); // p =  0060FEAC
printf("*pp = %p\n", *pp); // *pp = 0060FEAC

printf("&pp = %p\n", &pp); // &pp = 0060FEA4
 規(guī)律: 如果想通過多級指針獲取某個變量的值, 那么是幾級指針, 前面就寫幾顆星即可
 注意點: 在企業(yè)開發(fā)中, 最多二級指針, 三級頂天了, 四級沒講過
  • 指針和數(shù)組
 int ages[3] = {1, 3, 5};

    for(int i = 0; i < 3; i++){
        printf("ages[%i] = %i\n", i, ages[i]);
    }
    數(shù)組名稱保存的就是數(shù)組占用內(nèi)存最小的那個地址
    既然數(shù)組名稱保存的就是地址, 而指針也是用于保存地址的, 所以指針也可以指向數(shù)組
    int *p = &ages;
    printf("ages = %p\n", ages); // ages = 0060FEA0
    printf("&ages = %p\n", &ages); // ages = 0060FEA0
    int *p = ages;// int *p = &ages
    printf("p = %p\n", p); // 0060FEA0
    結(jié)論: 如果利用指針保存數(shù)組的地址之后, 那么 p = ages = &ages;
  • 要求你寫出三種訪問數(shù)組元素的寫法
     int ages[3] = {1, 3, 5};//第一種
     printf("ages[0] = %i\n", ages[0]);
     int *p = ages;//第二種
     printf("p[0] = %i\n", p[0]);
     printf("0[p] = %i\n", 0[p]);//第三種
  • 指針遍歷數(shù)組
int ages[3] = {1, 3, 5};
    int *p = ages;
//    printf("*p = %i\n", *p); // 1
//    printf("*(p + 1) = %i\n", *(p + 1)); // 3
//    printf("*(p + 2) = %i\n", *(p + 2)); // 5

//    printf("*p = %i\n", *p++); // 1
//    printf("*p = %i\n", *p++); // 3
//    printf("*p = %i\n", *p); // 5
//    printf("*p = %i\n", *(--p)); // 3


    for(int i = 0; i < 3; i++){
//        printf("ages[%i] = %i\n", i, ages[i]);
        printf("ages[%i] = %i\n", i, *p++);
    }
  • 指針和字符串(用指針變量保存字符串)

指針和字符串
* 字符串的本質(zhì)就是數(shù)組, 所以指針也可以指向字符串
* 正式因為如此, 所以定義字符串又多了一種方式

char str1[] = {'l', 'n', 'j', '\0'};
char str2[] = "lnj";//可以寫成str2[i]
char *str4 = "lnj";//同樣可以寫成str2[i]
利用數(shù)組和指針定義字符串的區(qū)別:
     * 1. 存儲的位置不同
     * 如果是通過數(shù)組定義的字符串, 那么存儲在內(nèi)存的棧中
     * 如果是通過指針定義的字符串, 那么存儲在內(nèi)存的常量區(qū)中
     *
     * 2.由于在內(nèi)存中存儲的位置不一樣, 所以特性也不一樣
     * 如果是通過數(shù)組定義的字符串, 我們是可以手動修改
     * 如果是通過指針定義的字符串, 我們不用手動修改
   char str[] = "lnj";
   char *str = "lnj";
   printf("str = %s\n", str);
   str2[1] = 'T';
   printf("str = %s\n", str);
     *
     * 3.由于在內(nèi)存中存儲的位置不一樣, 所以特性也不一樣
     * 如果是通過數(shù)組定義的字符串, 每次定義都會重新開辟存儲空間
     * 如果是通過指針定義的字符串, 重復(fù)定義不會重新開辟存儲空間

    char str1[] = "lnj";
    char str2[] = "lnj";
    printf("str1 = %p\n", str1); // 地址不一樣
    printf("str2 = %p\n", str2);

    char *str1 = "lnj";
    char *str2 = "lnj";
    printf("str1 = %p\n", str1); // 地址一樣
    printf("str2 = %p\n", str2);
  • 接收和打印,字符串?dāng)?shù)組和指針的區(qū)別
 1.接收字符串的時候, 只能通過字符數(shù)組, 不能通過字符指針
char str[10];
char *str;//程序編譯是不報錯,運(yùn)行時出現(xiàn)問題,這樣定義接收不到
scanf("%s", str);
rintf("str = %s\n", str);
 2.如果函數(shù)中返回的字符串是通過數(shù)組創(chuàng)建的, 那么外界無法獲取
    如果函數(shù)中返回的字符串是通過指針創(chuàng)建的, 那么外界可以獲取
    char *res = demo();
    printf("res = %s\n", res);
char* demo(){//返回值為指針類型,所以有星號
    char str[] = "lnj";//函數(shù)外面接收不到
    char *str = "lnj"
    return str;//str保存的是常量區(qū)lnj\0的地址

/ 注意點: 學(xué)習(xí)了指針之后, 建議將過去形參的數(shù)組類型修改為指針類型
int ages[3] = {1, 3, 5};
     test(ages);
     printf("ages[1] = %i\n", ages[1]);

//void test(int nums[]){
void test(int *nums){//由于test(ages)傳進(jìn)來的就是地址,用指針更形象
 nums[1] = 6;
  • 字符串?dāng)?shù)組
字符串?dāng)?shù)組
    // 字符串就是一個數(shù)組, 所以字符串?dāng)?shù)組就是一個二維數(shù)組

   char str[][] = {//字符串?dāng)?shù)組表示方法
       "lnj",
       "abc",
       "def"
    };

    // 字符串?dāng)?shù)組的第二種格式
    char *str[] = {
        "lnj",
        "abc",
        "def"
    };
  • 練習(xí)
    1.實現(xiàn)strlen()函數(shù)
char *str = "lnj666";//此處注意字符串為char類型,形參處也為char類型
//    char str[10] = {'l', 'n', 'j', '\0'};
    // 注意點: strlen計算的是保存了多少個字符串
    // 計算規(guī)則, 就是返回字符串中\(zhòng)0之前有多少個字符
int res = myStrlen(str);
    printf("res = %i\n", res);
    return 0;
}
int myStrlen(char *str){
    // 1.定義變量記錄當(dāng)前有多少個
    int count = 0;
    while(*str++){
        count++;
    }
//    while(*str++ != '\0'){   方法二
//        count++;
//    }

//    while(str[count] != '\0'){     方法三
//        count++;
//    }
   return count;
}

2.實現(xiàn)strcat()函數(shù)

注意點: 第一個參數(shù)必須足夠大
 //char *str1 = "lnj";  // 不能用于拼接,因為不能看出第一個字符空間大小
//    char *str2 = "it666";
    char str1[10] = "lnj";
    char str2[10] = "it666";
    myStrcat(str1, str2);
    printf("str1 = %s\n", str1);
    return 0;
}
void myStrcat(char *str1, char *str2){
方法一:
    while(*str1){
         str1++;
    }
 while(*str2){
        *str1 = *str2;
        str1++;
        str2++;
    }
*str1='\0';
}

方法二:
 // 1.拿到第一個數(shù)組\0的位置
    int index = 0;
    while(str1[index] != '\0'){
        index++;
    }
    // 2.將第二個數(shù)組添加到第一個數(shù)組末尾
    int count = 0;
    while(str2[count] != '\0'){
        str1[index] = str2[count];
        count++;
        index++;
    }

3.實現(xiàn)strcpy()

同樣不能用指針定義字符串?dāng)?shù)組,
int main()
{
      char str1[9] = "lnj";
      char str2[10] = "it";
//    strcpy(str1, str2);
    myStrcpy(str1, str2);
    printf("str1 = %s\n", str1);
    return 0;
}
void myStrcpy(char *str1, char *str2){
    while(*str2){
        *str1 = *str2;
        str1++;
        str2++;
    }
    *str1 = '\0';
}
/*
void myStrcpy(char *str1, char *str2){
    int index = 0;
    while(str2[index] != '\0'){
        str1[index] = str2[index];
        index++;
    }
    str1[index] = '\0';
}
*/

4.實現(xiàn)strcmp()

int main()
{
    // strcmp
    char str1[9] = "125";
    char str2[10] = "124";
    // 兩個字符串相同返回0
    // 第一個參數(shù)小于第二個參數(shù) -1 負(fù)數(shù)
    // 第一個參數(shù)大于第二個參數(shù) 1 正數(shù)
    // 如果前面的內(nèi)容都相同, 第一個參數(shù)的個數(shù)小于第二個參數(shù), -1 負(fù)數(shù)
    // 如果前面的內(nèi)容都相同, 第一個參數(shù)的個數(shù)大于第二個參數(shù), 1 正數(shù)
//    int res = strcmp(str1, str2);
    int res = myStrcmp(str1, str2);
    printf("res = %i\n", res);
    return 0;
}
int myStrcmp(char *str1, char *str2){

//    while(*str1 != '\0' || *str2 != '\0'){
     while(*str1 || *str2){
        if(*str1 > *str2){
            return 1;
        }else if(*str1 < *str2){
            return -1;
        }
        str1++;
        str2++;
    }
    return 0;
}

/*方法二:
int myStrcmp(char *str1, char *str2){
    int index = 0; // 3
    //      \0                        \0
    while(str1[index] != '\0' || str2[index] !='\0'){
        //      3           3
        if(str1[index] > str2[index]){
            return 1;
        }else if(str1[index] < str2[index]){
            return -1;
        }
        index++;
    }
    return 0;
}
*/

  • 指向函數(shù)的指針

    • 指針變量的作用: 專門用于保存地址
      * 指向函數(shù)的指針
      * 計算機(jī)也會給函數(shù)分配存儲空間, 既然函數(shù)會分配內(nèi)存空間,
      * 所以函數(shù)也有自己的地址, 所以指針變量也可以保存函數(shù)的地址
      *
      * 經(jīng)過前面的學(xué)習(xí), 我們知道數(shù)組名保存的就是數(shù)組的地址
      * 函數(shù)和數(shù)組很像, 函數(shù)名中保存的就是函數(shù)的地址
      *
      * 如何定義指針變量?
      * 1.將需要保存變量的定義拷貝過來
      * 2.在數(shù)據(jù)類型和變量名稱中間添加一個*
      * 3.修改變量名稱
      *
      * 如何定義保存函數(shù)的指針變量
      * 1.將函數(shù)的什么拷貝過來
      * 2.在函數(shù)返回值和函數(shù)名稱總監(jiān)添加一個*
      * 3.修改函數(shù)名稱
      * 4.注意點: 需要將*和變量名稱用括號括起來
      *
      * 我們說過指向函數(shù)的指針和指向數(shù)組的指針很像
      * 如果是數(shù)組, 我們可以直接將數(shù)組名稱賦值給一個指針變量
      * 如果是函數(shù), 我們也可以直接將函數(shù)名稱賦值給一個指針變量
      *
      * 如果一個指針指向了數(shù)組, 那么訪問數(shù)組就有了三種方式
      * 數(shù)組名稱[索引];
      * 指針變量名稱[索引]
      * *(指針編碼名稱 + 索引)
      *
      * 如果一個指針指向了函數(shù), 那么訪問方式也有多種方式
      * 函數(shù)名稱();
      * ( *指針變量名稱)();
      * 指針變量名稱();

     int num;
     int *p;
     p = &num;

    int ages[3] = {1, 3, 5};
     int *p;
     p = ages;
    printf("ages[1] = %i\n", ages[2]);//三種方式訪問數(shù)組
    printf("p[1] = %i\n", p[2]);
    printf("p[1] = %i\n", *(p+2));


    test(); // 第一種方式

    void (*funcP)();
    funcP = &test;//或者  funcP = test;
    (*funcP)(); // 第二種方式

    void (*funcP)();
    funcP = &test;//或者  funcP = test;
    funcP(); // 第三種方式
void test(){
    printf("test\n");
}
  • 練習(xí):定義指針指向這幾個函數(shù)
    void (*p1)();
    p1 = say;
    p1();

    void (*p2)(int, int);
    p2 = sum;
    p2(10, 20);


    int (*p3)(int a, int b);
    p3 = minus;
    int res = p3(10, 20);
    printf("res = %i\n", res);

    char* (*p4)();
    p4 = test;
    char* res2 = p4();
    printf("res2 = %s\n", res2);
    return 0;
}
void say(){
    printf("Hello World\n");
}
void sum(int a, int b){
    printf("res = %i\n", a + b);
}
int minus(int a, int b){
    return a - b;
}
char* test(){
    char* name = "lnj";
    return name;
}
  • 要求一個函數(shù)既可以計算兩個變量的和, 也可以計算兩個變量的差

//    int res1 = sum(10, 20);
//    printf("res1 = %i\n", res1);

//    int res2 = minus(10, 20);
//    printf("res2 = %i\n", res2);

    int res3 = test(10, 20, sum);//把sum地址傳給test
    printf("res3 = %i\n", res3);

    int res4 = test(10, 20, minus);
    printf("res4 = %i\n", res4);
    return 0;
}
 注意點: 指向函數(shù)的指針,作為函數(shù)的形參時, 指針變量的名稱, 就是形參的名稱
// 如果指向函數(shù)的指針作為函數(shù)的參數(shù), 那么這個可稱之為回調(diào)函數(shù)
// 這里相當(dāng)于, 給test函數(shù)傳入了一個sum函數(shù)或者minus函數(shù)
// 然后又在test函數(shù)中調(diào)用了sum函數(shù)或者minus函
int test(int num1, int num2, int (*funP)(int, int)){
    return funP(num1, num2);//把num1和num2的值傳給sum函數(shù)

int sum(int num1, int num2){
    return num1 + num2;
}
int minus(int num1, int num2){
    return num1 - num2;
}
  • 作業(yè)
    1.要求從鍵盤輸入一個字符串, 并且字符串中可以出現(xiàn)空格
  • 2.將用戶輸入的字符串, 單詞的首字母變成大寫, 單詞用空格劃分
  • hello world; --> Hello World;
  • 3.將用戶輸入的字符串, 單詞的首字母編程小寫, 單詞用空格劃分
  • Hello World; --> hello world;
  • 4.要求定義一個函數(shù), 既可以處理將首字母變?yōu)榇髮? 也可以處理將首字母變?yōu)樾?/li>
  • 需要用到指向函數(shù)的指針

  • 結(jié)構(gòu)體
    /*
     * 什么是結(jié)構(gòu)體?
     * 結(jié)構(gòu)體時構(gòu)造類型的一種
     *
     * 構(gòu)造類型前面我們已經(jīng)學(xué)習(xí)過了數(shù)組:
     * 數(shù)組的作用是用于存儲一組相`同類型`的數(shù)據(jù)
     * 結(jié)構(gòu)體的作用是用于存儲一組`不同類型`的數(shù)據(jù)
     *
     * 保存一個人的信息
     * 姓名/年齡/身高 ...
     * char *
     * int
     * double
     *
     * 如何定義結(jié)構(gòu)體變量
     * 1.先定義結(jié)構(gòu)體類型
     * 2.通過結(jié)構(gòu)體的類型定義結(jié)構(gòu)體變量
     *
     * 如何定義結(jié)構(gòu)體類型?
     * struct 結(jié)構(gòu)體類型名稱{
     *   數(shù)據(jù)類型 屬性名稱;
     *   數(shù)據(jù)類型 屬性名稱;
     *   ... ...
     * };
     *
     * 如何定義結(jié)構(gòu)體變量
     * struct 結(jié)構(gòu)體類型名稱 變量名稱;
     *
     * 如何訪問結(jié)構(gòu)體的屬性
     * 結(jié)構(gòu)體變量名稱.結(jié)構(gòu)體屬性名稱;
     */
    // 1.定義結(jié)構(gòu)體類型
    struct Person{
        char *name; // name我們稱之為結(jié)構(gòu)體的屬性名稱
        int age; // age也稱之為結(jié)構(gòu)體的屬性名
        double height; // height也稱之為結(jié)構(gòu)體的屬性名稱
    };
    // 2.定義結(jié)構(gòu)體變量
    struct Person p;

    // 3.使用結(jié)構(gòu)體變量
//    int ages[3] = {1, 3, 5};
//    ages[0] = 1;
    // 格式: 結(jié)構(gòu)體變量名稱.結(jié)構(gòu)體屬性名稱
    p.name = "lnj";
    p.age = 35;
    p.height = 1.9;
    printf("name = %s\n", p.name);
    printf("age = %i\n", p.age);
    printf("height = %lf\n", p.height);
    return 0;
}
  • 結(jié)構(gòu)體變量初始化的幾種方式
1.定義結(jié)構(gòu)體類型
    struct Dog{
        char *name;
        int age;
        double height;
2.1先定義后初始化
//    struct Dog dd;
//    dd.name = "ww";
//    dd.age = 1;
//    dd.height = 1.5;
2.2定義的同時初始化
//    struct Dog dd = {"ww", 1, 1.5};
注意點: 如果在定義的同時初始化, 那么初始化的順序必須和結(jié)構(gòu)體類型中的順序一致
struct Dog dd = {.age = 1, .name = "ww", .height = 1.5};//也可以指定初始化,可以部分初始化.
3.特殊的初始化方式
   數(shù)組只能在定義的同時完全初始化, 不能先定義再完全初始化
   但是結(jié)構(gòu)體既可以在定義的同時完全初始化, 也可以先定義再完全初始

//    int ages[3] = {1, 3, 5};
//    int ages[3];
//    ages = {1, 3, 5};//數(shù)組不可以

 // 企業(yè)開發(fā)不推薦這樣編寫
    struct Dog{
        char *name;
        int age;
        double height;
    };
     struct Dog dd;
     dd = (struct Dog){"ww", 1, 1.5};//強(qiáng)制轉(zhuǎn)換
     printf("name = %s\n", dd.name);
     printf("name = %i\n", dd.age);
     printf("name = %lf\n", dd.height);
  • 定義結(jié)構(gòu)體變量的方式
       1.先定義結(jié)構(gòu)體類型, 再定義結(jié)構(gòu)體變量
    /*
    struct Person{
        char *name;
        int age;
        double height;
    };
    struct Person p1;
    struct Person p11;
    */

     2.定義結(jié)構(gòu)體類型的同時定義結(jié)構(gòu)體變量
    /*
    struct Person{
        char *name;
        int age;
        double height;
    } p2;
    p2.name = "lnj";
    printf("name = %s\n", p2.name);
    struct Person p22;
    */
   3.定義結(jié)構(gòu)體類型的同時省略結(jié)構(gòu)體名稱, 同時定義結(jié)構(gòu)體變量
    // 匿名結(jié)構(gòu)體
    // 特點: 結(jié)構(gòu)體類型只能使用一次
    struct{
        char *name;
        int age;
        double height;
    } p3;
    p3.name = "it666";
    printf("name = %s\n", p3.name);
    return 0;
  • 結(jié)構(gòu)體作用域(和變量相同)
  • 結(jié)構(gòu)體數(shù)組及初始化
  struct Person{
        char *name;
        int age;
        double height;
    };
方法一:先定義在初始化
 //    struct Person p1 = {"lnj", 35, 1.90};
//    struct Person p2 = {"zs", 22, 1.2};
//    struct Person p3 = {"ls", 33, 1.4};
//    struct Person p4 = {"ww", 56, 1.8};
   數(shù)據(jù)類型 數(shù)組名稱[元素個數(shù)];
    struct Person ps[4];
     ps[0] = p1;//ps[0] = {"lnj", 35, 1.90};下面也如此
     ps[1] = p2;
     ps[2] = p3;
     ps[3] = p4;
方法二:定義同時初始化
 struct Person ps[4] ={
         {"lnj", 35, 1.90},
         {"zs", 22, 1.2},
         {"ls", 33, 1.4},
         {"ww", 56, 1.8},
     };
  • 結(jié)構(gòu)體內(nèi)存分析

注意點:
* 給整個結(jié)構(gòu)體變量分配存儲空間和數(shù)組一樣, 從內(nèi)存地址比較大的開始分配
* 給結(jié)構(gòu)體變量中的屬性分配存儲空間也和數(shù)組一樣, 從所占用內(nèi)存地址比較小的開始分配
*
* 注意點:
* 和數(shù)組不同的是, 數(shù)組名保存的就是數(shù)組首元素的地址
* 而結(jié)構(gòu)體變量名, 保存的不是結(jié)構(gòu)體首屬性的地址

 struct Person{
        int age;
        int score;
    };
    struct Person p;
    printf("p = %p\n", p); // p = 00000077
    printf("&p = %p\n", &p); // &p = 0060FEA8
    printf("&p.age = %p\n", &p.age); // &p.age = 0060FEA8
    printf("&p.score = %p\n", &p.score); // &p.score = 0060FEAC
    //結(jié)構(gòu)體在分配內(nèi)存的時候, 會做一個內(nèi)存對齊的操作
    // 會先獲取所有屬性中占用內(nèi)存最大的屬性的字節(jié)
    // 然后再開辟最大屬性字節(jié)的內(nèi)存給第一個屬性, 如果分配給第一個屬性之后還能繼續(xù)分配給第二個屬性, 那么就繼續(xù)分配
    // 如果分配給第一個屬性之后, 剩余的內(nèi)存不夠分配給第二個屬性了, 那么會再次開辟最大屬性直接的內(nèi)存, 再次分配
    //以此類推
  • 結(jié)構(gòu)體指針

結(jié)構(gòu)體指針
* 因為結(jié)構(gòu)體變量也會分配內(nèi)存空間, 所以結(jié)構(gòu)體變量也有內(nèi)存地址, 所以也可以使用指針保存結(jié)構(gòu)體變量的地址
*
* 規(guī)律:
* 定義指向結(jié)構(gòu)體變量的指針的套路和過去定義指向普通變量的一樣
*
* 如果指針指向了一個結(jié)構(gòu)體變量, 那么訪問結(jié)構(gòu)體變量的屬性就有3種方式
* 結(jié)構(gòu)體變量名稱.屬性名稱;
* (*結(jié)構(gòu)體指針變量名稱).屬性名稱;
* 結(jié)構(gòu)體指針變量名稱->屬性名稱;

struct Person{
        char *name;
        int age;
        double height;
    };
    struct Person per = {"lnj", 35, 1.9};//變量名為per
    struct Person *p;//定義一個指針變量p,struct Person為結(jié)構(gòu)體類型
    p = &per;
   printf("per.name = %s\n", per.name);
    printf("per.name = %s\n", (*p).name);
    printf("per.name = %s\n", p->name);
  • 結(jié)構(gòu)體的嵌套(提高代碼的復(fù)用性)
struct Person{
        char *name;
        int age;

        // 出生年月
        int year;
        int month;
        int day;

        // 死亡日期
        int year2;
        int month2;
        int day2;

        // 讀幼兒園日期
        // 讀小學(xué)日期
        // 讀中學(xué)日期
所以可以把年月日寫成一個結(jié)構(gòu)體
// 1.定義了一個日期的結(jié)構(gòu)體類型
    struct Date{
        int year;
        int month;
        int day;
    };
    // 2.定義一個人的結(jié)構(gòu)體類型
    struct Person{
        char *name;
        int age;
        struct Date birthday;
    };
    struct Person p = {"lnj", 35, {2020, 12, 12}};

    printf("name = %s\n", p.name);
    printf("name = %i\n", p.age);
    printf("name = %i\n", p.birthday.year);
    printf("name = %i\n", p.birthday.month);
    printf("name = %i\n", p.birthday.day);
  • 結(jié)構(gòu)體和函數(shù)(強(qiáng)調(diào)結(jié)構(gòu)體作用域和變量相同)

1.雖然結(jié)構(gòu)體是構(gòu)造類型, 但是結(jié)構(gòu)體變量之間的賦值
* 和基本數(shù)據(jù)類型賦值一樣, 是拷貝
2. 注意點: 定義結(jié)構(gòu)體類型不會分配存儲空間
* 只有定義結(jié)構(gòu)體變量才會分配存儲空間

struct Person{
        char *name;
        int age;
    };
    struct Person p1 = {"lnj", 35};
    struct Person p2;
    p2 = p1;
    p2.name = "zs";
    printf("p1.name = %s\n", p1.name); // lnj
    printf("p2.name = %s\n", p2.name); //  zs


把p1的name更改寫在函數(shù)中,更改后內(nèi)存會釋放掉,最終name值不變
struct Person p1 = {"lnj", 35};
    printf("p1.name = %s\n", p1.name); // lnj
    test(p1);
    printf("p1.name = %s\n", p1.name); // lnj
    return 0;
}
void test(struct Person per){
    per.name = "zs";
  }
  • 共用體(企業(yè)開發(fā)中用的比較少)

共用體
*
* 共用體的格式:
* union 共用體名稱{
* 數(shù)據(jù)類型 屬性名稱;
* 數(shù)據(jù)類型 屬性名稱;
* ... ...
* }
* 共用體定義的格式和結(jié)構(gòu)體只有關(guān)鍵字不一樣, 結(jié)構(gòu)體用struct,共用體用union
*
* 共用體特點:
* 結(jié)構(gòu)體的每個屬性都會占用一塊單獨的內(nèi)存空間, 而共用體所有的屬性都共用同一塊存儲空間(同樣先分配占用屬性最大的字節(jié)數(shù))
* 只要其中一個屬性發(fā)生了改變, 其它的屬性都會受到影響
*
* 應(yīng)用場景:
* 同一個變量, 在不同的時刻,需要表示不同類型數(shù)據(jù)的時候, 我們就可以使用共用體

     union Test{
        int age;
        char ch;
    };
    union Test t;
    printf("sizeof(p) = %i\n", sizeof(t));

    t.age = 33;
    printf("t.age = %i\n", t.age); // 33
    t.ch = 'a';
    printf("t.ch = %c\n", t.ch); // a
    printf("t.age = %i\n", t.age); // 97,,此時原來age的空間已經(jīng)給ch用了
  • 枚舉(開發(fā)中經(jīng)常用到)

枚舉?
* 枚舉用于提升代碼的閱讀性, 一般用于表示幾個固定的值
* 所以還有一個名稱, 叫做枚舉常量
*
* 如果某些變量的取值是固定的, 那么就可以考慮使用枚舉來實現(xiàn)
*
* 枚舉的格式:
* enum 枚舉類型名稱{
* 取值1,
* 取值2,
* };
* 注意點: 和結(jié)構(gòu)體,共用體不同, 枚舉是用逗號隔開
*
* 規(guī)范:
* 枚舉的取值一般以K開頭,后面跟上枚舉類型名稱, 后面再跟上表達(dá)的含義
* K代表這是一個常量
* 枚舉類型名稱, 主要是為了有多個枚舉的時候, 方便區(qū)分
* 含義, 你想表達(dá)的意思
*
* 枚舉的取值:
* 默認(rèn)情況下從0開是取值, 依次遞增
* 也可以手動指定從幾開始, 依次遞增,例如KGenderMale = 9,從9開始遞增

enum Gender{
        KGenderMale , //打印輸出為0,可寫成male,但是 KGenderMale表達(dá)更清晰,編寫的時候會提示輸出
        KGenderFemale, // 1,可寫成female
                       // 2 ... ...
    };
 struct Person{
        char *name; // 姓名
        int age; // 年齡
        enum Gender gender; // 性別
    };
    struct Person p1;
    p1.name = "lnj";
    p1.age = 58;
    p1.gender = KGenderFemale;

    struct Person p2;
    p2.name = "周芷若";
    p2.age = 88;
    p2.gender = KGenderFemale;
  • 枚舉的作用域(和結(jié)構(gòu)體類型的作用域一樣, 和變量的作用域一樣)
  • 枚舉的定義方式(和結(jié)構(gòu)體相同)
   2.1先定義枚舉類型, 再定義枚舉變量
     enum Gender{
        KGenderMale,
       KGenderFemale,
     };
    enum Gender g1;

    2.2定義枚舉類型的同時, 定義枚舉變量
   enum Gender{
        KGenderMale,
        KGenderFemale,
     } g2;

    2.3定義枚舉類型的同時,定義枚舉變量 ,并且省略枚舉類型名稱
    enum{
        KGenderMale,
        KGenderFemale,
    } g3;

回顧局部變量和全局變量
* 局部變量:
* 概念: 定義在{}中,函數(shù)中,形參都是局部變量
* 作用域: 從定義的那一行開始, 直到遇到}結(jié)束或者遇到return為止
* 存儲的位置: 局部變量會存儲在內(nèi)存的棧區(qū)中, 會隨著定義變量的代碼執(zhí)行分配存儲空間, 會隨著作用域的結(jié)束自動釋放
* 特點:
* 相同作用域類, 不能出現(xiàn)同名的局部變量
* 如果不同作用域內(nèi)有相同名稱的變量, 那么在訪問時, 內(nèi)部的會覆蓋外部的(就近原則)
*
* 全局變量:
* 概念: 定義在{}外或者函數(shù)外的變量, 都是全局變量
* 作用域: 從定義的那一行開始, 直到文件末尾
* 存儲的位置: 全局變量會存儲在內(nèi)存的靜態(tài)區(qū)中, 會隨著程序的啟動分配存儲空間, 隨著程序的結(jié)束釋放存儲空間
* 特點:
* 如果有多個同名的全局變量, 那么也只會分配一次存儲空間, 多個同名的全局變量共用同一塊存儲空間

  • 變量修飾符(auto ,register,extern,static)
    1.修飾局部變量(auto ,register)
  • auto和register都是用于修飾局部變量的
    auto int num = 9;
    register int num = 9;
    * 它們年的作用是修飾編程的存儲特性
    * auto: 特點就是告訴編譯器, 局部變量離開作用域自動釋放
    * 默認(rèn)情況下所有局部變量都是auto的, 所以這一句廢話, 所以了解--> 忘記 / 即可
    *
    * register: 特點就是告訴編譯器, 將局部變量存儲到CPU寄存器中
    * 好處就是訪問的速度會更快, 但是在不同平臺,不同編譯器下, 會做不同的優(yōu)化,寄存器存儲空間非常有限,不一定能夠存儲進(jìn)去, 所以還是一句廢話, 所以了解 -->

2.static

2.1static對局部變量的作用
如果利用static修飾局部變量, 那么會將局部變量的存儲區(qū)域從棧區(qū)移動到靜態(tài)區(qū)
* 靜態(tài)區(qū)只有程序結(jié)束才會釋放
void calculate(int r){
// PI使用的概率非常大, 如果是一個局部變量的話, 每次調(diào)用都會重新開辟存儲空間, 這樣性能不好
// 如果PI是static的變量, 那么只會開辟一次, 那么性能就會好很多
static double pi = 3.1415926;
return r * r * pi;
}
2.2 static對全局變量的作用
* 定義一個內(nèi)部的全局變量,
* 1.該變量只能在定義的文件中使用, 不能在其它文件中使用,例:在dashen.c文件中
* 2.并且該變量會獨占一塊內(nèi)存空間
*
* 全局變量的特性:
* 可以定義多個同名的全局變量, 多個同名的全局變量共享一塊內(nèi)存空間
* 哪怕不是同一個文件中的同名全局變量, 也會共享同一塊內(nèi)存空間
* 問題:
* 這樣是不是會導(dǎo)致數(shù)據(jù)混亂,因此引入static,在其他文件中無法使用
*
* 注意點:
* 局部變量如果沒有初始化, 里面存儲的是垃圾數(shù)據(jù)
* 全局變量如果沒有初始會, 系統(tǒng)會自動初始化為0
2.3 static修飾函數(shù)
*代表這事一個內(nèi)部函數(shù), 只能在當(dāng)前文件中使用
* 如果一些內(nèi)部函數(shù)不想提供給外界使用, 那么就可以給函數(shù)添加一個static
*static必須寫到函數(shù)的實現(xiàn)中才有效, 不能寫到函數(shù)的聲明中
* 并且如果一個函數(shù)已經(jīng)被聲明為static的了, 那么在.h文件中就不要編寫該函數(shù)的聲明了

3.extern

  • extern對局部變量的作用
    * extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
    * extern一般用于全局變量, 至今沒見過有人用extern來修飾局部變量
    *
    *
    * extern對全局變量的作用
    * extern用于聲明一個變量, 聲明變量并不會開辟存儲空間
    * exter只能用于全局變量, 不能用于局部變量
    *
    * 原因:
    * 局部變量, 只有執(zhí)行到那一行代碼才會分配存儲空間, 所以哪怕聲明了 ,但是在使用時還是沒有分配, 所以還是不能存儲數(shù)據(jù)
    * 全局變量, 會隨著程序的啟動分配存儲空間, 所以只要聲明了, 使用時已經(jīng)分配好了存儲空間, 一定能夠使用, 一定能夠存儲數(shù)據(jù)
    *
    *
    如果利用extern修飾函數(shù), 代表這是一個外部函數(shù), 其它文件中也可以使用
    * 注意點: 默認(rèn)情況下所有函數(shù)都是外部函數(shù), 所有的函數(shù)都可以在其它文件中訪問, 所以extern是一個廢物
    extern必須寫到函數(shù)的實現(xiàn)中才有效, 不能寫到函數(shù)的聲明中

{extern int num;
    num = 998;
printf("num = %i\n", num);
    return 0;
}  int num;
  • 企業(yè)開發(fā)中大部分都是多人開發(fā), 多人開發(fā)就是多個人一起寫一個項目,所以在企業(yè)開發(fā)中, 都是多人同時操作多個不同的文件

例如: 現(xiàn)在有兩個員工
* 一個是菜鳥, 一個是大神
* 調(diào)用大神寫好的代碼
* 編寫主要的業(yè)務(wù)邏輯代碼
*
在企業(yè)開發(fā)中如何進(jìn)行多人開發(fā)
* 一般情況下會將(函數(shù)的實現(xiàn)), 編寫到.c文件中, 同時會將.c文件中需要暴露給外界使用的方式名稱的聲明寫到.h文件中
* 為什么編寫了.c文件還需要編寫一個.h文件, 原因很簡單, (函數(shù)的實現(xiàn))是你編寫的, 那么函數(shù)的作用,形參你最了解, 所以應(yīng)該由你來編寫

注意:
* 在企業(yè)開發(fā)中, 其它人不需要關(guān)系函數(shù)具體是如何實現(xiàn)的, 只需要關(guān)心如何使用這個函數(shù)即可
* 所以(函數(shù)的實現(xiàn))和聲明都應(yīng)該讓同一個人來完成
*
* main函數(shù)中一般為主要業(yè)務(wù)邏輯,不能有太多冗余代碼
* #include的作用:
* 將后面指定文件中的內(nèi)容拷貝到當(dāng)前文件中
* <>從系統(tǒng)的環(huán)境變量中去拷貝, 一般情況下只有用到系統(tǒng)函數(shù)才使用<>
* " "從指定的路徑中去拷貝, 一般情況下使用同事/自己編寫的.h文件都用""

    #include "ds.h" // 導(dǎo)入大神編寫的.h文件,例:#include "D:\WWW\ds.h"
    本質(zhì)就是將大神編寫的.h文件中的代碼拷貝過來
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市弛矛,隨后出現(xiàn)的幾起案子蛮穿,更是在濱河造成了極大的恐慌,老刑警劉巖汤求,帶你破解...
    沈念sama閱讀 211,423評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件嘱么,死亡現(xiàn)場離奇詭異,居然都是意外死亡帮坚,警方通過查閱死者的電腦和手機(jī)盏道,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,147評論 2 385
  • 文/潘曉璐 我一進(jìn)店門稍浆,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人猜嘱,你說我怎么就攤上這事衅枫。” “怎么了朗伶?”我有些...
    開封第一講書人閱讀 157,019評論 0 348
  • 文/不壞的土叔 我叫張陵为鳄,是天一觀的道長。 經(jīng)常有香客問我腕让,道長,這世上最難降的妖魔是什么歧斟? 我笑而不...
    開封第一講書人閱讀 56,443評論 1 283
  • 正文 為了忘掉前任纯丸,我火速辦了婚禮,結(jié)果婚禮上静袖,老公的妹妹穿的比我還像新娘觉鼻。我一直安慰自己,他們只是感情好队橙,可當(dāng)我...
    茶點故事閱讀 65,535評論 6 385
  • 文/花漫 我一把揭開白布坠陈。 她就那樣靜靜地躺著,像睡著了一般捐康。 火紅的嫁衣襯著肌膚如雪仇矾。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,798評論 1 290
  • 那天解总,我揣著相機(jī)與錄音贮匕,去河邊找鬼。 笑死花枫,一個胖子當(dāng)著我的面吹牛刻盐,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播劳翰,決...
    沈念sama閱讀 38,941評論 3 407
  • 文/蒼蘭香墨 我猛地睜開眼敦锌,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了佳簸?” 一聲冷哼從身側(cè)響起乙墙,我...
    開封第一講書人閱讀 37,704評論 0 266
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后伶丐,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體悼做,經(jīng)...
    沈念sama閱讀 44,152評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,494評論 2 327
  • 正文 我和宋清朗相戀三年哗魂,在試婚紗的時候發(fā)現(xiàn)自己被綠了肛走。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,629評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡录别,死狀恐怖朽色,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情组题,我是刑警寧澤葫男,帶...
    沈念sama閱讀 34,295評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站崔列,受9級特大地震影響梢褐,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜赵讯,卻給世界環(huán)境...
    茶點故事閱讀 39,901評論 3 313
  • 文/蒙蒙 一盈咳、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧边翼,春花似錦鱼响、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至债鸡,卻和暖如春江滨,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背娘锁。 一陣腳步聲響...
    開封第一講書人閱讀 31,978評論 1 266
  • 我被黑心中介騙來泰國打工牙寞, 沒想到剛下飛機(jī)就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人莫秆。 一個月前我還...
    沈念sama閱讀 46,333評論 2 360
  • 正文 我出身青樓间雀,卻偏偏與公主長得像,于是被迫代替她去往敵國和親镊屎。 傳聞我的和親對象是個殘疾皇子惹挟,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 43,499評論 2 348

推薦閱讀更多精彩內(nèi)容