C語(yǔ)言入門

gcc的簡(jiǎn)單使用

gcc 源自GNU

  1. gcc -E hello.c -o hello.i
    • 預(yù)處理.c文件女仰,處理文件包含猜年,宏定義,條件編譯
  2. gcc -S hello.i -o hello.s
    • 編譯董栽,將預(yù)處理過(guò)的文件編譯成匯編文件码倦。過(guò)程包括:詞法分析,語(yǔ)法分析锭碳,語(yǔ)意分析袁稽,代碼生成等。
  3. gcc -c hello.s - o hello.o
    • 匯編擒抛,生成目標(biāo)文件推汽,此時(shí)是二進(jìn)制代碼。目標(biāo)文件沒(méi)有鏈接動(dòng)態(tài)庫(kù)歧沪,還不是可執(zhí)行文件歹撒。
    • 靜態(tài)庫(kù)生成(ar -rc libxxx.a xxx1.o xxx2.o xxx3.o),靜態(tài)庫(kù)以.a后綴結(jié)尾
    • 動(dòng)態(tài)庫(kù)的生成(gcc -fPIC -shared xxx1.c xxx2.c xxx3.c -o libxxx.so)诊胞,動(dòng)態(tài)庫(kù)以.so后綴結(jié)尾
  4. 鏈接:目標(biāo)文件必須鏈接過(guò)后才可以執(zhí)行
  • 靜態(tài)鏈接:gcc -o hello hello.o libxxx.a
  • 動(dòng)態(tài)鏈接:gcc -o hello hello.o libxxx.so
  • lld.hello.exe:在linux中可以通過(guò)shell腳本 lld 查看可執(zhí)行模塊的dependency

