天天看點

《C和指針》總結

連結屬性

1.extern 關鍵字用于辨別符第二次或以後的聲明并不會改變第一次聲明所指定的屬性。
2.static 防止被通路 
           

存儲類型

1.變量存儲地方:普通記憶體,堆棧,硬體寄存器
2.代碼塊外聲明的是靜态變量,存于靜态記憶體(普通記憶體),程式運作前存在,始終存在
3.自動變量
4.代碼塊内變量 + static --> 靜态變量
           

運算符

1. i+++ ++i 這種5個加号的存在,gcc中間有空格可以通過,甚至可以是6個,i+++ + ++i,一個表示正數
           

指針

1.定義:
    #define NULL (void*) 0
    #define EOF -1
2. int *a; *a=1;
    unix:段違例 segmentation violation ,記憶體錯誤:memory fault,總線錯誤:bus error
3.對NULL指針間接通路,有的編譯器通路記憶體0,有的引發一個錯誤并終止程式
4.盡量顯示初始化指針
5.指針與整型值的互相轉換罕見
6.*100=25  不是對100記憶體位址的地方指派25,是非法的,100是整型, *(int *)100=25;
    通路硬體本身,很少用
7. * 從右向左的結合性
8.字尾 ++ -- 順序點   ()不是順序點!
     ;  ,  ||  &&  ?:  還有函數的所有的指派之後,第一條語句執行之前。
     函數傳回值已拷貝給調用者但在該函數外代碼執行之前
     每個基類和成員初始化之後
9. *cp++  *++cp  ++*cp  (*cp)++ ++*++cp  ++*cp++  *(*cp)++  字尾++優先級大于*   *不是++順序點
10.for(cp=&arr[0];cp<&arr[LEN];) *cp++;
    for(cp=&arr[LEN];cp>&arr[0];) *--cp;
    for(cp=&arr[LEN-1];cp>=&arr[0];cp--) *cp;//都是作為左值操作
    第三句cp到達&arr[0]之後還減1,再進行比較 cp>=&arr[0]值是未定義的,因為cp移動到了數組邊界之外
    标準允許數組元素的指針與指向數組最後一個元素後面的那個記憶體進行比較,但不允許與數組第一個元素之前!
11.指針的加減法會根據類型自動調整
           

函數

1.函數原型:向編譯器提供一些關于函數的特定資訊,更為安全
2.函數原型參數不是必須
3.沒有參數的原型 int *func(void); void提示沒有參數,而不是表示一個void類型的參數
4.函數的預設認定:程式調用一個無法見到原型的函數是,編譯器認為該函數傳回整型值
5.stdarg 宏
    定義在stdarg.h中,這個檔案聲明了一個類型 va_list和三個宏 va_start va_arg va_end
    float average(int values,...){
        va_list args;
        int sum=0;
        va_start(args,values);//準備通路可變參數
        for(int count=0;count<values;count++){
            sum+=va_arg(args,int);//通路可變參數
        }
        va_end(args);//完成處理可變參數
        return sum/values;
    }
    參數清單至少有一個參數,否則無法va_start
    無法知道實際參數長度,無法判斷參數類型
           

數組

1.數組名是指針常量,而不是指針變量,編譯器用數組名來記住數組屬性
2.程式在完成連結之後,記憶體中的數組的位置是固定的,不能移動,是以數組名是指針常量
3.兩種情況下數組名并不用指針常量來表示:
    1)作為sizeof的操作數:傳回整個數組長度
    2)作為單目操作符&的操作數:取一個數組名的位址傳回指向數組的指針,
    而不是指向一個指針常量的指針!!
4.array[subscript]等同于*(array+(subscript))  subscript甚至可以為負
    2[array]=*(2+array)=array[2]  對編譯器來說無差别
5.下标絕不會比指針更有效率,但指針有時比下标更有效率
    int array[10] a; for(a=0;a<10;a++) array[a]=0;//每次都會有a*4
    int array[10] *ap; for(ap=array;ap<array+10;ap++) *ap=0;//每次都是1*4
    指針效率更高的前提是正确的使用,否則更容易寫出劣質的代碼!
    有些地方維護更難,有些地方追求峰值效率
