C語言基礎部分二
1、函數
函數,可以稱爲方法、子例程或程序等等。定義一個函數需要聲明函數名稱、返回類型和參數。函數就是一組執行特定任務和邏輯的代碼語句。C程序至少有一個函數—main();
main函數要有返回值,void類型或者int,float等,有的編譯器要求必須int,需注意這點,有的竟然還可以不寫返回類型,編譯器標準不同,c89,c99,c11之類的,好吧,最好寫上吧。
定義函數
return_type function_name(parameter list){ body of the function; }
函數的構成:
- 返回類型,若有則是返回數據的數據類型,若無則是void,或者不寫。
- 函數名稱,也就是函數的實際名,與參數列表一起構成函數簽名。
- 參數,參數類似與佔位符,用於接收傳遞過來的參數,在函數內部調用使用,參數可選,函數可無參數。
- 函數主體,也就是執行一定的任務邏輯的代碼語句。
/* * 函數返回兩個數字中較大的那個數。 */ int max(int num1,int num2){ //聲明局部變量 int result; if(num1>num2){ result = num1; }esle{ result = num2; } return result; }
Note:函數聲明包含返回類型、函數名、參數列表。而不必有函數主體。
C語言中,函數聲明參數列表中的參數名並不重要,只要有參數類型亦可。
//如此也是合法的 int max(int,int);
函數參數多類似爲形式參數,調用者需要傳遞實際參數。
調用類型 | 描述 |
---|---|
傳值調用 | 調用方將數值傳遞給形參,如果形參在函數內修改數值,但不會形象到外部的實際參數值。C 語言多用此方式。 |
引用調用 | 調用方將參數的地址複製給形參,此時在函數內形參修改數值,會同步影響到外部實際參數的。 |
指針形式 | 通過指針傳遞的方式,形參爲實參的指針,操作會影響到實際參數的數據。 |
- 作用域
任何一個編程語言中,定義的變量都應有其作用範圍,稱爲作用域
- 局部變量,作用與函數內。
- 全局變量,作用與函數外部所有範圍。
形式參數,在函數的參數定義中,也就在函數內有效。類似局部變量了。
C語言中,函數的局部參數可以和全局參數同名稱,但是內部僅使用局部變量的值。局部變量需要手動初始化,全局變量會自動初始化。
- 數組
C語言數組和Java的數組類似,用於存儲一類相同類型的數值,聲明一個變量,用序列編號指定元素數據。
元素 | 元素 | 元素 | 元素 |
---|---|---|---|
numbers[0] | numbers[1] | …… | numbers[n] |
聲明與初始化:
//聲明
type arrayName[arraySize]
double balance[10];
double balance[3] = {12.0,3.5,8.8};
double balance[] = {7.9,0.1,3.2}
//單個元素賦值
balacne[0] = 8.9;
數組的訪問使用數組下標,從左至右依次爲0—-n,某個元素則爲arrayName[i];
概念 | 描述 |
---|---|
多維數組 | 即數組的元素又是數組,常見的爲二維數組。 |
傳遞數組函數 | 通過指定不帶索引的數組名稱,來給函數傳遞一個數組的指針。也就是作爲形參。 |
從函數返回數組 | 函數的返回類型。 |
指向數組的指針 | 通過指定不帶索引的數組名稱,來生成一個指向數組中第一個元素的指針。 |
2、指針
C語言指針是簡單而有趣的一個概念,便於簡化程序任務,比如動態內存分配等。
每一個變量都有一個內存地址,這個地址是有一個編號的。
#include <stdio.h>
void main(){
int var1;
char var2[10];
//printf函數參數爲可變參數,必有string哦,不然會報錯。和java不同。
printf("var1變量的地址:%x \n",&var1);
printf("varsb變量的地址:%x \n",&var2);
}
指針
是一個特殊變量,它的值是另一個變量的地址,內存位置的直接地址。類似其他變量和常量,在使用指針存儲其他變量的地址之前,需要聲明和存儲。
//type爲指針的基類型,var-name爲指針變量的名稱。星號用於標示一個變量是指針。 type *var-name; //指針示例 int *ip; double *dp; float *fp; char *cp; //不管基類型爲何種類型,指針的實際數據類型,都是一個代表內存地址的十六進制數。
如何使用指針
使用指針涉及以下操作:
- 定義指針變量
- 把另一變量地址賦值給指針
- 訪問指針變量中可用地址的值
*
用來返回位於操作數所指定的地址的變量的值。#include <stdio.h> void main(){ int var = 20;//實際變量的聲明 int *ip;//指針變量的聲明 ip = &var;//在指針變量中存儲var變量的地址,也就是給指針變量賦值 printf("var變量的地址:%x\n",&var);//C語言printf函數不會換行,所以習慣都是內部加上\n,初學者可別弄混了哦。 //在指針變量中存儲的地址 printf("在ip中存儲的地址:%x \n",ip); //使用指針方位實際變量的值,也就用指針指向的內存地址,讀取地址裏面的數值。 printf("指針所指向地址的存儲數據:%d \n",*ip);//使用*ip格式來獲取指針對應的實際數值。 }
使用*符號來作用與指針,便可得到指針所指向地址內的實際數值。
Null指針
變量聲明的時候,如果沒有明確賦值,最好給指針賦值一個NULL值,如此爲空指針。
NULL
指針是定義在標準庫中值爲零的常量。#include <stdio.h> void main(){ int *ip=null; //輸出ip的值,就是0 printf("ip的值是:%x \n",ip); }
Note:弄清幾個概念,&var表示var的地址,*var是指var指針所指向地址的儲存的數值。
多數操作系統都不允許訪問地址爲0的內存,其爲系統保留內存,有特殊含義,表明指針不指向可訪問的內存位置。依據慣例指針包含空值,則認爲它不指向任何地址。
if(ptr)//表示,如果ptr非空,則完成 if(!ptr)//表示,如果ptr爲空,則完成。
指針詳解
概念 | 描述 |
---|---|
指針的算數運算 | 指針可以進行四種算術運算++、–、+、- |
指針數組 | 可以定義用來存儲指針的數組 |
指向指針的指針 | C語言允許指向指針的指針 |
傳遞指針給函數 | 通過引用過地址傳遞參數,使傳遞的參數在調用的函數中被改變 |
從函數返回指針 | C允許函數返回指針到局部變量、靜態變量和動態內存分配 |
字符串
C語言中字符串實際上是使用
null
字符\0
終止的一維字符數組。
char cs[6] = {'H','e','l','l','o','\0'};
//這就是hello字符串,用的是null字符'\0'結尾的一個字符數組,所以數組長度比字符串數字多1,因爲末尾是null的字符標記。也可以寫作:
char cs[] = "Hello";
C/C++中定義的字符串的內存表示:
其實可以不必寫出null標記,C編譯器會自動追加,在初始化時候。
常用的C語言字符串操作函數:
函數 | 描述 |
---|---|
strcpy(s1,s2) | 複製字符串s2到s1 |
strcat(s1,s2) | 連接字符串s2到s1末尾 |
strlen(s1) | 返回字符串s1的長度 |
strcmp(s1,s2) | s1==s2則返回0,s1<s2 則返回小於0,s1>s2則返回大於0 |
strchr(s1,ch) | 返回一個指針,指向字符串s1中字符ch的第一次出現的位置 |
strstr(s1,s2) | 返回一直指針,指向字符串s1中字符串s2的第一次出現的位置 |
3、結構體
C語言中數組是存儲一類相同類型的數據變量,而結構
則可以存儲不同類型的數據項。有點類似面向對象的編程語言中的實體類bean
的封裝。
定義結構
struct
語句,可以定義一個包含多個成員的數據類型。struct [structure tag]{ member definition; member definition; ... member definition; }[one or more structure variables]
其中
structure tag
是可選的,每個member definition
是標準的變量定義,比如int i;double d;
如下示例,定義一個Book結構的方式struct Books{ char title[50]; char author[50]; char subject[100]; int book_id; } book;
訪問結構成員
使用成員運算符
.
示例:#include <stdio.h> #include <string.h> //定義結構體 struct Books{ char title[50]; char author[50]; char subject[100]; int book_id; }; //main函數 void main(){ //聲明結構體的變量,使用關鍵詞struct struct Books Book1; struct Books Book2; //初始化變量的內部數據,結構體成員,用變量名.成員名 strcpy(Book1.title,"C語言編程"); strcpy(Book1.author,"譚浩強"); strcpy(Book1.subject,"C語言編程入門"); Book1.book_id = 10086; //輸出Book1信息 printf("Book1的標題:%s \n",Book1.title); printf("Book1的作者:%s \n",Book1.author); printf("Book1的副標題:%s \n",Book1.subject); printf("Book1的編號:%d \n",Book1.book_id); //Book2類似 ... }
結構體作爲函數參數
結構體也可以作爲參數傳遞給函數調用,類似變量和指針。不同與Java語言的方法函數,這裏需要先聲明結構函數,然後實現,在調用。
#include <stdio.h> #include <string.h> struct Books { char title[50]; char author[50]; char subject[100]; int book_id; }; /* 函數聲明 */ void printBook( struct Books book ); int main( ) { struct Books Book1; /* 聲明 Book1,類型爲 Book */ struct Books Book2; /* 聲明 Book2,類型爲 Book */ /* Book1 詳述 */ strcpy( Book1.title, "C Programming"); strcpy( Book1.author, "Nuha Ali"); strcpy( Book1.subject, "C Programming Tutorial"); Book1.book_id = 6495407; /* Book2 詳述 */ strcpy( Book2.title, "Telecom Billing"); strcpy( Book2.author, "Zara Ali"); strcpy( Book2.subject, "Telecom Billing Tutorial"); Book2.book_id = 6495700; /* 輸出 Book1 信息 */ printBook( Book1 ); /* 輸出 Book2 信息 */ printBook( Book2 ); return 0; } void printBook( struct Books book ) { printf( "Book title : %s\n", book.title); printf( "Book author : %s\n", book.author); printf( "Book subject : %s\n", book.subject); printf( "Book book_id : %d\n", book.book_id); }
指向結構的指針
可以定義結構體的指針,類似變量和指針,
//聲明指針 struct Books *struct_pointer; //將變量的地址,賦值給指針 struct_pointer = &Book1; //要想讓這個指針能夠訪問到結構體中的某個成員,就需要用->運算符 struct_pointer->title;//指針訪問結構體中的title變量的值
上例使用指針模式:
//其他不變 ... printBook(&Book1);//這裏傳入的是指針 ... void printBook(struct Books *book){//結構體的指針,作爲參數,傳遞給函數 printf("Book title:%s \n",book->title);//使用->運算符訪問結構體成員 ... }
位域
又稱”位段”,爲了節省空間,僅存儲佔用一個或者幾個二進制位的數據字節,滿足一些特殊需要,如開關變量0和1,只用一個二進制位即可滿足。
所謂位域,就是吧一個字節中的二進制位,劃分爲幾個不同的區域,位域有域名,可以在程序中用域名來操作,從而可以把不同的對象用一個字節的二進制位域來表示。
位域定義和變量說明
struct 位域結構名{ 位域列表 };
其中位域列表的形式:
類型說明符 位域名:位域長度
示例:
struct bs{ int a:8; int b:2; int c:6; };
位域變量的說明與結構變量的說明方式相同,可採用先定義後說明,同時定義說明或者直接說明三種方式。
struct bs{ int a:8;//佔用的二進制位數8個 int b:2; int c:6; } data;
如上則是同時定義和說明一個
bs
的變量data
,佔用兩個字節(兩個8位)。struct packed_stuct{ unsigned int f1:1; unsigned int f2:1; unsigned int f3:1; unsigned int f4:1; unsigned int type:4; unsigned int my_int:9;//注意此處,前面有int,和下面的注意事項中不能超過一個字節的要求,並不矛盾, } pack;
這裏pack_struct就包含了6個成員,四個1位的標識符,一個4位的type,還有一個9位的my_int。
位於定義的注意事項
- 一個位域必須存儲在同一個字節中,不能跨兩個字節,但是可以指定位域的開始位置:
struct bs{ unsigned a:4; unsigned :4;//空域 unsigned b:4;//從下一單元開始存放,而不是使用上面空的那4個 unsigned c:4; }
位域不恩你個跨兩個字節,所以位域長度不能超過8,超過了的話,可能會被重疊,或者放入下一個字節了。
位域可以是無名位域,之用來佔位,其不能使用。
struct k{ int a:1; int :2;//佔位的,無名位域,不可用 int b:3; int c:2; }
其實,位域也算是一種結構類型,只不過成員是按照二進制位表示而已。
位域的使用
類似與結構成員的使用
位域變量名.位域名
位域允許各種格式輸出,示例:
void main(){ //定義了一個位域,並聲明瞭一個變量bit,和它的指針變量*pbit struct bs{ unsigned a:1; unsigned b:3; unsigned c:4; }bit,*pbit; //給位域賦值,注意不要超過位域的值的範圍。 bit.a = 1;//就只能是0或1,因爲定義佔1個二進制位 bit.b = 7;//0--7,因爲佔用3個二進制位 bit.c = 15; //以整型量輸出 printf("%d,%d,%d \n",bit.a,bit.b,bit.c); //使用位域指針訪問成員 pbit = &bit;//給指針賦值 pbit->a = 0;//指針訪問成員,並賦值給成員 pbit->b&=3;//使用了&=運算符,相當與pbit->b = pbit->b&3; pbit->c|=1;//使用了|=運算符,相當與pbit-> = pbit->b|3; printf("%d,%d,%d \n",pbit->a,pbit->b,pbit-c); }