基本語(yǔ)法

  1. system函數(shù):int system(const char * string);
    • 在庫(kù)<stdlib.h>中暖夭,作用是開辟一個(gè)新的進(jìn)程執(zhí)行(調(diào)用fork()),來(lái)執(zhí)行參數(shù)字符串所代表的命令撵孤。
    #include <stdio.h>
    #include <stdlib.h>
    // gcc 中有這個(gè)庫(kù)
    #include <windows.h> 
    
    int main() 
    {
      printf("before\n");
      // 系統(tǒng)調(diào)用
      system("dir");
      // 調(diào)用外部程序
      system("hello.exe");
      printf("after\n");
      // 使用 windows提供的庫(kù)函數(shù)迈着,調(diào)用windows操作系統(tǒng)的功能
      WinExec("calc", SW_NORMAL);
      return 0;
    }
    
    • 宏定義,會(huì)在預(yù)編譯時(shí)展開
    // 定義一個(gè)宏 main.c
    #define NUM 100
    int main(int argc, char const *argv[])
    {
      int a;
      // 預(yù)處理會(huì)展開宏
      a = NUM; 
      return 0;
    }
    
    // 使用 `gcc -E`  預(yù)編譯后 main.i
    # 1 "main.c"
    # 1 "<built-in>"
    # 1 "<command-line>"
    # 1 "main.c"
    int main(int argc, char const *argv[])
    {
      int a;
      100 = 10;
      a = 100;
      return 0;
    }
    
  2. 進(jìn)制表示
    • 0x:十六進(jìn)制邪码,0:八進(jìn)制
    • 輸出時(shí)裕菠,%d:有符號(hào)十進(jìn)制,%u無(wú)符號(hào)十進(jìn)制輸出闭专,%o:無(wú)符號(hào)八進(jìn)制奴潘,%x:無(wú)符號(hào)十六進(jìn)制,輸出時(shí)影钉,以格式化輸出的類型來(lái)決定輸出的類型画髓,輸出時(shí)都是以四字節(jié)輸出。在以%u平委,%o雀扶,%x輸出時(shí),如果原數(shù)據(jù)最高位是1,則高位補(bǔ)1愚墓。
    char b = 0x81;
    printf("%x\n", b); // 輸出ffffff81
    
    • 計(jì)算機(jī)在存儲(chǔ)時(shí)予权,都是以二進(jìn)制補(bǔ)碼形式存儲(chǔ),且每種類型的第一位為補(bǔ)碼浪册,注意在以%d輸出時(shí)的進(jìn)制轉(zhuǎn)換問(wèn)題扫腺。
    char b = 0x81;
    printf("%x\n", b); //輸出 -127
    
    // 1000 0001 char 類型是8位存儲(chǔ),第一位為符號(hào)位
    // 原碼為 1111 1110 + 1 = 1111 1111 = -127
    
    • 如果以小容量類型存儲(chǔ)大容量類型的數(shù)據(jù)村象,超出部分被舍棄笆环。以大容量數(shù)存儲(chǔ)小容量數(shù)據(jù),如果最高位是1厚者,則在前面全部補(bǔ)1補(bǔ)齊位數(shù)躁劣。
  3. 數(shù)據(jù)類型:每種數(shù)據(jù)類型都有一定的存儲(chǔ)大小,如果超過(guò)了存儲(chǔ)空間大小库菲,多出的位數(shù)則會(huì)舍棄账忘,所以在給一種類型賦值或計(jì)算時(shí),應(yīng)注意該數(shù)據(jù)類型的取值范圍熙宇,如果越界鳖擒,可能產(chǎn)生意料之外的結(jié)果。
    • 整型
      • char烫止、unsigned char:占一個(gè)字節(jié)蒋荚。'a'為字符常量,打印格式為%c馆蠕;"a"為字符串(字符數(shù)組)期升,打印格式為%s,每個(gè)字符串結(jié)尾互躬,編譯器都會(huì)自動(dòng)添加一個(gè)結(jié)束標(biāo)志位 \0播赁,兩者不一樣。-128 到 127 或 0 到 255吨铸。
      • short、unsigned short類型祖秒,存儲(chǔ)整數(shù)诞吱,2字節(jié)
      • int、unsigned int類型竭缝,存儲(chǔ)整數(shù)房维,4字節(jié)
      • long、unsigned long抬纸,4字節(jié)
    • 浮點(diǎn)型
      • float咙俩,4字節(jié)耘擂,精度是六位小數(shù)
      • double蹬挺,8字節(jié),精度15位小數(shù)
      • long double,16字節(jié)颠悬,精度19位小數(shù)
    • 數(shù)組
      • 聲明
        int arr[2] = {1, 2}
      • 數(shù)組越界:編譯時(shí)不會(huì)出錯(cuò),但是當(dāng)程序運(yùn)行時(shí)拇派,如果用到了存儲(chǔ)越界數(shù)組項(xiàng)這塊空間時(shí)劈猪,會(huì)報(bào)錯(cuò)。
      • 數(shù)組名指向數(shù)組的首地址命黔。c語(yǔ)言里面沒(méi)有獲取數(shù)組長(zhǎng)度的方法呜呐,sizeof(arr) / sizeof(arr[0])可以獲取數(shù)組長(zhǎng)度。
      • 多維數(shù)組:int arr[2][3] = { { 1, 2, 3 },{ 4, 5, 6 } }
      • 字符數(shù)組和字符串:字符串以\0 或 0結(jié)尾悍募。字符串可以以%s格式輸出蘑辑,原因是字符串輸出指針需要結(jié)束符0
      #include <stdio.h>
      int main(int argc, char const *argv[])
      {
        char ch[] = { 'h', 'e', 'l', 'l', 'o' }; // 字符數(shù)組坠宴,長(zhǎng)度是5
        char str2 = "hello" // 這種方式初始化洋魂,默認(rèn)加 \0 結(jié)束符,所以長(zhǎng)度是6
        char str[] = { 'h', 'e', 'l', 'l', 'o' '\0'}; // 字符串
        char str1[] = { 'h', 'e', 'l', 'l', 'o' 0}; // 字符串
        return 0;
      }
      
  4. 流程控制s
    • for(continue啄踊,break忧设,goto)
    #include <stdio.h>
    int main(int argc, char const *argv[])
    {
      goto next; // 跳轉(zhuǎn)到同一個(gè)作用域的標(biāo)記處
      printf("zzzz1\n");
      printf("zzzz2\n");
      next:
        printf("123\n"); // 執(zhí)行這里
      return 0;
    }
    
    • while
    • do...while
  5. 庫(kù)函數(shù)
    • 隨機(jī)數(shù)
    #include <stdio.h>
    #include <stdlib.h>
    #include <time.h>
    
    int main(int argc, char const *argv[])
    {
      /*
      1. srand函數(shù)是隨機(jī)數(shù)發(fā)生器的初始化函數(shù)。
      2. 原型:void srand(unsigned int seed)颠通,如果傳入?yún)?shù)相同址晕,則產(chǎn)生隨機(jī)數(shù)也相同。
      3. srand和rand()配合使用產(chǎn)生偽隨機(jī)數(shù)序列顿锰。
      */
      srand((unsigned int) time(NULL));
    
      int i = 0;
      int num;
      for(i = 0; i < 5; i++) 
      {
        // rand()函數(shù)返回一個(gè)隨機(jī)數(shù)
        num = rand();
        printf("%d\n", num);
      }
      return 0;
    }
    
    • 字符串處理函數(shù)
      • 每個(gè)字符串常量都是一個(gè)地址谨垃,指向字符串的首元素地址。字符串存儲(chǔ)在_data的文字常量區(qū)硼控,相同的字符串擁有相同地址刘陶。
      • gets(char []):獲取一個(gè)鍵盤輸入的字符串,以回車結(jié)束牢撼,和 scanf() 不同的是它可以讀取空格匙隔,這個(gè)函數(shù)不安全,已經(jīng)棄用熏版。
      • fgets(char *, size, *stream);:可以指定讀取長(zhǎng)度纷责,換行符也會(huì)讀取。
      • strlen(s1):返回字符串s1的長(zhǎng)度撼短。和sizeof()不同的是再膳,strlen()不包括結(jié)束符\0的長(zhǎng)度,而且遇到\0結(jié)束曲横。
      • strcpy(dst, src):字符串拷貝喂柒,以首元素開始,遇到\0結(jié)束。
      • strncpy(dst, src, length):字符串拷貝灾杰,可以拷貝\0蚊丐,但是不能拷貝\0后面的數(shù)據(jù)。
      • strcmp(s1, s2):如果 s1 和 s2 是相同的吭露,則返回 0吠撮;如果 s1<s2 則返回小于 0;如果 s1>s2 則返回大于 0讲竿。依次比較每個(gè)字符的ASCII碼泥兰。
      • strncmp(s1, s2, n):可以指定比較長(zhǎng)度n
      • strcat(s1, s2)题禀,字符串拼接鞋诗。
      • sprintf(buf, "%d %d %d", a, b, c),將數(shù)據(jù)(a迈嘹,b削彬,c)以指定格式填充到buf中。
      #include <stdio.h>
      #include <string.h>
      
      int main(int argc, char const *argv[])
      {         
        int a, b , c;
        char buf[100];
        a = 1, b = 2, c = 3;
        sprintf(buf, "%d %d %d", a, b, c);
        printf("buf = %s\n", buf); // buf = 1 2 3
        return 0;
      }
      
      • sscanf(buf, "%d %d %d", a, b, c):將buf中的數(shù)據(jù)以指定格式提取到(a秀仲,b融痛,c)中。注意格式要嚴(yán)格一致神僵。
      #include <stdio.h>
      #include <string.h>
      
      int main(int argc, char const *argv[])
      {         
        int a, b , c;
        char buf[] = "a = 1, b = 2, c = 3";
        sscanf(buf, "a = %d, b = %d, c = %d", &a, &b, &c);
        printf("a = %d b = %d c =  %d\n", a,b,c); // a = 1 b = 2 c =  3
        return 0;
      }
      
      • strchr(s1, ch):返回一個(gè)指針雁刷,指向字符串 s1 中字符 ch 的第一次出現(xiàn)的位置。
      • strstr(s1, s2):返回一個(gè)指針保礼,指向字符串 s1 中字符串 s2 的第一次出現(xiàn)的位置沛励。
      • char *strtok(char *str, const char *delim):該函數(shù)返回被分解的第一個(gè)子字符串,如果沒(méi)有可檢索的字符串炮障,則返回一個(gè)空指針目派,該函數(shù)會(huì)破壞原字符串,在匹配的位置加上\0胁赢。第二次調(diào)用時(shí)企蹭,第一個(gè)參數(shù)寫NULL =>char *strtok(NULL, const char *delim),切割的還是第一次調(diào)用被破壞的字符串智末。
  6. 函數(shù)
    • 庫(kù)函數(shù)谅摄,使用頭文件引入,然后使用吹害。
    • 自定義函數(shù)螟凭。
    #include <stdio.h>
    // 無(wú)參 無(wú)返回值
    void hello()
    {
      printf("hello func\n");
    }
    // 含參無(wú)返回值
    void add(int a, int b){
      printf("%d\n", a + b);
    }
    // 含參有返回值
    int multiply(int a, int b){
      return a * b;
    }
    int main(int argc, char const *argv[])
    {
      hello();
      add(1, 2);
      printf("2 * 3 = %d\n", multiply(2, 3));
      return 0;
    }
    
    • 函數(shù)的聲明和定義: 在main函數(shù)中調(diào)用其他函數(shù)時(shí)虚青,只會(huì)往前去找函數(shù)的定義它呀,如果沒(méi)有定義就找函數(shù)的聲明, 如果聲明也沒(méi)有,c編譯器會(huì)報(bào)警告纵穿,c++會(huì)報(bào)錯(cuò)下隧。所以在調(diào)用函數(shù)之前,一定要進(jìn)行函數(shù)的聲明谓媒,聲明時(shí)形參名可以不寫淆院。
    #include <stdio.h>
    // 這里是函數(shù)的聲明
    int func(int a, int b);
    int main(int argc, char const *argv[])
    {
      func(1, 2);
      return 0;
    }
    // 這里是函數(shù)的定義
    int func(int a, int b) {
      return a + b;
    }
    
    • 封裝庫(kù)函數(shù)。在.c文件中編寫自定義函數(shù)句惯,在.h文件中寫函數(shù)聲明土辩,在用到這些函數(shù)時(shí),先將.h文件包含進(jìn)來(lái)抢野,(用雙引號(hào)引入)拷淘,將這些c文件一起打包編譯。
    • .h文件為了防止被多次包含指孤,可以在第一行加入启涯。
    // 1. 條件編譯
    #ifndef MY_STRLEN
    #define MY_STRLEN
    extern int my_strlen(char arr[]);
    #endif 
    // 2. 或者在第一行加入
    #pragma once
    

