字符指針變量
字符指針在之前我們有提到過,(字符)(指針)前面的字符代表著存儲的元素為字符類型,而指針則是表示這存儲的方式。
寫法為char*
一般使用的方式如下:
nt main()
{
char ch = 'w';
char *pc = &ch;
*pc = 'w';
return 0;
}
還有一種使用方式如下:
int main()
{
const char* pstr = "hello jack.";
printf("%s\n", pstr);
return 0;
}
值得注意的是:代碼 const char pstr = “hello jack.”; 特別容易以為是把字符串 hello jack 放到字符指針 pstr 里了,但是本質(zhì)是把字符串 hello jack. 首字符的地址放到了pstr中*
《劍指offer》中有一道和字符串有關(guān)的題如下:
#include <stdio.h>
int main()
{
char str1[] = "hello jack.";
char str2[] = "hello jack.";
const char *str3 = "hello jack.";
const char *str4 = "hello jack.";
if(str1 ==str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if(str3 ==str4)
printf("str3 and str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
這里的char str1[]和charstr2[]在創(chuàng)造時是單獨開辟了一個儲存空間,因此二者的儲存地址是不同的
所以打印出來的是str1 and str2 are not same
char str3和charstr4中因為帶有了指針,因此指針指向的字符串hello jack是已經(jīng)單獨創(chuàng)造了一個儲存空間,而str3和str4則是存儲字符串的儲存地址,因此最后是str3 and str4 are same
數(shù)組指針變量
數(shù)組指針變量是什么
之前的是指針數(shù)組,現(xiàn)在的數(shù)組指針其實理解方式是相同的(數(shù)組)(指針),儲存的元素是數(shù)組,而儲存的方式就是通過指針儲存數(shù)組的地址
我們舉一些其他熟悉的例子:
? 整形指針變量: int * pint; 存放的是整形變量的地址,能夠指向整形數(shù)據(jù)的指針。
? 浮點型指針變量: float * pf; 存放浮點型變量的地址,能夠指向浮點型數(shù)據(jù)的指針
下面代碼哪個是數(shù)組指針變量?
1:int *p1[10];
2:int (*p2)[10];
. * p1[10]是指針數(shù)組
( * p2)[10]是數(shù)組指針
我們發(fā)現(xiàn)這二者的差別就在于括號,指針數(shù)組是沒有括號的,而數(shù)組指針是有括號的,我們可以這樣理解
* || p [10]我們把指針數(shù)組分開,一個是指針*,一個是數(shù)組p[10],表示數(shù)組p[10]里面存放的是指針
(* p)|| [10]我們可以看到p和結(jié)合起來了,因此可以理解為將數(shù)組p的地址通過指針的形式存放起來
數(shù)組指針變量怎么初始化
數(shù)組指針變量是用來存放數(shù)組地址的,那怎么獲得數(shù)組的地址呢?就是我們之前學(xué)習(xí)的 &數(shù)組名
int arr[10]={0};
&arr//取的是整個數(shù)組的地址
如果要存放個數(shù)組的地址,就得存放在數(shù)組指針變量中,如下:
int(*p)[10]=&arr;
我們通過調(diào)試可以看到,p和&arr的內(nèi)存地址是一樣的
我們對int (*p)[10]=&arr進行解析
int(*p)[10] = &arr
| ___ | ___ | ___ |
| ___ | ___ | ___ |
| ___ | ___ | ___ 整個數(shù)組的地址
| ___ | ___ p指向數(shù)組的元素個數(shù)
| ___ p是數(shù)組指針的變量名
p指向的數(shù)組的元素類型
二維數(shù)組傳參的本質(zhì)
過去我們有一個二維數(shù)組的需要傳參給一個函數(shù)的時候,我們是這樣寫的
#include <stdio.h>
void test(int a[3][5], int r, int c)
{
int i = 0;
int j = 0;
for(i=0; i<r; i++)
{
for(j=0; j<c; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);//arr傳的是數(shù)組首元素地址
return 0;
}
這里實參是二維數(shù)組,形參也寫成二維數(shù)組的形式,那還有什么其他的寫法嗎?
首先我們再次理解一下二維數(shù)組,二維數(shù)組起始可以看做是每個元素是一維數(shù)組的數(shù)組,也就是二維數(shù)組的每個元素是一個一維數(shù)組。那么二維數(shù)組的首元素就是第一行,是一個維數(shù)組
所以,根據(jù)數(shù)組名是數(shù)組首元素的地址這個規(guī)則,二維數(shù)組的數(shù)組名表示的就是第一行的地址,是一維數(shù)組的地址。
根據(jù)上面的例子,第一行的一維數(shù)組的類型就是 int [5] ,所以第一行的地址的類型就是數(shù)組指針類型 int(*)[5] 。
那就意味著二維數(shù)組傳參本質(zhì)上也是傳遞了地址,傳遞的是第一行這個一維數(shù)組的地址,那么形參也是可以寫成指針形式的。如下:
#include <stdio.h>
void test(int (*p)[5], int r, int c)
{
int i = 0;
int j = 0;
for(i=0; i<r; i++)
{
for(j=0; j<c; j++)
{
printf("%d ", *(*(p+i)+j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5}, {2,3,4,5,6},{3,4,5,6,7}};
test(arr, 3, 5);
return 0;
}
可能會有人感到疑惑,為什么不是指針數(shù)組呢?我來談?wù)勎覀€人的理解:
我們再來回顧一下指針數(shù)組的含義:
指針數(shù)組是數(shù)組里面存放的指針也就是地址,好像和二維數(shù)組存放一維數(shù)組的地址是一樣的.
但是當我們傳遞參數(shù)時我們發(fā)現(xiàn)傳遞的是指針的地址,而不是一維數(shù)組的地址,也就是說因為指針是存放的一維數(shù)組的地址,而如果我們用了指針數(shù)組的話,我們就是取了指針的地址
總結(jié):二維數(shù)組傳參,形參的部分可以寫成數(shù)組,也可以寫成指針形式
函數(shù)指針變量
函數(shù)指針變量的創(chuàng)建
什么是函數(shù)指針變量呢?
根據(jù)前面學(xué)習(xí)整型指針,數(shù)組指針的時候,我們的類比關(guān)系,我們不難得出結(jié)論:
函數(shù)指針變量應(yīng)該是用來存放函數(shù)地址的,未來通過地址能夠調(diào)用函數(shù)的。
那么函數(shù)是否有地址呢?
我們做個測試:
#include <stdio.h>
void test()
{
printf("hehe\n");
}
int main()
{
printf("test: %p\n", test);
printf("&test: %p\n", &test);
return 0;
}
運行結(jié)果如下:
確實打印出來了地址,所以函數(shù)是有地址的,函數(shù)名就是函數(shù)的地址,當然也可以通過 &函數(shù)名 的方式獲得函數(shù)的地址。
如果我們要將函數(shù)的地址存放起來,就得創(chuàng)建函數(shù)指針變量,函數(shù)指針變量的寫法其實和數(shù)組指針非常類似。如下:
void test()
{
printf("hehe\n");
}
void (*pf1)() = &test;
void (*pf2)()= test;
int Add(int x, int y)
{
return x+y;
}
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;//x和y寫上或者省略都是可以的,我們只需要函數(shù)參數(shù)的類型即可
我們對int (*pf3)[int x,int y]進行解析
int(*pf3)[int x,int y]
| ___ | ___ ___ |
| ___ | ___ ___ |
| ___ | ___ ___ |
| ___ | ___ ___ pf3指向函數(shù)的參數(shù)類型和個數(shù)的交代
| ___ 函數(shù)指針變量名
pf3指向函數(shù)的返回類型
int ( * ) (int x, int y) //pf3函數(shù)指針變量的類型
函數(shù)指針變量的使用
通過函數(shù)指針調(diào)用指針指向的函數(shù)
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
運行結(jié)果如下:
代碼
代碼1:
(*(void (*)())0)();
代碼2:
void (*signal(int , void(*)(int)))(int);
代碼1:我們分布解析:( * (void ( * )( ) ) 0 )()可以拆成(a)(),a=( void ( * ) ( ) )0
我們先分析a,首先我們看到void()()就應(yīng)該想到這是一個返回類型為void的函數(shù)類型,并且括號將*號括了起來,說明這是函數(shù)指針,而那個0可能會把我們頭都搞大,但是我們舉一個例子
int a=0;
char b='A';
int c=a+(int)b;
這是強制類型轉(zhuǎn)換,也就是說0被強制轉(zhuǎn)換為一個返回值為void,參數(shù)為空的函數(shù)指針
而(*a)()又是一個函數(shù)指針,因此這段代碼有兩個函數(shù)指針
代碼2:
void (*signal(int , void ( * )(int)))(int)我們依然將代碼才分開:
void( * a)(int)
a=signal(int,b)
b=void( *)(int)
顯然這幾個都是函數(shù),第一個和第三個都為函數(shù)指針,第二個b為void返回類型的函數(shù),然后a的參數(shù)包含了int 和void,返回類型為signal
typedef關(guān)鍵字
typedef是用來類型重命名的,可以將復(fù)雜的類型,簡單化,也可以理解為設(shè)置一個代號,方便理解并且減少代碼的長度
比如,你覺得 unsigned int 寫起來不方便,如果能寫成 uint 就方便多了,那么我們可以使用:
typedef unsigned int uint;
//這里感覺和define有點類似
define a 8
如果是指針類型,能否重命名呢?其實也是可以的,比如,將 int* 重命名為 ptr_t ,這樣寫
typedef int* ptr_t
但是對于數(shù)組指針和函數(shù)指針稍微有點區(qū)別
比如我們有數(shù)組指針類型 int(*)[5] ,需要重命名為 parr_t ,那可以這樣寫
typedef int(*parr_t)[5]; //新的類型名必須在*的右邊
函數(shù)指針類型的重命名也是一樣的,比如,將 void(*)(int) 類型重命名為 pf_t ,就可以這樣寫:
typedef void(*pfun_t)(int);//新的類型名必須在*的右邊
要簡化代碼,可以這樣寫
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
這里面其實有一些規(guī)律,我們設(shè)置新的類型名時,都會將類型名寫在函數(shù)的變量中
函數(shù)指針數(shù)組
數(shù)組是一個存放相同類型數(shù)據(jù)的存儲空間,我們已經(jīng)學(xué)習(xí)了指針數(shù)組
比如:
int *arr[10];
//數(shù)組的每個元素是int*
那要把函數(shù)的地址存到一個數(shù)組中,那這個數(shù)組就叫函數(shù)指針數(shù)組,那函數(shù)指針的數(shù)組如何定義呢?
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];
parr1 先和 [ ] 結(jié)合,說明parr1是數(shù)組,數(shù)組的內(nèi)容是什么呢?
是 int (*)() 類型的函數(shù)指針。文章來源:http://www.zghlxwxcb.cn/news/detail-697841.html
轉(zhuǎn)移表
函數(shù)指針數(shù)組的用途:轉(zhuǎn)移表文章來源地址http://www.zghlxwxcb.cn/news/detail-697841.html
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("請選擇:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("輸?操作數(shù):");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("輸?操作數(shù):");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("輸?操作數(shù):");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("輸?操作數(shù):");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("選擇錯誤\n");
break;
}
} while (input);
return 0;
}
到了這里,關(guān)于C語言深入理解指針(非常詳細)(四)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!