1 p- `2 _! N2 B9 c r6 ~; Bp_ary = &b[2]; // 多维数组,可以看作数组的数组,7 Y# x( J9 T8 J* L& |
// b[2][0] ~ b[2][9]的值都被改成2了 3 y( u& ^# s4 A# R. {" ]for (int i = 0; i < 10; i++) {+ i: V4 }) Z1 h6 u/ K. N1 q
(*p_ary) = 2;# s* V3 E- _, b+ n8 j
} : k; c# K; _2 `多维数组指针 1 q$ x& N" j1 C8 d" D3 ]" o多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。) q5 T& T) g& ~/ k A3 d6 v
( Y- s f+ x- g2 Z9 n/ B
int a[2][5][10];& |6 u/ l1 y/ c. C; W
int(*p_ary1)[10] = &a[1][2]; // 1维数组指针* B2 o; N0 H7 k+ s5 a
int(*p_ary2)[5][10] = &a[1]; // 2维数组指针 V( V( U$ ]+ n% W9 P; Sfor (int row = 0; row < 5; row++) { 6 v- z5 ~. w$ [) F for (int col = 0; col < 10; col++) { # l0 k& c, g, Q O) Z V (*p_ary2)[row][col] = row * col;" c Z2 s, p0 P* Q$ f: V' I; ^) O
}0 W/ B6 d8 z# m+ n, \+ j4 A- y
} ( I4 {) m' v! \* E数组指针和指针数组对比实例 * W8 \7 J( R! U5 e: c @数组指针还是记住两步法即可$ t* {) N' D) q& _& d
+ `& e) L# Y/ F' l7 D
定义一个数组 ) E: d8 M% i# ~' E) I+ S2 w9 m括号包围1中定义的名称,再在名称前加个*号。1 q. _( {) Y+ |+ q: I5 I4 C4 p* ]
int a[10];, c% b8 ?' _: Q
int(*ary_pointer1)[10] = &a; // 数组指针 ; d* S+ C& e, R3 o2 M, k, H3 rint* pointer_ary1[10]; // 指针数组。元素类型是int* ; p# l2 r+ ~4 R" x/ ^) {- sint *(ponter_ary2[10]); // 指针数组。另外一种定义方式。$ `2 z% b! q$ z" v3 S8 [
int (*ponter_ary3[10]); // 指针数组。另外一种定义方式。 5 n D! j: `7 m8 j$ zint c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。 2 G. s+ b5 P' Y) s1 Q9 n- m5 ` - Q8 L0 Z4 v. p% H& K! r5 ]// 指针数组可以把每个元素指向数组对应位置的地址。& M" x9 l0 |# n* e2 Q0 @9 N
// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针, K8 v9 ~8 X& t5 M0 u
// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。 0 y! H. s7 Z8 W7 @int* pointer_ary[10]; 1 E4 |6 {" R7 A: w! ^: o- J( r- Ffor (int i = 0; i < 10; i++) {5 J8 l7 |! S1 P4 Q3 j' g
pointer_ary = &a;- D8 D9 D) I/ P4 R! Q D+ I2 V! z
} ; i) c' I# I4 R: D1 m2 t% s! P0 B// 类似遍历原数组效果。 6 V# k2 C6 v M: A, z) `7 rfor (int i = 0; i < 10; i++) {1 r$ M1 Q/ u8 H
*pointer_ary = i; // 修改原数组。 , n% [4 m o* R2 a0 }5 E} 1 S. \# q" O+ Y, o/ o9 `. a9 T 4 g7 H' D& J( c o4 W, P函数指针 8 G: x) d/ V, b% y0 A7 C* N取得函数地址 3 t3 V: X; v$ D! X5 c函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。0 G, W3 l5 T2 j* b( s- Z- T
' f7 e3 q9 _ w0 h
void f(int);' u0 R* f+ k4 S$ o
int main() + [9 I- M/ D- g8 F5 }" f( o- `9 C{* [& }- D" h7 S3 N* X H8 z
void (*p1)(int) = &f; ' z3 U6 _* y a* U4 {2 e" F) a void (*p2)(int) = f; // same as &f* D B( I8 z2 T3 P
return 0; . x5 R& F% ]0 _0 [( W. t}" ~; l4 I/ b6 f8 T# \$ B' h7 O; d
翻译成汇编代码, p1和p2的赋值是一样的。 Q. W8 `* y+ f! x: o3 p
+ i1 I4 c7 k: P4 Y3 f3 j : B1 G$ @9 i3 V, E) | / \5 a8 k* o2 e8 M函数指针的声明 ( J% Y" K: R% S+ A' L单个函数指针变量定义步骤 9 x! H" j. k: m( J0 z定义一个函数。void fun1(int a, int b); int fun2(double a);% b+ D6 _7 d* v* L/ O
用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a); * N3 ^ ^' U2 c8 n# G如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a); 9 q8 D' w( f9 M# mtypedef定义函数指针 ' _8 _# }1 T$ T. B$ N- M4 }9 ?! ?可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。 3 n: J( X t1 o! Y9 B . J, K/ a9 V. B9 W `typedef定义函数指针的语法 ) V& r9 w2 b: o* l( x/ Atypedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。 , ~! c8 h# }/ M/ v# ?! T1 M- {8 {) ~9 Y1 \
typedef int FuncObject(int a, int b); // FuncObject类型是函数对象# x1 }- q) d# ], Q. T
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针 ) y8 [9 _9 ]1 ^& S* iFuncObject* f1 = &Add;4 D3 }/ [/ m) I' t
FuncPointer f2;: {3 Q d! P( E# ?
f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。 1 G9 Z0 j3 ^8 J- O0 j& h( M& CFuncObject f3 = Add; // 报错! 函数对象不支持拷贝 " e1 k' g8 B; q# s6 C& ?" u+ A7 mFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配 2 ]/ B L+ ?% D6 k) a2 b7 YFuncObject& f5 = Add; // 正确4 s! F( ~. H9 @; ]7 `
int ret = f5(2, 3); // 正确* O4 B8 k9 U7 ~. H' k, w) H9 c
FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配 ; P4 t3 G$ x' a+ c& u如何记住typedef定义函数指针的步骤4 U. Y1 N7 K# G6 M6 x6 S6 f
像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b); $ Z( W' d+ p6 p! Y/ U; _在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b); 4 u2 M- g% \! z. R! `& t完整例子; U2 u, K) t6 {! ?4 X; B# v7 G
typedef int(*CalFun)(int a, int b); 2 R; T) U$ F4 g8 A & }1 S0 `3 R4 U6 Yint Add(int a, int b) k6 y6 m( ~: r9 w
{* @5 W2 j+ T' P' {
return (a + b);* {5 X/ \# n# ?! e2 |
} 5 Y: {; B: `; p/ w5 ]$ C% W6 E+ u
int Sub(int a, int b), T m% x# T5 v
{ 2 K% o% s/ k" n5 E2 K( [2 T. t! S7 O return (a - b);& E9 N- n# B2 A. a9 h5 B) g
}: w: M: G5 p. j
: K- N/ W. i1 t; z5 ]int main(int argc, char** argv)9 |5 L8 T" o D' B0 b
{ 8 U# e. t3 M, g/ v1 } CalFun f1 = Add; z K6 s9 m# w; H/ _ CalFun f2 = Sub; 5 Y1 t6 p& k5 o5 w int a = f1(2, 3);& A$ }( s9 ]% l0 n% z! U
int b = f2(10, 5);7 L' K4 L" L2 M
r; {- Y9 ~( t
// typedef定义的函数指针数组。$ r$ C! m) b& r- D6 t
CalFun f_ary[2]; - d$ ?" e: s- u( Z f_ary[0] = Add; T7 F; r$ u* l+ J/ Z; _( K* |
f_ary[1] = Sub;! b0 d9 W/ O$ f# G! w( B
% O" \# l( |- V/ |
// 单个定义的函数指针数组。; m* u" V; Q% G8 Y: s
int(*f_ary2[2])(int a, int b);9 v+ ~! }3 V' d0 U6 [
f_ary2[0] = Add; & T r) z0 b$ E' L f_ary2[1] = Sub;! E( `+ d- l$ M0 v* F
* Z- t( d1 a7 v# U/ a- j return 0; 7 P! e! e# E5 r; k5 [}+ z/ \ f8 G3 \+ N8 }
' |$ N4 d( C5 A! p% Q& d
using别名定义函数指针 2 P* G" f1 `# ~% u, M' y6 @$ Ec++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。 % I+ s4 y- V, J4 R$ D3 D7 m1 I) s; P3 [- u
typedef int FuncObject(int a, int b); // FuncObject类型是函数对象# T1 |: b- e. ^$ A. Q$ Z( o
using FuncObject = int(int a, int b); ; u# F) t. k5 Gtypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针+ {5 Z8 m# @5 `( t' p# I6 R( q$ H
using FuncPointer = int(*)(int a, int b);6 R# ~; e/ |' |/ x
函数指针的调用1 r9 K O g& I( J# a
函数指针和函数对象都可以直接后加括号调用4 Y; j# _" J! M& e! x. Q2 H- U& X
int f();: L" l0 H( t8 P0 B
int (*p)() = f; // pointer p is pointing to f1 W( g0 K6 I4 P* z" L% ^
int (&r)() = *p; // the lvalue that identifies f is bound to a reference$ x0 J& H( ?- @0 G
r(); // function f invoked through lvalue reference 0 b3 V% i, m; m: K" g7 m! l(*p)(); // function f invoked through the function lvalue. ~+ d- n1 j6 M! r4 m2 i3 x
p(); // function f invoked directly through the pointer) k/ f* {7 F- E8 c. ?8 [2 I7 G
如果函数有重载, 函数指针会指向匹配的那个版本。 ) ^8 x. f2 C1 e9 O9 Vtemplate<typename T> 1 @% Z/ _& x; F' V: G; w5 ST f(T n) { return n; }& W0 ]- U- G, j( b4 {
- L& y1 e7 L8 q+ C" Lint main() ' r$ ^. \1 c! r" _; h( D{ : p. q. A$ [ f0 K" q+ d8 C, }2 x int (*p)(int) = f; // instantiates and selects f<int>$ {6 l( F r9 y) z8 U/ c7 J
}% _4 B6 K. A$ j9 N4 T( Q8 A
成员函数指针0 m, C* N0 q4 m" x
静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。 + h' V/ _8 ]3 j, W! i7 G6 |( d; s/ @6 ]0 Q, o
成员函数指针定义。 4 ^, ]7 P$ @& d6 e b9 m像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int); - i t9 _1 X- |) f$ s括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int); 0 E4 p4 y: e) p1 q. q在名称的前面加个*号void (ClassName::*func)(int);- J6 P/ I' `, C5 {3 U$ @+ y( n
成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);. n7 X* j+ a* f& Q% q4 `: l$ W- p J
成员函数指针如何调用。& }* A) H, ?+ e! k2 i
假设成员函数指针名字为func . c; F) s9 X. v3 R1 w. t- p* E1 y S* w* N' ~) Y# s
void (ClassName::*func)(int); @8 Q3 j; O- k$ W" r对象式调用。! `! ]6 Q5 S) f/ q& R- u
ClassName c; // 被调用的对象 `1 }6 J1 P" s4 v: O0 q1 K/ l4 h成员函数指针名字当作正常函数那样写。6 O7 D" c, H/ Z% b1 q9 A/ v. P
c.func(3); / Y' t% m+ s ]& O5 p成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。 & Z5 p7 H, J2 q- w7 B9 Kc.*func(3); 7 W) @+ ~7 |+ l最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。2 }# y( m7 i: I2 t
(c.*func)(3); ) I( F& l) t) _, \( R为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外. ` j: V. p9 @
(c.(*func))(3); 9 k" h3 z1 k A4 ]这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。9 ?+ H! u% F! N' N) t9 @, @
指针式调用 3 \7 B" ^# S$ a$ {ClassName* p; // 被调用的对象的指针 # l# U, R& G; P% z( T; p! c成员函数指针名字当作正常函数那样写。 5 p7 W: n, \( A' O @2 Vp->func(3); * _: [( }: w/ B成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。, r; g+ _4 {) R% ]$ M% H
p->*func(3);* W* I) c( w1 H1 ~. |
最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。 2 t. N( H" e1 Y2 }(p->*func)(3); % Y& F7 k2 E$ q0 M1 P/ G函数指针使用完整例子" j; C! S) E+ S+ B4 P' {+ _
struct Cal ; u7 k, j8 G0 C6 I L; s. e{ / l7 r7 V2 j# j1 [. k+ [. g6 n; S
int add(int a, int b); & a( K" y# b& V$ Z$ |9 d
int sub(int a, int b); & k! N, b6 H/ ~% J% }};# R, f5 @5 C @4 L& k( G; j
) k% e8 [& H& ^int main()) t4 k. D k; }3 v. S
{/ U5 L, ?& A. g+ {
int (Cal::*fun)(int, int) = &Cal::add; % _3 M2 K: m( r: w3 W fun = &Cal::sub; 5 w, Y4 v3 A! G9 ^ / H0 v8 u1 L+ y8 P# G; C/ l Cal* p_cal = new Cal(); t8 K7 ]6 ]' @& V3 s1 O
int r1 = (p_cal->*fun)(2, 3); ! {+ m. H( G6 w7 y7 Q delete p_cal; " s, H: S" L& P+ @. O 4 D/ S' \ u2 N% ?+ W Cal local_cal;2 A* F' r! @2 B+ N8 ^, H7 F6 ]5 f
int r2 = (local_cal.*fun)(8, 6);) ]* H3 h9 q+ ^6 D5 V7 R4 R1 v3 o0 J
} 9 O' C' z6 I6 |1 c9 ^ `/ H( ~8 N* z4 |8 K成员变量指针 - f: r9 u d$ U, a& V7 |5 v+ c' J成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。. f/ c/ Y( v \3 y7 l6 z5 R6 P
/ j3 V2 k/ e/ ^/ B) ~: ^* Q4 u. P0 J
成员变量指针的定义 ( `' I. M0 q5 ^假如以下结构体C。' e! X: r9 l; o2 ^* x- ?) L# T( o
$ q0 w: w+ d3 jstruct C 9 z' b; W4 l6 T% {* B) a{ 9 ?5 f+ K& w5 k E8 N5 ~
int m; 1 I/ j: \5 a7 m9 ~7 d3 F/ d# ~$ w" t}; 4 K5 H: S0 r1 d: N& o5 _+ i单个成员变量指针定义 4 j& r& N( ]7 i; r0 l6 G8 h" O" I假设名称为p, 类似静态成员变量定义那样声明* R: `0 t' p+ ~& I, Z( u
int C::p;+ o9 Y/ Z Y m
在名称前面加上指针标识号* # A! ^/ n3 z" a+ `5 t" }+ Iint C::*p;& T; y9 Z0 ?5 u
typedef或using方式定义 ( N7 Y2 R# E; _ P9 \2 q0 K4 P5 Ftypedef int C::*MemberPointer;- W( y( x, y% ^6 D: `
using MemberPointer = int C::*;) Q9 D( I; U* T0 x+ P
成员变量指针的使用。 3 z* [0 ~ ^1 ]: A% n/ O$ d# L类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。. @! S, x$ {0 N# `' H
成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。4 h( G' p) Y1 k: j# `) n
2 n& x& { \/ i0 \3 I 完整例子8 T" N6 E, E" i I+ m7 |- F
struct C { int m; };/ X q; F2 f, k! B& M% A/ y& v
int main()4 b; |6 p5 m% ^+ T/ M
{ ) P% }: k1 n/ a1 W4 M9 o int C::* p = &C::m; // pointer to data member m of class C 0 @- C, x K! E C c = {7};' K' t* A1 N* I. y0 y( j! h! n
std::cout << c.*p << '\n'; // prints 7 Q0 a/ H8 J& L# X
C* cp = &c; 7 U5 g5 {( [: v- a cp->m = 10;1 c& @/ } o1 Q: c3 g/ d
std::cout << cp->*p << '\n'; // prints 10 9 o, O1 b; Y7 y( |8 u H2 ]( r# D 2 w! L2 j9 V% ^0 L \ }% t————————————————" u: O& }! W h; T7 _. K: F. n1 e: g
版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。8 Q* f6 M+ c8 V5 ~3 a
原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115% z* ]4 w; R" O2 ~+ Y- _" k& U
: M& a$ G) C+ t% X/ u+ ], M
3 q7 m, r" q5 E. t% }6 l