指針2
==========
指針二
==========
1、認(rèn)識(shí)指針
地址 編號(hào) 常量
指針 變量
2、定義指針
所指類型 * 變量名
3、給指針變量進(jìn)行初始化【&】
【注】給指針變量進(jìn)行初始化只可以賦值地址。
4、指針?biāo)嫉淖止?jié)數(shù)
64位系統(tǒng)下面都是8字節(jié)。
5、為什么使用指針?
為了跨棧訪問數(shù)據(jù)。
【補(bǔ)充】當(dāng)函數(shù)被調(diào)用時(shí),cpu會(huì)在棧區(qū)給該函數(shù)開辟一塊空間。當(dāng)函數(shù) 結(jié)束調(diào)用時(shí),該空間被摧毀。
6、指針與數(shù)組
指針 + 1 所指類型的字節(jié)
數(shù)組名 + 1 數(shù)組元素的字節(jié)數(shù)
數(shù)組名—代表著數(shù)組首元素的地址; int a[10]; a == &a[0]
*(a+1)—>*&a[1]—>a[1]
int * p = &a[0] *(p+1)—>*&a[1]—>p[1]
【結(jié)論】
1、數(shù)組名代表著數(shù)組首元素的地址。是一個(gè)常量。
int a[10]; a++; 常量不可以加加 數(shù)組名是一個(gè)常量
2 、指針指向數(shù)組首元素時(shí),可以使用下標(biāo)法遍歷數(shù)組。
int *p = a(=&a[0]) p[i] 3、a代表著數(shù)組首元素的地址;&a代表著數(shù)組的地址。
這兩個(gè)地址的值是一樣的,但是a+1的結(jié)果與 &a+1的結(jié)果是不一 樣的。
為什么要使用指向數(shù)組的指針?有指向數(shù)組首元素的指針不是就完全可 以嗎?
對(duì)于指向數(shù)組的指針,主要是用在二維數(shù)組傳參時(shí)。
二維數(shù)組的首元素是一個(gè)數(shù)組。所以將二維數(shù)組的數(shù)組名作為實(shí)參,那 么形參需要是一個(gè)指向數(shù)組的指針。
7、定義一個(gè)指向數(shù)組的指針
int (*p)[10]
8、指向指針的指針
int * q;
int **p = &q;
9、指向函數(shù)的指針
返回值類型 (*p)(形參列表) = 函數(shù)名;
【見代碼 指針函數(shù)的應(yīng)用】
【目標(biāo)】
1、定義一個(gè)指向函數(shù)的指針;
2、給函數(shù)指針進(jìn)行初始化;賦值的是函數(shù)名。
3、通過函數(shù)名可以調(diào)用函數(shù);通過指向函數(shù)的指針也可以調(diào)用函數(shù)。
4、函數(shù)名代表著函數(shù)的入口地址。
【擴(kuò)展】函數(shù)的入口地址
我們編輯的文件,在編譯之后會(huì)變成二進(jìn)制文件,保存在硬盤上,當(dāng)程 序運(yùn)行的時(shí)候,會(huì)將該二進(jìn)制文件讀取到常量區(qū)中。二進(jìn)制文件就是 cpu的使用說明書。函數(shù)的入口地址就相當(dāng)于是這個(gè)說明中該函數(shù)所在 的頁數(shù)(地址)。當(dāng)發(fā)生函數(shù)調(diào)用的時(shí)候,cpu會(huì)根據(jù)函數(shù)名找到到函 數(shù)的地址,然后讀取指令,進(jìn)行操作。
【注】函數(shù)名代表著入口地址,并不是函數(shù)在運(yùn)行階段發(fā)生調(diào)用時(shí)候的 函數(shù)棧的地址。
一個(gè)程序經(jīng)歷編輯(coding)、編譯(command+b)、運(yùn)行的過 程。
10、返回指針的函數(shù)
【見代碼 函數(shù)指針的應(yīng)用】
11、認(rèn)識(shí)復(fù)雜的類型
【如何辨別與指針相關(guān)的妖魔鬼怪?】
1、先找到變量名,
2、然后觀察左右兩邊的運(yùn)算符,() [] *
3、根據(jù)優(yōu)先級(jí)去確定這到底是個(gè)什么鬼;優(yōu)先級(jí)最高的運(yùn)算符如果是,說明這是一個(gè) 指針;優(yōu)先級(jí)最高的是[],說明這是一個(gè)數(shù)組;優(yōu)先級(jí)最高的是(),說明這是一個(gè) 函數(shù)的聲明。
4、如果是數(shù)組,我們要看數(shù)組元素是什么類型,遮住數(shù)組名與數(shù)組的大小剩余部分就 是數(shù)組元素的類型;
如果是指針,我們要看指針指向什么類型,遮住變量名與剩余部分就所指向的類 型。
如果是函數(shù),我們要看函數(shù)的返回值類型與形參列表。函數(shù)名前半部分都是返回值 類型,函數(shù)名后面都是形參列表;
【課堂練習(xí)】
int (p[10])[10] 數(shù)組
int (p[10])[10] 數(shù)組元素是指針
int (*p[10])[10] 指針是指向數(shù)組的
這是一個(gè)數(shù)組,數(shù)組元素是指向數(shù)組的指針
int (p[10])(int) 數(shù)組
int (p[10])(int)數(shù)組元素是指針
int (*p[10])(int)指向函數(shù)
這是一個(gè)數(shù)組,數(shù)組元素是指向函數(shù)的指針
int (p)[10]; 指針
int (p)[10] 指向數(shù)組
int (p)[1 數(shù)組元素是指針
int (func[10])(int,int); 數(shù)組
int (func[10])(int,int);數(shù)組元素是指針
int (func[10])(int,int);指向的是函數(shù),函數(shù)的返回值int * 形參列表(int,int);
int (p[10])(int) 數(shù)組
int (p[10])(int)數(shù)組元素值指針
int (*p[10])(int)指向函數(shù) 函數(shù)返回值int 形參int
int (p)[10]; 指針,
int (p)[10];指向數(shù)組
int (p)[10];數(shù)組元素是指針
int (func[10])(int,int);數(shù)組
int (func[10])(int,int);數(shù)組元素是指針
int (func[10])(int,int);指向函數(shù),函數(shù)返回值int *,形參 (int,int)
12、特殊的指針—空指針 與 野指針
當(dāng)我們聲明一個(gè)指針變量時(shí),不給指針變量賦值,那么此時(shí)這個(gè)指針就 是一個(gè)野指針,他的指向是不明確的。在程序使用野指針是非常危險(xiǎn) 的。
當(dāng)我們暫時(shí)無法給一個(gè)指針變量進(jìn)行賦值時(shí),我們可以將其置為NULL, 即:int * p = NULL;
13、const常量修飾符與指針的關(guān)系
int * const p = &b;
p = &c;//錯(cuò)誤!
//const放在*后面,修飾的是指針,即指針的指向不允許發(fā)生改變。
const int * p = &b; int const * p = &b;
// *p = 100; 錯(cuò)誤!
//const放在*前面,修飾的是指針的指向。也就是說不能通過指針去修改所指變 量的內(nèi)容。
指針的復(fù)習(xí)
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a[10];
//數(shù)組首元素的地址
printf("%p\n",a);
//數(shù)組的地址
printf("%p\n",&a);
//數(shù)組首元素地址+1 4個(gè)字節(jié)
printf("%p\n",a+1);
//數(shù)組地址+1 40個(gè)字節(jié)
printf("%p\n",&a+1);
return 0;
}
返回指針的函數(shù)
#include <stdio.h>
#include <stdlib.h>
int * func();
int main(int argc, const char * argv[]) {
int * p = func();
int * r = (int*)malloc(sizeof(int)*10);
for(int i = 0;i<10;i++){
r[i] = i*1111;
printf("%p\n",&r[i]);
}
//func函數(shù)調(diào)用結(jié)束之后,func??臻g被摧毀,數(shù)組a也就不存在了。
for (int i = 0; i < 10; i++) {
printf("%d ",p[i]);
}
return 0;
}
int * func(){
//在堆上開辟4*10個(gè)字節(jié)的空間,malloc是程序員手動(dòng)開辟空間的方法。返回值是一個(gè)地址。
//在堆上開辟的空間需要程序員手動(dòng)去釋放,只有調(diào)用free函數(shù)才會(huì)被摧毀。
int * p = (int*)malloc(sizeof(int)*10);
for(int i = 0;i<10;i++){
p[i] = i*10;
printf("%p\n",&p[i]);
}
// free(p);
return p;
//a在func函數(shù)棧開辟空間
// int a[10] = {1,2,3,4,5,6,7,8,9,10};
// return a;
}
函數(shù)指針應(yīng)用
#include <stdio.h>
int test(int (*p)(int a,int b),int a,int b);
int gcd(int a,int b);
int lcm(int a,int b);
int main(int argc, const char * argv[]) {
int a,b,c;
printf("請(qǐng)輸入a,b,c的值\n");
scanf("%d%d%d",&a,&b,&c);
if (c>0) {
//求a與b的最大公約數(shù)
printf("%d\n",test(gcd,a,b));
}else{
//求a與b的最小公倍數(shù)
printf("%d\n",test(lcm,a,b));
}
//函數(shù)名代表著函數(shù)的入口地址
printf("+++++%p\n",test);
printf("+++++%p\n",&a);
// 【oc階段】回調(diào)。
// [btn addtarget:withSelector:]
return 0;
}
int test(int (*p)(int a,int ),int a,int b){
return p(a,b);
}
int gcd(int a,int b){
if (a<b) {
int temp = a;
a = b;
b = temp;
}
int c;
while (1) {
c = a%b;
if (c == 0) {
//返回最大公約數(shù)
return b;
}else{
a = b;
b = c;
}
}
}
int lcm(int a,int b){
return a*b/gcd(a, b);
}
野指針與空指針
#include <stdio.h>
int main(int argc, const char * argv[]) {
//在Xcode編譯器中會(huì)將沒有初始化的數(shù)據(jù),初始化為默認(rèn)的值。
//int初始化為0,float初始化為0.0 指針初始化為NULL
//當(dāng)我們聲明一個(gè)指針,暫時(shí)沒有辦法為其初始化,我們將置為NULL.
//否則的話就是野指針。
int * p = NULL;
printf("%p\n",p);
return 0;
}
const修飾符
#include <stdio.h>
int main(int argc, const char * argv[]) {
//const常量修飾符 被const修飾變量是只讀變量
int const a = 10;
int b = 20;
int c = 33;
int * const p = &b;
//const放在*后面,修飾的是指針,即指針的指向不允許發(fā)生改變。
// p = &c;
// const int * p = &b; int const * p = &b;
//const放在*前面,修飾的是指針的指向。也就是說不能通過指針去修改所指變量的內(nèi)容。
// *p = 100;
return 0;
}