C語言語法基礎--S2函式和指標

中古傳奇發表於2020-10-01


文章主要是針對計算機基礎二級知識點內容而言;

1 函式

1.1 庫函式

努力多使用標準庫函式,既標準又快速移植性又強;
要求:只需要學會能看懂庫函式並且學會呼叫即可;

1.1.1 庫函式及其呼叫

一般形式:函式名(引數列表)
**前提一定是使用檔案包含 #include “”/<> 最後千萬不要加分號。

  • 出現在表示式中 y=sin(x);
  • 作為獨立的的語句完成某種操作 printf("*\n");

1.2 函式定義

  • 函式時C語言程式的基本功能模組,用於完成一項相對獨立的任務。一個C語言中由若干個函式構成,但main函式有且只有一個。函式時程式的最小組成單位。
  • 所有函式之間的關係是平行的,沒有從屬的概念。即定義不能在函式內部巢狀定義,但是可以函式巢狀呼叫。函式的平行概使得函式的編寫相對獨立,便於模組化程式的實現

1.2.1 函式定義形式

  1. 函式定義的一般形式
    函式返回值型別 函式名(型別名 形式引數1, 型別名,形式引數2,…){
    // 函式說明部分
    函式體

}
若省略返回值型別則為預設為int型別返回值

  • 不能在函式的內部定義函式,函式名和形式引數都是由使用者命名的識別符號。在同一程式中,函式名必須唯一,不能出現重名的情況。 形式引數名只要在同一函式中唯一即可,可以與其他函式中的變數同名。
    【函式舉例】
#inlcude <stdio.h>
#include <math.h>
float fun1(float x,float y);

main(){
	float a,b,c,d;
	scanf("%f,%f",&a,&b);
	c=fun1(a,b);
	d=fun1(b,a);
	printf("%f,%f",c,d);

}

1.3 函式引數和返回值

引數傳遞有值傳遞和地址傳遞兩種;函式定義的引數列表稱為形式引數,呼叫函式時引進的引數稱為實際引數。

1.3.1 值傳遞和地址傳遞

在定義函式時,函式名後面的引數列表稱為形式引數。
在主調函式中,函式名後面括號中的引數稱為實際引數。

  • 值傳遞:實參複製到形參,不同的記憶體單元,指互不影響
  • 地址傳遞:操縱的是同一塊記憶體空間上的數;地址傳遞有:指標傳遞和引用傳遞;

1.3.2 函式返回值

函式的值通過return語句返回,return語句一般形式:

  • return 表示式;或 return(表示式);return語句中的表示式的值就是所求的函式值,返回的型別必須與函式定義的型別一致。若不一致,會自動轉換。
    在一個函式體內,可以在多處出現return語句,但是無論函式體中有多少個return語句,return語句都只可能會被執行一次
  • return ;後面不含表示式,但此時必須定義為void型別,作用只是使流程返回到呼叫函式的位置。
  • 函式體內沒有return語句,這時必須定義為函式為void型別,執行完語句體,然後返回撥用函式。

1.4 函式呼叫

  • 一個C程式中主要有一個主函式和若干子函式組成,主函式使用被調函式的功能,稱為對被調函式的呼叫。
  • 函式呼叫是通過函式名稱和函式引數的組合使用來實現的。
  • 一個函式可以被主函式呼叫,也可以被其他函式呼叫,各函式之間可以相互進行呼叫;

1.4.1 函式呼叫

函式呼叫分為 有引數呼叫和無引數呼叫;

  1. 一般形式
  • 函式名(引數列表);
  • 函式名();
  1. 函式呼叫語法
  • 呼叫函式時,函式名與所呼叫的函式名字完全一致;
  • 實參可以是表示式,在型別上按照位置與形參一一對應;
  • 函式先申明,再使用,後定義;
  • 函式直接或者間接地呼叫自己,稱為遞迴呼叫;
    程式遞迴呼叫就是使用棧的技術,一個個使用完之後再返回
    【例子】
#include<stdio.h>
main(){
	int i=1,p;
	p=f(i,i+1);
	printf("%d\n",p);

}
int f(int a,int b){
	int c;
	c=a;
	if(a>b){
		c=1;
	}
	else if(a==b){
		c=0;
	}
	else{
		c=-1;
	}
	return(c);
}
  1. 函式的申明和定義
    定義是指對函式功能的確立,包括指定函式名、函式值型別、形參及其型別、函式體等。是一個完整的、獨立的函式單位。
    函式申明的作用:是利用它在程式的編譯階段對呼叫函式的合法性進行全面檢查。它把函式名、函式型別以及形參的型別、個數和順序告知編譯系統,以便在呼叫該函式時系統對此進行對照檢查。
  • 函式申明一般形式:
    型別名 函式名(引數型別1,引數型別2,…)
    或者:
    型別名 函式名(引數型別1 引數名1,引數型別2 引數名2,…)
    【例如】double add(double x, double y){…} //申明加定義
    double add(double,double); // 申明
    double add(double x, double y); //申明

