基礎(chǔ)
- 內(nèi)存:是通過連續(xù)的內(nèi)存編號來管理的(內(nèi)存的地址)
- 指針:指針變量里邊存儲的就是內(nèi)存地址
值和類型
不能簡單通過檢查一個值的位來判斷它的類型,值得類型取決于其使用方式
未初始化和非法指針
int *a;
*a = 12;
訪問未初始化的指針是非法的
指向指針的指針
int a = 12;
int *b = &a;
int **c = &b;
*操作符具有從右向左的結(jié)核性,所以這個表達(dá)式相當(dāng)于*(*c)
定義
*a 間接訪問
通過一個指針訪問它所指向的地址的過程稱為間接訪問(indirection)或解引用指針(dereferencing the pointer),這個用于執(zhí)行間接訪問的操作符是單目操作符*
int a = 10;
int *p = &a;
-
int *p
表示:表達(dá)式*p產(chǎn)生的結(jié)果類型是int類型 -
int *
并不是一個類型,原因如下
int* b,c,d;
這并不是將三個變量聲明為指向整型的指針,b是一個指針,其余兩個變量為普通整型
&b 取地址符
int *pb = &b;// & scanf函數(shù)中有一個取地址符指針變量可以通過地址賦值
%p 指針占位符
printf("%p\n",p);
NULL指針
對一個NULL指針進(jìn)行解引用操作是非法的
NULL類似于給普通變量賦初始值的0,其實(shí)就是地址為0
int *p =NULL;//定義了一個指針變量,初始值為NULL
float*p1 = NULL;//浮點(diǎn)型指針
double *p2 =NULL;
char *p3 =NULL;
指針的算術(shù)運(yùn)算
int *pp =NULL;
int num1 = 10;
pp = &num1;
printf("%p\n",pp);
printf("%p\n",(pp+1));
指針的算術(shù)運(yùn)算需要根據(jù)指針類型去計算,指針+ 1是向高位移動一個類型所占的字節(jié)數(shù),指針- 1是向低位移動一個類型所占的字節(jié)數(shù)
printf("%p\n",pp++);
printf("%p\n",++pp);
printf("%p\n",pp);
++ --還是遵循之前++ --運(yùn)算符法則
指針的重指向
咱們可以讓指針重新指向另一個變量(內(nèi)存的另一塊區(qū)域),這就叫指針的重指向
我們可以通過指針修改內(nèi)存地址上的值
int number1 = 10;
int *pNum1 = &number1;
int number2 = 8;
pNum1 = &number2;
*pNum1 = 9;
printf("%d\n",*pNum1);
printf("%d\n",number2);
用指針交換兩個數(shù)的值
- 函數(shù)在傳參數(shù)的時候,形式參數(shù)只是拷貝了實(shí)際參數(shù)的內(nèi)容或者值,實(shí)際參數(shù)并未發(fā)生改變
- 有了指針我們可以直接操作指針指向的內(nèi)存區(qū)域,然后修改內(nèi)存中的值
int num1 = 12;
int num2 = 16;
swapTwoNumbers(&num1, &num2);
printf("%d %d\n",num1,num2);
指針與數(shù)組的關(guān)系
數(shù)組名就是數(shù)組的地址,它恒等于數(shù)組元素的首地址
實(shí)際上*(arr1 + i) = arr[i];
int arr1[4] = {1,2,3,4};
for(inti = 0; i < 4; i++) {
printf("%p\n",arr1+0);
}
printf("%lu\n",sizeof(arr1));//數(shù)組名雖然是地址,但是我們利用它計算內(nèi)存空間的話,還是根據(jù)里邊存儲的數(shù)據(jù)類型而定
int *pp = &num1;
printf("%lu\n",sizeof(pp));//打印一個單純的變量地址的話,并不是根據(jù)類型而定
printf("%lu\n",sizeof(&num1));
char aa = 'w';
char *pp1 = &aa;
printf("%lu\n",sizeof(pp1));//指針類型所占的字節(jié)數(shù)與計算機(jī)本身的計算位數(shù)有關(guān),如果是32位的計算機(jī),指針類型占4個字節(jié),如果是64位的計算機(jī),指針類型占8個字節(jié)
short array[4] = {1,2,3,4};
int *p = array;//不匹配的指針類型
數(shù)組名確實(shí)能夠打印出數(shù)組元素的首地址,但是如果把數(shù)組名認(rèn)為就是地址是不準(zhǔn)確的,實(shí)際上數(shù)組名是一個常量
指針與字符串的關(guān)系
char name[] ="guozhenyan";
char *p1 = name;
printf("%p\n",p1);
for(int i = 0; i <strlen(name); i++) {
printf("%c\n",*(p1+i));
}
指針數(shù)組
char *a[10] = {"ddd","sdq","das","qwe","cva","btg","aaa","ccc","eee","fff"};
printf("%p\n",a);//數(shù)組指針的地址
printf("%p\n",a+1);//對數(shù)組指針地址進(jìn)行操作
printf("%p\n",a[0]);//對存的數(shù)據(jù)地址進(jìn)行操作
printf("%p\n",a[1]);
printf("%p\n",*a);//存的是地址
printf("%p\n",*(a+1));
printf("%c\n",**a);
printf("%c\n",**(a+1));
printf("%c\n",*a[0]);
printf("%c\n",*a[1]);
結(jié)構(gòu)體指針
typedef struct person {
char name[20];
int age;
int score;
}Person;
Person p1 = {"guangguang",16,90};
Person p2 = {"jintao",18,99};
Person p3 = {"wangce",17,88};
void printArr(Person*p,intcount){
for(int i = 0; i < count; i++) {
printf("%s,%d,%d\n",(*(p+i)).name,(p+i)->age,(*(p+i)).score);
}
//使用指針去訪問結(jié)構(gòu)體數(shù)組的成員變量的時候,可以使用->
Person arr[3] = {p1,p2,p3};
函數(shù)指針
函數(shù)名跟數(shù)組名類似,實(shí)際上它也是一個地址
printf("%p\n",helloWorld);
我們也可以找一個指針,指向函數(shù)名所在的地址.這個指針就是所謂的函數(shù)指針
函數(shù)的指針類型根據(jù)函數(shù)的返回值和參數(shù)而定
返回值類型+ (*指針名) +參數(shù)的類型=函數(shù)指針
void (*p1)() =NULL;
p1 =helloWorld;//聲明
p1();//調(diào)用方式
void (*p2)(int) =NULL;
p2 =printNumber;
p2(31415);
BOOL (*p3)() =backYes;
printf("%d\n",p3());
int (*p4)(int,int) =sum;
printf("%d\n",p4(8,4));
函數(shù)指針可以重新指向別的函數(shù),只要函數(shù)類型一樣就可以重指向
char a[10];
printf("請輸入函數(shù)名:\n");
scanf("%s",a);
int(*p5)(int,int) =NULL;
printf("請輸入a,b的值:\n");
int b,c;
scanf("%d%d",&b,&c);
if(strcmp(a,"maxValue") == 0){
p5 =max;
}else if(strcmp(a,"sum") == 0){
p5 =sum;
}else{
printf("輸入有誤\n");
return 0;
}
//現(xiàn)在p5這個指針并沒有指向認(rèn)可地址,也可以導(dǎo)致程序崩潰,這種情況叫作野指針
printf("%d\n",p5(b,c));
替換指針類型
typedef int (*pGet)(int,int);//重新定義函數(shù)指針的類型:pGet
//我們利用typedef這個關(guān)鍵字還可以重新定義函數(shù)指針的類型變成一個新的類型*后邊跟上我們新類型的名字
函數(shù)指針的作用
- ?函數(shù)指針是OC中block的底層實(shí)現(xiàn),OC中block得形式跟函數(shù)指針一模一樣.
- ?函數(shù)指針因?yàn)榭梢灾刂赶虿煌瘮?shù),可以起到簡化代碼和解耦合的作用
函數(shù)回調(diào)(用函數(shù)指針作為函數(shù)參數(shù))
int(*p)(int,int) =NULL;
p = max;//函數(shù)指針指向的函數(shù)可能實(shí)現(xiàn)的功能比較復(fù)雜,或者是公司核心代碼,我們一般程序員不需要知道里面的詳細(xì)內(nèi)容,只需要會調(diào)用即可.(面向?qū)ο笳Z言,封裝特性的底層(雛形))
函數(shù)轉(zhuǎn)移表
//常規(guī)寫法
switch ( oper ){
case ADD:
result = add( oper1, oper2 );
break;
case SUB:
result = sub( oper1, oper2 );
break;
case MUL:
result = mul( oper1, oper2 );
break;
case DIV:
result = div( oper1, oper2 );
break;
...
}
//使用轉(zhuǎn)移表之后的寫法
double add( double, double );
double sub( double, double );
double mul( double, double );
double div( double, double );
...
double (*oper_func[])( double, double ) = {
add, sub, mul, div, ...
};
//下面的語句替換前面整條switch語句
result = oper_func[ oper ]( op1, op2 );
范指針
void *p;
memcpy;
類型轉(zhuǎn)換
fptr = (float *) iptr;