【C語言】9.指針,字符串和數組的補充,指針函數&函數指針,結構體,枚舉

  • 只有指針是可以運算(移動)的,數組名是不可以的。

     int x[10];
     x++;  //illegal
     int* p = x;
     p++; //legal
    
  • 兩指針變量相減所得之差是兩個指針所指數組元素之間相差的元素個數。

    • 實際上是兩個指針值(地址)相減之差再除以該數組元素的長度(字節數)。
    • (pointer2地址值 - pointer地址值) / sizeof(所指向數據類型)
    • 指針之間可以相減,但不可以相加(相加無意義)。
  • 定義字符串:

    • 字符數組:

      char string[] = "hello";
      printf("%s\n",string);
      
    • 字符串指針指向字符串:

      char *str = "hello"
      
  • 使用字符數組來保存的字符串是存在”棧”里的,所以它是可讀可寫的,所以我們可以修改字符數組里的某個元素的值。

    但是,使用字符指針來保存字符串,它保存的是字符串常量地址,"常量區"是只讀的,所以是不可改的。

    char *str = "hello";
    *(str+1) = 'w'; // 錯誤
    
  • 使用注意:

    char *str;
    scanf("%s", str); 
    
    /* str是一個野指針,他并沒有指向某一塊內存空間,所以不允許這樣寫。如果給str分配內存空間是可以這樣用的 */
    
     /********* 數組的方法****************/  
    
     char name[20];  
     scanf("%s",name);    
    
    /************* 給字符針針分配內存空間的辦法***********/  
    
     char *name;  
     name=(char*)malloc(50);      //此時name已經指向一個剛剛分配的地址空間。  
     scanf("%s",name);  
    

    ?

  • 指針函數(是函數,返回值是指針)注意:

    如果函數返回一個字符串,那么如果用一個數組以下面的形式來接的話,是會報錯的:

    char *test() {
        return "hello";
    }
    
    int main(int argc, const char * argv[]) {
    
        char names[10];
    
        names = test();
    
        return 0;
    }
    

    這是因為,返回的字符串相當于一個這樣的數組:{‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’},但是前面我們說過,數組如果在定義的時候沒有用{}這種方式初始化,那么后面就不能再用這種方式初始化了,所以會出錯。

    解決方法:將char names[10]改為char *names或者char names[10]直接等于test()

    ?

  • 函數指針(是指針,指向函數):

    • 格式:函數的返回值類型 (*指針變量名) (形參1, 形參2, ...);

      int sum(int a,int b)
      {
        return a + b;
      }
      
      int (*p)(int,int);
      p = sum;
      
    • 應用場景:

      • 調用函數

      • 將函數作為參數在函數間傳遞

      • 函數指針能更靈活:

        int minus(int a, int b) 
        {
          return (a - b);
        }
        
        int add(int a, int b)
        {
          return (a + b);
        }
        
        int myFunction(int a, int b, int (*funcP) (int, int))
        {
          return funcP(a, b);
        }
        
        int main()
        {
          int minusResult = myFunction(10, 20, minus);
            int addResult = myFunction(10, 20, add);
              ...
          return 0;
        }
        
        /*
          函數指針能讓程序更靈活,比如后續有乘、除函數的時候,只需實現這兩個函數然后在主函數調用myFunction函數即可。如果是多人協作,不同的人寫不同的功能,如果我們來寫myFunction那么基本就不用修改就可以一直使用,非常靈活。
        */
        

        ?

    • 技巧:

      1、把要指向函數頭拷貝過來

      2、把函數名稱使用小括號括起來

      3、在函數名稱前面加上一個*

      4、修改函數名稱

    • 使用注意:

      • 由于這類指針變量存儲的是一個函數的入口地址,所以對它們作加減運算(比如p++)是無意義的。

      • 如上例,如果想使用p這個函數指針,可以直接向使用sum一樣:

        int result = p(10, 10);
        

        也可以這樣:

        int result = (*p)(10, 10);
        

        ?

  • 結構體是一種自定義數據類型,注意,它是數據類型

    struct Student {
         char *name;
         int age;
     };
    
     struct Student stu;
    

    注意,結構體的后面是有 ; 的。

  • 在使用結構體類型的時候,要加上struct關鍵字。

  • 定義結構體類型的同時定義變量:

    struct Student {
        char *name;
        int age;
    } stu;
    

    這種在定義的同時也定義了變量,就相當于:

    struct Student {
         char *name;
         int age;
     };
    
     struct Student stu;
    

    定義結構體類型的同時定義變量,以后如果想繼續使用這個結構體類型,仍然可以使用常規的方式定義:

    struct Student newStu;
    
  • 匿名結構體定義結構體變量:

    struct {
        char *name;
        int age;
    } stu;
    

    這種匿名方式與上面的方式相比,雖然看起來更簡潔(省去了結構名),但是要注意,這只能定義一個stu變量,而不能再定義新的變量,因為結構名沒有了。

  • 結構體變量的初始化:

    1. 先定義結構體變量,然后再初始化

      struct Student {
           char *name;
           int age;
       };
      
      struct Student stu = {“QY", 21};
      
    2. 定義的同時初始化

      struct Student {
           char *name;
           int age;
       } stu = {“QY", 21};
      
    3. 可以使用另外一已經存在的結構體初始化新的結構體

      struct Student stu2 = stu;
      
    4. 先定義stu再初始化(與1對應):

      struct Student stu3;
      stu3 = (struct Student){"QY", 21};
      
      /*
      這種情況要與數組對應起來,因為前面說過數組是不可以這樣子的,其實結構體是可以的,但是要加上強制類型轉換,告訴編譯器這不是數組而是一個結構體。
      */
      
    5. 下面的做法是錯誤的:

    6. struct Student {
           char *name;
           int age;
       };
      struct Student stu;
      stu = {“QY", 21}; // 錯誤,需要強制類型轉換
      
    7. 或者以這種方式來給成員賦值:

      struct Student {
           char *name;
           int age;
       };
       struct Student stu;
      
       stu.age = 27;
      
  • 結構體變量占用存儲空間大小

    1. 找到成員里占用字節最大的來分配空間,如int a, double b, char c,則一次分配8個字節。要注意對齊的問題來看總共多少個字節。注意聲明變量的順序,不同順序分配的字節數也不一樣。比如現在這個順序要分配8 + 8 + 8個字節,但是如果int a, char c, double b則分配 8 + 8個字節。

    2. 可以通過#pragma pack(2)來設置對齊模數,這樣一次分配2個字節。

      struct A
      {
          int my_test_a; // 分配2次,共4個字節
          char my_test_b; //分配1次,共2個字節
          double my_struct_a; //分配4次,共8個字節
          int my_struct_b; //分配2次,共4個字節
          char my_struct_c; //分配1,共2個字節
      }
      
      /* 結果是共有20個字節。
      */
      
  • 結構體數組:

    跟結構體變量一樣,結構體數組也有3種定義方式 :

    • 先定義結構體類型,再定義結構體數組

      struct Student {
          char *name;
          int age;
      };
      struct Student stu[5];
      
    • 定義結構體類型的同時定義結構體數組

      struct Student {
          char *name;
          int age;
      } stu[5];
      
    • 匿名結構體定義結構體結構體數組

      struct {
          char *name;
          int age;
      } stu[5];
      

    結構體數組初始化:

    • 定義的同時進行初始化

      struct { 
        char *name; 
        int age; 
      } stu[2] = { {"jo", 27}, {"y", 30} };
      
    • 先定義,后初始化,整體賦值

      s[1] = (struct stu){23,"hello"};
      
    • 先定義,后初始化 分開賦值

      s[1].age=12; 
      strcpy(stu[1].name, "hello");
      
  • 結構指針變量中的值是所指向的結構變量的首地址

  • struct 結構名 *結構指針變量名

  • 示例:

    // 定義一個結構體類型
    struct Student {
        char *name;
        int age;
    };
    
    // 定義一個結構體變量
    struct Student stu = {“QY", 21};
    
    // 定義一個指向結構體的指針變量
    struct Student *p;
    
    // 指向結構體變量stu
    p = &stu;
    
    /*
    這時候可以用3種方式訪問結構體的成員
    */
    // 方式1:結構體變量名.成員名
    printf("name=%s, age = %d \n", stu.name, stu.age);
    
    // 方式2:(*指針變量名).成員名
    printf("name=%s, age = %d \n", (*p).name, (*p).age);
    
    // 方式3:指針變量名->成員名
    printf("name=%s, age = %d \n", p->name, p->age);
    
  • 通過結構體指針訪問結構體成員:

    • (*結構指針變量).成員名
    • 結構指針變量->成員名(用熟)
    • (pstu)兩側的括號不可少,因為成員符“.”的優先級高于“”。 如去掉括號寫作pstu.num則等效于(pstu.num),這樣,意義就完全不對了。
  • 在用結構指針的時候,就把它當作int *a來看,比如:

    struct Student stu = ... //相當于 int a = ...
    struct Student *p; //相當于 int *p
    p = &stu; //相當于 p = &a
    
  • 結構體變量作為參數傳遞是值傳遞,而結構指針則是址傳遞。

  • 枚舉:

    枚舉通常是一些固定的取值范圍,如一年四季,一周七天這種。

    與結構體類似:

    enum 枚舉名 {
        枚舉元素1,
        枚舉元素2,
        ……
    };
    
    enum Season {
        spring,
        summer,
        autumn,
        Winter
    };
    enum Season s;
    定義枚舉類型的同時定義枚舉變量
    enum Season {
        spring,
        summer,
        autumn,
         winter
    } s;
    省略枚舉名稱,直接定義枚舉變量
    enum{
        spring,
         summer,
         autumn,
         winter
    } s;
    
    s = spring; //等價于s = 0;
    s = 3; //等價于s = winter;
    

    枚舉類型本質上來說就是整形,默認為0,1,2,3…,但是也可以賦值,比如第三個賦值為10,那么第1個為0,第2個為1,第3個為10,第4個為11。

    通常來說,枚舉的規范為:以k + 枚舉名稱 + 枚舉元素名。比如:

    enum Season {
        kSeasonSpring,
        kSeasonSummer,
        kSeasonAutumn,
        kSeasonWinter
    };
    
    enum Season s = kSeasonSummer;
    
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容