6.聲明數組參數
    int strlen(char *string);//更加準确,因為實參實際上是個指針而不是數組
    int strlen(char string[]);
7.靜态記憶體的數組隻初始化一次,在程式開始之前,由連結器完成,未顯式初始化會初始化0
    對于自動變量,記憶體位置并不确定,預設為未初始化。衡量利弊是否加static
8.char message[]="hello";隻是 char message[]={'h','e','l','l','o'};另一種寫法
    char *message1="hello";才是真正的字元串常量初始化
9.在C中,多元數組存儲順序按照最右邊下标率先變化的原則,成為行主序 row major order
10. int (*p)[10];//聲明指向整型數組指針int matrix[3][10];p=matrix;//p指向matrix第一行
11.避免 int (*p)[]=matrix;這種聲明。當執行指針運算時,它的值會根據空數組的長度
    進行調整,也就是與0相乘
12.作為函數形參的多元數組需要知道第二次元及以後各維的長度,必須聲明
    void func(int (*p)[10]);void func(int p[][]);寫成void func(**p);是不對的
13.指針數組 int *p[10] 下表優先級較高,是一個數組,10個元素,元素類型是指向整型的指針
14.sizeof傳回值是unsigned int,數組作為其參數不退化,傳回整個數組占用位元組
15.指針數組最後一個元素初始化為NULL,友善操作
           

字元串,字元和位元組

1.包含string.h,它裡面所包含的原型可以是編譯器更好的執行錯誤檢測
2.自己編寫字元串函數,保留以str開頭的函數名,用于标準庫的擴充
3. size_t strlen(char const *string)
    size_t 在stddef.h中定義,是一個unsigned int
    程式保護不會溢出
4.在表達式中無符号數可能導緻不可預料的結果
    if(strlen(x) >= strlen(y))....
    if(strlen(x) - strlen(y) >= 0)....
    看似相等,事實上第二局永遠是真,無符号數不可能為負
5.标準庫函數有的是用彙編實作的
6.char *strcpy(char *dst,char const *src);
    src和dst出現重疊,結果是未定義的
    如果src比dst長,多餘的字元仍将複制!必須程式控制
7.char *strcat(char *dst,char const *src);
    src和dst出現重疊,結果是未定義的
    和strcpy一樣,必須保證剩餘的空間大于src
8.int strcmp(char const *s1,char const *s2);
    s1小于s2,傳回小于0的數,大于傳回大于0的數,等于傳回0
    不等 if( strcmp(a,b) )
9.char *strncpy(char *dst,char const *src,size_t len);
  char *strncat(char *dst,char const *src,size_t len);
  char *strncmp(char const *dst,char const *src,size_t len);
  strncpy 總是複制的長度為len,如果strlen(src)小于len,dst用額外的NUL位元組填充到len長度
  如果大于len,隻有len長度被複制,但是!他的結果不會以NUL結尾!
  strncat 與 strncpy 不同,它總是在結果字元串後面加NUL,不會進行NUL填充,
  最多複制len長度外加一個NUL,不管空間夠不夠
10.char *strchr(char const *str,int ch);
   char *strrchr(char const *str,int ch);
    strchr查找ch第一次出現的位置,傳回指向其的指針,strrchr傳回最後一個
11.char *strpbrk(char const *str,char const *group);
    傳回一個指向str中第一個比對group中任何一個字元的字元位置
12.char *strstr(char const *s1,char const *s2);
13.size_t strspn(char const *str,char const *group);//不再group内
    size_t strcspn(char const *str,char const *group);
14.char *strtok(char *str,char const *seq);
    找到下一個由seq組成的字元合集分割str的标記,并将其用NUL結尾,傳回指向這個标記的指針
    沒有const修飾str,會被修改,會被重複調用,知道傳回NUL
    static char whitespace[]=" \t\f\r\v\n";//有個空格  char *token;
    for(token=strtok(line,whitespace);token!=null;token=strtok(NULL,whitespace))
    可以在每次調用strtok時使用不同的seq,字元串的不同部分用不同的分割
    strtok儲存了函數的局部狀态資訊,不能同時解析多個字元串,
    在for循環體内調用内部調用,strtok的函數将導緻程式失敗
