C語言學習筆記--基礎語法三

来源:互联网 发布:衣柜 知乎 实木 品牌 编辑:程序博客网 时间:2024/05/08 06:33

C語言學習筆記–基礎語法三

1、共用體與位域

共用體,是一種特殊的數據類型,允許在相同的內存位置,存儲不同的數據類型。可以定義一個帶有多個成員的共用體,但是使用的時候,只能有一個成員有效的擁有數值。

​ 共用體提供了一種使用相同內存的有效方式。

  • 定義共用體

    union關鍵字,類似與結構體的定義。

    union [union tag]{member definition;member definition;...member definition;}[one or more union variables];

    union tag爲可選項,類似結構體的定義,可以先聲明,也可以聲明並定義。

    #include <stdio.h>#inclued <string.h>//這個共用體,聲明了一個Data共用體,它的變量data,以及包含了三種數據類型,i、f、str[20],共用一個內存地址,這塊內存大小由共用體內最大成員決定。此處就是char類型的str,有20個字節位置。union Data {int i;float f;char str[20];};//main()函數void main(){//聲明共用體變量。union Data data;//顯示共用體內存佔用大小printf("data的內存佔用:%d \n",sizeof(data));}//編譯運行,結果會顯示爲20

    共用體成員的訪問也類似與結構體,使用.符號。

    #include <stdio.h>#include <string.h>union Data {int i;float f;char str[20];};void main(){union Data data;//賦值data.i = 10;data.f = 220.5;strcpy(data.str,"C Programming");//輸出printf("data.i: %d \n",data.i);printf("data.f: %f \n",data.f);printf("data.str: %s \n",data.str);//這樣的輸出結果,只有最後一次會輸出正確的結果,上兩次都會數據損壞,因爲共用體只有一個可用內存變量。//如果每次賦值後都輸出一下,是沒問題的,或者同時使用同一個成員變量,可以更改變量值,也是可行的。data.i = 20;printf("data.i: %d \n",data.i);data.i = 99;printf("data.i %d \n",data.i);}
  • 位域

    位域雖然上一節也有講,但是不清楚,這麼理解吧,位域、結構體、共用體三個概念比較理解,結構體和位域用struct,而共用體用union關鍵字

名稱 是否共用內存 備注 結構體 內部成員各自有自己聲明的內存空間 看作JAVA語言中的一個bean實體類 共用體 內部成員共享內存地址和空間,最大的決定整體空間大小 類似與槍支,可能適配多個型號的子彈,但是每一次開槍,槍管裏就只能一個類型的子彈(姑且這麼比喻吧) 位域 類似共用體,但是算作特殊的結構體,可以單個指定成員佔用的內存二進制位數 算是一個只有一種類型成員的共用體,暫且這麼理解。

示例:

  #include <stdio.h>  #include <sring.h>  //定義簡單的結構體  struct{    unsigned int widthValidated;    unsigned int heightValidated;  } status1;  //定義位域  struct {    unsigned int widthValidated : 1;    unsigned int heightValidated :1;  } status2;  //main()  void main(){    printf("status1佔用內存:%d \n",sizeof(status1));    printf("status2佔用內存:%d \n",sizeof(status2));  }  //結果會分別是8位和4位,因爲結構體算是兩個成員獨立佔用,而位域,算是兩個成員共用,而且雖然佔用4個字節,但是數據只用了1位,若是int widthValidated : 5; 那麼就會是8位了吧,位域不恩那個超出的,也不太清楚了,以後再回來看

位域的聲明

  struct {    /*type爲整數類型,決定了如何解釋位域的值,    member_name爲位域的名稱,    width爲位域中位的數量,不能大於制定類型的位寬度,不如int,就不能大於4個字節。    */    type [member_name] : width;  }