指針

  1. 指針是一個(gè)變量,其值為另一個(gè)變量的地址恃轩,即结洼,內(nèi)存位置的直接地址。就像其他變量或常量一樣叉跛,您必須在使用指針存儲(chǔ)其他變量地址之前,對(duì)其進(jìn)行聲明昧互。
    • 指針也是一種數(shù)據(jù)類型挽铁。
    • 指針指向誰(shuí),就把誰(shuí)的地址賦值給指針
    • 直接操作指針變量本身沒(méi)有意義敞掘,需要操作*p---指針?biāo)赶虻膬?nèi)存叽掘。
    • 指針的大小是由編譯器決定的。
    #include <stdio.h> 
    int main(int argc, char const *argv[])
    {
      int *p;
      int a = 10;
      p = &a;
      printf("%p %p\n", p, &a); // 0061FF28 0061FF28
      *p = 10;
      printf("%d %d\n", a, *p); // 10 10
      return 0;
    }
    
  2. 野指針:指針保存了一個(gè)非法地址玖雁。(只有定義變量后系統(tǒng)分配的地址才是合法的)更扁,操作野指針時(shí)不會(huì)操作,但是在操作野指針指向的內(nèi)存時(shí)赫冬,由于系統(tǒng)在該進(jìn)程中未對(duì)改地址授權(quán)浓镜,會(huì)報(bào)一個(gè)段錯(cuò)誤。
  3. 空指針:給指針變量賦值為NULL劲厌。