15.char *strerror(int error_number)
    調用函數或者請求作業系統功能如打開檔案出錯時,作業系統通過設定并傳回一個外部整型
    變量errno進行錯誤代碼報告,此函數接受error并傳回描述
16.字元分類:ctype.h 接受一個字元的ASCII碼,函數測試這個字元,并傳回整形值表示真假
    iscntrl isspace isdigit isxdigit islower isupper isalpha isalnum前三個合集 
    ispunct标點符号 isgraph圖形字元 isprint圖形字元和空格
17.字元轉換:ctype.h   int tolower(int ch)   int toupper(int ch)
18.直接測試或測試會降低可移植性 if(ch>='A'||ch<='Z')在ASCII字元集機器上可以
    但使用EBCDIC字元集的機器不可以,if(isupper(ch)) 都可以!
19.記憶體操作:以長度作為操作終止标志,解決字元串\0結尾問題。void指針,其他資料類型也可使用
    void *memcpy(void *dst,void const *src,size_t length)
    void *memmove(void *dst,void const *src,size_t length)
    相比于memcpy,dst和src可以重疊,它利用了一個臨時空間複制,速度慢
    void *memcmp(void const *a,void const *b,size_t length)
    根據無符号位元組進行比較,如果比較的不是單位元組資料,結果不可測
    void *memchr(void *a,int ch,size_t length)
    void *memset(void *a,int ch,size_t length)從a開始設定length個ch
    length是操作的位元組數,不是一位元組的資料類型要數量乘長度,例如int 結構體
           

結構體

1.struct{....}x;struct{....}*y;這兩個聲明被編譯器當成不同的聲明,y=&x也是非法的,即使成員清單相同
2.想在多個源檔案中使用定義的結構體,放在頭檔案中
3.間接通路運算符*的優先級小于成員通路 .     ->大于&
4.可以包含自身的引用,但不能在沒定義别名前用别名。不能包含自身,因為遞歸不能終止
5.互相引用用不完整聲明
6.如果不存在将相關成員放在一起的要求,應該根據邊界進行重排結構體的成員存儲結構
7.sizeof傳回結構體的整體長度,offsetof宏(stddef.h)傳回指定成員開始存儲的位置距離結構體開始
    存儲的位置偏移幾個位元組,傳回size_t,offset(type,member)type是結構名,member是成員名
8.位段:
    隻能聲明為int,signed int,unsigned int(用作表達式時自動更新的類型),
    用int并不好,它究竟是解釋為有符号還是無符号由編譯器決定
    段中的最大位數限制在一個整型之内,成員的記憶體配置設定從左到右還是從右到左未知
    後面的位段不能夠容納與前面一個位段剩餘空間時,可能直接接在前面的位段後面(内
    存位置邊界上形成重疊),也可能放在記憶體的下一個字。
9.位段實作的功能都可以用移位和屏蔽來實作,在目标代碼中,移位和屏蔽是必需的,
    位段唯一好處源代碼簡潔,但是可移植性較弱
           

聯合體

1.初始化必須是第一個元素,必須位于一對花括号内,如果類型不符,會轉換(如果可能)并賦給第一個
           

動态記憶體配置設定

1.void *calloc(size_t num_elements,size_t element);void *malloc(size_t size);
    void *realloc(void *ptr,size_t new_size);void free(void *pointer);
    calloc 會初始化為0。free,realloc傳遞NULL作為參數無作用。配置設定不成功傳回NULL
    定義在stdlib.h。NULL定義在stdio.h 
           

進階指針

1.指向指針的指針
2.了解聲明方法 1)推論聲明  2)用于聲明變量的表達式和普通的表達式在求值時使用相同的規則
    int *f()   int (*f)()  int *(*f)()   int *f[]  int(*f[])()  int *(*f[])()
    int f()[] f是一個函數,它的傳回值是一個整形數組。它是非法的,函數不能傳回數組
    int f[]() f是一個數組,它的元素類型是傳回整型的函數。非法的,數組元素長度相同,不可能是函數
    unix系統cdecl程式可以轉換聲明和英語