位域指定了變量的位數,也就限制了數值的範圍,超出後會無法正確完成

  #include <stdio.h>  #include <string.h>  //define a bitrange  struct {    //限定範圍了,0--7    unsigned int age:3;  } Age;  void main(){    Age.age = 4;    printf("Sizeof(Age):%d \n",sizeof(Age));    pringf("Age.age : %d \n",Age.age);    //但是不能超過3位二進制數的大小,    Age.age = 8;    printf("Age.age : %d \n",Age.age);    //這時候編譯會警告,數值就會是默認的0  }
  • typedef

    typedefC語言提供的一個用於給類型起新名字的關鍵字。類似與linux系統alias命令,就是用於自定義別名的一個關鍵字指令。

    //定義unsigned char 爲BYTEtypedef unsigned char BYTE;//那麼就可以用來聲明變量BYTE b1;//就相當與unsigned char b1;

    習慣性的將typedef別名化的類型寫作大寫,當然也可以小寫。可以作用與基本數據類型,也可以作用與結構體、共用體、位域之類的自定義類型。

  • typedef 和 #define

    #define是C指令,用於定義數據類型的別名,類似typedef

    • typedef僅用於爲類型定義別名,而#define也可以定義數值的別名,如1可以定義爲ONE
    • typedef由編譯器執行解釋,#define由預編譯器處理
    #include <stdio.h>//定義#define TRUE 1#define TWO 22void main(){printf("TURE:%d,TWO:%d \n",TURE,TWO);}
  • I/O

    C語言提供了一系列的內置函數用於輸入和輸出操作。

    • 標準文件

      C語言把左右設備都當作文件處理,類似與linux下,一切皆文件。

    標準文件 文件指針 設備 標準輸入 stdin 鍵盤 標準輸出 stdout 屏幕 標準錯誤 stderr 您的屏幕

    文件指針是訪問文件的方式。

    • getchar()和putchar()函數

      int getchar(void)函數從屏幕讀取下一個可用字符,一次讀一個,可以房子循環裏面使用。

      int putchar(Int c)函數向屏幕輸出字符,一次輸一個,循環使用。

      #include <stido.h>void main(){int c;printf("請輸入字符:");c = getchar();printf("\n 您輸入的字符是:");putchar(c);}
    • gets()和puts()

      char *gets(char *s)函數從stdin讀取一行到s所指向的緩衝區,一知道遇到終止符或者EOF

      int puts(const char *s)函數把字符串s和一個尾隨的換行符寫入到stdout

      #include <stdio.h>void main(){char str[100];//緩存區域printf("Enter a value:");gets(str);printf("\n Your value:");puts(str);}
    • scanf()和printf()

      int scanf(const char \*format,...)函數從標準輸入流stdin讀取輸入,根據fromat來瀏覽輸入。

      int printf(const char \*format,...)函數吧輸出以format格式顯示,可以是%s、%d、%c、%f等。

    Note:scanf()讀取輸入,需要輸入的數據格式跟format的一樣才行,不然會報錯。遇到空格會停止讀取,便認爲結束了一個字符的讀取。

  • 文件讀寫

    • 打開文件,fopen()函數來創建一個新的,或者打開已有的文件。
    FILE *fopen(const char * filename,const char * mode);

    其中finename是字符串,命名文件用的。mode是讀寫模式

mode description r 打開一個已有的文本文件,允許讀取文件 w 打開一個文本文件,允許寫入文件。若不存在,會新建文件,從頭開始寫起 a 打開一個文本文件,追加模式寫入,不存在則新建 r+ 打開一個文本文件,允許讀寫文件 w+ 打開一個文本文件,允許讀寫。若已存在,文件會被截斷爲另長度,不存在則新建 a+ 打開一個文本文件,允許讀寫。不存在則新建,讀取從頭開始,寫入則是追加模式

