函數指針
int *a; //定義變量,a是整型指針變量
int a(); //定義函數,a是函數,返回值爲int
int * a(); /定義函數,a是函數,返回值爲int*
int *a[10]; //定義變量,a是數組,存放10單元整型指針的數組
int (*a)[10]; //定義變量,a是指針,指向10個數組單元的指針
int (*a)(); //定義函數,a是指針,指向函數的指針,返回值爲int
1.函數代碼存儲空間的起始地址(又稱入口地址)稱爲這個函數的指針。
如:
int (* p)( int ,int );
這是定義一個指向函數的指針變量,指針變量p的返回值類型是整型,並且這個函數有兩個整型參數。
2.用函數指針變量調用函數
舉例:
int Max(int a,int b)
{
return a>b?a:b;
}
int main()
{
int a=5;
int b=3;
//int c=Max(a,b); //(1)通過函數名調用Max函數
//printf("%d\n",c);
int (*p)(int ,int ); //定義指向函數的指針變量p,
//只需要說明p的返回值是整型且有兩個整型參數就行,即只指明int,用int x也可以
p=Max; //使p指向Max函數首地址, p=&Max也可以
//int c=(*p)(a,b); //(2)通過指針變量調用Max函數
printf("%d\n",p(a,b)); //printf("%d\n",(*p)(a,b));也可以
return 0;
}
3.定義和使用指向函數的指針變量
形式:類型名 (*指針變量名)(函數參數表列);
如:
int (*p)(int ,int );
說明:
(1)定義指向函數的指針變量,只能指向在定義時指定的那個類型的函數,如int (*p)(int ,int);是屬於一個整型返回值,兩個整型變量的參數的類型。即函數指針指向一個函數族
(2)如果指針調用函數,必須使指針指向該函數,如 p=Max;
(3)在給函數指針變量賦值時,只給出函數名不必給出參數
如:p=Max; //將函數入口地址賦給p
(4)函數指針調用函數時,只需用(*p)代替函數名即可
(5)指向函數的指針變量不能進行算術運算
(6)函數名調用函數,只能調用指定的某一個函數;而指針變量調用函數,可以根據不同的情況先後調用不同的函數。
結構體
C語言允許用戶自己建立有不同類型數據組成的組合型的數據結構,它稱爲結構體。
1.結構體類型形式:
struct 結構體名
{
類型名 成員名;
};
2.結構體中可以使用的類型:
(1)基本數據類型 (2)前面已經定義好的結構體 (3)結構體本身的指針
//以下是舉例,不是具有功能的完整代碼
#include<stdio.h>
#include<string.h>
struct Student //struct Student是類型名,student是結構體名
{
char name[20];
int age;
};
struct A
{
int a;
//char a;//error,不能同名
char b;
};
struct B
{
int c; //(1)
struct A sa; //(2)
//struct B sb; //error,因爲不知道sb的內存大小
struct B *pb; //OK,因爲指針爲4個字節,已知 (3)
};
struct C
{
struct A c;
int a;//此處的結構體名稱a與結構體A中的a不在一個級別,不衝突
};
int main()
{
struct Student stu1={"liubei",30};
struct Student stu2;
stu2=stu1; //同類的結構體變量可以互相賦值
//stu2.name="caocao";//error 不能直接將字符串常量賦給字符數組
strcpy(stu2.name,"caocao");//ok,用字符串拷貝函數
printf("%s,%d\n%s\n",stu1.name,stu1.age,stu2.name);
struct C sc;
sc.c.a=10;
sc.a=100;//a不衝突,因爲等級不同
printf("%d.%d\n",sc.c.a,sc.a);
return 0;
}
3.定義結構體類型變量
(1)先聲明結構體類型,再定義該類型的變量
如:
struct Student
{
char name [20];
int age;
};
int main()
{
struct Student stu1;
struct Student *p;
return 0;
}
(2)在聲明類型的同時定義變量
struct Student
{char name[20];
int age;
};stu1;
4.訪問成員的方式:(1)結構體普通變量通過 · 訪問成員;(2)結構體指針變量通過->訪問成員
struct A
{
int a;
int *p;
};
struct B
{
struct A c;
struct A *d;
int e;
int *f;
};
struct Student
{
char name[20];
int age;
};
int main()
{
struct B bb;
struct B *pb;
bb.c.a; //訪問成員
bb.c.p;
bb.d->a;
bb.d->p;
bb.e;
bb.f;
pb->c.a;//或(*pb).c.a (*pb)是對pb進行解引用,不過用起來麻煩,使用較少
pb->c.p;
pb->d->a;
pb->d->p;
pb->e;
pb->f;
struct Student stu1={"liubei"};//聚合類型(數組和結構體)只初始化一部分時,其餘部分默認爲0,此處即age=0
stu1.age=33;//修改stu1的年齡爲33
struct Student *ps=&stu1;
(*ps).age=28;//或ps->age=28;比較常用 //指向符->和[]自帶解引用
}
區分函數調用與函數聲明的方法:函數聲明有數據類型,函數調用沒有數據類型
在C語言中,調用不帶參數的函數,如 Student stu1();
在C++中,調用不帶參數的函數,如 Student stu1; //c++沒有參數的時候不用帶括號
在C語言中,結構體關鍵字struct在調用時不可以省略;
在C++中,結構體關鍵字struct在調用時可以省略;
struct A
{
int a;
A()
{
printf("haha ");
}
A(int x)
{
a=x;
printf("A(int x)\n");
}
};
int main()
{
A aa; //c++沒有參數的時候不用帶括號
A bb(3);
return 0;
}
打印結果: haha A(int x)
用typedef聲明新類型名
typedef是關鍵字,作用是類型重定義,即給類型取個別名
如:typedef unsigned longlong uint64;即給unsigned long long類型取個別名叫uint64
1.用一個別名代替複雜的類型名
(1)用別名代替結構體類型
typedef struct Student Student; //Student是別名
即:
typedef struct Student
{
char name[20];
int age;
}Student; //Student爲新的類型名(別名),代替typedef struct Student{code}
用typedef聲明後再去定義變量時不需要再寫類型名struct Student,如直接定義爲Student sa;
(2)用別名代替數組類型
typedef int Num[20]; //聲明Num爲整型數組類型名
Num a; //定義整型數組a
(3)用別名代替指針類型
typedef int * p; //聲明p爲整型指針類型
p a[10]; //a是指針數組
(4)用別名代替指向函數的指針類型
typedef int (* pfun)(int ,int); //pfun是函數指針類型,該函數返回整型值
pfun p; //p爲函數指針類型
歸納 聲明新類型名的方法:
按定義變量的方式,把變量名換上新類型名,並且在最前面加上typedef,就聲明瞭新類型名代替原來的類型。
typedef 與#define
typedef int * Pint;//重定義類型
# define PINT int *//字符替換
int main()
{
Pint d,e; //d爲指針,e爲指針
PINT f,h;//int *f,h,故f爲指針,h爲int型
}
typedef 是類型重定義,在編譯階段有效,有類檢查功能,有自己的作用域
# define
作用是字符替換,是預編譯命令,沒有檢查功能,沒有作用域的限制