[C程序設計語言]第四部分,c程序設計第四部分
聲明:原創作品,轉載時請注明文章來自
SAP師太博客,並以超鏈接形式標明文章原始出處,否則將追究法律責任!
結構體... 1
sizeof3
類型定義(typedef)3
聯合體(union)4
可變參數... 4
字符串操作函數... 5
字符測試和轉換函數... 6
數學函數... 6
隨機函數... 7
存儲管理函數(malloc/ calloc)7
結構體
struct point {
intx;
inty;
} x, y;
關鍵字struct後面的point可以省略,它是結構標記。結構標記用於為結構命名,在定義之後,結構標記就代表花括號內的聲明,可以用它作為該聲明的簡寫形式,並可以用在後面的結構體聲明語句中。
如果結構聲明的後面不帶變量表(這裡為x y),則不需要為它分配存儲空間,它僅僅描述了一個結構的模板或輪廓。但是,如果結構聲明中帶有標記,那麼在以後定義結構實例時便可以使用該標記定義:
struct point z;
結構體初始化只能是常量表達式:
struct point maxpt={320,200}
訪問結構體成員:maxpt.x
結構體可以嵌套。
結構體參數傳遞時是按值傳遞。
如果傳遞給函數的結構體很大,使用指針方式的效率通常比復制整個結構的效率要高。結構體指針類似於普通變量指針:
struct point *pp=&maxpt;
(*pp).x = 3;
圓括號是必需的,因為結構成員運算符“.”的優先級比“*”的優先級要高。
結構指針的使用上頻度非常高,為了使用方便,C語言提供了另一種簡寫方式,假定P是一個指向結構的指針,可以用
p->結構成員 這種形式引用相應結構成員。
運算符“.”和“->”都是從左至右結合的。
在所有運算符中,下面4個運算符的優先級最高:結構運算符“.”和“->”、用於函數調用的“()”以及用於下標“[]”:++p->len相當於++(p->len),可以使用括號改變其運算結合順序,如(++p)->len,但(p++)->len則還是先對len執行操作,然後再將p加上1,這裡的括號其實可以省略,因為這是由於 p++ 的特性決定的(先使用再加1); *p->str讀取的是指針str所指向的對象的值;
*p->str++先讀取指針str指向的對象的值,然後再將str加上1(與*s++相同):
struct point {
char * str;
inty;
}/* *p */;
int main(
int argc,
char * argv[]) {
//這裡的結構指針變量不能在外層結構體聲明時在後面定義,
//否則運行時會出問題,不知道原因是什麼,但書上好像也
//是這麼定義的
struct point *p;
p->str = "abcd";
printf("%c\n", *p->str++);//a
printf("%s", p->str);//bcd
return 0;
}
(*p->str)++將指針str指向的對象的值加1;
*p++->str先讀取指針str指向的對象的值,然後再將p加1;
結構數組: struct point {
char * str;
inty;
} keytab[] = { { "auto", 0 }, { "break", 1 } };
如果初值是簡單變量或字符串,並且其中的任何值都不為空,則內層的花括號可以省略。通常情況下,如果初值存在並且方括號[]中沒有數值,編譯程序將計算數組keytab中的基數。
結構指針: struct point {
char * str;
inty;
};
int main(
int argc,
char * argv[]) {
struct point keytab1 = { "auto1", 1 };
struct point keytab2[] = { { "auto2", 2 }, { "break", 1 } };
struct point *p1 = &keytab1;//可以指向一個結構體變量
printf("%s\n", p1->str);//auto1
printf("%c\n", *p1->str);//a
printf("%s\n", p1->str + 1);//uto1 注,這裡是先取地址,再將地址加1
struct point *p2 = keytab2;//也可以指向一個結構體數組對象
printf("%s\n", (p2 + 1)->str);//break
return 0;
}
sizeof
C語言提供了一個編譯進一元運算符sizeof,它可以用來計算任一對象的長度,表達式:
sizeof 對象 以及
sizeof(類型名) 將返回一個無符號整數,它等於指定對象或類型占用的存儲空間字節數。其中對象可以是變量、數組或結構;類型可以是基本類型,如int、double,也可以是派生類型,如果結構類型或指針類型。針對以下結構體:
struct point {
char * str;
inty;
} keytab[] = { { "auto", 0 }, { "break", 1 } };
結構對象與結構類型長度計算如下:
printf("%d\n",
sizeof keytab);//16
//因為結構體中每個成員的長度是按最長成員來計算的,所以不是5,而是8
printf("%d",
sizeof (
struct point));//8
注,sizeof可以用在 #define 語句中,但不能用在條件編譯語句 #if 中。
類型定義(typedef)
typedef int Length;將Length定義為與int具有同等意義的名字。類型Length可用於類型聲明、類型轉換等,它和類型int完全相同:
Length len,maxlen; Length *lengths[]; 類似地,聲明
typedef char * String; 將String定義為與char*或字符指針同義,此後便可以在類型聲明和類型轉換中使用String,例如:
String p; int strcmp(String,String); p=(String)malloc(100); 也可定義結構體:
typedef structtnode {
char * str;
inty;
} Treenode;
typedef struct tnode * Treeptr;
定義函數指針:
typedef int (*PFI)(char *,char *);
該語名定義了類型PFI是“一個指向函數的指針,該函數具有兩個char * 類型的參數,返回值類型為int”,定義後,可以這樣使用:
PFI strcmp,numcmp;
聯合體(union)
一個聯合體變量可以合法地保存多種數據類型中任何一種類型的對象(同一時刻只能保存某一種),讀取的類型必須是最近一次存入的類型:
可變參數
可以使用<stdarg.h>中的一組宏定義來操縱可參數。
VA_LIST的用法:
(1)首先在函數裡定義一具VA_LIST型的變量,這個變量是指向參數的指針
(2)然後用VA_START宏初始化變量剛定義的VA_LIST變量,這個宏的第二個參數是第一個可變參數的前一個參數,是一個固定的參數。
(3)然後用VA_ARG返回可變的參數,VA_ARG的第二個參數是你要返回的參數的類型。
(4)最後用VA_END宏結束可變參數的獲取。然後你就可以在函數裡使用第二個參數了。如果函數有多個可變參數的,依次調用VA_ARG獲取各個參數。
#include<stdio.h>
#include<stdarg.h>
/*求和*/
int sum(
int first, ...) {
int sum = 0, i = first;
va_list marker;//定義一具VA_LIST型的變量,這個變量是指向參數的指針
/*first一般表示可變參數列表中的前面最近第一個有名參數,如果前面有多個有
* 名參數,即使這裡指定為前面最近第二個有名參數,但可變參數列表還是從最
* 後一個有名參數後面算起,對無名可變參數列表沒有影響,但編譯時會出警告,
* 另外,參數列表中至少要有一個命名參數,如果連一個命名參數也沒有,就無法使用可變參數*/
va_start( marker, first ); /* 用VA_START宏初始化變量剛定義的VA_LIST變量,
這個宏的第二個參數是第一個可變參數的前一個參數,
是一個固定的參數。*/
while (i != -1) {
sum += i;
i = va_arg( marker,
int);//VA_ARG返回可變的參數,VA_ARG的第二個參數是你要返回的參數的類型。
}
va_end( marker ); /* VA_END宏結束可變參數的獲取 */
return sum;
}
main(
void) {
/* Call with 3 integers (-1 is used as terminator). */
printf("sum is: %d\n", sum(2, 3, 4, -1));//9
/* Call with 4 integers. */
printf("sum is: %d\n", sum(5, 7, 9, 11, -1));//32
/* Call with just -1 terminator. */
printf("sum is: %d\n", sum(-1));//0
}
字符串操作函數
在頭文件<string.h>中定義。在下面的各個函數中,s與t為char*類型,c與n為int類型。
strcat(s, t) 將t指向的字符串連接到s指向的字符串的末尾
strncat(s, t, n) 將t指向的字符串中前n個字符連接到s指向的字符申的末尾
strcmp(s, t) 根據s指向的字符串小於(s<t)、等於(s==t)或大於(s>t) t指向的字符串的不同情況,分別返回負整數、0、或正整數
strncmp(s, t, n) 同strcmp相同,但只在前n個字符中比較
strcpy(s, t) 將t指向的字符串復制到s指向的位置
strncpy (s, t, n) 將t指向的字符串中前n個字符復制到s指向的位置
strlen(s) 返回s指向的字符串的長度
strchr(s, c) 在s指向的字符串中查找c,若找到,則返回指向它第一次出現的位置的指針,否則返回NULL
strrchr(s, c) 在s指向的字符串中查找c,若找到,則返回指向它最後一次出現的位置的指針,否則返回NULL
字符測試和轉換函數
頭文件<ctype.h>中定義了一些用於字符側試和轉換的函數.在下面各個函數中,c是一個可表示為unsigned char類型或EOF的int對象。該函數的返回值類型為int。
isalpha(c) 若c是字母,則返回一個非0值,否則返回0
isupper(c) 若c是大寫字母,則返回一個非0值,否則返回0
islower(c) 若c是小寫字母,則返回一個非0值,否則返回0
isdigit(c) 若c是數字,則返回一個非0值,否則返回0
isalnum(c) 若 isalpha(c)或isdigit(c),則返回一個非0值,否則返回0
isspace(c) 若c是空格、橫向制表符(\t)、換行符(\n)、回車符(\r),換頁(\f)符或縱向制表符(\v),則返回一個非0值,否則返回0
toupper(c) 返回c的大寫形式
tolower(c) 返回c的小寫形式
數學函數
頭文件<math.h>中聲明了20多個數學函數。下面介紹一些常用的數學函數,每個函數帶有一個或兩個double類型的參數,並返回一個double類型的值。
exp (x) 指數函數e
x log (x) x的自然對數(以e為底),其中,x>0
1og10(x) x的常用對數(以10為底),其中,x>0
pow(x, y) 計算x
y的值
sqrt(x) x的平方根(x>=0)
fabs(x) x的絕對值
隨機函數
函數
rand()生成介於0和RAND_MAX之間的偽隨機整數序列。其中RAND_MAX是在頭文件<stdlib.h>中定的符號常量。下面是一種生成大於等於0但小於1的隨機浮點數的方法
#define frand() ((double) rand() / (RAND_MAX+1.0)) (如果所用的函數庫中已經提供了一個生成浮點隨機數的函數,那麼它可能比上面這個函數具有更好的統計學特性)
函數
srand(unsigned)設置rand函數的種子數。
存儲管理函數(malloc/ calloc)
函數malloc和calloc用於動態地分配存儲塊。函數malloc的聲明如下:
void *malloc(size_t n) 當分配成功時,它返回一個指針,設指針指向n字節長度的未初始化的存儲空間,否則返回NULL。函數calloc的聲明為
void *calloc(size_t n, size_t size) 當分配成功時,它返回一個指針,該指針指向的空閒空間足以容納由n個指定長度的對象組成的數組,否則返回NULL。該存儲空間被初始化為0。
根據請求的對象類型,ma11oc或calloc函數返回的指針滿足正確的對齊要求。下面的例子進行了類型轉換:
int *ip;
ip=(int *) calloc(n, sizeof(int))
free(p)函數釋放p指向的存儲空間,其中,p是此前通過調用malloc或calloc函數得到的指針。存儲空間的釋放順序沒有什麼限制,但是,如果釋放一個不是通過調用malloc或callloc函數得到的指針所指向的存儲空間,將是一個很嚴重的錯誤。
使用己經釋放的存儲空間同樣是錯誤的。下面所示的代碼是一個很典型的錯誤代碼段,它通過一個循環釋放列表中的節點:
for (p=head; p !=NULL; p=p->next)
free(p);
正確的處理方法是,在釋放節點之前先將一切必要的信息保存起來,如下所示
for (p=head; p!=NULL; p=9){
q= p->next;
free(p);
}