若是處理的是二進制文件,寫法有點差異,多個b字符

  "rb","wb","ab","rb+","r+b","wb+","w+b","ab+","a+b"
  • 關閉文件,fclose()關閉文件的函數

    int fclose(FILE *fp);若關閉成功,返回0,關閉失敗,返回EOF(定義在stdio.h中的常量)

  • 寫入文件

    fputc()函數,將c的字符,寫入到fp的輸出流,成功則返回寫入的字符,失敗返回EOF。

    int fputc(int c,FILE *fp);

    fputc()函數也可以吧字符串s寫入到fp的輸出流中,成功返回非負值,失敗返回EOF。

    int fputs(const char *s,FILE *fp);int fpuintf(FILE *fp,const char *format,...);

    示例,會在當前文件夾下,生成text文檔,含有兩句話。

    #include <stdio.h>void main(){  FILE *fp;  fp = fopen("./text.txt","w+");  fprintf(fp,"This is testing for fprintf...\n");  fputs("這是測試fputs輸入\n",fp);  fclose(fp);}
  • 讀取文件

    fgetcfgets函數

    //讀取一個字符,正確返回該字符,錯誤返回EOFint fgetc(FILE * fp);//讀取字符串流,直到讀取到null的標識符,所以之前讀取了有效的n-1個,要是遇到\n或者EOF則會返回讀取的字符。char *fgets(char *buf,int n,FILE *fp);//從文件中讀取,遇到空格會停止int fscanf(FILE *fp,const char *format,...);

    示例,讀取上面的文件

    #include <stdio.h>void main(){  FILE *fp;  char buff[255];  fp = fopen("./text.txt","r");  //只會讀取第一個單詞,因爲遇到空格了  fscanf(fp,"%s",buff);  printf("1: %s\n",buff);  //會讀取一句話,遇到\n或者EOF結束  fgets(buff,255,(FILE*)fp);  printf("2: %s\n",buff);  fgets(buff,255,(FILE*)fp);  printf("3: %s\n",buff);  fclose(fp);}

    首先fscanf只讀取了This,因爲它遇到了空格,之後調用fgets讀取剩餘部分,知道遇到\n或者EOF,而第二次調用fgets讀取了一整句。

  • 二進制讀寫函數

    size_t fread(void *ptr,size_t size_of_elements,size_t number_of_elements,FILE *a_file);size_t fwrite(const void *ptr,size_t size_of_elements,size_t size_of_elements,FILE *a_file);

    常用與存儲塊的讀寫,通常數組結構體

2、預處理器

預處理器不是編譯器的組成部分,其會在編譯器實際編譯之前,指示編譯器做一些預處理工作。C與處理器(C Preprocessor)簡稱CPP,以#開頭,位於行首,文件首。

指令 描述 #define 定義宏 #include 包含一個源代碼文件 #undef 取消已定義的宏 #ifdef 如果定義了宏,返回真 #ifndef 如果沒有定義宏,返回真 #if 條件語句,滿足條件,則執行下面代碼 #else 與if搭配使用 #elif 也就是else if語句快 #endif 結束一個#if…#else語句塊 #error 遇到標準錯誤時,輸出錯誤 #pragma 使用標準化方法,向編譯器發布特殊指令

示例:

//定義常量別名,預處理指令,不需要;分行寫#define MAX 10//引入頭文件#include <stdio.h>#include "myheader.h"//取消宏定義,並更改#undef FILE_SIZE#define FILE_SIZE 88;//條件判斷,預處理的,與代碼塊的if..else不用弄混哦#ifndef MSG    #define MSG "message"#endif
  • 預定義宏

    ANSI C定義了許多宏,但是不能直接修改

宏 描述 _DATE_ 當前日期,”MMM DD YYYY”格式顯示 _TIME_ 當前時間,”HH:MM:SS”格式顯示 _FILE_ 這會包含當前文件名,一個字符串常量 _LINE_ 這會包含當前行號,一個十進制常量 _STDC_ 當編譯器以ANSI標準編譯時,則定義爲1

