+ ]% c3 H; A* P) c# l9 Pint d[10];# [2 Y, b& T/ e t* A
g = &d; $ l& W' V* y' }$ w) p) b3 m% w9 [9 y& N6 z
int* e[10];) |( D% G& \, r6 S( i3 _( p9 ?3 W
h = &e;3 t* X/ n* @2 i0 F# m0 z+ _
数组指针的使用$ ]' D" q. X7 G) s
数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。 ; ~8 H3 k+ `( F4 E ' n* z v/ v H) W5 `) l, Oint a[10]; & C% R( H: t) Yint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组 3 K6 g6 _9 e% e# E' d/ A' \for (int i = 0; i < 10; i++) { W# H" p: S- k+ \) v
// p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象 4 p) d9 D7 P8 T) q // 再通过下标操作访问元素。 : v3 L4 `- {6 v2 k9 X* K3 a (*p_ary) = i; . }% v: u" W- x8 X/ N}5 O" Z6 o F+ L3 z) M/ k3 {0 {
5 `+ l/ s" S, h5 m/ b: o* a/ |
int b[10]; 4 ? T; a1 d& gint c[20]; & x" `+ X5 `* ~p_ary = &b; // 合法 / z, d, u/ ^% L& b, p' Vp_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体* p1 f- E: C3 f+ `1 g' A2 f: m
数组指针指向多维数组的子数组% }% A4 y' E; X% B7 N
int a[10]; % y# {: m8 n& ?. j; f4 yint b[4][10]; # _( I; C, E8 f% r7 X+ A! ^. r1 h- fint(*p_ary)[10] = &a; ' A( o- s4 ~6 ?* _) _for (int i = 0; i < 10; i++) { 9 ]8 B! X u. G: F: q (*p_ary) = 1; - M9 Z0 ?" Y, r; L
}; x2 H$ j; M; w/ u% g
5 M1 a1 G/ p c" n
p_ary = &b[2]; // 多维数组,可以看作数组的数组,8 u4 X: H, L# P
// b[2][0] ~ b[2][9]的值都被改成2了 ) Y' o" Q- u5 Qfor (int i = 0; i < 10; i++) { 8 V/ k6 k( j; [; X6 ]0 W (*p_ary) = 2; / e1 p* v3 b6 I} : f e$ p" O' e* F T4 s/ h多维数组指针( R* b3 `+ p+ f1 L K& L
多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。- O6 L; g5 p6 k$ n; k/ E
% c" U# w3 U( Gint a[2][5][10];1 o" C# e# |+ {7 s" T
int(*p_ary1)[10] = &a[1][2]; // 1维数组指针5 a5 N' a2 D7 \; A8 e% K+ V' B
int(*p_ary2)[5][10] = &a[1]; // 2维数组指针( r+ D/ P3 s6 ^& H& S1 ]) Z& Y5 g% W
for (int row = 0; row < 5; row++) { 0 a/ m6 I8 n- p4 Q8 k* F for (int col = 0; col < 10; col++) {9 @ W- a n6 W" p! C* i
(*p_ary2)[row][col] = row * col;% w! | J: w) _4 J
} * ?. V0 ?. Q' O o& y9 v}0 p J( [# h8 ?0 d$ |
数组指针和指针数组对比实例 z, @4 C h: M# N4 c% M1 m3 w
数组指针还是记住两步法即可+ H5 I( I! a/ f6 P4 z
* p5 T; t0 r2 q! g% X+ ^
定义一个数组 " P, x/ R, h* f/ U4 q括号包围1中定义的名称,再在名称前加个*号。% k6 B" Q+ R1 a+ S0 v
int a[10]; 2 w6 E' F/ t! yint(*ary_pointer1)[10] = &a; // 数组指针 2 M) }0 c/ M3 ]; o% wint* pointer_ary1[10]; // 指针数组。元素类型是int*; r+ w! L2 S1 I0 g& b
int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。3 m' s$ w2 h0 Z- `* Q# [3 d
int (*ponter_ary3[10]); // 指针数组。另外一种定义方式。! V4 V6 R3 a- u( P
int c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。: J) ?0 i' g9 v2 Z, u# C B
6 d5 ^, T2 B. _% N
// 指针数组可以把每个元素指向数组对应位置的地址。2 W* V6 B7 U# G) l8 E& @4 I
// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,+ n. K% J% w u' D
// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。0 u- G0 A0 l" Q( o' V
int* pointer_ary[10]; % w: L6 N; d/ F: \for (int i = 0; i < 10; i++) {. s! k$ O5 U4 n! S6 W$ v1 \
pointer_ary = &a;) u% P; k$ H3 s4 E
}% O& e8 D+ \0 p6 M
// 类似遍历原数组效果。& `5 Y- v: S% s0 B, x8 c
for (int i = 0; i < 10; i++) {0 P g& A: J# s/ P& z
*pointer_ary = i; // 修改原数组。 . D! [$ v' r6 \# z}6 |" L2 g1 C! A) b P
/ A! t/ |: e+ x' Z1 m
函数指针 1 M# i) U6 d2 v- k" ]/ W取得函数地址% O$ e* m6 ?) S8 i
函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。9 H* D) _7 p* q% y: r
' u$ C# Z, p" s; F1 N
void f(int);9 t% p* Q' _, o+ e" I5 ^
int main()6 Z6 n8 ?1 t T7 S( T
{: O0 A, v: d4 V7 z6 _
void (*p1)(int) = &f; 9 B" ~) I2 v8 |# q. D/ ?; h void (*p2)(int) = f; // same as &f5 g8 D$ F' c1 a6 `" S7 n
return 0;2 _* c$ P* |) D; B; `+ L2 q) F s1 @2 s
} 5 n1 K" x* M- X# w8 ~! W翻译成汇编代码, p1和p2的赋值是一样的。! P( b% |$ Y3 W6 _1 l
( O6 B- \* U' J7 E! d8 I" \
3 x$ ^6 V5 n5 H3 M7 g" G: G6 @: A6 v$ S {( u6 E' e3 `
函数指针的声明* ^+ t4 z: m1 M* L% ^* C
单个函数指针变量定义步骤 $ B" o1 e- K3 K6 t& g, F" K! k定义一个函数。void fun1(int a, int b); int fun2(double a);2 ?; f$ Y) ?4 @+ c; H: u
用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);1 |7 c9 a! @ C7 o8 P$ W; T7 F1 }
如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a); 2 H$ Z ~8 U7 j+ r6 Atypedef定义函数指针 6 x$ U4 |9 a+ |9 o可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。 / E8 N7 s( u: B- S! b $ P3 e% k% o4 p8 n' m$ Xtypedef定义函数指针的语法+ R8 R, H# d7 [1 q
typedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。 $ P) o( I2 n# H0 H( M0 x) G/ ]- N4 |/ H9 _" @ a7 I0 @
typedef int FuncObject(int a, int b); // FuncObject类型是函数对象' K. k; z$ I/ z0 d# l8 q
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针 @. \0 R9 b8 { [9 y# }3 ~
FuncObject* f1 = &Add; 8 i# F v t( r9 J! IFuncPointer f2;; X4 D% a. {$ H7 h
f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。8 L1 x& B2 d- G( I3 `5 C# y2 ~- k
FuncObject f3 = Add; // 报错! 函数对象不支持拷贝2 Y: @: i e+ r$ m
FuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配 7 Q9 @( e6 _: \0 D: z6 e) ^FuncObject& f5 = Add; // 正确 9 M; B9 ]1 j, {) `% |4 f# Rint ret = f5(2, 3); // 正确' J: H: r- M& X% [+ n" T1 B+ D& K+ O
FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配 & V* f) a* h7 P5 g- _; @8 f如何记住typedef定义函数指针的步骤 & A) U- R( Z- z6 w$ o c1 V, y像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);) {2 l w1 Z- {3 ^: ~
在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b); - E0 W3 y$ ?: r: i: r完整例子 ; M& P- |1 @5 m7 Otypedef int(*CalFun)(int a, int b); - b6 m9 J6 E- l' z, n - @8 X- A I( n% s X/ [9 l2 qint Add(int a, int b) ( U- g' @& t; u8 Z% ^{ ' t' {5 |+ a, F return (a + b);7 _2 g2 x( k; M, X
} & B x3 X3 p- V1 u& E6 E 3 Z$ T8 q; r% h! ^7 G# Nint Sub(int a, int b)4 T$ q+ {% I" A
{ " k& I! b2 U* D7 h5 ]6 X- H0 | return (a - b); 2 J6 v- n- Z3 [, j! B! P1 f} 4 P4 D+ M% k x' X/ W % b/ m% P; ^! q$ ^* t# oint main(int argc, char** argv) " D# o- M0 i6 x3 Q{: a/ X/ q7 ~# F" o1 _
CalFun f1 = Add;4 J& l4 K7 v. i- }
CalFun f2 = Sub;9 c, C( k0 I4 \- T, A5 B; Q
int a = f1(2, 3);8 L$ }: Q$ X" P+ A4 H; N% C$ @% N
int b = f2(10, 5);0 B1 |9 @$ M& a6 d# d; r2 o* ]4 K
* g# c* h2 r" U/ x2 {
// typedef定义的函数指针数组。. y' Z; o# |* V2 X8 ^) |
CalFun f_ary[2]; B, v V$ `; Z
f_ary[0] = Add;* r; ^* D9 Q0 k+ S
f_ary[1] = Sub; q( A0 D2 x2 L- I3 F( Z
7 R) i+ {$ s' j4 J! C" n. g // 单个定义的函数指针数组。 ' ?, V# l }: ?6 {+ t int(*f_ary2[2])(int a, int b);- O* g8 V! a+ W
f_ary2[0] = Add;) O% j5 y* w9 c# Y* z
f_ary2[1] = Sub;: ]. T, p8 `" S6 C& g) f
, \) S, Y4 t2 Q v1 E return 0;" `1 `- M' j; [* j, N2 v. w
}: E0 z# _. n8 J. [
8 o* Z! Z8 ~8 M; u( ^% n
using别名定义函数指针7 i/ _' f! M6 A* ]; m
c++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。( A& }! Y1 w {; c9 y- R; T
2 y7 V9 J( G6 \9 d( N4 u) f% O. Ktypedef int FuncObject(int a, int b); // FuncObject类型是函数对象2 N; W9 |: |- w& K. s
using FuncObject = int(int a, int b);: C( U! V; H2 ?( j% k# C# Z: M
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针 + q9 p6 L7 I$ d rusing FuncPointer = int(*)(int a, int b);8 ?" e+ t( T1 {( ~% _) _' a& b
函数指针的调用0 v5 J) c7 i$ R2 }
函数指针和函数对象都可以直接后加括号调用 3 s; c# {, ]' C( U: Iint f(); - e2 E- f# p1 G) Pint (*p)() = f; // pointer p is pointing to f # _7 K, I( D7 R( Nint (&r)() = *p; // the lvalue that identifies f is bound to a reference - d2 O, q- ]7 u+ k- Jr(); // function f invoked through lvalue reference " j4 B3 w& @1 h) V. c1 W$ X5 Z- d(*p)(); // function f invoked through the function lvalue1 w4 F* a3 v$ \) v' Q: ^' m# V
p(); // function f invoked directly through the pointer w" `$ q* R: T2 p! D如果函数有重载, 函数指针会指向匹配的那个版本。 $ {2 J$ {: ? H0 C% ctemplate<typename T> ; i! d- y. Q9 g. d, k0 ZT f(T n) { return n; } * f3 W( E4 J5 c; a( K; V) I( s( c, Y
double f(double n) { return n; } # h' i6 `- |# B2 `: b + J& i; V. [+ F1 C7 R oint main() / b0 d: K, d* K' }8 p" D{$ R5 a8 d2 p: P2 f4 Z( N4 j
int (*p)(int) = f; // instantiates and selects f<int>4 u5 c" b. b# a
} / o, t. m! g& H! g9 N成员函数指针 8 U" {" \3 D1 F- i静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。" Y5 m+ f- S8 g3 w8 U. |5 L2 ~. `