3.函數指針
    初始化:int f(int );  int (*pf)(int) = &f;
    調用函數: f(12)  (*pf)(12) 或者 pf(12)
    f(12)簡單調用函數。過程:函數名f首先被轉換成一個函數指針,該指針指向函數在記憶體中的位置。
    然後,函數調用操作符調用該函數,執行開始與這個位址的代碼。是以,(*pf)(12)函數指針轉換
    成函數編譯器又将其轉換回去,pf(12)才是正道
4.回調函數,java中政策模式,接口實作,例如Comparator,但java中可以用實作接口和泛型解決
    類型的問題,C中隻能是 void*,靈活但不安全。
    在寫用于函數指針調用的函數時一般參數為void*,注意強制轉換。
    強制類型轉換會躲過一般的類型檢測,必須格外注意類型的問題。
    函數指針調用庫函數,例如strcmp,編譯器一般有警告,因為參數是void*,而不是char*
5.轉換表。彙編中TAB也經常這樣用
6.字元串是指針常量,字元串出現在表達式中的時候就是指針常量
    轉換二進制為字元 "0123456789ABCDEF"[value % 16];  *"xyz" 就是 x
           

預處理

1.文本性質的操作
2.預處理符号 _FILE_  _LINE_  _DATE_  _TIME_  _STDC_
3.#define 可以多行,除了最後一行,其他行行尾要加反斜杠。
4.宏定義中如果調用函數不要再末尾加上分号。有些語句還好,但例如if中,兩個分号的出現會出問題
5.#define name(parameter-list) stuff。參數清單左括号和name必須緊鄰,否則變成stuff
6.對數值進行求值得宏定義各個參數都要加上括号,最外層還要括号
7.宏參數和#define定義可以包含其他#define定義的符号,宏不可以出現遞歸
8.預處理對字元串常量内容并不進行進檢測。把宏參數插入字元串常量中兩種方法
    1)鄰近字元串自動連接配接特性
        #define PRINT(FORMAT,VALUE) printf("result:" FORMAT "\n",VALUE) PRINT("%d",1+1);
    2.使用預處理器把宏參數轉換成字元串  #argument會被翻譯成 "argument"
        #define PRINT(FORMAT,VALUE) printf(#VALUE "is" FORMAT "\n",VALUE) PRINT("%d",1+1);
9.## 把位于它兩邊的符号連接配接成一個符号
    #define ADD_T0_SUM(sum_num,value) sum ## sum_num +=value   
    ADD_T0_SUM(5,25) sum5變量加上25
10.宏與函數:
    定義簡單的工作 #define MAX(a,b) ((a)>(b)?(a):(b))
    考慮函數調用的消耗,與宏類似内聯函數占用的空間
    函數有資料類型的限制,例如上面的。
    一些任務函數無法實作,例如當參數是一種資料類型。
11.宏參數的副作用,宏參數在宏定義中出現超過一次時,宏參數副作用可能出現危險。
    common lisp 宏也有這個問題,但它的宏實在強大,可以很輕松解決
    z=MAX(x++,y++)  z=((x++) > (y++) ? (x++) : (y++))
    小的值增加了一次,大的增加了兩次。
    不僅限于改變變量的值,例如getchar()也有副作用。
12.條件編譯 #if constant-expression  不想出現在産品中,調試,維護卻需要
                statement
            #elif ......    #else  ....    #endif  ....  
        constant-expresion(常量表達式) 由預處理器進行求職,非0就statement。例如ASSIGN宏
    #if defined(symbol)  等同于 ifdef symbol , if !defined(symbol) 等同于 ifndef symbol
    #if 功能更強大 #if x>0 || defined(ABC) && defined(BCD)
    可以嵌套,
