( d# q# W0 D ^1 zint a = 1; 7 d% P8 n& w4 lint b = 2; ( C: @/ G; u8 ]" m+ ~int* p1 = &a;6 w( p' z- W2 i& Z1 U+ \ A+ V
int* p2 = &b; / S; U/ h% L0 ?5 e& tconst int* ct_p1 = &a; // ct for const type, q x L9 G% B5 g1 x
const int* ct_p2 = &b; // ct for const type % T% z6 L Z# N; u5 j$ o. v& l( m# f6 U, R; L
// int * * pp1; 指向(int*)类型的指针7 Y5 H& h; t. A( H* z
int** pp1 = &p1; + d) T9 o8 d5 X" Y/ i
pp1 = &p2; // 合法, 5 c5 R) q; y! App1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*) 1 x9 y2 W5 w( A3 Z7 f5 p: R0 ?- T" J/ u; e# P
// (const int) * * pp1; 指向((const int) *)类型的指针( [2 Z+ [. n% L; E% V3 b/ |
const int** ct_pp1 = &ct_p1; & S8 |2 t: \4 E, h1 y: L# g
ct_pp1 = &ct_p2; // 合法 " i1 @) _9 Q6 j' M8 i3 J# S. e9 Ict_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。3 Q7 m: B+ A0 Q% O
4 X+ Q8 }0 M( M. x* Q// (const int) (*const) * p3 J: J( ?+ u/ N1 J8 J! uconst int * const ct_cp1 = &a; // 指针也不能修改 0 P5 H& ~6 s: F$ B' aconst int * const ct_cp2 = &b; // 指针也不能修改$ w8 Q# i: G( N( b
ct_cp1 = &b; // 报错!指针有const修饰2 u: E+ w- M5 D- b' }4 {! ?, z ~
7 _2 m8 H. E2 J5 r% }2 J1 s0 t// (const int) (* const) * 指向((const int) (*const))的指针' G' ]4 O. `# T3 A6 v$ c6 H8 ?9 e
const int* const * ct_cp_p1 = &ct_p1; * C: V# T& N: e" M. Q
ct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰+ B. l8 P5 X5 z$ E% W
*ct_cp_p1 = &a; // 报错!等价于操作ct_cp2, 指向的指针是带const修饰的不能修改 6 m& v1 V) E k J( ` 1 G5 t" i. V- I* @# G8 v9 A( ]// (const int) (* const) (*const) 0 Z6 x3 g5 @+ R! }8 ?" k- p$ S
// 指向((const int) (*const))的指针,且该指针被const修饰 9 X1 d/ `; J+ W/ o. d& Wconst int* const * const ct_cp_cp1 = &ct_cp1; , s* _+ U6 B+ o* f0 `ct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。 3 }8 f- n; d9 u+ c p8 @" {8 h4 `9 E
一行声明多个变量# X7 n3 m8 \& Y. I5 O7 q( ]& O
类型 + 名称定义一个变量。 ' W4 o+ l9 s! J n5 I变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。4 a6 ?7 x. e$ }+ K- O7 s1 \
3 }& i: W0 P9 Y! y7 x
int a, *b, *c, d, **e; 0 N9 `# S; h$ d9 G& P+ la = 0;- p5 u) m" u+ N ?: u8 ~
d = 1; 6 x+ T Q* W8 l% n3 Zb = &a; ' t% a3 s- Q4 V6 s* W8 n$ G; bc = &d; ]) w {# U K3 ?: j0 Me = &b; // e为int**类型 指针的指针. f. i' [" n9 `: J3 X7 v9 ?3 @
e = &c; // e为int**类型 指针的指针- g' F+ W, t6 ? Y8 s. F9 I& I0 P
也可以用括号包围变量和*号。 ( Q7 n) y9 w5 g2 t/ V - p" ~0 ~. }" ^/ s# `* fint (a), (*b), (*c), (d), (**e); // 合法定义。0 c6 z s; t2 L$ [; m, b
括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如 4 P" A. s/ R! x- F& ?4 k3 t' }& V: X+ S# o& n4 p* N' z" W1 a0 l: E
int (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;* V" l- x% [; C
写成 - n" J# z* R$ F1 S8 B# z+ c- L- p 6 T: i: a Z3 D; E1 M7 ^int a, const *b, *const c = &a, const d, const* const* const e = &c;5 x% \$ y, d. Q( \
更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。 ) C) ^% A. P5 k" E( I: b- W$ @8 X6 I
数组指针 8 R6 v& l. m9 x8 {4 Q, G数组基本表达+ Y! h& L2 b) i" M3 R4 \5 @' K6 f5 `
int a[10]; // 定义了类型是int, 元素个数是10的一个数组。 # n& W3 M& h' w由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得! C2 R; |# W7 H' p# z3 {
! e! b( s- P* z ~# u
int[10] a;4 j Z% ~# L/ N: J
这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法: ~2 T4 I3 i1 A" S* r0 n2 `
1 ]5 w) g( W* Q' K2 L( M/ [- d" e
int a = 0, *b = nullptr, c[20], **d = nullptr;2 ]' S: |( f" m7 O& H
c++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。 / G( g& S- X# @3 G 2 I2 n" Y* {, ?* U7 yint a = 0;8 o" a8 x, h% p5 Y. k
int* b = nullptr; // 指针int* % A, }( l! Y! x4 \+ f, ]int c[20];2 Z1 } N: l+ o6 g
int** d = nullptr; // 指针的指针int**7 R! L ]5 b5 i& r" W3 |, q
数组的名称是什么类型 1 m1 b# M$ y) d' e; ]数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。 6 G9 X! a7 n1 [( I7 j2 w( E# w4 l) i' M e
int a[10];8 V5 K; M. }$ X3 d6 ?' G- z; G
int* p = a; // 指向a数组的第一个元素$ m6 W, b( l r$ @; H+ N3 H% m2 p
a[1] = 1;! g6 u9 |5 X" l$ Z0 I0 y
p[1] = 1; // 效果与a[1] = 1一样。2 Z/ G6 G- P" l( Z
数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。 - V& a% `2 p4 Y N 8 b( y8 O' _) S# b* `int a[10]; Y5 @8 \, `4 T9 _! B: ^
int b[10];7 u0 c# H& q: ^" |5 k
int* const p_a = a;. D( |+ h6 Z" j9 ~
a[0] = 1; // 合法。 数组的元素可以修改。; Z) |) e' |6 J ^: e
p_a[0] = 1; // 效果与a[0] = 1一样。 1 a- A' w3 S. Y. Q$ n* R' q # V! h5 P2 Z" G7 |! ya = b; // 报错! 数组本身的指向不能修改。 8 p$ F( [8 |9 F+ E4 v. Z N. o* k' Q) S e1 p( Y7 p( ]
// 所以数组a可以当作int* const来使用 / S7 J7 v$ y9 c" zint *const& ref1 = a; //正确。3 ]+ G/ q5 c/ A- U' K
int *& ref2 = a; // 报错! 9 K5 W3 R( Q" Z" Z8 P) f2 d8 O0 Y0 a
// 但是又跟int* const有些区别。 * {' Z% {# K- Eassert(sizeof(p_a) == 4); // 32bit程序。/ O0 {6 N' z& E+ K# l
assert(sizeof(a) == 4*10); // 32bit程序" n$ M$ [- V7 r$ l/ f
, f6 V8 Y* x' m3 N4 w9 q5 I, r
数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。 0 v# c% Q0 Y" G% q6 o& B * n" N, o/ N! l2 U0 [# p- ^以下3个函数翻译成汇编以后,汇编代码是一样的。- B) T2 [, C' r9 H9 x" k! n
* R( A# h7 T1 f6 C/ i
void Func1(int* p_ary) + O3 Z% E+ W4 C: z4 i{ . ?& R: j6 D$ s assert(sizeof(p_ary) == 4); // 32-bit 0 V0 y! {) l2 O i7 o p_ary[1] = 1; # Y% t7 V! D% l; f4 o0 S. U; l} + p$ P* V$ M# G$ l- ~) a 1 [0 t2 m* f) _: V6 S( X6 p0 Rvoid Func2(int ary[]) {; V! p: I8 \! n2 W" X$ z7 Y{ 0 a( u/ \ B3 b6 y2 q0 b, x assert(sizeof(ary) == 4); // 32-bit ; `# b" @/ f. f8 l2 D: M( e ary[1] = 1; 2 F$ B/ h, x4 Y' q7 P}* x8 L" p3 f3 ]0 n) P6 }
: X- o* X. H: B' d, Zvoid Func3(int ary[10])6 U" {/ i$ V2 u' o1 \) U3 a2 ^( u
{4 q( R; r3 _4 V/ S% J) h) v$ s2 P6 z
assert(sizeof(ary) == 4); // 32-bit- g7 S: i3 \3 j. O5 h
ary[1] = 1; # d9 O% h" a$ D; p, s1 f4 ]}! |0 T& i1 m7 y l3 v* R
7 {3 L/ |( ~; f+ N$ S0 y
int main(int argc, char** argv)$ I9 g1 r3 C9 D! \- p, g
{ + s. Q) A" Q( Y6 u& Q8 V) x int a[10];9 w9 j4 i8 C/ W. p/ n4 R
int b[20]; F, D, T: z: F% e9 d Func1(a);& }, b7 u- j) [, v" e
Func2(a); 1 V: n; R# h2 R- x1 I* m# A% o4 `$ n Func3(a); 0 r8 ]$ w) Q1 ]4 R* f0 z Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。 " F0 n. M, d a# n! w1 J' N: p return 0;& g2 N, `/ d) @1 I' n8 U
} ; F2 C$ X; w# r3 {+ \ / j$ C6 N8 h) t& N+ D. u$ T: s
4 t9 f/ ]3 @/ d. k多维数组% w$ T! j# n" R. c
一个3行,4列的数组, 结构如下: " T% T/ Q) O9 \! i2 S ^, _ H" A: u& \2 U. m+ r
int a[3][4];4 K: _* w- d+ s7 C! m; ?7 G+ b
column 0 column 1 column 2 column 38 }9 G, G: @+ F4 I1 q
row 0 a[0][0] a[0][1] a[0][2] a[0][3] 4 ~0 C( s& f6 f; Y4 Nrow 1 a[1][0] a[1][1] a[1][2] a[1][3] + `$ x8 n( [$ T O8 v: arow 2 a[2][0] a[2][1] a[2][2] a[2][3]2 c/ w, W0 C0 o0 u) U
数组初始化 7 S4 K) Q6 a2 ^ 1 V1 Z' f. ?, v; A) U* C# J# [/ T. w/ Hint a[3][4] = { 7 k4 [: N+ ^* Q6 W. r {0, 1, 2, 3},/ v, ?- x) j, k8 i
{4, 5, 6, 7}, 1 N& @$ [4 W5 \6 Y1 j9 @; |3 z; | {8, 9, 10, 11}/ q4 w; Z; s& B' N6 E
}; : [( i' ^% q7 N0 X* W 实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。 + r0 ?$ C+ }, o# `0 {$ `, K+ I) e+ l2 T9 }6 z! ^; P6 w
void Print(int* p_ary); & E+ d$ r/ V. P6 u( }; Z1 v; q/ n- {- f$ }( Y/ d, t8 f
void Test1() ' b4 _* d! ~; D; \8 P3 Z0 P{ 0 Q! g) o6 h4 |5 X- W/ D/ n* D9 A int a[10]; " w* _' u% j" ^3 t' ]7 Y) ` a[3] = 3; 4 \2 e8 S7 ?- i# }! U) e& k8 h a[7] = 7; 2 J7 E7 d+ @6 W. b Print(&a[0]);! j) C+ z% r' @# P5 n
}. f3 S0 m9 q0 R: |
( }+ }4 \: B) x. e+ [void Test2()4 W# \) M; Z5 k8 x" S
{ , a- u5 z4 N% f" \$ ] int a[2][5];2 ?+ T2 J# L C4 H% I/ r# m
a[0][3] = 3;+ B0 P$ L6 r( @9 U
a[1][2] = 7; $ G+ c# P. w- G9 I9 p Print(&a[0][0]);; O8 D+ t/ ?, ?
} - j1 f- _* E4 n& ? G, y$ f% c+ `/ S2 i- X& d6 G
. u3 u3 N1 j$ S* \- C7 e6 ^$ y6 t9 C5 S' E/ @1 P6 i: }& B' K2 L0 R
很自然地,多维数组也支持用1维数组的方式去初始化。; m. F+ `* P: U8 q2 D$ ]
0 }$ f; y# ]4 r) Z, E- A1 C
int a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };* F+ D. j; ~+ c& X5 _- }
既然多维数组与1维数组没什么区别, 为什么还需要多维数组?* ?# L w) G1 @) S
& G6 f- R: J( Q# R+ ^
假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下: * N' D. c u7 I! l: o* l! X" v7 t7 e4 W; H; I& l' w: i
unsigned char* p_rgb_image; 7 N" F' \+ v, b$ Z7 ^6 Nunsigned char r = p_rgb_image[40*720*3 + 50 + 0];2 I$ `( M, z. o; h- o5 }& [
unsigned char g = p_rgb_image[40*720*3 + 50 + 1]; / j$ b* z- \9 w, ?: q& Sunsigned char b = p_rgb_image[40*720*3 + 50 + 2]; " f8 ^$ y1 X; p* d& a类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。 * C) \: }5 I" g8 E6 ?. M 6 J7 f$ ]0 h. e3 T+ Ienum 0 V% K+ A4 _& ?7 b: _* e{9 P" z3 u. I1 {1 x# `( ^) h
Red = 0,6 |, |" d7 N: @+ @0 T; ]% ]+ ^+ Q3 ]
Green = 1, 3 K9 a2 b k3 ~$ K% H& _ Blue = 2. p9 `' o0 ~) Z& ?
};8 a9 A4 N# U7 K
unsigned char rgb_image[576][720][3];6 M9 m; W( [2 |' X) H7 l
int row = 40; & L1 ~2 I; l8 U5 B0 l8 aint col = 50; ' P1 s, \7 _; [unsigned char r = rgb_image[row][col][Red];5 Y9 C0 y: u9 O+ E4 } Q* N- `
unsigned char g = rgb_image[row][col][Green]; & ^% y' V; r Z' F6 Hunsigned char b = rgb_image[row][col][Blue]; h; s$ [7 `+ g数组指针以及与指针数组的区别$ L+ c4 z# B1 q9 z, |2 Z9 z: K
数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。 / R8 E5 y2 V' q- ?" |) `指针数组,是一个数组, 数组保存的元素的类型是指针。 ; e5 A0 i5 z O6 d, H+ p数组指针的定义 & ?- v: O; _' B" v+ R8 `$ T! Z+ p$ s数组指针定义先定义一个数组。0 ]* T& o8 [: ]: N8 x( ^
int a[10]; $ Q/ q' `1 n# e# G8 t然后对数组里的名称用括号括起来后再在变量名称前面加个*号 & {# u) h; `( n$ Z d; Yint (*a)[10]; 1 V" b* K# \' G# C9 ?; J: s后面你会发现函数指针定义类似。 , z6 P" u5 A! W/ t. z( p# `// 各类定义对比) L9 O1 Q& V7 x
int a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10]; 6 j% t8 k; a9 d( m \" r4 U' w7 C, D. o& ~# D9 z2 L# i
int *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int* 2 x* |. l! f1 \; r5 aint *(f2[10]); // 指针数组。另外一种定义方式。 x4 I. ~0 v& x* ^$ q8 Bint(*f3[10]); // 指针数组。另外一种定义方式。# q- \: b" y$ g% C: A
int(f4)[10]; // int数组( I m5 O% h" H$ h2 {
int(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组 * J& c/ z$ ^4 M2 v# l9 |7 L$ Xint* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组$ M! G" e @- A
3 n6 K0 m$ y3 Q2 Pint d[10];/ R! o5 x% H# \; g
g = &d; ) }) Y8 E+ m c' [. M1 q z5 v, B6 M' c
int* e[10]; 5 A! y/ B/ V5 M+ I$ ? ^; d2 Vh = &e;7 \, ~% n0 E* n2 u: z- L
数组指针的使用/ l' s; l! W+ T4 i, e
数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。 & Z" |8 a& z2 \3 n& r1 }5 ~# E& y0 v5 e! Q( E
int a[10]; : B% ^" \7 c, k/ Jint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组 + Q' Q. C: z' P% i2 ]2 `for (int i = 0; i < 10; i++) {; U2 O# J7 I( E0 p& S, o
// p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象7 S9 e# v' h) j- I
// 再通过下标操作访问元素。 9 k: |: S, D! H% I D+ g0 Z (*p_ary) = i; " U+ X4 H+ {" j7 N( M
} # j- M, M( B8 Z, V, n, R 7 d" r* _% u% R0 t6 |. h( p) ?int b[10];: B* \, u, E6 \4 i& ], \5 |
int c[20];' z9 c) Z7 k' _2 o
p_ary = &b; // 合法+ \2 [# w2 x8 L# ]" B* u7 Z
p_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体 ! c6 n% j5 \, |, o% G5 @% y数组指针指向多维数组的子数组 & o* z' k* c7 Q& A. u. D k8 Qint a[10];6 G* E# r8 v# t% s" z8 e3 m
int b[4][10];% V2 ?+ T! z% M6 k
int(*p_ary)[10] = &a; c; E* r7 \9 ^ A# nfor (int i = 0; i < 10; i++) { ) ^* S( q4 z: \3 y (*p_ary) = 1; , F5 J5 |4 d6 {+ |/ J} ( v, }5 l; ] Q* b4 ^; f, ` ; J6 N' r6 g; J+ C# e* L, Lp_ary = &b[2]; // 多维数组,可以看作数组的数组, 7 z0 E% L! e8 @' {- h; G d% R// b[2][0] ~ b[2][9]的值都被改成2了 q$ U# [2 [) u. q& W, T Tfor (int i = 0; i < 10; i++) { ; |+ W" S+ @+ {5 `) C2 g @ (*p_ary) = 2;% e J+ V3 v/ a4 h1 P
} . p: N: A5 n* ^: ^多维数组指针9 p5 Z N* y- K9 ]
多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。 3 M- G+ @9 u; t 9 A/ n7 _5 P8 B( a0 ^( aint a[2][5][10]; ' s. }9 Q+ f) uint(*p_ary1)[10] = &a[1][2]; // 1维数组指针0 T3 f5 k' _( S+ `
int(*p_ary2)[5][10] = &a[1]; // 2维数组指针 , T+ p# q" E. \( {) V8 ufor (int row = 0; row < 5; row++) {# Z$ l- r9 _% d
for (int col = 0; col < 10; col++) {! P9 u4 i$ B8 Q: L p
(*p_ary2)[row][col] = row * col; 1 k1 [( t8 m9 D8 M }6 [6 u% i1 Q* d* H- o \
}: @: ^1 G( A j7 K+ Y
数组指针和指针数组对比实例 / E- M3 K O$ m% B数组指针还是记住两步法即可 * l# o/ L0 a9 d+ a }1 l: |; ` L9 [7 O! N8 F% I+ X
定义一个数组 $ o# e$ M* H, }" m' A括号包围1中定义的名称,再在名称前加个*号。 1 m8 o. H& [! }% j8 ~) nint a[10];4 a% g: Z. M4 ~6 {
int(*ary_pointer1)[10] = &a; // 数组指针% N3 y2 y& K9 \* L" p
int* pointer_ary1[10]; // 指针数组。元素类型是int*) J. _! d" b% W$ a3 {- ^4 I$ c
int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。) \+ B/ n- \. {
int (*ponter_ary3[10]); // 指针数组。另外一种定义方式。 # O: ~6 W8 d/ W- Y% T+ l! G, Eint c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。 : ~" X* g( D& Q) _: u+ K+ {. ]: a/ U! B2 t* g0 P
// 指针数组可以把每个元素指向数组对应位置的地址。3 c- u0 f L+ G( ^; {4 @5 ?, ~& d
// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,% ^$ C) N: G$ ^; l+ S' t
// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。 # [. X9 G! S& u( _& t; i4 T7 @! Kint* pointer_ary[10]; 3 m3 p) K* t9 o( {0 U0 Ofor (int i = 0; i < 10; i++) { . Q$ Q/ c/ Z% C0 N7 j% H pointer_ary = &a; * G- c0 _2 }0 n( H} ( U+ t5 q* z+ ~. R1 c& E// 类似遍历原数组效果。 . t& N) f4 x& F6 g* jfor (int i = 0; i < 10; i++) {% [! {$ ^2 { w6 p4 Y
*pointer_ary = i; // 修改原数组。( r: |6 i( a1 A3 v
} j( {* l# `! R; c$ S6 X+ b 4 T" ^: g4 {+ X- n: k函数指针 7 d( R' d, f2 q# H: P( Y取得函数地址 O5 l2 C% X- R% I: v+ p函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。 + J1 }) G, i. q: c/ X5 W, p+ U3 V3 `/ b" q& ]
void f(int); $ B7 ^$ S1 r0 H/ k. Z: m! W1 Vint main() * w" p! e% _1 _) t# l{ 8 E8 d+ C3 N+ \" w' K4 M void (*p1)(int) = &f; * w7 w; c# A0 w4 i" j C void (*p2)(int) = f; // same as &f ) I+ X$ d& L+ `2 W return 0;+ y. u7 E [3 e( F
}4 B9 U1 _8 D: }& O
翻译成汇编代码, p1和p2的赋值是一样的。: r6 Y4 ^/ r, E
' l' K; W5 [) ~2 A# U) r7 p" W* o# y! ?0 q- s: }
- J9 @! L0 a/ b3 |. U: C
函数指针的声明 & {9 D7 ^& C8 l B* _单个函数指针变量定义步骤, P" r0 y. c. ]! C; g# n
定义一个函数。void fun1(int a, int b); int fun2(double a);. K2 a: u: h7 i8 }
用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);( z. n, Q$ n8 s, u& b
如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a); 1 A; ?- u9 G) D6 L6 {+ x( vtypedef定义函数指针 : W' z7 f9 C8 V% c; r可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。 " A: W! F2 G0 P; V7 T2 t9 F7 R 9 Y u$ X# _: M( y# Etypedef定义函数指针的语法 j m9 ^4 x' }( r- xtypedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。 . r3 r! m. k2 H* j3 U- S2 E/ f ; ?6 M: _9 y @% L0 x! [/ ?/ \typedef int FuncObject(int a, int b); // FuncObject类型是函数对象$ B3 ~3 l% u' g- l2 E4 t/ Q+ Q
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针 9 f0 |6 d. M0 a4 M+ Z5 fFuncObject* f1 = &Add;+ c8 ~* I+ n; C) ~ ~
FuncPointer f2;9 C$ D1 J, t8 [
f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。 ; J0 \( R( p8 B& d4 nFuncObject f3 = Add; // 报错! 函数对象不支持拷贝 8 Y" ?/ j% e/ T- B1 g+ sFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配 8 q8 |- N. d, M" ?1 i3 E* oFuncObject& f5 = Add; // 正确% k% i$ I- D$ U( @& G0 P
int ret = f5(2, 3); // 正确 3 U8 n- k, _% M* a, BFuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配1 U8 `6 s- F) f" a, r
如何记住typedef定义函数指针的步骤 ' _1 `: w; R i2 V) b像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b); % t9 X( m( V! {在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);' D4 p% T% b% Z3 k
完整例子8 v% b5 s' ^6 D# \
typedef int(*CalFun)(int a, int b); # K7 Y4 I$ k! d8 |2 F8 J 2 [& h, M; F7 Sint Add(int a, int b) & E" a' Y/ I, z) B2 c$ y{, l& A" ~/ E( b: x
return (a + b); # H! F- h b) D* j G}3 B0 Y6 e+ i) `7 W! S# _( d3 o
/ j1 f4 }' a$ f+ k/ w/ o
int Sub(int a, int b), ?: S/ u: I. _
{0 K" w. \& q0 G+ t; }3 |1 K
return (a - b); 9 c" N/ |7 E3 ?/ X}3 v' o; x7 a: Y5 E$ a; ^
D8 s: f2 t6 k" u5 Jint main(int argc, char** argv) 8 d9 M& t3 |% l1 L{ . f/ c& W* Z: o2 V' a' z CalFun f1 = Add; 5 h7 {3 i7 ?9 l) b2 ]4 e4 M CalFun f2 = Sub;( T, `' S" w6 p7 t, S5 U
int a = f1(2, 3); 2 \9 u4 |5 C5 ^" N int b = f2(10, 5);( r. s" ~/ H$ t$ Y& |& G2 N |$ f
; R& h! w: i4 Y; E: t
// typedef定义的函数指针数组。8 ~8 Y3 p. P5 N z. z$ ^" Z' e7 M
CalFun f_ary[2]; 1 W% i$ U* w5 G. u; ?5 E- K4 [7 P& l f_ary[0] = Add; ( G; k1 |! }) b8 { f_ary[1] = Sub;7 z# E9 Z* W& `( k
5 F% R: ?$ j: F% [ // 单个定义的函数指针数组。3 u/ v, s0 S! h* I0 V
int(*f_ary2[2])(int a, int b); * ?! R: e {$ W0 U f_ary2[0] = Add;+ t6 U9 T, L, J1 B5 w( k8 r
f_ary2[1] = Sub; F8 ~2 P+ o j+ G1 D* K. k+ e1 ` ; U1 g8 T1 e# ^4 A+ k5 h. e% ^ h) l return 0;% F/ ~4 ~. s, r! `* d" x$ a' b
}; V, [2 k8 Z8 F3 t: n/ s
4 ?/ A% k4 r9 m6 Pusing别名定义函数指针 ! ^0 k5 [- G7 h% z4 o/ gc++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。3 v0 U' y- x- m; J( D. V. c, Y) ?
2 [, F0 P" i; Y& ?" |/ ]) Qtypedef int FuncObject(int a, int b); // FuncObject类型是函数对象 ( G# M/ x& p. v: D8 u+ _ Ousing FuncObject = int(int a, int b);) P5 V/ b1 V; a/ G2 P
typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针6 ] A4 G7 \# c
using FuncPointer = int(*)(int a, int b);% ]1 r( o3 P1 a9 ]* Q! P3 R5 H
函数指针的调用8 N1 }$ g, e# V3 f& R: J. z
函数指针和函数对象都可以直接后加括号调用% j* e+ F: |% `7 C0 L/ T: X0 R6 a
int f(); ; a# _0 y5 w, V2 ^' o/ kint (*p)() = f; // pointer p is pointing to f & F U. G+ P! L. K: _; D$ bint (&r)() = *p; // the lvalue that identifies f is bound to a reference# G B% Z' F& l' Z
r(); // function f invoked through lvalue reference: G2 v9 A' M1 ~
(*p)(); // function f invoked through the function lvalue : {+ ?) G8 c& Hp(); // function f invoked directly through the pointer 9 h9 k/ u u" b. D8 w. I如果函数有重载, 函数指针会指向匹配的那个版本。 ' d" N# W! c' _: Stemplate<typename T>/ q' t+ q9 a3 s/ n. l/ n
T f(T n) { return n; }* N) M' |) t) {( {
2 k' p! P& i. ]7 d+ u4 Kdouble f(double n) { return n; } # \! R7 G/ J+ B$ x, B; A8 E$ Y( X$ B" y8 K; c
int main()# d. _0 Q) O, _9 M. ^: }) `
{ / G+ {, e- Z. o int (*p)(int) = f; // instantiates and selects f<int> 2 d( w4 \, H: `( Q3 A* g} " Z8 I+ e, `" C8 g) @* s5 Q& k8 J成员函数指针 9 i* v6 e" H5 E0 \ h" ~: ^- i静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。0 ~2 J" F0 Y1 S) k# ^
) x8 v. e& ~# n4 l& S成员函数指针定义。 : v. O Z/ I0 n( L9 J4 K& ~像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int); 0 |7 S5 Y: N, C9 Z$ X; L" A, o- i括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);! B6 _: g: O; F2 g [
在名称的前面加个*号void (ClassName::*func)(int); m% B" h4 x* \- T
成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int); ! m7 t: C. e2 L' J# `成员函数指针如何调用。* F! \; E& Z; x4 q. v, D" ]
假设成员函数指针名字为func ( T: ^7 z; d2 a+ n9 c f$ S4 F. N1 h' @: r* ?4 Cvoid (ClassName::*func)(int); u0 j) L8 N; D. A- I) s对象式调用。9 \* x ^3 o0 H' d* \
ClassName c; // 被调用的对象1 k V1 P* }1 r6 V2 j3 B4 `! |
成员函数指针名字当作正常函数那样写。# G" M! i c& P# C
c.func(3);) T4 }) Y( a0 J3 R
成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。 + E% [; n. q7 j, t) ^c.*func(3);) D6 _+ T& |( l; f/ `
最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。 0 N, d! Q* Y7 V. A4 s' Y/ A(c.*func)(3); 9 h0 Y- S. {- M# q- {6 D% }' s- G为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外 B8 L8 v( m9 v* q(c.(*func))(3); 2 @ e' l' C0 N- J4 B' {) X+ D这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。 9 O5 x9 G, q' Q- R/ c. b6 k2 |指针式调用1 M$ V3 w$ L5 L6 @ i
ClassName* p; // 被调用的对象的指针 |- H$ M6 |9 O2 h& r
成员函数指针名字当作正常函数那样写。5 |) z7 Y1 T( c
p->func(3);8 s: O. {2 ^8 w# n: R6 X5 x+ n
成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。1 k: |6 Q; }! m3 W! w! @# Y% j# T! `
p->*func(3); & v, c8 t9 n4 ]" m' G- @+ ]7 x最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。% j6 L' ]: j# E" F5 ^) c
(p->*func)(3);3 Z! \4 R" g J1 j1 p8 u2 |/ l1 l
函数指针使用完整例子 9 b" H. j" B$ g3 lstruct Cal ' t8 H$ d4 l3 c5 q! z
{ & R) Q( O' l0 k+ h+ g
int add(int a, int b); 3 O+ y! L$ ]5 } q" }
int sub(int a, int b);) C7 E" E4 _4 Y: i
};/ u6 |+ z2 z* ]: M' D
: C# V; U4 J, d$ W& ]7 |
int main()/ S4 U& y( X& K
{) z: ]! t/ q0 H$ P" h8 q. A' E4 Y: F
int (Cal::*fun)(int, int) = &Cal::add;% X( |5 l9 q+ P& r: q
fun = &Cal::sub;5 j" T( W; u4 M& P6 P
0 T* b+ f1 q, f& J( a; r: _ Cal* p_cal = new Cal(); 6 w, } `2 P, W6 l) L int r1 = (p_cal->*fun)(2, 3); 7 J M/ T- @- Z! O5 A delete p_cal; . B) T- k: Y+ L7 Z 6 V7 H5 I$ v9 G( f2 `" p9 r Cal local_cal; + ~& J# S7 g m0 h int r2 = (local_cal.*fun)(8, 6); $ J: @' F' {5 E! L2 t: t1 h} 4 h$ s2 t1 Y* B2 d8 Q; ] * x1 h- Q* ^4 l成员变量指针 * E5 g! }4 m* w3 l& S成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。 $ _; U; \* S" c, x: G" [, t* V+ X5 h2 M, z: {4 V/ ~
成员变量指针的定义0 i$ E4 B1 S S( o' Q; S' y
假如以下结构体C。 & T2 S, f8 u9 ~& t: Q' t 1 R9 f# i+ | \$ ?struct C $ r; V/ M* V+ e, I* N* K% i
{ $ J) t. f$ H5 d6 ]% \! m/ Z, ? int m; ' @3 k. w! h# F3 C @' F! a
};+ B$ h/ `, d0 U$ w
单个成员变量指针定义 1 A) {' X: i. e, e; x) Z* d5 i4 x假设名称为p, 类似静态成员变量定义那样声明 ; R+ @* U5 K8 |5 R9 B4 L8 Uint C::p;/ F+ ?+ m9 d) _4 B2 Y" }- U
在名称前面加上指针标识号* ( B; e2 ?4 j$ c) |& aint C::*p;) y$ g. x( W8 L% t
typedef或using方式定义 2 i, \3 D8 E' t# utypedef int C::*MemberPointer; , ]: x% K, s, s0 ausing MemberPointer = int C::*;5 ]+ U( A2 `# B6 N% ^ \
成员变量指针的使用。 + D$ l3 n* E( H$ O9 M类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。 1 D) ~3 u8 v% G, |2 W; J成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。 4 `8 ?; |. k* W1 c2 w ' U$ U Z7 e$ U& [! B 完整例子 + B, u! {, N1 F- g! a6 Pstruct C { int m; };) m8 o& ^" w. T$ ` ^9 ~
int main() ; f3 [, E8 O: V7 d( @: U{ $ T2 g/ L3 U e, q; ~' T int C::* p = &C::m; // pointer to data member m of class C/ a$ {5 o6 j( u2 c
C c = {7}; # o0 w" q& i+ R- Y7 Z std::cout << c.*p << '\n'; // prints 7* U! C- d7 v( d# K/ \) e2 H/ s- @
C* cp = &c; ' q7 G2 t! f. V, t" M6 L% Y cp->m = 10; 3 B3 M7 ~) H k7 x& N; } std::cout << cp->*p << '\n'; // prints 10 _* ^- ~. h+ X( ~8 G
+ u# ?9 e' s7 P% @
————————————————) y1 I4 u3 u1 u5 S2 H$ p2 R
版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 6 Z! W6 n; b# i7 X& E- P' I5 ^原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115 / s. q+ b& a x) x4 M! L5 S3 j- [+ n: c# A0 F5 i+ V2 Q. d0 _, v