int *p = NULL;
// 相當(dāng)于是
int *p;
p = NULL;
  1. 多級(jí)指針:指向指針的指針是一種多級(jí)間接尋址的形式膛薛,或者說(shuō)是一個(gè)指針鏈。通常补鼻,一個(gè)指針包含一個(gè)變量的地址哄啄。當(dāng)我們定義一個(gè)指向指針的指針時(shí)雅任,第一個(gè)指針包含了第二個(gè)指針的地址,第二個(gè)指針指向包含實(shí)際值的位置咨跌。
#include <stdio.h>
int main(int argc, char const *argv[])
{
  int a = 10;
  int *p = &a;
  int **q = &p;
  *p = 20; 
  **q = 30;
  // a= 30 *p = 30 **q = 30 
  printf("a= %d *p = %d **q = %d \n ", a, *p, **q);
  return 0;
}
  1. 指針操作方式
#include <stdio.h>
int main(int argc, char const *argv[])
{
  int *p;
  int a = 10;
  p = &a;
  // a = 10 *p = 10 *(p+0) = 10 p[0] = 10
  printf("a = %d *p = %d *(p+0) = %d p[0] = %d\n", a, *p, *(p+0), p[0]);
  return 0;
}
  1. void類型指針
#include <stdio.h>
int main(int argc, char const *argv[])
{
  int a = 10;
  /* 
    1. 不能用 void 定義變量沪么,因?yàn)闊o(wú)法確定變量要分配的內(nèi)存大小
    2. 但是可以用 void 關(guān)鍵字定義指針,因?yàn)橹羔樀拇笮∈枪潭ǖ男堪耄鶕?jù)編譯器來(lái)確定禽车。
    3. 在使用 void 關(guān)鍵字定義的指針是,需要進(jìn)行類型適配刊殉。 
        * 理由是地址指向的都是這個(gè)數(shù)據(jù)的首地址(int 4 字節(jié)的首地址)殉摔,操作整個(gè)4
          字節(jié)的數(shù)據(jù),但是 void 類型沒(méi)有大小無(wú)法確定操作多大空間记焊。
  */ 
  void *p;
  p = &a;
  //printf("*p = %d\n", *p); // error: invalid use of void expression
  printf("*p = %d\n", *(int *)p); // 把 void 類型的指針轉(zhuǎn)換成 int 類型的指針 
  return 0;
}
  1. 指針的步長(zhǎng)钦勘,指針的加減法根據(jù)指針的步長(zhǎng)來(lái)進(jìn)行加減。指針的步長(zhǎng)由指針的類型決定亚亲,比如int類型的指針步長(zhǎng)是4彻采。