13.編譯器定義一系列标準位置查找函數庫頭檔案,/user/include,可以添加
14.标準要求頭檔案嵌套至少支援8層,但沒有理由讓嵌套超過兩層,很難判斷源檔案之間真正依賴關系
    unix的make實用工具必須知道這些依賴關系決定檔案修改後哪些要重新編譯。
15.#error  #line  #progma   # 無效指令
           

輸入輸出函數

1.ANSI編譯器允許在函數庫基礎上增加函數,但如果關心可移植性,應該避免
2.void perror(char const *message);  stdio.h ;會在message後面加個冒号和錯誤内容
3.标準庫函數在一個外部整型變量errno(errno.h中定義)中儲存錯誤代碼後把這個資訊傳遞給使用者程式
4.隻有當一個庫函數失敗時errno才會被設定,是以我們不能通過測試errno值來判斷是否有錯誤發生
5.void exit(int status);stdlib.h; status值傳回個作業系統,用于提示程式是否正常,這
    個值和main傳回的整型狀态相同。經常在perror後調用,實在出錯的不能忍了。無處可傳回
6.絕大多數流失完全緩沖的,意味着讀取和寫入實際上是從一塊緩沖區記憶體區域來複制資料
7.printf分布于程式中用于調試。但這些函數的輸出結果被寫入緩沖區,并不會立刻顯示。
    如果程式失敗,緩沖輸出可能不會被實際寫入,會得到錯誤出現的位置不正确的結論
    解決辦法:printf("something");fflush( stdout ) ;立即 fflush
8.對于那些文本行的外在表現形式與換行符表示文本結束的标準不同的系統上,庫函數負責翻譯
9.stdio.h聲明了FILE結構,用于通路一個流。每個流對應一個FILE。
10.每個程式運作,系統至少提供三個流,stdin,stdout,stderr,都是指向FILE結構的指針
11.标準輸入輸出和錯誤是預設的位置,因編譯器而異,通常鍵盤和終端
    可以重定向 $ program < data > answer  DOS和UNIX都支援
12.FILE *fopen( char const *name , char const *mode ); FILE *變量的名字用于儲存fopen的傳回值,它并不影
    響哪個檔案被打開。name為字元串,避免在各系統中的差異。文本:r w a 二進制:rb wb ab 更新:a+,可讀寫
    失敗傳回NULL指針,errno會提示問題的性質,應該始終檢查傳回值
13.FILE *freopen(char const *filename,char const *mode,FILE *stream);
    打開或重新打開stream,首先試圖關閉stream,然後用指定的filename和mode重新打開這個流,失敗傳回NULL
14.int flose(FILE *f);成功傳回0,否則傳回EOF
15.fopen和fclose直接的程式可能導緻FILE*的改變,造成fclose失敗
16.int fgetc(FILE *stream); int getc(FILE *stream); int getchar(void);
    讀取一個字元,沒有傳回EOF。傳回值是int而不是char,是因為EOF
17.int fputc(int character,FILE *stream);int putc(int character,FILE *stream);int putchar(int character);
    第一個參數是要被列印的字元,在列印前,函數把這個整型參數裁剪為一個無符号字元值
    putchar('abc');隻列印一個字元,至于是哪一個由編譯器決定。寫入到已關閉流或其他失敗,傳回EOF
18.fgetc和fputc是函數,getc,putc,getchar,putchar,是宏
19.int ungetc(int character,FILE *stream);把character傳回到流中
20.char *fgets(char *buffer,int buffer_size,FILE *stream);
    緩沖區到達buffer_size-1或者讀到換行符,停止讀取。下一次調用從流下一個字元開始,無論如何末尾都是NIL
    如果在任何字元讀取前就到達了檔案尾,緩沖區沒改變,fgets傳回NULL,否則傳回指向緩沖區的指針
21.int fputs(char const *buffer,FILE *stream);buffer必須包含一個字元串,但它不像fgets最多隻能讀取一行。
    寫入錯誤時,傳回EOF,否則傳回非負值
22.char *gets(char *buffer);int puts(char const *buffer);和fgets,fputs類似,保留它們是為了向後相容
    不同在于gets讀取一行時,并不在行尾保留換行符,puts寫入時會再添加一個換行符。
    而且,gets沒有緩沖長度參數,多出來的字元會被寫入緩沖區後面的記憶體!!