- 預處理器運算符

  • 宏延伸\

    一個宏通常寫在一行,若是太長,可以用\符號延伸

    #define message_for(a,b) \    printf(#a"and"#b ":Hello\n");
  • 字符串常量化#

    定義宏時候,參數轉化爲字符常量,需要用#

    #include <stdio.h>#define msg(a,b) \    printf(#a "和" #b "是朋友");//此處就是用了#符號,void main(){  msg("小白","小黑");}

    輸出結果:

    小白和小黑是朋友
  • 標記粘貼##

    合並兩個參數

    #include <stdio.h>#define paster(n) printf("token:" #n " = %d",token##n)void main(){  int tocken22 = 80;  paster(22);//這裏輸入22,在paster函數內合並參數,就得到了token22,然後輸出token22這個變量,就得到了下面的結果,分寫一下printf函數,字符token+#n,也就是token22,token##n,代表了上面定義int token22}

    輸出結果:

    token22 = 80

    可以判斷定義

    #inclue <stdio.h>#if !defined(MSG)    #define MSG "hahaha"#endifvoid main(){  printf("msg == %s\n",MSG);}
  • 參數化的宏

    CPP可以使用參數化的宏來模擬函數

    //求平方的函數int square(int x){  return x * x;}//使用宏來定義,名稱和參數的括號之間不能有空格,且緊跟#define#define square(x) ((x)*(x))

    然後就可以在源文件中使用

    #include <stdio.h>#define MAX(x,y) ((x)>(y)?(x):(y))void main(){  printf("Max between 20 and 10 is : %d\n",MAX(20,10));}
    • 頭文件

    .h文件,包含了C函數聲明和宏定義,被多個源文件共享。分爲編譯器自帶程序員自定義兩類。用#include導入。

    //<>包裹的,是系統的.h文件,""包裹的,是程序員自定義的.h文件#include <stdio.h>#include "myheader.h"//都可以通過編譯源碼時候,-l選項,將文件置於列表前。

    其實,頭文件,就相當與復制,類似與java中的導入包和文件類。導入一次就夠了,不用多次導入。一般需要判斷一下

    #ifndef HEADER#define HEADER...#endif

    有條件引用

    #if SYSTEM_1#include "system_1.h"#elif SYSTEM_2#include "system_2.h"//太多的話,可以宏定義一下#define SYSTEM_H "system_1.h"

3、類型轉換

編程語言中常見的數據類型轉換,從低類型轉高類型,從高類型轉低類型。向下轉換不安全的,會丟失數據精度。

(type_name) variable;
  • 整數提升

    其實也就是一種類型轉換,將小範圍類型,轉爲大範圍的,char–int

    char i = 'c';//ascii碼 99int a = 17;int sum = i + c;//得到的sum值,就是int的,116
  • 常用轉換

    一般都會隱式的向上轉型,向下轉型會丟失精度,且需要強制轉型。

    translate

    這並不適合賦值運算符、邏輯運算符的&&||

  • 錯誤處理

    C語言不提供對錯誤的處理,其返回值出錯時多是1或者NULL,同時生成錯誤碼errno,可以在<error.h>中找到各種錯誤碼。通常通過返回值查找錯誤。一般初始化設這errno=0;

    errno、perror()和strerror(),C語言提供了兩個函數來顯示errno的錯誤信息。

    • perror()函數顯示您傳送的字符串,後面一個冒號、一個空格、和當前errno的文本描述
    • strerror()函數返回一個指針,指針指向errno的錯誤描述。

    示例:

    #include <stdio.h>#include <errno.h>#include <string.h>extern int errno;int main(){FILE * pf;int errnum;//以二進制形式打開文件,此處文件不存在,用於模擬錯誤pf = fopen("./no.txt","rb");if(pf==NULL){  errnum = errno;  fprintf(stderr,"錯誤號: %d \n",errno);  perror("通過perror函數輸出錯誤");  fprintf(stderr,"strerror函數顯示,打開文件錯誤: %s \n",strerror(errnum));}else{  fclose(pf);}return 0;}

    錯誤種類很多,非法參數,數組越界之類的。程序正常退出時候會帶有一個EXIT_SUCCESS值,其爲宏定義,爲0。錯誤時候EXIT_FAILURE,爲-1。

0 0
原创粉丝点击