- 博客主頁:向不悔
- 本篇專欄:[C]
- 您的支持,是我的創作動力。
文章目錄
- 0、總結
- 1、字符指針變量
- 2、數組指針變量
- 2.1 數組指針變量是什么?
- 2.2 數組指針變量怎么初始化?
- 3、二維數組傳參的本質
- 4、函數指針變量
- 4.1 函數指針變量的創建
- 4.2 函數指針變量的使用
- 4.3 兩端有趣的代碼
- 4.3.1 typedef關鍵字
- 5、函數指針數組
- 6、轉移表
0、總結
1、字符指針變量
思考字符數組和常量字符串的區別?經思考,發現,字符數組里的數組是可變的,常量字符串是不可變的。
#include <stdio.h>
int main()
{char arr[] = "abcdef";char* p1 = arr;*p1 = 'w';const char* p2 = "abcdef";// *p2 = 'w';printf("%s\n", p1);printf("%s\n", p2);return 0;
}
運行:
wbcdef
abcdef
如圖加深理解:
我們知道,字符指針變量一般使用如下:
#include <stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'z';printf("%c\n", *pc);return 0;
}
那么,為什么這個也可以呢?
#include <stdio.h>
int main()
{const char* pstr = "hello world.";printf("%s\n", pstr);return 0;
}
經思考,代碼const char* pstr = "hello world.";
本質上是把字符串首字符的地址放到了pstr
中。如圖:
《劍指Offer》中收錄了一道和字符串相關的題,如下:
#include <stdio.h>
int main()
{char str1[] = "hello bit.";char str2[] = "hello bit.";const char* str3 = "hello bit.";const char* str4 = "hello bit.";if (str1 == str2)printf("str1 and str2 are same\n");elseprintf("str1 and str2 are not same\n");if (str3 == str4)printf("str3 and str4 are same\n");elseprintf("str3 and str4 are not same\n");return 0;
}
運行:
str1 and str2 are not same
str3 and str4 are same
在C/C++中,常量字符串會被存儲到一個共享的內存區域。當多個指針指向相同的常量字符串時,它們實際上指向同一塊內存。而如果用相同的常量字符串初始化不同的數組,則會為每個數組開辟獨立的內存塊。因此,str1
和str2
指向不同的內存,而str3
和str4
指向同一塊內存。
2、數組指針變量
2.1 數組指針變量是什么?
數組指針變量是指針變量,存放的是數組的地址,指向數組的指針變量。
思考以下,哪個是數組指針變量?
int *p1[10];
int (*p2)[10];
結論:
p1
是指針數組,具體的說,一個包含10個指針的數組,每個指針指向一個int
。p2
是數組指針,具體地說,指向一個包含10個int的數組。- 主要區別在于括號的使用,括號的位置決定了聲明的優先級。
p1
是數組優先,p2
是指針優先。 - 值得注意:
[]
的優先級要高于*
號的,所以聲明數組指針,就必須加上()
來保證p2
先和*
結合。
2.2 數組指針變量怎么初始化?
數組指針變量是用來存放數組地址的,因此用&數組名
。如下:
#include <stdio.h>
int main()
{char arr[10] = { 0 };char* p1 = arr;printf("%p\n", p1);printf("%p\n", p1+1);// 初始化數組指針變量char (*p2)[10] = &arr;printf("%p\n", p2);printf("%p\n", p2+1);return 0;
}
運行(32位平臺):
00B5F7A8
00B5F7A9
00B5F7A8
00B5F7B2
經調試,可知,&arr
和p2
的類型是完全一致的。
數組指針類型解析:
int (*p) [10] = &arr;| | || | || | p指向數組的元素個數| p是數組指針變量名 p指向的數組的元素類型
3、二維數組傳參的本質
二維數組的數組名表示的就是第一行的地址,是一維數組的地址。所以第一行的地址的類型是數組指針類型。二維數組傳參本質上也是傳遞了地址,傳遞的是第一行這個一維數組的地址。形參可以寫成指針形式的,如下:
#include <stdio.h>
void print(int (*ptr)[5], int r, int c)
{for (int i = 0; i < r; i++){for (int j = 0; j < c; j++){printf("%d ", *(*(ptr + 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} };print(arr, 3, 5);return 0;
}
運行:
1 2 3 4 5
2 3 4 5 6
3 4 5 6 7
總結:
二維數組傳參,形參部分可以寫成數組,也可以寫成指針形式。
4、函數指針變量
4.1 函數指針變量的創建
函數指針變量用來存放函數地址的,未來通過地址能夠調用函數。如下:
#include <stdio.h>
void test()
{printf("hello\n");
}
int main()
{printf("test: %p\n", test);printf("&test: %p\n", &test);return 0;
}
運行:
test: 008513CA
&test: 008513CA
可知,函數是有地址的,有兩個方式:
- 函數名就是函數的地址
&函數名
獲得函數的地址。
如果要把函數的地址存放起來,就需要創建函數指針變量,函數指針變量的寫法跟數組指針寫法非常類似,如下:
#include <stdio.h>
void test()
{printf("hello\n");
}
void (*pf1)() = &test;
void (*pf2)() = test;int Add(int x, int y)
{return x + y;
}
int (*pf3)(int, int) = &Add;
int (*pf4)(int, int) = Add; // x和y寫上或者省略都是可以的。
函數指針類型解析:
int (*pf3) (int x, int y) | | ------------| | || | pf3指向函數的參數類型和個數的交代| 函數指針變量名pf3指向函數的返回類型int (*) (int x, int y) // pf3函數指針變量的類型
4.2 函數指針變量的使用
#include <stdio.h>int Add(int x, int y)
{return x + y;
}int main()
{int (*pf)(int, int) = Add;printf("%d\n", (*pf)(2, 3));printf("%d\n", pf(3, 5));return 0;
}
運行:
5
8
4.3 兩端有趣的代碼
代碼1:
(*(void (*)())0)();
代碼2:
void (*signal(int, void(*)(int)))(int);
這兩段代碼均出自:《C陷阱和缺陷》這本書。
4.3.1 typedef關鍵字
typedef
是用來類型重命名的,可以將復雜的類型進行簡單化。
例如,可以對unsigned int
進行簡化,如下:
typedef unsigned int uint;
如果對指針類型呢?如下:
// 將int * 重命名為 ptr_t
typedef int* ptr_t;
但是對于數組指針和函數指針稍微有點區別:
比如說,數組指針類型int(*)[5]
,需要重命名為parr_t
,那可以這樣寫:
typedef int(*parr_t)[5];
函數指針類型的重命名也是一樣的,比如將void(*)(int)
類型重命名為pf_t
,就可以這樣寫:
typedef void(*pf_t)(int);
因此,如果對void (*signal(int, void(*)(int)))(int);
進行簡化,可以這樣寫:
typedef void(*pf_t)(int);
pf_t signal(int, pf_t);
5、函數指針數組
把函數的地址存放到一個數組中,那么這個數組就叫函數指針數組,以下哪個是函數指針數組?
int (*parr1[3])();
int *parr2[3]();
int (*)() parr3[3];
答案是:parr1
parr1
先和[]
結合,說明parr1
是數組,那么數組內容是什么呢?
是int (*)()
類型的函數指針。
6、轉移表
函數指針數組的用途一般為轉移表。
舉個例子,計算機通過函數指針數組的實現:
#define _CRT_SECURE_NO_WARNINGS 1
#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;int (*p[5])(int x, int y) = { 0, add, sub, mul, div };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);if ((input <= 4 && input >= 1)){printf("輸入操作符:");scanf("%d %d", &x, &y);ret = (*p[input])(x, y);printf("ret = %d\n", ret);}else if (input == 0)printf("退出計算機\n");else printf("輸入有誤\n");} while (input);return 0;
}
完。