#include <stdio.h>
int main(int argc, char const *argv[])
{
  int a = 10;
  int *p = &a;
  printf("p = %p P+1 = %p\n", p, p+1); // p = 0061FF28 P+1 = 0061FF2C
  return 0;
}
  1. 指針與數(shù)組
    • 數(shù)組存儲(chǔ)的是指針類型的數(shù)據(jù),每個(gè)數(shù)據(jù)是一個(gè)指針捌归。
      int *p[3];
    • 形參中的數(shù)組:形參中的數(shù)組不是數(shù)組肛响,是一個(gè)指針。
    func(int a[100]){} // 形參大小是一個(gè)指針的大小
    func(int a[]){} // 形參大小是一個(gè)指針的大小
    func(int *a){} // 形參大小是一個(gè)指針的大小
    
    • 數(shù)組名是一個(gè)指針惜索,指向數(shù)組第一個(gè)元素的地址特笋,可以利用指針步長(zhǎng)的特性來(lái)遍歷數(shù)組。
  2. 指針作為函數(shù)返回值:注意巾兆,不能返回一個(gè)局部變量的地址猎物,局部變量在函數(shù)調(diào)用完成時(shí)會(huì)自動(dòng)銷毀。如果再用一個(gè)指針去接收這個(gè)地址會(huì)產(chǎn)生一個(gè)野指針角塑。
#include <stdio.h>

int a; // 全局變量 在 bss區(qū)(未初始化全局變量)

int *func(){
  return &a;
}

int main(int argc, char const *argv[])
{
 *func() = 100;
  printf("a = %d\n", a);
  return 0;
}

作用域

  1. 局部作用域
    • {}內(nèi)定義的變量只能在該局部作用域內(nèi)使用蔫磨,在代碼執(zhí)行到這個(gè)變量的定義時(shí),編譯器才會(huì)為這個(gè)變量分配空間圃伶。
    • 在不同的作用域中可以定義同名的變量堤如。
    #include <stdio.h>
    
    int main(int argc, char const *argv[])
    {
      int a = 1;
    
      if(1) {
        int a = 2;
        printf("%d\n", a); // 2
      }
      return 0;
    }
    
    • static關(guān)鍵字聲明的靜態(tài)局部變量在程序編譯的時(shí)候就會(huì)分配地址空間(_data區(qū)),不能用局部變量初始化窒朋,靜態(tài)局部變量在程序退出時(shí)銷毀搀罢。static初始化的局部變量只會(huì)初始化一次,但是可以賦值多次侥猩。
    #include <stdio.h>
    void func(){
      static int a = 1;
      a++;
      printf("%d\n", a);
    }
    int main(int argc, char const *argv[])
    {
      func(); // 2
      func(); // 3
      func(); // 4
      return 0;
    }
    
  2. 全局作用域
    • 全局變量榔至,在編譯時(shí)分配地址空間,如果沒(méi)有初始化欺劳,默認(rèn)值為0唧取,在程序(進(jìn)程)結(jié)束時(shí)銷毀瓣俯,提前使用(定義前使用)需要先聲明,可以聲明多次兵怯。
    #include <stdio.h>
    void func(){
      extern int a; // 在這之前未定義,需要聲明
      printf("func = %d\n", a);
    }
    
    int a = 10;
    
    void func1(){
      printf("func1 = %d\n", a);
    }
    
    void func2(){
      int a = 20;
      printf("func2 = %d\n", a);
    }
    
    int main(int argc, char const *argv[])
    {
      func();
      func1();
      func2();
      return 0;
    }
    
    • static全局變量(包括函數(shù))腔剂,和普通全局變量的區(qū)別就是作用域不同媒区,只在本文件內(nèi)可用。