1.4.2 遞迴呼叫

在呼叫一個函式的過程中又出現或間接得呼叫該函式本身的,稱為函式的遞迴呼叫;
【程式例項】

#include <stdio.h>
long fun(int);
main(){
	printf("\n %ld",fun(4))

}
long fun(int x){
	if(n==1 && n==2){
		s=2;
	}
	else{
		s=n+fun(n-1);
	}
	return s;
}

1.5 全域性變數和區域性變數

  1. 全域性變數:全域性變數是指在函式外部定義的變數;即main函式外部定義的變數;不是屬於哪一個函式,都可以被函式共用,但是在函式內部會用同名作用域遮蔽。
    全域性變數都可以被所有函式共用和改變,如果一個函式中改變了全域性變數的值,就會影響到其他函式
  2. 區域性變數:區域性變數是指在函式內部或複合語句內部定義的變數。
    區域性變數只在定義它的函式或複合語句範圍內有效。
    形參變數屬於被呼叫函式的區域性變數;實際引數屬於主調函式的區域性變數。
  3. 函式間是平行關係,區域性變數不能相互呼叫,區域性變數名是在不同函式之間是可以同名的。

1.5.1 變數儲存型別及生存期

變數時資料的傳遞者,具有操作屬性和儲存類別兩種屬性。儲存類別:auto、static、register、extern。
靜態變數:可以一直繼續存值;
暫存器變數:訪問速度更快,屬於自動類變數;
extern:向程式外擴充。

2 指標

2.1 基本概念

基本資料型別:是指抽象出來的原子的一個資料型別。
陣列、結構體、… :都是大量資料型別組織起來的結構。
地址:計算機記憶體是以位元組為單位的一片連續的儲存空間,每一個位元組都有一個編號,這個編號就稱為記憶體地址。形象比喻:宿舍就是記憶體單元;宿舍號就是記憶體單元的地址;學生就是記憶體單元的內容;記憶體的儲存空間是連續的,記憶體中的地址號也是連續的。

  • 指標:指標的實質就是地址。通過指標可以找到以它為地址的記憶體單元。

2.2 指標變數

2.2.1 指標變數的定義和引用:

指標變數就是用來存放記憶體地址的。
【例如】:int *p=a; 將變數a的記憶體地址(1012)存放到變數p中,要訪問變數a所代表的儲存單元,可以先找到變數p的地址2002,從中取出a的地址1012,然後再去訪問以1012為首地址的儲存單元。
通過變數p間接得到變數a的地址,然後再存取變數a的值的方式稱為"間接存取"方式;用來存放地址的變數稱為"指標變數"。

  1. 指標變數的一般定義形式
    型別名 指標變數名1 ,指標變數名2,…;【例如】int p1;intp2;
  • 星號緊跟資料型別,說明指標變數指向的資料型別
  • p1,p2是兩個指向整型(int型別)變數的指標,就是說變數p1,p2中只能存放int型別變數的地址。
  • 變數值(指標)所指向的變數的資料型別稱為"基型別"。
  1. 對指標變數的操作
    指標運算子:取地址運算子&和取內容運算子*。
    & <變數名> 表示取變數的記憶體地址;只能用於一個具體的內容或者陣列元素,不能是表示式或者常量。
  • <指標變數名> 表示指標變數所指的內容;

2.2.2 指標變數的初始化和與運算

  1. 指標變數的初始化
    指標變數在初始化之前必須對其進行初始化,使得指標變數指向一個確定的記憶體單元,否則會造成野指標。
    指標變數的初始化一般格式形式:
    型別名 指標變數名 = 初始化地址值
    char ch;
    char *p_ch=&ch;
  • 任意型別的指標變數都要遵守"先定義,再初始化,後使用"的原則。未經初始化的指標禁止使用;
  • 必須使用同一型別資料的地址進行指標初始化;
  • 不能把一個整數賦值給指標變數。
  1. 指標的運算
  • 通過求地址運算子&獲得地址值
  • 通過指標變數獲得地址值
    int k=1,*q,*p;
    q=&k;
    p=q; // 使得兩個指標指向同一個地址
  • 進行賦值運算時,賦值號兩邊的指標型別必須相同。
  1. 通過標準庫函式獲得地址值
    通過呼叫庫函式malloc和calloc在記憶體中開闢動態儲存單元,並把所開闢的動態儲存單元的地址賦給指標變數。
  2. 給指標變數賦"空"值。
    int* p1 = NULL; //等價於p1=0;p1=’\0’;