23.int fscanf(FILE *stream,char const *format,...);
    int scanf(char const *format,...);int sscanf(char const *string,char const *format,...);
    省略号代表一個可變長度的指針清單,從輸入轉換而來的值逐個存儲到這些指針指向的記憶體。
    會跳過空白字元。當格式化字元串到達末尾或者讀取的輸入不再比對格式字元串所指定的類型時,
    輸入就停止,輸入值的數目被作為傳回值,沒有任何輸入就到結尾傳回EOF。
    格式代碼format都是以%開頭,後面可以是一個可選星号(丢棄不存儲這個字元),一個可選的寬度(非負整數,
    限制被讀取用于轉換的長度,在類型長度長于或短于預設長度時,不指定寬度将出錯,總是指定更具移植性),
    一個可選限定符(h,l,L),和一個格式代碼
24.                 h                   l                  L
    d,i,n       short                   long        
    o,u,x       unsigned short      unsigned long   
    e,f,g                           double              long double
25.格式碼:
    c(char *) 讀取單個字元,如果給出寬度,就讀取寬度個,末尾不會加NUL,
    i,d(int *) 有符号,i根據第一個字元決定基數,
    u,o,x(unsigned *) u十進制,o八進制,x十六進制
    e,f,g(float *) 
    s(char *) 一串非空字元,發現空白停止,自動加NUL
    [xxx](char *) 一圈字元,發現第一個不在給定組合内的字元停止,自動加NUL,清單頭可加^,表示不在組合内,
        是否支援橫杠表示範圍内字元因編譯器而異,例如 %[a-z]
    p(void *) 輸入預期為一串字元,轉換方式因編譯器而異,但轉換結果用p列印産生的字元和輸入是相同的
    n(int *) 到目前為止通過scanf從輸入讀取的字元數被傳回。%n轉換的字元并不計算在scanf傳回值之内。
        它本身不消耗輸入
    % 與輸入中的%相比對,該%被丢棄
26.int fprintf(FILE *stream,char const *format,...);
    int printf(char const *format,...);  int sprintf(char *buffer,char const *format,...);
27.size_t fread(void *buffer,size_t size,size_t count,FILE *stream);
    size_t fwrite(void *buffer,size_t size,size_t count,FILE *stream);
    size是緩沖區每個元素的位元組數,count是讀取或寫入的元素數,buffer的空間要有size*count這麼大
    傳回子是實際吸入或讀取的元素(不是位元組)數目。可以實作java中對象流的操作
28.int fflush(FILE *stream);迫使一個輸出流的緩沖區的資料進行實體寫入。
29.一般情況資料室線性寫入,
    long ftell(FILE *stream);傳回流的目前位置,在二進制流中,這個值就是目前位置距離檔案起始位置的
        位元組數偏移量,在文本流中,并不一點準确地代表字元數偏移量,因為有些系統将對行末字元進行翻譯
        但ftell的傳回值總可以用于fseek函數中。
    int fseek(FILE *stream,long offset,int from);定位一個流,改變下一次操作的位置。
    from:SEEK_END,從尾部起offset個位元組,可正可負。SEEK_CUR,offset可正可負。SEEK_SET,起始位置,offset非負
30.試圖定位到檔案起始位置之前是錯誤,定位到檔案尾之後并進行寫入會擴充這個檔案,試圖通路會導緻傳回
    一條“到達檔案尾”的資訊。二進制流可能不支援SEEK_END,文本流中SEEK_CUR,SEEK_END,offset必須是0,
    如果from是SEEK_SET,offset必須是ftell的傳回值
31.fseek副作用:行末訓示字元被清除。如果fseek之前ungetc,那麼被退回的字元會被丢棄,因為定位操作後,它不
    再是下一個字元。定位允許從寫入模式切換到讀取模式,或者回到打開的流以便更新