內(nèi)存分配

  1. 在程序執(zhí)行前掸犬,有幾個(gè)分區(qū)的內(nèi)存已經(jīng)確定袜漩,可以在linux使用size命令查看。

    • text(代碼區(qū)):一般只讀湾碎,函數(shù)宙攻。
    • data:已初始化的數(shù)據(jù),全局變量介褥,static變量座掘,文字常量區(qū)(只讀)。
    • bss:未初始化數(shù)據(jù)柔滔,全局變量溢陪,static變量
      目標(biāo)文件段大小
  2. 在程序運(yùn)行時(shí)除了加載上述已確定的內(nèi)存外,還加載包括堆區(qū)內(nèi)存和棧區(qū)內(nèi)存睛廊。

    • 棧內(nèi)存(stack):存放普通局部變量形真,自動(dòng)管理內(nèi)存。


      查看棾空間
    • 堆內(nèi)存(heap):手動(dòng)申請(qǐng)空間咆霜,手動(dòng)釋放或程序結(jié)束系統(tǒng)釋放,使用函數(shù)malloc(int size)申請(qǐng)堆內(nèi)存嘶朱。
    #include <stdio.h>
    #include <stdlib.h>
    
    int main(int argc, char const *argv[])
    {
      int *p;
      p = (int *)malloc(sizeof(int));
      if(NULL == p){
        return -1; // 空間分配失敗
      }
      *p = 100;
      printf("%d\n", *p); // 100
      printf("%p\n", p); // 00FF2A48
      if(NULL != p){
        // 釋放 p 所指向的內(nèi)存蛾坯, p 中存儲(chǔ)的地址信息不變,這個(gè)地址不能再被使用疏遏。
        free(p); 
        p = NULL;
      }
      //*p = 20; // err
      return 0;
    }
    }
    

結(jié)構(gòu)體

  1. 定義和聲明
#include <stdio.h>
#include <string.h>

struct Student
{
  char name[50];
  int age;
};

struct Teacher // 定義并聲明
{
  char name[50]; 
  int age;
}t1, t2 = { "hgz",18 };

struct // 定義匿名結(jié)構(gòu)體并聲明
{
  char name[50];
  int age;
}w1 = { "hgz",18 }, w2;


int main(int argc, char const *argv[])
{
  struct Student stu1 = { "hgz",18 };

  struct Student stu2;
  strcpy(stu2.name, "hgz");
  stu2.age = 18;

  struct Student stu3;
  struct Student *p;
  p = &stu3;
  strcpy(p->name, "hgz");
  p->age = 18;

  printf("%s\n", "ok");
  return 0;
}
  1. 結(jié)構(gòu)體數(shù)組
struct Student stu[2] = {
  {"hgz", 18},
  {"zzz", 19}
};
  1. 結(jié)構(gòu)體直接作為參數(shù)傳遞是值傳遞偿衰,如果需要修改原結(jié)構(gòu)體,需要用地址傳遞(參數(shù)為結(jié)構(gòu)體指針)改览。
#include <stdio.h>

struct Student
{
  int age;
};

void func(struct Student *p){
  p->age = 20;
}

int main(int argc, char const *argv[])
{
  struct Student s1 = {18};
  func(&s1);
  printf("%d\n", s1.age);
  return 0;
}
  1. 結(jié)構(gòu)體中的指針下翎。(要先給地址再使用,可以給文字常量區(qū)字符串地址宝当,棧地址或者堆區(qū)地址)
#include <stdio.h>
#include <string.h>

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


int main(int argc, char const *argv[])
{
  // 相當(dāng)于 s1.name = "zzz"; 將data區(qū)文字常量的字符串的首地址賦值給指針
  struct Student s1 = {"zzz", 18}; 
  printf("s1 = %s %d\n", s1.name, s1.age);

  struct Student s2;
  // strcpy(s2.name, "hgz"); // err 野指針 s2.name 還沒(méi)有地址
  char name[100];
  s2.name = name; // 分配棧區(qū)空間
  strcpy(s2.name, "hgz");
  s2.age = 20;
  printf("s2 = %s %d\n", s2.name, s2.age);

  struct Student s3;
  s3.name = (char *)malloc(strlen("hgzzz")); // 分配堆區(qū)空間
  strcpy(s3.name, "hgzzz");
  s3.age = 22;
  printf("s3 = %s %d\n", s3.name, s3.age);
  if(NULL != s3.name){ // 注意使用堆區(qū)地址時(shí)视事,使用完要釋放
    free(s3.name);
    s3.name = NULL;
  }
  return 0;
}