2.3 陣列和指標

用指標變數來指向陣列中的首地址;通過首地址就可以依次找到其他陣列元素,同樣指標變數也可以指向陣列中的任意元素。

2.3.1 指向陣列元素的指標

陣列的指標是指陣列的首地址,陣列元素的指標是各個陣列元素的地址。陣列名就是這塊連續記憶體單元的首地址。

  1. 指向二維陣列的行指標變數
    資料型別 (*指標變數)[n];
    【例如】 int (*p)[3] // 在二維陣列中,可以使p分別指向a[0],a[1],a[2];
    指向陣列的指標;和指標型別的陣列;

2.3.2 通過指標引用陣列元素

  1. 移動指標–指向陣列元素
  2. *(p++)

2.4 字串和指標

在C語言中,對於字串沒有專門的字串型別類說明,一般都是使用字元型別的陣列來儲存字串。使用指標對字串進行運算。但是在C++中的庫函式中,有定義好的tring型別,可以直接定義字串型別。

2.4.1 字元指標

用字元指標指向一個字串。
char* str=“I am a student.”; // 定義str為指標變數,並指向字串的首地址
printf("%s\n",str);
【例子】

#include <stdio.h>
main(){
	char* p_str="beijing";	//定義指標p_str,並使其指向字串"beijing"
	printf("%s\n",p_str);	//通過指標引用字串的首地址來輸出字串
	while(*p_str){
		printf("%c",*p_str);	//通過指標引用每個字元來輸出字串
		p_str++;
	}
	printf("\n");
}
// 在程式中先採用格式符"%s"來輸出字串的格式,又通過改變字串指標變數的值依次輸出字串的字元。

2.4.2 字串指標作函式引數

將一個字串從一個函式傳遞到另一個函式,可以用地址傳遞的方法,即用字元陣列名作為引數或用指向字串的指標變數作為引數進行傳遞。

#include <stdio.h>
void fun(char*);

main(){
	gets(s);
	fun(s);
	puts(s);
}

void fun(char *c){
	while(*c){
		if(*c>='a'&&c<='z'){
			*c=*c-('a'-'A');
		}
		c++;
	}

}

2.5 函式和指標

2.5.1 用函式指標變數呼叫函式

  1. 函式指標
    在程式執行中,函式的程式碼是程式的指令部分,和陣列一樣也佔用記憶體空間。編譯時,系統會給每個函式分配一個入口地址,就是函式程式碼的記憶體單元你的首地址。函式名就是這個函式的首地址,可以使用指標變數指向函式引數的入口地址,這樣的指標稱為函式指標。

函式指標的定義格式形式:
資料型別(指標變數名)(形參列表);
【區別】
int
fun(int a,int b); // 這個函式返回值指向整型變數的指標;
int (*fun)(int a ,int b); // 定義了一個指向函式的指標,而這個函式的返回值型別時整型。
2. 用函式指標變數呼叫函式
一般形式:在給函式指標變數賦值時,只需給出函式名而不必給出引數。
int (*s)();
s=fun; // fun為已有定義的有參函式
因為是將函式入口的地址賦給s,不涉及引數的問題,不能寫成:s=fun(a,b);

  • 用函式指標變數呼叫函式時,只需將(*s)袋貼函式名即可;s為已經定義過的指向函式的指標變數名;(*s)之後的括號中根據需要寫上實參。
  • 函式指標也需賦初值,才能指向具體的函式。函式名代替了該函式的入口,可以直接用函式名為函式指標變數賦值。
    【程式例項】
int fa(int x){
	return x*x;
}

int fb(int x){
	return x*x*x;
}
int f( int (*f1)() , int (*f2)() , int x){
	return f2(x)-f1(x);
}

main(){
	int i;
	i = f(fa,fb,2);
	printf("% d \n",i);

}
// 但是感覺這個功能很雞肋,命名函式呼叫,直接使用函式名即可,為什麼還要再使用函式指標。

2.5.2 函式返回指標型別值

函式值的型別不僅僅是基本資料型別,而且還可以是指標型別。

#include<stdio.h>
int *fun(int* ,int*); 	// 函式申明語句
main(){
	int* p,i,j;
	printf("Enter two number:");
	scanf("%d%d",&i,&j);
	p=fun(&i,&j);
	printf("i=%d,j=%d,*p=%d\n",i,j,*p);

}

int* fun(int* a,int* b){
	if(*a>*b){
		return a;
	}
	return b;
}

相關文章