指針作為函數的形參的另一個典型應用是當函數有多個傳回值的情形。比如,需要在一個函數中統計一個數組的最大值、最小值和平均值。當然你可以編寫三個函數分别完成統計三個值的功能。但比較啰嗦,如:
int GetMax(int a[],int n)
{
int max=a[0],i;
for(i=1;i<n;i++)
{
if(max<a[i]) max=a[i];
}
return max;
}
int GetMin(int a[],int n)
{
int min=a[0],i;
for(i=1;i<n;i++)
{
if(min>a[i]) min=a[i];
}
return min;
}
double GetAvg(int a[],int n)
{
double avg=0;
int i;
for(i=0;i<n;i++)
{
avg+=a[i];
}
return avg/n;
}
其實我們完全可以在一個函數中完成這個功能,由于函數隻能有一個傳回值,可以傳回平均值,最大值和最小值可以通過指針類型的形參來進行實作:
double Stat(int a[],int n,int *pmax,int *pmin)
{
double avg=a[0];
int i;
*pmax=*pmin=a[0];
for(i=1;i<n;i++)
{
avg+=a[i];
if(*pmax<a[i]) *pmax=a[i];
if(*pmin>a[i]) *pmin=a[i];
}
return avg/n;
}
1.1.5.2 函數的指針
一個函數總是占用一段連續的記憶體區域,函數名在表達式中有時也會被轉換為該函數所在記憶體區域的首位址。我們可以把函數的這個首位址賦予一個指針變量,使指針變量指向函數所在的記憶體區域,然後通過指針變量就可以找到并調用該函數。這種指針就是函數指針。
函數指針的定義形式為:
returnType (*pointerName)(param list);
returnType 為函數傳回值類型,pointerNmae 為指針名稱,param list 為函數參數清單。參數清單中可以同時給出參數的類型和名稱,也可以隻給出參數的類型,省略參數的名稱,這一點和函數原型非常類似。
用指針來實作對函數的調用:
#include <stdio.h>
//傳回兩個數中較大的一個
int max(int a, int b)
{
return a>b ? a : b;
}
int main()
{
int x, y, maxval;
//定義函數指針
int (*pmax)(int, int) = max; //也可以寫作int (*pmax)(int a, int b)
printf("Input two numbers:");
scanf("%d %d", &x, &y);
maxval = (*pmax)(x, y);
printf("Max value: %d\n", maxval);
return 0;
}
1.1.5.3 結構體和指針
結構體指針有特殊的文法: -> 符号
如果p是一個結構體指針,則可以使用 p ->【成員】 的方法通路結構體的成員
typedef struct
{
char name[31];
int age;
float score;
}Student;
int main(void)
{
Student stu = {"Bob" , 19, 98.0};
Student*ps = &stu;
ps->age = 20;
ps->score = 99.0;
printf("name:%s age:%d
",ps->name,ps->age);
return 0;
}
1.2 指針的意義_間接指派
1.2.1 間接指派的三大條件
通過指針間接指派成立的三大條件:
2個變量(一個普通變量一個指針變量、或者一個實參一個形參)
建立關系
通過 * 操作指針指向的記憶體
void test(){
int a = 100; //兩個變量
int *p = NULL;
//建立關系
//指針指向誰,就把誰的位址指派給指針
p = &a;
//通過*操作記憶體
*p = 22;
}
1.2.2 如何定義合适的指針變量
void test(){
int b;
int *q = &b; //0級指針
int **t = &q;
int ***m = &t;
}
1.2.3 間接指派:從0級指針到1級指針
int func1(){ return 10; }
void func2(int a){
a = 100;
}
//指針的意義_間接指派
void test02(){
int a = 0;
a = func1();
printf("a = %d\n", a);
//為什麼沒有修改?
func2(a);
printf("a = %d\n", a);
}
//指針的間接指派
void func3(int* a){
*a = 100;
}
void test03(){
int a = 0;
a = func1();
printf("a = %d\n", a);
//修改
func3(&a);
printf("a = %d\n", a);
}
1.2.4 間接指派:從1級指針到2級指針
void AllocateSpace(char** p){
*p = (char*)malloc(100);
strcpy(*p, "hello world!");
}
void FreeSpace(char** p){
if (p == NULL){
return;
}
if (*p != NULL){
free(*p);
*p = NULL;
}
}
void test(){
char* p = NULL;
AllocateSpace(&p);
printf("%s\n",p);
FreeSpace(&p);
if (p == NULL){
printf("p記憶體釋放!\n");
}
}
1.2.4 間接指派的推論
用1級指針形參,去間接修改了0級指針(實參)的值。
用2級指針形參,去間接修改了1級指針(實參)的值。
用3級指針形參,去間接修改了2級指針(實參)的值。
用n級指針形參,去間接修改了n-1級指針(實參)的值。
1.3 指針做函數參數
指針做函數參數,具備輸入和輸出特性:
輸入:主調函數配置設定記憶體
輸出:被調用函數配置設定記憶體
1.3.1 輸入特性
void fun(char *p /* in */)
{
//給p指向的記憶體區域拷貝内容
strcpy(p, "abcddsgsd");
}
void test(void)
{
//輸入,主調函數配置設定記憶體
char buf[100] = { 0 };
fun(buf);
printf("buf = %s\n", buf);
}
1.3.2 輸出特性
void fun(char **p /* out */, int *len)
{
char *tmp = (char *)malloc(100);
if (tmp == NULL)
{
return;
}
strcpy(tmp, "adlsgjldsk");
//間接指派
*p = tmp;
*len = strlen(tmp);
}
void test(void)
{
//輸出,被調用函數配置設定記憶體,位址傳遞
char *p = NULL;
int len = 0;
fun(&p, &len);
if (p != NULL)
{
printf("p = %s, len = %d\n", p, len);
}
}
1.4 字元串指針強化
1.4.1 字元串指針做函數參數
1.4.1.1 字元串基本操作
//字元串基本操作
//字元串是以0或者'\0'結尾的字元數組,(數字0和字元'\0'等價)
void test01(){
//字元數組隻能初始化5個字元,當輸出的時候,從開始位置直到找到0結束
char str1[] = { 'h', 'e', 'l', 'l', 'o' };
printf("%s\n",str1);
//字元數組部分初始化,剩餘填0
char str2[100] = { 'h', 'e', 'l', 'l', 'o' };
printf("%s\n", str2);
//如果以字元串初始化,那麼編譯器預設會在字元串尾部添加'\0'
char str3[] = "hello";
printf("%s\n",str3);
printf("sizeof str:%d\n",sizeof(str3));
printf("strlen str:%d\n",strlen(str3));
//sizeof計算數組大小,數組包含'\0'字元
//strlen計算字元串的長度,到'\0'結束
//那麼如果我這麼寫,結果是多少呢?
char str4[100] = "hello";
printf("sizeof str:%d\n", sizeof(str4));
printf("strlen str:%d\n", strlen(str4));
//請問下面輸入結果是多少?sizeof結果是多少?strlen結果是多少?
char str5[] = "hello\0world";
printf("%s\n",str5);
printf("sizeof str5:%d\n",sizeof(str5));
printf("strlen str5:%d\n",strlen(str5));
//再請問下面輸入結果是多少?sizeof結果是多少?strlen結果是多少?
char str6[] = "hello\012world";
printf("%s\n", str6);
printf("sizeof str6:%d\n", sizeof(str6));
printf("strlen str6:%d\n", strlen(str6));
}
八進制和十六進制轉義字元:
在C中有兩種特殊的字元,八進制轉義字元和十六進制轉義字元,八進制字元的一般形式是'\ddd',d是0-7的數字。十六進制字元的一般形式是'\xhh',h是0-9或A-F内的一個。八進制字元和十六進制字元表示的是字元的ASCII碼對應的數值。
比如 :
'\063'表示的是字元'3',因為'3'的ASCII碼是30(十六進制),48(十進制),63(八進制)。
'\x41'表示的是字元'A',因為'A'的ASCII碼是41(十六進制),65(十進制),101(八進制)。
1.4.1.2 字元串拷貝功能實作
//拷貝方法1
void copy_string01(char* dest, char* source ){
for (int i = 0; source[i] != '\0';i++){
dest[i] = source[i];
}
}
//拷貝方法2
void copy_string02(char* dest, char* source){
while (*source != '\0' /* *source != 0 */){
*dest = *source;
source++;
dest++;
}
}
//拷貝方法3
void copy_string03(char* dest, char* source){
//判斷*dest是否為0,0則退出循環
while (*dest++ = *source++){}
}
1.4.1.3 字元串反轉模型

void reverse_string(char* str){
if (str == NULL){
return;
}
int begin = 0;
int end = strlen(str) - 1;
while (begin < end){
//交換兩個字元元素
char temp = str[begin];
str[begin] = str[end];
str[end] = temp;
begin++;
end--;
}
}
void test(){
char str[] = "abcdefghijklmn";
printf("str:%s\n", str);
reverse_string(str);
printf("str:%s\n", str);
}