共用體

  1. 共用體所有成員公用一塊內(nèi)存地址,內(nèi)存大小為最大成員內(nèi)存大小庆揩。所有成員都指向首地址俐东。
#include <stdio.h>

union Test{
  unsigned int a;
  unsigned short b;
  unsigned char c;
};

int main(int argc, char const *argv[])
{
  union Test t1;

  // &t1.a = 0061FF2C &t1.b = 0061FF2C &t1.c = 0061FF2C 共用體成員指向同一塊地址頭
  printf("&t1.a = %p &t1.b = %p &t1.c = %p\n", &t1.a, &t1.b, &t1.c);

  t1.a = 0x44332211;
  // t1.a = 44332211 t1.b = 2211 t1.c = 11 共用體公用一塊內(nèi)存
  printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);

  t1.c = 0xaa;
  // t1.a = 443322aa t1.b = 22aa t1.c = aa 修改共用體中的成員可能會(huì)影響其他成員
  printf("t1.a = %x t1.b = %x t1.c = %x\n", t1.a, t1.b, t1.c);

  return 0;
}

枚舉類型

  1. 定義
#include <stdio.h>

enum{ // 不初始化賦值默認(rèn)為 0 開始遞增
  pink, red, yellow
};

int main(int argc, char const *argv[])
{
  printf("%d\n", pink == 0 ); // 1
  printf("%d\n", red == 1 ); // 1
  printf("%d\n", yellow == 2 ); // 1
  return 0;
}
  1. typedef關(guān)鍵字:給一個(gè)已存在的類型取一個(gè)別名跌穗。
#include <stdio.h>

int main(int argc, char const *argv[])
{
  typedef int int32; // 當(dāng)前使用的是32位gcc編譯器

  typedef struct Student
  {
    int32 age; // typedef替換發(fā)生在編譯階段 宏定義是在預(yù)編譯階段
  }Student;

  Student s1 = {18};
  printf("%d\n", s1.age);

  return 0;
}

文件操作

  1. FILE 結(jié)構(gòu)體:我們?cè)谶M(jìn)行文件操作時(shí)要用到這個(gè)結(jié)構(gòu)體,在我們調(diào)用fopen(const char * filename, const char * mode)時(shí)虏辫,會(huì)返回一個(gè)指向堆區(qū)的指針(這個(gè)指針指向結(jié)構(gòu)體的地址蚌吸,和文件沒(méi)有直接關(guān)系)。這個(gè)函數(shù)會(huì)初始化結(jié)構(gòu)體成員砌庄,這些成員和文件有著聯(lián)系羹唠,當(dāng)我們?cè)谶M(jìn)行文件操作時(shí),實(shí)際上是這些成員在操作娄昆。
typedef struct{
  short level; // 緩存區(qū)‘滿’或者‘空’的程度
  unsigned flags; // 文件狀態(tài)標(biāo)志
  char fd; // 文件描述
  unsigned char hold; // 如無(wú)緩沖區(qū)不讀取字符
  short bsize; // 緩沖區(qū)大小
  unsigned char *buffer; // 數(shù)據(jù)緩沖區(qū)位置
  unsigned ar; // 指針佩微,在文件中的指向位置
  unsigned istemp; // 臨時(shí)文件指示器
  short token; // 用于有效性檢查
}FILE;
  1. 使用方式
#include <stdio.h>

int main(int argc, char const *argv[])
{
  // 第二個(gè)參數(shù)為 w 表示以寫操作打開文件,如果文件不存在萌焰,創(chuàng)建文件哺眯,
  // 如果存在,刪除內(nèi)容并打開
  FILE *fp = NULL;
  fp = fopen("b.txt", "w");

  if(NULL == fp){
    perror("fopen");
    return -1;
  }

  // fputs(int a, FILE *stream)
  char a = 'a';
  while(a <= 'z'){
    fputc(a, fp);
    a++;
  }

  // 關(guān)閉文件
  fclose(fp);
  fp = NULL;

  return 0;
}
  1. 標(biāo)準(zhǔn)文件輸出
#include <stdio.h>

int main(int argc, char const *argv[])
{
  printf("%s\n", "zzzz"); // 標(biāo)準(zhǔn)輸出(屏幕輸出) zzzz

  fclose(stdout); // 關(guān)閉標(biāo)準(zhǔn)輸出文件指針
  printf("%s\n", "hgz"); // 無(wú)輸出
  perror("abc"); // 打印函數(shù)調(diào)用失敗的原因 abc : Bad file descriptor 
  return 0;
}
  1. 按塊大小讀寫文件fread() fwrite()