32.void rewind(FILE *stream);定位到流的起始位置,同時清楚流的錯誤辨別
    int fgetpos(FILE *stream,fpos_t *position);int fsetpos(FILE *stream,fpos_t const *position);
    ftell與fseek的替代方法,fgetpos存儲目前位置到fpos_t,fsetpos設定檔案到fpos_t
    fpos_t表示檔案位置不是标準定義的,可能是偏移量也可能不是,
33.void setbuf(FILE *stream,char *buf);int setvbuf(FILE *stream,char *buf,int mod,size_t size);
    必須在流打開但并沒有執行任何其他操作前執行,setbuf設定一個數組對流進行緩沖,長度為BUFSIZ,
    在stdio.h中的定義。為流自行制定緩沖區可以防止I/O函數庫為它動态配置設定緩沖。buf為NULL則關閉緩沖。
    為流使用自動數組緩沖可能導緻流還沒關閉,但數組已經出了代碼快,記憶體被别的函數使用,流也繼續使用
    setvbuf:mode用于指定緩沖的類型,_IOFBF指定一個完全緩沖的流,_IONBF指定一個不緩沖的流,_IOLBUF指定一個
    行緩沖流,就是每當一個換行符寫入緩沖區,緩沖區進行重新整理。buf為NULL,size必須為0,一般為BUFSIZ
34.流錯誤函數;int feof(FILE *stream);流處于檔案尾傳回真。int ferror(FILE *stream);報告流的錯誤狀态,
    如果出現任何讀寫錯誤就傳回真。void clearerr(FILE *stream);對流的錯誤标志進行重置
35.臨時檔案: FILE *tmpfile(void);以wb+模式打開,這使它可用于二進制和文本資料。如果檔案必須由其他模式打開
    或者由另一個程式打開,必須用fopen,而且不再需要時必須使用remove删除。
    char *tmpnam(char *name);建立臨時檔案的名字。參數為NULL,傳回一個指向靜态數組的指針,該數組包含
    檔案名,否則指定一個長度至少為L_temnam的數組,檔案名在傳回的指針代表的數組中。會保證不重名,TMP_MAX
36.int remove(char const *filename);int rename(char const *oldname,char const *newname);
    成功傳回0,否則傳回非0。remove檔案已經打開,結果取決編譯器。rename存在newname,結果編譯器,oldname仍可用
           

标準庫函數

1.stdlib.h   int abs(int value); long int labs(long int value);長整形的abs
    div_t div(int numerator,int denominator);第二個參數是分母,第一個是分子。産生商和餘數,
    div_t的結構 int quot;//商,int rem;//餘數
    ldiv_t ldiv(long int number,long int denom);
2.stdlib.h   下面函數産生的是僞随機數 int rand(void);0-32767 為了避免每次調用rand獲得相同的随機清單,用
    void srand(unsigned int seed);常用每天的時間做種子 srand( (unsigmaned int ) time(0));
3.math.h   double sin(double angle);....cos....tan.....double asin(double value);..atan....
    double atan2(double x,double y);y/x的正切值。都是弧度表示
    double sinh(double angle);.....cosh....tanh.....
4.math.h  double exp(double x); double log(double x);  double log10(double x);
5.math.h  浮點表示形式  double frexp(double value,int *exponent);計算一個指數exponent和小數fracion使
    fraction*pow(2,exponent)的值等于value,其中0.5<=fraction<1。
    double ldexp(double fraction,int exponent);傳回fraction*pow(2,exponent);
    double modef(double value,double *ipart);把浮點數value分成整數*ipart和小數傳回值
6.math.h  double pow(double x,double y); x的y次方
    double sqrt(double x);
7.定義域錯誤demain error,實參不再函數定義域内。範圍錯誤range error數過大或過小超過表示範圍
8.double floor(double x);double ceil(double x);double fabs(double x);double fmod(double x,double y);
    fmod傳回x除以y産生的餘數,商被限制為整數值。用double隻是為了表示更大的範圍
