) t4 S5 ^) V, pint a[10]; r% P6 d* W0 m# v0 i* Y+ K6 Kint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组& J6 w0 v) t* ~& Y
for (int i = 0; i < 10; i++) { / M8 O8 k' _: R; _ // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象 7 G$ y8 |; M& Y, u // 再通过下标操作访问元素。 % v4 G4 ?+ a t) S+ Q# Z (*p_ary) = i; 3 ?( a: B* e ^, {" _9 W}) w6 D+ }& A! p) R: N! G
, w9 n" F& d/ x- E( Z
int b[10];# A( e$ v: @9 B0 t( h
int c[20];, `' B( B3 A0 E, \
p_ary = &b; // 合法 ! X$ O! ~% z# Q+ yp_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体 1 a' @1 k/ w3 g7 E( t: M数组指针指向多维数组的子数组 ; D, g! m7 } z2 i6 vint a[10];. L" N: n8 Y0 _
int b[4][10];0 {/ @4 ]& }, a2 I
int(*p_ary)[10] = &a;4 d0 G; S* ^: n8 V
for (int i = 0; i < 10; i++) { D5 |% @. R( a& [ ?: M, M; B
(*p_ary) = 1; ; Y: z3 q) z; I @' R1 v2 {" j T
}, j$ z: x: [0 O. x' X/ v3 U7 ?
( X, _. `* n& p/ |
p_ary = &b[2]; // 多维数组,可以看作数组的数组,$ c; X b+ l# z& f- X
// b[2][0] ~ b[2][9]的值都被改成2了 ; Z) z1 h# Q- ]# u5 Pfor (int i = 0; i < 10; i++) {9 Q! _, _, |& E6 n* {6 n/ H
(*p_ary) = 2;0 h# r: W+ X7 D6 w( A1 @+ G
}$ z. q! E* N( W% Y% m A! Q" l
多维数组指针8 a0 Q/ q" d9 N# j5 C4 P
多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。 4 m- U @% A/ ?% G3 a7 L" j! A- m4 t5 M0 @1 I. }0 E
int a[2][5][10];2 P0 m# ^4 @, H
int(*p_ary1)[10] = &a[1][2]; // 1维数组指针 ; j1 D1 [( h: R0 x% k6 wint(*p_ary2)[5][10] = &a[1]; // 2维数组指针 - o S. M+ Q8 S# ^2 a+ {for (int row = 0; row < 5; row++) {; C0 o+ m* `2 u* k4 h H
for (int col = 0; col < 10; col++) {! k: {/ D# d8 Q" `) p: j
(*p_ary2)[row][col] = row * col;. O, T8 Z9 l6 `
}1 P5 x. w) w) w0 a4 J; R
} 9 T+ K, X' w0 b5 e; d* G( X数组指针和指针数组对比实例2 c" v# F" f% w9 u& c) O
数组指针还是记住两步法即可8 u1 ^! @& x6 R2 ^, F; L2 ?: {
. L# o' `7 ]3 k' a$ U定义一个数组 4 Q" I5 ?/ P% u* R) S% ]5 S6 a括号包围1中定义的名称,再在名称前加个*号。 + f8 e% p: W8 vint a[10]; 1 I7 V; u, R* l& Rint(*ary_pointer1)[10] = &a; // 数组指针7 U! M$ U1 E* P( l' R6 L0 I
int* pointer_ary1[10]; // 指针数组。元素类型是int*1 P( f' S3 j$ v
int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。 7 B( X1 T6 ~& l; Y. C. g! tint (*ponter_ary3[10]); // 指针数组。另外一种定义方式。 2 R; w" E& p+ f" Dint c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。, u4 [! j! e; k7 P& t+ L9 y
( D8 C9 |' T9 Y" i
// 指针数组可以把每个元素指向数组对应位置的地址。 ! i; D# D2 L: V4 B7 V// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,4 {$ `* y$ g% g ~
// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。. ^' O/ i+ i; N
int* pointer_ary[10]; % }& H+ m3 n$ n7 ?/ [/ ~
for (int i = 0; i < 10; i++) { 0 c+ J1 e$ M* e+ T8 { pointer_ary = &a; % ?: a/ z# \8 a/ r: p} / L, |/ H+ E, R( a( H: c// 类似遍历原数组效果。 e/ i/ x9 {: Q
for (int i = 0; i < 10; i++) {+ b( i+ ]' d6 L+ u1 t- x1 e
*pointer_ary = i; // 修改原数组。 ' g6 B- S! n1 p! l} 8 F3 _% d$ K+ n' J# B' B- X% C. ^ [3 z
函数指针3 {/ X! S; ~( i7 Q
取得函数地址 + F! ^+ }1 ?1 J) w" ~( o函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。 # v, m$ i4 [; @ * W9 K4 `5 G0 X+ I3 _/ gvoid f(int); 0 Z. |5 ?1 ?8 i8 _! {* t6 d$ ]int main() 6 [2 t1 B8 @5 m) ]& `- u& Q{ 1 @, `3 I4 P% C" D+ Q% N `6 h, C void (*p1)(int) = &f; $ T6 p+ o: K- a% \. {. E1 f* l6 R void (*p2)(int) = f; // same as &f 9 i. D9 e' e- `: q, C0 y return 0; 1 G) x1 _0 a/ O( o+ ~4 }} ( N. h$ y, f+ t. p% T, j) ^翻译成汇编代码, p1和p2的赋值是一样的。( P) T# _3 Z7 o
1 a, U' I! X; t `4 r$ {, V- w
' z. U# k; b! O0 f3 m& @( T
- V$ E o9 f' R$ g4 ~- K函数指针的声明 / o1 v1 e( ?! B! |& b k单个函数指针变量定义步骤3 C# k% b" c% t: X) S$ W7 d9 \- k4 `
定义一个函数。void fun1(int a, int b); int fun2(double a); ! U/ F! d6 f# Z0 y( Z# U# T! m用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);9 N! a- U) W% Q( e D/ H# H A
如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);% ]4 ~! A# P; s
typedef定义函数指针! ^. x9 f: b6 E, r, b* A) c6 Y! R) h
可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。3 i4 W: y8 i7 [8 @6 Y3 z
4 q" e7 A+ b( I# }
typedef定义函数指针的语法 , \8 X y9 ? u( [/ O8 k: ftypedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。3 y# A1 A5 l$ T9 d* d2 c
( z1 Z/ q$ a3 |- f2 t5 O3 V" |typedef int FuncObject(int a, int b); // FuncObject类型是函数对象 5 g. x" H6 l4 A! t. o2 J$ Otypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针$ f: x# Y+ h$ D) B7 N/ k, y
FuncObject* f1 = &Add;# y3 t7 ^6 H1 A+ l' V
FuncPointer f2; w6 D$ P v5 ^: e- P7 Qf2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。4 @+ G. I/ l$ C% f* Q/ S
FuncObject f3 = Add; // 报错! 函数对象不支持拷贝 1 V) w" L% L& l. I m. I. l2 NFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配1 w6 x1 k; }$ F$ s( ^: Q
FuncObject& f5 = Add; // 正确0 n2 v& O/ R) p- y9 u0 L
int ret = f5(2, 3); // 正确. Q2 t; `1 U. Q7 Y
FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配 9 R0 c$ c# f2 L0 P+ X- T如何记住typedef定义函数指针的步骤 % N# T% | s% X2 o8 k" I ~) G像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);, F( Q2 X+ d* y$ X
在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b); x5 T1 H; U R
完整例子 9 [( a1 q9 x* s6 O* X) Ctypedef int(*CalFun)(int a, int b); 9 V/ [* C- I; \9 Z( f, L! m( o& G d {
int Add(int a, int b) " I# m/ `% p/ y{( ^: Q M' }- X+ G
return (a + b); v# W2 T9 I8 x+ N}4 m& t p' D' }# d/ M" E
% Z# q: I [! p. I
int Sub(int a, int b) 6 o# b5 V( d1 \* I* K" k* l7 |{: ^" G: z" b5 b& U8 }
return (a - b);+ t# L& c2 i; v9 K$ O- [# _0 G
} ' ?5 ~! Y& C% [& F" G+ c9 Y 3 s X" C ~" b7 X& K+ tint main(int argc, char** argv) 3 _4 [% O$ E% L C1 O{ " g0 k5 i& c$ R. l2 Z% E" v+ f CalFun f1 = Add;$ B6 z7 j' r1 d# H5 M3 a
CalFun f2 = Sub;0 d& l, Z8 S& Q1 k
int a = f1(2, 3);7 m; G7 g) O* H5 o8 j$ k
int b = f2(10, 5);1 [ T$ D& h5 d
, |+ ^' m* _) n* S
// typedef定义的函数指针数组。 - o- \/ {5 {4 u8 D | R3 O# A CalFun f_ary[2];; {4 h9 `/ a# a: K1 v6 c
f_ary[0] = Add;% y. K# z1 @8 c' p/ i0 X
f_ary[1] = Sub;* D$ \2 C1 S3 q9 D: i' f
* G# g0 L3 y. n; ~9 d // 单个定义的函数指针数组。 . c0 \' C# U/ F int(*f_ary2[2])(int a, int b);1 e8 U$ V# w' I% H
f_ary2[0] = Add; 8 t4 ^0 F: Z$ Y( _ f_ary2[1] = Sub;- K+ U3 a; x( M. T; c
: g: U. @/ @. j0 K1 ?) ?! o3 ` return 0;8 L- y X; f1 V2 o
}8 l1 r H. V$ T; x
5 {" u+ l( e& nusing别名定义函数指针 W8 M% s% U1 q: K6 B) S
c++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。' ?, s$ m0 d' O4 K. G& h, ~
8 {. g5 p, T7 C: c
typedef int FuncObject(int a, int b); // FuncObject类型是函数对象2 x. t3 Q4 W5 e' a3 M( ]# E
using FuncObject = int(int a, int b);" x. J0 f2 N/ {( V+ M1 W
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针 , X& b7 u0 N7 [% C1 Rusing FuncPointer = int(*)(int a, int b); 5 D. O+ x" S6 o函数指针的调用 ) ~; n2 E" x: V+ Q- J$ q函数指针和函数对象都可以直接后加括号调用$ {+ x* I. t2 F* h
int f(); 3 \ X' v$ M. e& x3 r, tint (*p)() = f; // pointer p is pointing to f 6 E' {4 w9 j2 q4 A7 J' X( fint (&r)() = *p; // the lvalue that identifies f is bound to a reference0 h4 M6 z9 D9 d. ~9 ~0 e
r(); // function f invoked through lvalue reference . W' b Q0 m& G' ^7 o/ g+ A(*p)(); // function f invoked through the function lvalue9 ]( E; h, J: O2 T' X. _. f
p(); // function f invoked directly through the pointer 9 B' c6 F5 Q+ J# U. w如果函数有重载, 函数指针会指向匹配的那个版本。) N/ d4 Y: a: C e
template<typename T>* _6 o. M5 g' |7 Z% W M1 j
T f(T n) { return n; } , N# D& o A& q0 X# b; t; B) Z' p 7 U3 e+ @ N' N3 O3 U) R! gdouble f(double n) { return n; }) |9 B$ [' @7 `/ T+ _
+ F t# m( q6 c4 `4 bint main()/ ^# |: }5 i( c# a
{2 }# }( A, i% z, X
int (*p)(int) = f; // instantiates and selects f<int> 1 |) u) ?. M( @- A( N7 t; |# g} 6 V! d1 s3 K9 G成员函数指针, c3 P* C9 T4 p& u% @" r
静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。 ' m/ k; S+ |. l0 m' e" K- `+ @7 ^$ w0 [
成员函数指针定义。 ]# b& g' t5 n$ D5 Q" ]/ G像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);& W% t6 A+ a( C& D& `" Q! ~% f
括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);: k- S6 X4 O! n( Y8 y5 T
在名称的前面加个*号void (ClassName::*func)(int);( a+ b" G7 P5 F$ M/ k
成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int); ( K& v1 J3 [! z/ {成员函数指针如何调用。 % \% l. ^$ W" d5 s1 z, h假设成员函数指针名字为func - f/ e" a. V, ?$ w# Y& {% }0 s# g+ S: `/ {, ?& h% \
void (ClassName::*func)(int); 8 B2 D$ L! U$ F, v j, S对象式调用。0 T' @- o f* r! L
ClassName c; // 被调用的对象 " {; h% q/ W, b a2 K成员函数指针名字当作正常函数那样写。 5 n* e T( T4 Y( s3 @c.func(3); , |9 y+ u* H6 _5 {+ U% M# b! D5 O成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。& ` B- u H& B- F, M5 P0 P
c.*func(3);7 `1 x( ]7 r9 c$ }+ Z3 T4 ]
最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。 0 i' c! s3 T2 {+ q8 F- p6 Y4 R) l+ r(c.*func)(3); # w, ~$ u% V9 z3 R- Q {5 G$ Z为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外% q" P$ ~$ i, t X* Z
(c.(*func))(3); 5 P7 y& q4 e' v5 f* A N% F这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。 # e3 J/ h3 y- m8 z指针式调用8 [# z$ @) p. w- z$ i
ClassName* p; // 被调用的对象的指针 / u' {0 y8 S! [( E; N+ X成员函数指针名字当作正常函数那样写。; V2 p5 A- a* Q# J+ s* V* E
p->func(3); 9 { Y: h0 S. A1 K成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。9 S& J( Z+ a' J" x
p->*func(3); 5 d+ M# u: H4 V: A7 K; [6 C9 a \- q最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。 % y6 G r6 f4 z(p->*func)(3); 7 ^2 N& l: Q: ~) t+ D& c9 [函数指针使用完整例子 4 K& s8 B- m m& D3 o, ]7 h. D# |struct Cal j- |+ m$ c. K: M
{ ! a2 S k5 h% }& i1 [2 P int add(int a, int b); ' r% w# k) n* A1 ?0 ~, S
int sub(int a, int b); ( k- F+ t# e& ^3 T7 O% C/ \- E}; : i0 J, [* q+ W+ p. v ) ]' B3 C% K, ~; _$ i. r) A! Dint main() . j) I8 N- u8 Y6 T d7 W{ x7 d1 F% B) y/ a# S int (Cal::*fun)(int, int) = &Cal::add; 4 v0 e: Y5 f! r/ m* S fun = &Cal::sub; 1 Q+ K" C! h6 ^8 r9 D+ J . y6 E. n0 b: d: J" K/ v, {; W8 K' K Cal* p_cal = new Cal();, D* f6 d5 W! k- P. ^0 ]' ?" V
int r1 = (p_cal->*fun)(2, 3);( u+ o- T0 G# S9 g5 h
delete p_cal; 3 d" S! m. q. f. @- w+ W( ]) J9 W) P( _! i
Cal local_cal; / g5 Z8 ~$ O1 D5 g int r2 = (local_cal.*fun)(8, 6);, Q/ `: l H: v: H* Y
}" ~8 N! i A! R- _1 s
8 } M9 \% x& r; v
成员变量指针 2 Q7 P, a( q0 V4 N* H( {% t! ^成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。 + v( r" O* Q6 I7 m: A2 k, w 5 i+ d0 \9 v0 T成员变量指针的定义 1 e4 m# H5 {7 V, {) b& j假如以下结构体C。& z" Q' r, @' I$ e8 s- S6 Z
9 q- |5 M/ K- D* cstruct C ) I( c" h4 ]6 p: S! v: C
{ $ B) x. R" W3 P# J. }, y2 T int m; , D" X0 i/ U1 P3 Q
}; ) P% {, t; x7 K. i6 S. A+ C单个成员变量指针定义 " s4 _5 u8 ?9 g/ O( V8 x! L假设名称为p, 类似静态成员变量定义那样声明, L$ y/ B: T: \* [4 N' F
int C::p;# `1 A4 A% U/ | S. Z8 @
在名称前面加上指针标识号* # n8 I N3 s! z S
int C::*p;5 F3 Q6 b6 F" p
typedef或using方式定义 % u# I( ^& d4 U$ t& Btypedef int C::*MemberPointer;% x- x' H" p9 w( n a3 I r# V
using MemberPointer = int C::*; 7 ^3 v; t, V' i& V/ y0 |4 c" w成员变量指针的使用。 ) Z; f+ T/ \: P- w! h( s% K* v类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。 . \# u. t3 r9 `4 r0 K$ x成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。 ' n$ d* D6 B* z" |8 Q; z d5 C1 n0 F 5 q& c. G$ P( `$ b+ N 完整例子6 N9 l. J3 d+ }) P( V# U+ {
struct C { int m; };# J4 }3 z0 |2 i/ ]' a- r
int main() . L0 f8 `4 V( a' k) U2 h" T{ % F6 |) s9 b3 h( Q% u" q% r; p int C::* p = &C::m; // pointer to data member m of class C 6 x9 P: D! _8 ` C c = {7}; ' E& ]3 p9 M6 I5 A/ B, |8 ~ std::cout << c.*p << '\n'; // prints 7! M( Y- U; F) D4 ^1 p+ p3 ~
C* cp = &c; , P, G6 J+ |/ A cp->m = 10; . J3 N' E! V$ I std::cout << cp->*p << '\n'; // prints 10* [. K. L% G* S) G. X
3 g: d' H* h" b, k) g————————————————; g1 j* ^5 S' T+ r& k
版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。+ V+ Z0 c% _* a% z4 M+ q
原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115$ [' S4 \0 b8 V3 U/ ^
" Q. d* O1 i1 E. r' A9 T