#include <stdio.h>

typedef struct Student
{
  char name[50];
  int age;
}Student;

int main(int argc, char const *argv[])
{
  Student stus[4] = {
    { "hgz1", 18 },
    { "hgz2", 19 },
    { "hgz3", 20 },
    { "hgz4", 21 }
  };
  FILE *fp = fopen("6.txt", "w");
  if(NULL == fp){
    perror("fopen");
    return -1;
  }
  int res = fwrite(stus, sizeof(stus), 1, fp);
  printf("%d\n", res);

  fclose(fp);
  fp = NULL;
  return 0;
}
#include <stdio.h>

typedef struct Student
{
  char name[50];
  int age;
}Student;

int main(int argc, char const *argv[])
{
  FILE *fp = fopen("6.txt", "r");

  Student stus[10];

  int res = fread(stus, sizeof(Student), 4, fp);

  printf("res = %d\n", res);

  for(int i = 0; i < 4; i++){
    printf("%s %d\n", stus[i].name, stus[i].age);
  }

  fclose(fp);

  return 0;
}

fread() fwrite()實(shí)現(xiàn)文件拷貝命令

#include <stdio.h>

int main(int argc, char const *argv[])
{
  // 模擬linux cp 命令 -> cp a.txt b.txt 將a.txt 內(nèi)容復(fù)制到b.txt中

  if(argc != 3){
    printf("%d\n", argc);
    printf("argument count error:a.exe src dst\n");
    return 0;
  }
  // 如果第二個(gè)參數(shù)是 r 在windows處理二進(jìn)制文件會(huì)出問(wèn)題
  FILE *srcfp = fopen(argv[1], "rb"); // 源文件
  FILE *dstfp = fopen(argv[2], "wb"); // 拷貝目的文件

  char buf[4*1024];
  int len; // 每次讀取的長(zhǎng)度
  while(1){
    len = fread(buf, 1, sizeof(buf), srcfp);
    printf("len = %d\n", len);
    if(len == 0){
      break;
    }
    fwrite(buf, 1, len, dstfp);

  };

  fclose(dstfp);
  fclose(srcfp);

  return 0;
}
  1. 文件讀寫緩存區(qū)扒俯,在讀寫文件時(shí)奶卓,先把數(shù)據(jù)讀寫到緩沖區(qū)中。當(dāng)緩存區(qū)滿撼玄、調(diào)用(fclose()寝杖、fflush())等刷新緩存區(qū)函數(shù)、程序關(guān)閉時(shí)才從緩沖區(qū)讀寫文件互纯。
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末瑟幕,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子留潦,更是在濱河造成了極大的恐慌只盹,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件兔院,死亡現(xiàn)場(chǎng)離奇詭異殖卑,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)坊萝,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門孵稽,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人十偶,你說(shuō)我怎么就攤上這事菩鲜。” “怎么了惦积?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵接校,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我狮崩,道長(zhǎng)蛛勉,這世上最難降的妖魔是什么鹿寻? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮诽凌,結(jié)果婚禮上毡熏,老公的妹妹穿的比我還像新娘。我一直安慰自己侣诵,他們只是感情好痢法,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著窝趣,像睡著了一般。 火紅的嫁衣襯著肌膚如雪训柴。 梳的紋絲不亂的頭發(fā)上哑舒,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天,我揣著相機(jī)與錄音幻馁,去河邊找鬼洗鸵。 笑死,一個(gè)胖子當(dāng)著我的面吹牛仗嗦,可吹牛的內(nèi)容都是我干的膘滨。 我是一名探鬼主播,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼稀拐,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼火邓!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起德撬,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤铲咨,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后蜓洪,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體纤勒,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年隆檀,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了摇天。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡恐仑,死狀恐怖泉坐,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情裳仆,我是刑警寧澤坚冀,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布,位于F島的核電站鉴逞,受9級(jí)特大地震影響记某,放射性物質(zhì)發(fā)生泄漏司训。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一液南、第九天 我趴在偏房一處隱蔽的房頂上張望壳猜。 院中可真熱鬧,春花似錦滑凉、人聲如沸统扳。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)咒钟。三九已至,卻和暖如春若未,著一層夾襖步出監(jiān)牢的瞬間朱嘴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工粗合, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留萍嬉,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓隙疚,卻偏偏與公主長(zhǎng)得像壤追,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子供屉,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

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