9.stdlib.h  字元串轉換
    int atoi(char const *string);
    long int atol(char const *string);
    long int strtol(char const *string,char **unused,int base);
    unsigned long int strtoul(char const *string,char **unsed,int base);
    double atof(char const *string);
    double strtod(char const *string,char **unused);
    任何一個函數的第一個參數包含前導空白字元将被忽略,任何非法字尾,它們也将被忽略
    atoi和atol執行基數是10的運算。strtol儲存一個指向轉換值後面第一個字元的指針,如果函數的第二個參數
    并非NULL,這個指針便儲存在第二個參數所指向的位置。這個指針允許字元串的剩餘部分進行處理而無需推測
    轉換在哪個位置終止。base是轉換的基數,如果基數是0,任何用于書寫整數字面量的形式都被接受,包括指定
    數字基數的形式,如0x2af4,0377。否則,基數值應該在2到36直接,對于基數11-36,字母A/a-Z/z被解釋為10-35
    如果string無法表示合法的數值,傳回0,函數在errno中存儲ERANGE
10.time.h  clock_t clock(void);程式開始運作處理器消耗時間,近似值,想得到精确值在main開始調用,相減。
    傳回的是處理器時鐘滴答次數,想得到秒要除以常量CLOCKS_PER_SEC。太大傳回-1
11.time_t time(time_t *returned_value);太大傳回-1,标準沒規定結果用秒表示,不能用于判斷時間流逝,
12.char *ctime(time_t const *time_value);傳回字元串,例如 Sun Jul 4 04:02:28 1976\n\0;
    字元串内的空格是固定的,
    double difftime(time_t time1,time_t time2);計算差,并轉化成秒
13.struct tm *gmtime(time_t const *time_value);把時間轉換成格林尼治時間,世界協調時間
    struct tm *localtime(time_t const *time_value);轉換成當地時間,但标準沒有UTC和當地時間關系
    strutc tm{int tm_sec;0-61;int tm_min;0-59;int tm_hour;0-23;int tm_mday;1-31;
    int tm_mon;0-31;int tm_year;0-?;要加1900;int wday;0-6;int tm_yday;0-356;int tm_isdat;夏令時标志}
14.char *asctime(struct tm const *tm_ptr);傳回的格式和ctime相同
    size_t strftime(char *string,size_t maxsize,char const *format,struct tm const *tm_ptr);
    非常靈活的tm結構轉換成字元串
15.time_t mktime(struct tm *tm_ptr);
16.setjmp.h   int setjmp(jmp_buf state); void longjmp(jmp_buf state,int value); 類似goto機制
    首先聲明jmp_buf,并調用setjmp對它初始化,傳回值是0,setjmp把程式狀态儲存到緩存區,調用處的函數
    稱為頂層函數,以後調用longjmp,會導緻儲存的狀态恢複,setjmp傳回longjmp的參數value
17.信号 signal.h
18.void abort(void); void atexit(void (func)(void)); void exit(int status);
    abort不正常終止程式,引發SIGABRT信号。atexit注冊退出信号,當eixt調用時,注冊的函數将按注冊順序
    反序被調用,然後流的緩沖區被重新整理,檔案關閉,tmpfile檔案被删除
19.assert.h  void assert(int expression);假時列印資訊并終止,  #define NEBUG禁用斷言
20.stdlib.h  char *getenv(char const *name);傳回作業系統維護的name對應的資訊
21.stdlib.h  void system(char const *command);系統執行,存在可用,傳回非零值,否則傳回零
22.void qsort(void *base,size_t n_elements,size_t el_size,int (*compare)(void const *,void const *));
    void bsearch(void const *key,void const *base,size_t n_element,size_t el_size,
        int (*compare)(void const*,void const *));
           

運作時環境

1.-S保留彙編代碼
2.參數按參數清單相反的順序壓入堆棧,這樣第一個參數位于堆棧中參數的頂部
    如果相反順序,第一個參數距離幀指針的偏移量就和壓入堆棧的參數數量有關。雖然編譯器可以計算,但
    可變實參清單就無法計算偏移量了
3.函數三部分:函數序prologue,函數啟動需要的工作。函數體body。函數跋epilogue,函數傳回前清理堆棧
           

繼續閱讀