字符指針變量
在指針的類型中我們知道有一種指針叫做字符指針
它的使用情況如下:
#include<stdio.h>
int main()
{char pa = 'w';char*p1=&pa;*p1 = 'a';printf("%c\n", *p1);return 0;
}
在這段代碼當中,我們將‘w’字符的地址傳到了p1里面,而p1就是一個字符指針。
除了上面這種使用方法,還有一種關于字符指針變量的使用方法如下:
#include<stdio.h>
int main()
{char* p1 = "abced";printf("%s\n", p1);return 0;
}
有沒有感到很好奇,對于這段代碼的解釋,大家有什么想法嗎?
咱們的第一個反應應該是,這個是不是把字符串“abced”放到字符指針p1里面啊,但真實情況不是這樣的,這個是將“abced”中的首字母的地址放到了p1的指針變量當中,從而在打印的時候,可以通過p1找到字符串首字母的地址,從而順藤摸瓜地打印出整個字符串。
下面呢,我們來看一段代碼,這段代碼是一道經典的面試題,來自《劍指offer》
#include<stdio.h>
int main()
{char arr3[] = "hello,bit.";char arr4[] = "hello,bit.";char* p1 = "hello,bit.";char* p2 = "hello,bit.";if (arr3 == arr4){printf("arr3 and arr4 are the same\n");}elseprintf("arr3 and arr4 are not same\n");if (p1 == p2){printf("p1 and p2 are the same\n");}elseprintf("p1 and p2 are not same\n");return 0;
}
下面是這段代碼運行的結果
這個結果大家想到了嗎??
其中的原理如下:
在數組中,用相同的常量字符串初始化數組時,系統會開辟不同的內存空間。
而在指針當中,兩個指針指向的是同一個常量字符串,也就是指向同一個開辟下的內存空間。這個就是上面答案的原理所在。
數組指針變量
前面我們講了指針數組,指針數組是一個數組,存放的是指針(地址),而數組指針是一個指針,存放的是數組的地址,看下面兩個:
//指針數組和數組指針
//1.int*arr[10]
//?
//2.int(*arr)[10]
大家可以看看這兩個,哪個是指針數組,哪個是數組指針。
很明顯,第一個是指針數組,數組名是arr,數組中存放有10個元素,每個元素是int*類型
然后第二個是一個數組指針,根據優先級考慮,在這個當中,首先arr應該與*結合,構成一個指針,然后指向的是一個10個元素的數組,數組中的每個元素都是int類型
數組指針變量的初始化
數組指針是一個指針,存放的應該是數組的地址,那我們怎么可以得到數組的地址呢,&arr,通過這個便可以得到數組的地址
#include<stdio.h>
int main()
{int arr[10] = { 0 };int(*pte)[10] = &arr;return 0;
}
?
從這個當中我們可以看到,&arr和pte的地址是相同的,二者指向了同一塊內存空間,這個就是數組指針變量的初始化。
二維數組傳參的本質
有了前面的數組指針變量的基礎,我們就可以好好地了解一下二維數組傳參的內容,之前,我們寫過二維數組傳參的內容,請看下面的代碼:
?
#include<stdio.h>
void print(int arr[3][4], int r, int j)
{for (int i = 0; i < r; i++){for (int h = 0; h < j; h++){printf("%d ", arr[i][h]);}printf("\n");}}
int main()
{int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };print(arr, 3, 4);return 0;
}
?我們在之前的代碼當中,形參是數組形式,實參也是數組的形式,除了這個寫法,我們還有其他的寫法嗎?
我們再來看二維數組,二維數組其實可以看成是每個元素都是一維數組的數組,然后數組名是數組首元素的地址,也就是說,實參的第一個參數的意思是二維數組第一行的四個元素的地址,所以,我們可以在形參部分寫成這樣:
#include<stdio.h>
void print(int (*ptr)[4], int r, int j)
{for (int i = 0; i < r; i++){for (int h = 0; h < j; h++){printf("%d ", *(*(ptr+i)+h));}printf("\n");}}
int main()
{int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };print(arr, 3, 4);return 0;
}
在這里,我們在詳細講一個*(*(ptr+i)+h),首先,先看最里面的那個括號,ptr是數組首元素的地址,ptr+i代表著二維數組的第幾行的地址,然后再加*找到第幾行的元素,也就是arr[i],然后用arr[i]+j代表的是第i行第j列的地址,然后在使用解引用符,就可以找到該地址所代表的元素,然后打印出來,就是數組。
數組名,是數組首元素的地址
&數組名,是數組的地址
函數名,是函數的地址
&函數名,也是函數的地址
二維數組傳參,形參可以形成數組形式,也可以寫成指針形式。
函數指針變量
通過前面對于指針變量的理解,我們大概可以知道,函數指針變量應該是一個指針變量,指向的應該是函數的地址,那么問題來了,函數有地址嗎?讓我們來看一下:
#include<stdio.h>
int main()
{printf("printf = %p\n", &printf);return 0;
}
從上面的結果,我們可以看到,函數是有地址的,函數名就是函數的地址,那我們也可以通過&函數名來獲得函數的地址,那我們也可以通過函數地址的調用實現對于函數的調用。
如果我們將函數變量的地址存放起來,就可以創建函數指針了,函數指針其實和數組指針是極其相似的。
void test()
{printf("hehe");
}
void (*ptr1)() = test;
void(*ptr2)() = &test;int Add(int x, int y)
{return x + y;
}
int (*ptr3)(int, int) = Add;
int (*ptr4)(int x, int y) = &Add;//加不加 x,y都是可以的
?函數指針類型分析:
函數指針變量的使用
通過函數指針調用指針所指向的函數
#include<stdio.h>
int Add(int x,int y)
{return x + y;
}
int main()
{int (*ptr)(int, int) = Add;printf("%d\n", (*ptr)(3, 4));printf("%d\n", ptr(3, 4));return 0;
}
?
兩段有趣的代碼
接下來,我們來看兩段出自《C陷阱和缺陷》這本書中的代碼:
(*(void(*)())0)()?
//第一段:
//(*(void(*)())0)()
/*在這段代碼當中:
* 第一步:void(*)()這個是函數指針的類型
* 第二步:(void(*)())0----這個是對0的強制類型轉換,使0轉換成函數指針類型
* 第三步:(*(void(*)())0)()----是一個指針
*/
第二段:
第二段:
void (*signal(int, void(*)(int)))(int);
第一步:signal是一個函數名
第二步:函數名后面的(int, void(*)(int))是函數參數
第三步:整個又是一個函數指針,參數類型是int
typedef關鍵字
typedef是關鍵字沒在C語言當中可以起到重命名的作用
?
typedef int* unit;
typedef void(*ptr)(int);
typedef void(*deff)();
函數指針數組
我們之前學了指針數組,同理,函數指針數組是將函數指針放入數組當中,那么這個該怎么實現呢??
//函數指針數組
void (*)()ptr[10] =;?
?
?
?
?