QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2416|回复: 0
打印 上一主题 下一主题

C++数组指针、函数指针、成员函数指针

[复制链接]
字体大小: 正常 放大
杨利霞        

5273

主题

82

听众

17万

积分

  • TA的每日心情
    开心
    2021-8-11 17:59
  • 签到天数: 17 天

    [LV.4]偶尔看看III

    网络挑战赛参赛者

    网络挑战赛参赛者

    自我介绍
    本人女,毕业于内蒙古科技大学,担任文职专业,毕业专业英语。

    群组2018美赛大象算法课程

    群组2018美赛护航培训课程

    群组2019年 数学中国站长建

    群组2019年数据分析师课程

    群组2018年大象老师国赛优

    跳转到指定楼层
    1#
    发表于 2022-9-12 18:49 |只看该作者 |倒序浏览
    |招呼Ta 关注Ta
    C++数组指针、函数指针、成员函数指针5 }2 p% `# |2 K7 V- ?
    C++数组指针、函数指针、成员函数指针+ G% }8 U. t8 p" Z

    & \/ J4 e, b$ e+ i8 f; i
    - k6 I. I; z/ F! T4 ^操作符名称- }9 U  ]4 x! D! @5 O
    & 取地址符(Address-Of operator)" A) p, {9 E% P& d' P$ m
    * 间接寻址运算符(Indirection operator)( Z( N3 @) ^7 [: ^& s  P# `
    .和-> 成员访问运算符(Member-Access operators),用于取对象的成员。4 a2 j8 w, c8 Y; O5 o; S9 L
    .*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。& M: ~2 ]- t- ^$ o% q1 R
    () 函数调用运算符(Function-Call operator)- u2 d: x1 m  Q6 U3 d
    :: 范围解析运算符(Scope-Resolution operator)
    5 Q" ^# G6 r$ x9 J' ?如何定义一个指针变量  L0 c- [8 N& D0 M( n
    假设类型T, 变量名称name, 指针的定义如下:, ?$ K- h$ |, i. \3 m
    9 Z' C% j2 F+ \( e
    T* name;
    5 o$ F3 k9 ]/ w! z4 E4 D/ I6 `9 @7 }标识变量名字name, 它是T类型的指针。例如
    + E. J5 m+ a) _0 p$ G; b& s/ ?2 q( i+ k. T: j6 G
    int n = 0;3 L; l6 y% v' i8 I) k3 A* d( f; m' i
    int* p_n = &n;
    , r" t) }; N. w& T3 V% c; Up_n是int指针类型, 指向某个int型的对象。4 X1 R) G3 q- S% E( t

    4 L$ H5 |- ^9 L" I5 Q% A& z' R指针变量的修饰
    1 y5 w5 L! X( C$ P指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。
    - r$ ^) h: e! b8 z
    9 I6 G& A! W# ]7 [4 c指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:
    - M1 w, D0 s# K* M; ^  s9 f
    : @5 Y# t9 j% c) u5 fconst int a = 1;
    3 I  \+ k. i7 @8 [4 W% Yint const b = 2;
    ' G+ S3 D% s1 b既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:
    4 A) m) ^2 f# z3 L. G! I
    : I6 o+ Q, T  U6 O+ S" j+ vint a = 1;
    " E+ K; X5 S* @" N6 S5 vint b = 2;! H0 H6 Z  L$ g/ v" g
    int* const cp_a = &a; // 指针的修饰词,放在*号后面。
    " ~& F2 }! E, m' w*cp_a = 10; // 指针指向的值可以修改8 K4 H2 m) Q* r8 g
    cp_a = &b; // 指针不能被修改,报错!
    5 |1 Z" v: G% z8 Q. z0 m6 u总结带修饰的指针的格式:
    8 g4 O( X; W% l( `只要记住修饰词总是放在被修饰的内容后面。
    ; O; `) ~3 Q& U2 @- ?3 O" p  p6 C8 a
    8 `+ ~3 w# n" Z. }) Ncv表示const / volatile修饰词。指针定义形式如下:
    9 O6 C- J) t) q
    9 d' ], c( K1 ~: }. u! [' ^T [cv for T] * [cv for pointer] name; b$ p4 F; }6 O* X3 [, t( u2 ^
    注意对T的修饰放在T的前面也是合法的写法。# V7 B( y; [8 S$ W- J$ ?8 \9 Y

    8 ]) O+ ?# }6 V3 k) Aconst int const c = 2;  A3 x3 s9 ?+ i2 s& k0 {- E3 ~! o
    在mscv编译器下也不会报错。/ X1 S- B7 C; l! [0 J7 u7 k

    # z4 Q/ j" Z4 I& y4 e完整的格式:
    5 ~" F8 e5 I$ g9 z% h4 _7 B+ \[cv for T] T [cv for T] * [cv for pointer] name( n/ ~! X2 G1 {0 B8 a

    $ C7 B9 G7 j5 D4 V% fSyntax        meaning
    * s! R. Q( b3 U4 a' }const T*        - q- I- v  \. @' A$ z% g1 P5 @4 `
    pointer to constant object
    ! @( x6 i3 p: ^3 I5 d) b' ?7 T% I( T% ]5 b
    T const*        pointer to constant object. p- U( X- W9 h$ G4 U/ Z
    T* const        constant pointer to object
    # z/ M' ~$ I. X- m# d$ M; ^9 Gconst T* const        constant pointer to constant object
    / `$ w% ~! P. m$ ~! X& Z  wT const* const        constant pointer to constant object
    & n1 i+ I# E$ c& U7 y上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。
    # G3 X* j) E3 B# s8 v1 `& U) i2 O. f# j' W2 z& S* D. [
    int a = 1;
    - @  l6 T( z" Aint b = 2;
    * @6 w- T0 q. [& B/ R0 N' R7 d3 U( q
    int* p_a = &a;
    . W. A( W/ {# a*p_a = 10; // 合法% u' [4 K. T; B
    p_a = &b; //合法" \/ Z( E5 T$ ?& o1 e
    7 Y: G$ q/ N8 U8 p: S
    const int* cp_a = &a; // const修饰int类型, 并非修饰指针
    # e8 m! d( H6 P. C% v: ^*cp_a = 11; //报错! const int类型不能修改
    ; Z; ~$ ^+ T) ecp_a = &b; // 合法, 指针没有const修饰,指针可以修改。
    # t6 l# V6 f$ T9 Q  c+ U, M& ^* i  o* c! H1 A1 p2 V1 t$ [- ]
    int* const pc_a = &a; // const修饰指针。类型没有const修饰) L6 t) U& K. F. Z
    *pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。
    1 X1 y, j+ X: T. f8 X7 Dpc_a = &b; //报错! 指针被const修饰, 不能修改指针。' E; h& ^+ Q! x$ \2 p/ S

    ( z6 P  e- c, Aint const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰( P( w* v8 Q) T5 f& ~
    *cpc_a = 13; // 报错! 类型被const修饰,不能修改。
    ( q0 M. v9 t$ D( @& \7 x2 A: jcpc_a = &b; // 报错! 指针被const修饰,不能修改。, x5 r! w' ?+ H+ Q6 \9 p: u

    ! G& R0 J! O2 l9 Z 更复杂的指针的指针# [, {6 o8 U2 P/ m7 I
    1 y( c0 P: q: C5 ~1 t
    int a = 1;
    9 q* D" F9 d+ V' Gint b = 2;$ \$ Y% ?+ w* s2 P4 ~9 D/ n
    int* p1 = &a;; m. b8 R" o" j9 k! m3 n
    int* p2 = &b;
    . I: F0 e' b. H( S; M+ U; \% iconst int* ct_p1 = &a; // ct for const type* i: @& [2 I& @# ^) V' l* }+ m8 z
    const int* ct_p2 = &b; // ct for const type+ G( S; j/ G) w8 m0 r

    # n1 P1 _. s/ n, ~3 M7 [4 Q// int * * pp1; 指向(int*)类型的指针
      U1 E7 Y$ s, v8 H# N! p0 `$ s& @( T3 k, Qint** pp1 = &p1;  
    ( D  ~, ~3 @5 \; [1 a7 Wpp1 = &p2; // 合法,
    5 z( P# z7 B& |( o; t% U2 P/ ]7 G) wpp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)
    , p- ]' y& J; {& h0 Z
    5 z+ [9 f% n7 P! ]4 g// (const int) * * pp1; 指向((const int) *)类型的指针
    + w  v/ ?' m: A" J* {const int** ct_pp1 = &ct_p1;  
    / y/ E7 n9 ^' U4 U& O9 x3 {0 Ect_pp1 = &ct_p2; // 合法8 m1 P# L: z. u2 V9 k
    ct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。% s. S) u# K' Y4 j- C2 _0 r6 t4 s# S

    ! r3 i$ Y9 y8 h// (const int) (*const)$ p% S  q2 X6 G1 N: F
    const int * const ct_cp1 = &a; // 指针也不能修改# P% h' Y4 r8 g7 O
    const int * const ct_cp2 = &b; // 指针也不能修改# g/ h' c7 o, u  A/ T  M1 t, I4 j
    ct_cp1 = &b; // 报错!指针有const修饰
    3 N7 ~, f7 V: w) J% M# S" {+ h* X% w7 K4 Y) D3 a# u! G7 ^
    // (const int) (* const) *  指向((const int) (*const))的指针. G. ]; v" O2 {+ e7 W2 Q
    const int* const * ct_cp_p1 = &ct_p1;  2 j' R' I/ Q5 B: N/ V5 D4 @
    ct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰
    * ~; T3 I9 i: w' V8 Y; a9 J- X4 A*ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改" w  H9 w8 \& [1 R
    2 A! E$ {1 r3 Z% C  s* {
    // (const int) (* const) (*const)  
    * D) c& h/ x3 h  v5 o// 指向((const int) (*const))的指针,且该指针被const修饰4 x0 y0 a% D$ M: _2 r
    const int* const * const ct_cp_cp1 = &ct_cp1; / G0 Z3 j* |2 h/ ^" q  v3 P
    ct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。
    ( m% r, o9 h. U$ `% A. D3 w6 z8 e+ X; ?* e7 X
    一行声明多个变量
    ; M8 A* g# p% G. u) g类型 + 名称定义一个变量。3 V% l2 W. L* b5 Y0 K# U
    变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。
    3 a4 P' e4 x" X
    0 k; y* A; j2 f6 b( hint a, *b, *c, d, **e;% P2 @. Y4 \$ T
    a = 0;
    8 p8 W, Q5 `  D7 J/ ~/ u7 ~* Od = 1;( F$ A& u& C$ i
    b = &a;
    ! z8 w# Y# ~0 C% Xc = &d;5 X4 y# ]/ b' d1 {( H1 _. a
    e = &b; // e为int**类型 指针的指针- x: {9 O8 v$ m7 H
    e = &c; // e为int**类型 指针的指针
    ' z$ J1 A4 d* q也可以用括号包围变量和*号。
    ( @' N* m. ]0 H8 a8 o
    % M3 u: a3 ~+ |& h& W/ wint (a), (*b), (*c), (d), (**e); // 合法定义。
    ! U5 \) ]6 x5 w$ ^; k括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如9 {/ u$ @. v, `8 j# r
    $ N, f4 m" `3 S9 V- F* c
    int (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;, T( j7 U/ ^8 }& K7 \
    写成
    ! I+ x' T3 N$ B1 P5 H# `, A4 h! Q9 C6 _) `
    int a, const *b, *const c = &a, const d, const* const* const e = &c;
    7 A. E2 Z! r1 C7 r; N5 k7 O7 X更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。
    9 n! I5 ^, m8 J1 v* K: D
      t: ^6 s( f8 z! S2 W数组指针& I% Q% C4 u. L2 n6 S( D, K; W) ~6 E
    数组基本表达0 f3 J  C/ @" }
    int a[10];  // 定义了类型是int, 元素个数是10的一个数组。+ p( z  N0 i6 z0 e: P2 Q% f
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得
    4 e$ a9 P8 }4 \. Q& s# _# w; i* b3 g0 B( Q
    int[10] a;
    * ~4 V/ j; U# Z# u) N这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:+ S8 L. @) t+ N; ]7 e- J% c
    # D8 ]. o5 N7 X! R8 X( w
    int a = 0, *b = nullptr, c[20], **d = nullptr;2 z' D1 b0 W, ~7 C2 s
    c++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。
    # m  y/ s7 ~8 W# E" }( a# e4 e6 E5 G8 W1 B) l& L7 D( B
    int a = 0;
    ) d9 X9 L# V0 X, r1 p2 ?0 s+ Fint* b = nullptr; // 指针int*/ |7 o  j4 B  f4 j3 e
    int c[20];
    1 A+ t2 g8 M' y" L; ^int** d = nullptr; // 指针的指针int**8 i( ^4 D9 J/ B: m
    数组的名称是什么类型
      F6 p7 y  A& }% q7 j/ k7 D4 |) K: R数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。& x* G7 B$ d- f
    / h* Q* d2 C' u1 x- q& E2 |- M5 z' s5 o
    int a[10];
    " N/ n6 h6 {5 S* d# r7 Iint* p = a; // 指向a数组的第一个元素# _: l% Z8 J9 Y  ^8 a' ]
    a[1] = 1;
    1 Z9 `+ A5 |- r0 B. v0 Vp[1] = 1; // 效果与a[1] = 1一样。# L$ A, ~- m& o1 X' f/ Q1 i- S8 @9 V
    数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。& _' \& \- L" Q8 H( h* P

    # O. i1 `5 \) S% }9 Mint a[10];
    * R% R  H2 U. N' o% k9 Y. Fint b[10];  d& G; C0 g7 X$ |. J2 e
    int* const p_a = a;
    5 ~6 X8 j. D! w* T2 |a[0] = 1; // 合法。 数组的元素可以修改。
    1 p$ l! b4 f; h% U4 S/ b9 hp_a[0] = 1; // 效果与a[0] = 1一样。' z4 A2 ]* @# k4 C; d

    . }9 F: ]' ]0 q4 za = b; // 报错! 数组本身的指向不能修改。
    7 O3 Y1 q: {3 _' V( a, v+ x; M4 ^% S# P$ r3 E
    // 所以数组a可以当作int* const来使用7 m8 ]" {+ \6 k. @& R& W- k
    int *const& ref1 = a; //正确。$ g+ B* ]! q! `9 h9 l
    int *& ref2 = a; // 报错!
    & X9 a% m# X' g" ?" d3 l" {" p9 p, u/ z# r& y9 M+ q0 L" a
    // 但是又跟int* const有些区别。* ~* z" Q6 n: O2 x5 V+ ~1 a6 \
    assert(sizeof(p_a) == 4); // 32bit程序。
    ! Z2 t7 b3 A# f" m/ Vassert(sizeof(a) == 4*10); // 32bit程序
    ! s+ p. M8 [; [( [5 L) D+ H3 |' \9 b: k: D+ F' {
    数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。# y& ?" _3 K" Z/ x8 G# R% z6 D

    2 m( @* r/ b5 j9 g' m7 H以下3个函数翻译成汇编以后,汇编代码是一样的。7 P5 y* r8 b, J. l
    ; L: i5 H* E4 p  R* `, d- F# ~
    void Func1(int* p_ary)" q' M, A: ^5 Y' h% r
    {0 ~- m, k7 E' @! g( V+ v
        assert(sizeof(p_ary) == 4); // 32-bit3 R- s/ u* K* Y8 Z: ^: o
        p_ary[1] = 1;$ J$ w1 r$ G; y& Z+ E
    }
    / N* V9 C0 E5 ^; y2 N6 B# E/ [* Q, r, o& m
    void Func2(int ary[])0 Z* Y" l, Y" P
    {
    , e# T/ K& R6 f" h6 O    assert(sizeof(ary) == 4); // 32-bit
    # ~/ W+ G6 G+ z+ H2 j! ]    ary[1] = 1;- m+ {$ H1 ]/ W3 f# Z  s# ^' e3 x9 P
    }
    & {8 L/ K# N% J- |0 p( T
    9 i$ t$ |7 ]% T6 M: J5 Rvoid Func3(int ary[10])
    4 g" g0 ]( `7 i! S' o{" E# X  R9 h9 M
        assert(sizeof(ary) == 4); // 32-bit
    ( `* |8 B* C+ Q) F  p! v    ary[1] = 1;; v+ O; \# i6 d7 [" B
    }
    . [9 ~* Q! k) w4 U3 F0 U
    3 t4 c; |# u: I' d* c$ xint main(int argc, char** argv)
    ; l, _* T8 F. c) G; u7 J. i{
    4 G" P- `3 g" {3 C6 g$ v    int a[10];
    & H0 k( T: P" j' ]( Z0 d    int b[20];
    3 n5 O8 t" f7 ?& z' n    Func1(a);( _1 i) j* {& @) G) m5 y' n! g4 w
        Func2(a);6 X: m+ ]" v4 x: r
        Func3(a);- [" F0 V3 M8 d) h! B/ c# a
        Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。" V5 i: z# X0 D# P1 i9 i# ?2 O
        return 0;
    6 M; R# V0 {# a9 D! ~}$ U, N1 W6 ]( ~& n" N
    9 K% y. ?4 }! y/ q2 q; i" ~) g, m% F

    ' i3 m3 B8 `' @: `; _' R' a8 W$ c8 V3 T! ~& w
    多维数组
    / Y+ \, {1 S' A* s  _  J0 g一个3行,4列的数组, 结构如下:
    ' A; I6 j' l* ]: S& \1 C& D" p
    8 N9 }, d# C+ o8 s' G# P( Dint a[3][4];0 Q8 ~5 ^0 G. M- S+ w& B
    column 0        column 1        column 2        column 3
    * P/ N9 j4 h. V7 drow 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]: @$ z+ C6 `2 O, c: w2 [5 ]: z
    row 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]- }1 U& _$ X5 L1 v7 a' v
    row 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]
    + [3 j3 [: H# J2 \, O5 v+ {数组初始化' Z3 y& z2 Q( V

    9 o) ~6 W4 S; K7 }! Q" w/ yint a[3][4] = {
    * v+ I8 s- h; Y5 N4 W3 x    {0, 1, 2, 3},
    4 n( ]' e2 [  q    {4, 5, 6, 7},! h6 T/ _1 u! b- Y  ~; Y
        {8, 9, 10, 11}6 u; f- p) F' o6 |
    };0 @8 N: V" F) C; Z+ p
    实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。% S( Y# q9 O, o
    % D! v) I- ~1 S  M7 e3 D8 a2 v
    void Print(int* p_ary);
    1 ]- f; ]3 b3 G5 k! }0 c1 s3 v
    2 z+ p& J8 u8 O; m$ t5 y  ^( _7 uvoid Test1()
    5 e0 k: M& z& L. j2 N. r{, _) {7 z" a5 y) C( ?; }
        int a[10];: ?- ^$ _8 D4 D/ q0 z
        a[3] = 3;
    7 J7 O5 q' v; I" _7 P# h* ]    a[7] = 7;$ L" i, S" a; A3 N; S" i
        Print(&a[0]);- C3 d5 E! ]) K5 w. o
    }& ~, _* |! H# m  d
    ( N: \8 {+ \* g; {$ T" a3 [
    void Test2()
    % Z+ j! P, ~# i0 W! w0 }, P( ?{
    + T% |4 t/ L+ Q9 X) K3 o    int a[2][5];6 ?0 q5 O5 l. w; [
        a[0][3] = 3;$ `4 O# }1 }+ A0 _& A7 \, q/ k
        a[1][2] = 7;$ v. S" M- C$ t0 T. p
        Print(&a[0][0]);
    ; u& }6 W) l# J( ~6 F6 b0 s* X+ n}
    ) V' O5 r' P1 y3 J- n, @6 A* i5 u
    $ g0 Q0 A! X3 J/ m7 E/ O
    5 B) e3 F8 H" U  s9 s8 z2 Q- [2 g' z  M. G& X# ^
    很自然地,多维数组也支持用1维数组的方式去初始化。* H# y' _, e5 {9 G
    6 H# h! B7 Z* c8 {, L; j" j
    int a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };# h* ^, G6 A6 O7 j8 r4 y) A5 q& u
    既然多维数组与1维数组没什么区别, 为什么还需要多维数组?
    & C- x. i9 ~7 M  v. ]& T! H: P
    5 `0 @: |. Q7 i2 e- @' d3 n& @假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:( C) F$ ~& i7 [, F) g

    ! e2 F6 X; @! W0 i& Zunsigned char* p_rgb_image;# \- _8 g/ T3 h
    unsigned char r = p_rgb_image[40*720*3 + 50 + 0];
    9 g9 v. o/ W6 k! C( {; hunsigned char g = p_rgb_image[40*720*3 + 50 + 1];0 |3 }' l& p1 t/ P8 ]+ s
    unsigned char b = p_rgb_image[40*720*3 + 50 + 2];1 z# y9 e# r' |6 b% K2 C4 h
    类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。# j- p. D2 d7 O+ J

    - L3 p+ z4 r. qenum2 s$ {2 ?* G7 a
    {
    , y. W, @2 p/ k: x2 e1 g' B Red = 0,
    ( ]0 L, Q- e) O. Z3 N6 ^/ { Green = 1,# T& Q6 |9 V, s9 K  n0 R8 U- M+ H# t
    Blue = 2% _8 f$ z1 F# o: x) I5 k1 U
    };
    6 |4 l" t: D7 M, h' Z2 k* y+ _unsigned char rgb_image[576][720][3];' e4 z  Y4 E/ X3 T% g
    int row = 40;! w; l) k$ h/ ~2 p, {' o8 q4 C; H
    int col = 50;1 `# a$ J! [' o$ q. r: u
    unsigned char r = rgb_image[row][col][Red];0 f3 G# w2 z0 p
    unsigned char g = rgb_image[row][col][Green];
    ( X7 Z; X" g$ O' D, h* tunsigned char b = rgb_image[row][col][Blue];; O3 e4 ]2 t2 Q! E( T3 R* L4 [
    数组指针以及与指针数组的区别/ T; u, s; o4 b5 k
    数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。
    * S# U8 z- A7 t0 x/ Y+ ^( w指针数组,是一个数组, 数组保存的元素的类型是指针。/ d/ q; x$ W1 N' f" O6 x+ J$ s
    数组指针的定义: _+ _$ V, o0 o
    数组指针定义先定义一个数组。
    7 A& S# l1 V. H. dint a[10];2 J6 s+ U2 }- s
    然后对数组里的名称用括号括起来后再在变量名称前面加个*号$ b3 f+ B9 s+ [; S6 D: u
    int (*a)[10];
      j# F9 `9 ~$ ]: Q) K0 G* `, p后面你会发现函数指针定义类似。
    . C* P$ U$ P& j, b7 c3 r// 各类定义对比7 n! M" }7 |; U* |0 Q# a- C
    int a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];% {& z+ Z! ~7 u

    ! I; {8 s! Y" X$ K# p: hint *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int*
    & u& v6 L% l1 p" s" x6 hint *(f2[10]); // 指针数组。另外一种定义方式。
      y7 ]' J5 J! b: w7 |int(*f3[10]); // 指针数组。另外一种定义方式。" t4 F  y% k1 ~; C# ]- P" |7 n1 [
    int(f4)[10]; // int数组
    8 R/ E) ^% H* I$ R! P' Aint(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组
    $ ^, M  J- w. |) g- hint* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组3 C. r7 j, u! a+ g
    - K" X, {$ Q% x/ a3 \0 L
    int d[10];
    * C# O+ y" i+ [3 i6 d2 k( ag = &d;, X6 ^8 H: f7 f7 L! \; R' b  U

    & a! f+ q" S- V* \* K0 rint* e[10];) C% T  @$ A/ b; F# k: F& H# y
    h = &e;
    & v/ i% z7 H  V! n3 k' C; L数组指针的使用
    3 u: H* F& b+ }- o5 @) o! O- }0 F数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。4 T! s/ U8 F8 b) l

    ) a7 B1 D. C+ r3 S" {* x4 Fint a[10];
    4 N: ~$ P( y% k5 V& d  h- M5 Uint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组
    % G5 K7 I7 p7 B0 [for (int i = 0; i < 10; i++) {
      K  x1 M) [5 w // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象% k/ Q8 i& V" n4 s5 L. L, ~
    // 再通过下标操作访问元素。# ^4 \$ w' A! A+ u9 Y: q4 D+ A- ]/ e% \
    (*p_ary) = i;
    % ^% Y2 l2 p  j; u: y}$ p6 j: k; W* F6 Q( j6 e* [
    $ n9 O; h. j5 m3 t" ]  G$ U
    int b[10];/ T+ L; [& a& \) o- l! N% a9 ^3 K
    int c[20];
    ; T( r: a& G* X7 k  f: K6 X# j) n8 up_ary = &b; // 合法0 c8 P: b9 @( h0 {" |5 ]* {
    p_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体
    $ Y+ D; U! B3 U2 v( C& F2 V) }数组指针指向多维数组的子数组9 O: s1 T* _8 D+ [% {
    int a[10];
    % q7 P: @: k; w3 eint b[4][10];
    ! s6 a5 _0 R" \3 S5 g9 v+ Pint(*p_ary)[10] = &a;% u3 S: P7 |4 g  }% k
    for (int i = 0; i < 10; i++) {
    ) n8 q* I+ ]5 N( e3 A (*p_ary) = 1;
    % x! c) l6 `) O7 l1 K" H0 G  \}
    ! e+ S/ m- A% I" O, g
    # ]( h/ \6 H! |& K* s: ap_ary = &b[2]; // 多维数组,可以看作数组的数组,- T% p5 S' N: v  ^
    // b[2][0] ~ b[2][9]的值都被改成2了
    2 ~$ {: c0 N5 O1 h7 w* Xfor (int i = 0; i < 10; i++) {
    $ C+ B' ~/ D. @$ H (*p_ary) = 2;
    6 G; H! q1 {1 `4 X2 R% Z}
    ; g. b& s& @+ b6 n+ Q! P# j多维数组指针
    5 ^) Y) ]# m, Q  r- b0 B1 m) A# v+ a' T多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。
    ! _+ u: I! w" L* Z
    * ?; l7 g( g9 G# i' Pint a[2][5][10];
    8 \9 z9 h  H& o0 X8 K3 e# I3 b  Vint(*p_ary1)[10] = &a[1][2]; // 1维数组指针
    9 O( m0 k* z2 _9 Uint(*p_ary2)[5][10] = &a[1]; // 2维数组指针# n% @* H" g! B& Z
    for (int row = 0; row < 5; row++) {# V) i6 {* ~5 K5 i! i( s
        for (int col = 0; col < 10; col++) {
    / ~0 t' r! {1 V% W0 Q( s0 G9 Y% B        (*p_ary2)[row][col] = row * col;
    + {9 z, o# S. Y& D' p' e    }: N4 D) I& I, u( D! g# A% V4 s
    }% j. c8 `* s$ H5 B
    数组指针和指针数组对比实例1 _+ J# a# Z% R7 D
    数组指针还是记住两步法即可
    / `( H7 \2 |6 U$ {4 O+ f6 c
    4 {7 b9 d1 B9 Y定义一个数组* t  D) _- p/ _" l) j
    括号包围1中定义的名称,再在名称前加个*号。7 b. j2 ]% j, D/ [  c. d8 S
    int a[10];
    , D+ x" b$ @0 I9 L, J- Qint(*ary_pointer1)[10] = &a; // 数组指针
    2 I! s1 K4 T* I4 vint* pointer_ary1[10]; // 指针数组。元素类型是int** O" J3 P6 M5 c
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。) I4 F: s- U2 y2 G
    int (*ponter_ary3[10]); // 指针数组。另外一种定义方式。
    ! y5 c/ F6 c& z. p! \  _5 h/ Gint c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。
    1 I% n& A5 x8 n8 m0 ]' ?  ^% Q1 c5 r. B
    // 指针数组可以把每个元素指向数组对应位置的地址。
    $ w+ C5 S0 d" e8 V+ X2 U// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,7 H! K, O  _& d% q1 s% e: M: c
    // 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。
    $ S% k8 G" u" \' k6 b% ~5 lint* pointer_ary[10]; ) m4 @" q0 |  V/ C7 j& o- s, u
    for (int i = 0; i < 10; i++) {6 j% o5 ~$ K8 d9 M. f
        pointer_ary = &a;
    . y( l# s6 F' W/ R, L9 g}! B& w+ q9 S4 j1 M- z3 J
    // 类似遍历原数组效果。
    0 M& n5 L8 y8 A+ A7 j3 T% yfor (int i = 0; i < 10; i++) {
    # P$ ]3 ]4 r2 e7 W" k    *pointer_ary = i; // 修改原数组。
    9 Q* ^! I2 N5 e4 V- S; J}0 \4 N0 w+ U& f( g

    2 Y; `& t1 ?  \% K函数指针
    8 k8 }% J! o% w4 C! ?取得函数地址
      N7 ], R. B4 a' _函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。
    ) b, R1 i* C1 d3 A# ]( U% R
    7 P. J+ H5 R% Z% ivoid f(int);
    , ], b" m( E: W8 O6 {7 i: A5 jint main()
      v8 j$ _4 }( ]' z, o{' |! ?. D3 ~- l; c3 S
        void (*p1)(int) = &f;
    ; `1 v+ X) l* J* a4 V  q    void (*p2)(int) = f; // same as &f
    + O' ^& v! N( z    return 0;2 V1 I9 a- P& j! t- Y
    }
    5 g" C9 O& v! \" K& H& e4 G9 ~9 G5 b翻译成汇编代码, p1和p2的赋值是一样的。% s1 v' V* a4 R, u  O1 i) I
    ( `1 L  V7 I/ g; d
    % z4 Z- Z% H0 p: s& V/ E) i

    / \/ `) J% [" J4 \函数指针的声明
    * l: f7 {) J/ R单个函数指针变量定义步骤- W  O" v, T- O+ L2 [6 S/ y2 Y
    定义一个函数。void fun1(int a, int b); int fun2(double a);; U: o4 \" b0 P3 H, H
    用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);
    * {# [! H0 t5 q4 o如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);% P8 v! {: P% A7 [" X
    typedef定义函数指针0 y3 K0 O9 g3 @6 Z! e
    可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。# t7 B1 ~, f, K; T+ o
    6 Z" X% w2 r. J! p: a! Q
    typedef定义函数指针的语法' G* f- o- r& s8 |- o- n
    typedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。! J: {2 |+ \, {

    ' Y3 Y9 D/ b; F% |/ C1 a8 |typedef int FuncObject(int a, int b); // FuncObject类型是函数对象, G) f( t3 [) u
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    ) z3 Z& [) N, e* C5 QFuncObject* f1 = &Add;
    - T. W: i8 u9 n+ p. ^) AFuncPointer f2;
    . `( t' J* k& n/ qf2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。
    : }  M& r& {: r6 s: y$ q6 ZFuncObject f3 = Add; // 报错! 函数对象不支持拷贝
    4 c, G% c* I$ _( {3 v0 yFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配+ x; N, Y! x. \7 r
    FuncObject& f5 = Add; // 正确
    , ]  X, S* V4 ~# g( Kint ret = f5(2, 3); // 正确
    6 V. q1 \. t5 E& k, X! dFuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配+ r, I' V8 Y( T% v
    如何记住typedef定义函数指针的步骤
    % H6 S3 I* ]- t" O1 ]$ C6 `像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);: Y9 _  ]* o3 U1 e' t) j
    在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);
      e! P$ q, O8 w  S& I1 i完整例子' f& G8 V, w" G0 w3 b" B
    typedef int(*CalFun)(int a, int b);( ]2 v( Y9 `% d! o0 f3 z
    , B9 I( K" F) b4 ]
    int Add(int a, int b)
    ' s& F2 {( b' G2 D, G  }/ Q0 R2 k4 u{
    ' u, Y( @! {  i" R  p    return (a + b);
    * d& m0 j- G4 Y1 X' \1 s6 V4 ?}3 M1 d+ L# V: j9 E/ c

    ' h9 ]$ c: c& O, F6 _; e2 s6 Qint Sub(int a, int b)/ ~/ m1 Q1 v5 g9 H* U$ d, S
    {4 e# K( C% T; V# {2 |$ [
        return (a - b);4 i, n( _) S; c. y
    }3 \9 {7 @% O) S8 k+ r

    7 ?% g. `# N1 b1 d% D/ B; r$ N+ qint main(int argc, char** argv)
    % `% W5 g0 ?# \  Q, `{) M$ M! h$ o) ?# n
        CalFun f1 = Add;1 X: p5 {/ f1 r4 `* n
        CalFun f2 = Sub;
    ; @+ s* `, t& T( }    int a = f1(2, 3);, V& i3 G2 P$ h# X& {& [( |! p. J, |
        int b = f2(10, 5);: k4 ]* ?6 h7 s! \% @( A6 P4 V

    + T6 {7 X- A$ y8 s3 ^  x5 q    // typedef定义的函数指针数组。
    ) k! A" m( T: J# L- B    CalFun f_ary[2];8 x( N7 R0 {8 a, H
        f_ary[0] = Add;
    # T- s1 B( n- t2 J$ s- N  C+ {    f_ary[1] = Sub;8 }) m$ _" y) U1 C! L& q

    $ Z7 M0 {* n' Z) n$ }6 ^" o    // 单个定义的函数指针数组。3 Y7 Q1 o; B. ]  X
        int(*f_ary2[2])(int a, int b);/ Y( Z* t; _1 B# \0 ?
        f_ary2[0] = Add;7 F& u5 T& K+ B" }) B! Q
        f_ary2[1] = Sub;- p4 }5 H% B$ m! z$ p% H
    # p+ i8 e* Q# d% l) q  \
        return 0;' p8 M0 D/ M$ X, n; m+ O
    }8 l9 d3 E( Y% U/ w) A1 d
    / ~; `% V: |' {) w" z9 K! o
    using别名定义函数指针1 D, g( _. m1 I# ^; [9 Z, o
    c++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。
    0 A1 H$ S- E! a$ b& ^; m& D* i, `8 R3 U. l1 f
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象5 X$ C# R9 I) F& D; m) i, N
    using FuncObject = int(int a, int b);2 p$ ~1 Z8 V7 {) F
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    ( H# |; T. x) L& Y2 lusing FuncPointer = int(*)(int a, int b);
    # i8 u; t# V% S- N7 B  B函数指针的调用
    ; g2 G7 g& Q# t* l函数指针和函数对象都可以直接后加括号调用2 }6 |1 V4 i, V5 r9 }- C+ |5 n* p
    int f();
    . K! l& v( l! |3 P4 @2 N8 l9 Hint (*p)() = f;  // pointer p is pointing to f
    " p2 Q0 O1 G9 ?( e7 jint (&r)() = *p; // the lvalue that identifies f is bound to a reference
      ^8 m/ d; U% x$ T: P9 z3 mr();             // function f invoked through lvalue reference
    ( z2 c+ J: X. Y(*p)();          // function f invoked through the function lvalue
    ) r+ D5 @3 v5 X  P) Hp();             // function f invoked directly through the pointer
    . p4 e9 E- p! w" V2 e4 r3 }0 R如果函数有重载, 函数指针会指向匹配的那个版本。' M& s* o6 U1 t7 I, e, E6 R% O
    template<typename T>
    $ c, `3 W, G; V) ~6 \% u7 v% @T f(T n) { return n; }
    6 t0 {& h# t1 _9 i! T$ H- \& ^  a7 `: n, k& f: O4 U
    double f(double n) { return n; }
      k* `; P: \8 u, G% e
    0 l) b5 D. S+ }# B$ Pint main()5 d& y4 o% N1 H- T& q- D
    {
    & P2 z7 P2 U2 @# _' p/ y/ [& i5 q    int (*p)(int) = f; // instantiates and selects f<int>
    ) f: y  X6 W! v; r}/ g$ M$ i9 i. X! b% Y
    成员函数指针5 ~8 r. N! B4 r* W! U4 p
    静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。
    6 m) O6 v# V) l: U9 e
    ' A/ V+ w/ ?. D* }成员函数指针定义。" ]; C8 H( r6 N
    像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);! q& `5 G/ j- m7 B2 S2 I8 G- P
    括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);
    ! a6 v& I3 ], z5 y3 C3 Q! l在名称的前面加个*号void (ClassName::*func)(int);
    9 J9 B8 I2 S1 u$ _* V成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);
    9 ?1 a  q1 H' u5 E成员函数指针如何调用。0 y" ]# m- F6 K% f9 A2 b
    假设成员函数指针名字为func
    7 X6 f2 W4 O: p: z+ C9 i- p
    6 b* e9 I6 o& [void (ClassName::*func)(int);
    6 |% o8 S4 D' B4 M4 @对象式调用。5 b/ A7 ^4 T/ k3 [( [, E" f* \
    ClassName c; // 被调用的对象8 E% v# I* J6 r
    成员函数指针名字当作正常函数那样写。
    7 z2 Q+ [9 d$ j1 d2 `% b% Jc.func(3);
    : B/ a5 N" {. v6 b成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。, Z8 d/ D8 V5 T$ J& z) L
    c.*func(3);
    ! A" k6 Y# i( k& w1 V最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
    0 ?8 r' }3 V8 T$ Y1 D9 C(c.*func)(3);
    ) a( L& q+ T: Y: B; p为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外
    ; f. ^( \6 S2 ?: Q. U3 m(c.(*func))(3);3 m0 p! b" W4 |; K1 @- M) t% u
    这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。
    % |- |9 {! b  R指针式调用
    , R$ X, y* J. |& G0 `' nClassName* p; // 被调用的对象的指针3 T; Z% r7 v) Z$ A& \
    成员函数指针名字当作正常函数那样写。
    % d! b$ h6 Q; s$ D$ {. s$ Y. qp->func(3);8 k, F1 m' h& l4 J! R. b
    成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。( u1 r9 x' t+ E3 {, F! H$ F' Y" W
    p->*func(3);0 Z# X5 y. k5 F- e
    最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。
    ; X9 G8 k. g( ~( M' \) l; D* [(p->*func)(3);% k) o, [& @9 ^3 x) k& X' K1 r# k' c
    函数指针使用完整例子# N/ \( N, u- ^: x/ ?' @* I/ \
    struct Cal
    3 }& h4 f$ }/ a1 p, `* s( S: g{ : m) q6 L* H0 ^, M
        int add(int a, int b); ! f/ c5 b( s) _
        int sub(int a, int b);
    ' L. I3 I/ D/ f: R# m# Z/ H) k! Z2 {};/ a) y5 c' o& f9 C3 N. T
    $ Q- N& F5 A! r+ N" M+ Z
    int main()9 h  X8 @$ a  S% e. [
    {
    / n& o5 I5 O- t    int (Cal::*fun)(int, int) = &Cal::add;4 t2 W& n" j6 }6 `0 H
        fun = &Cal::sub;
    " R) ]. f& H! c7 S
    5 ?3 E, Z1 c! w( R( ?( I$ s    Cal* p_cal = new Cal();+ f5 ~2 U3 S4 y6 \
        int r1 = (p_cal->*fun)(2, 3);
    5 a7 I! C$ I2 ~7 a& M    delete p_cal;! Q0 ]4 m7 c4 t* H+ k4 J5 _0 N! n/ x

    7 p0 Q! o( E/ ?5 b! o    Cal local_cal;0 u) k3 f: R) E
        int r2 = (local_cal.*fun)(8, 6);% n1 h" I; k9 K: t' B
    }4 a7 y8 y7 T/ N7 p1 F( m# H  z
    5 N0 T. f" e& P, R
    成员变量指针
    * e  ]' ]2 R9 m* h4 Q成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。$ w/ i$ U; u7 e# ~

    4 i- K$ _" x/ u; U3 t成员变量指针的定义! ~! ]9 P0 z: |. h; v
    假如以下结构体C。  H5 b% j% y  Q7 V- n7 c! {! q  X
    2 Q7 U* V" p2 ^3 o: j1 f( ^
    struct C 0 O" H6 {; @1 |1 e1 i9 A
    {
    % M/ f( i' G; H) n3 _    int m;
    5 T) M3 ^5 A" ~6 V};& Z& v7 V1 m6 \- {3 e! l& {. O
    单个成员变量指针定义
    ( u- \1 ]2 W9 b4 L8 H2 ~9 l假设名称为p, 类似静态成员变量定义那样声明
      F% e" }- }2 ^4 R! _& i$ q% Gint C::p;  ~0 [% X$ M8 {9 q* a- ]7 N  T
    在名称前面加上指针标识号*
    ; t+ C+ {$ b2 V) e: A& S" Vint C::*p;
    ( {; c  R2 i) n, A' T1 ?) Otypedef或using方式定义
    6 ?7 \2 W! w% M  Stypedef int C::*MemberPointer;
    4 ~- ^0 \9 D& o$ k% Fusing MemberPointer = int C::*;2 `/ E* ]5 V- H- X
    成员变量指针的使用。4 [% h6 {1 t4 b8 G+ X5 L8 e
    类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。
    , s: y$ A8 o2 F) M成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。$ B( P0 S7 \1 p% _
    $ w- [4 o3 o7 @+ r3 n5 Q$ _
    完整例子
    + p" I% M- j  S+ P! V" z! Hstruct C { int m; };' F/ S* B+ Y7 T  Y- I* r; I
    int main()
    ; d+ d+ _" b  R: h$ [- {: s0 k{
    & P0 p7 d7 f5 f" i0 |' g, m    int C::* p = &C::m;          // pointer to data member m of class C
    5 B( A  ?, C/ V1 D    C c = {7};
    $ l, L) H2 c8 T3 b    std::cout << c.*p << '\n';   // prints 7
    3 C" _, Q8 r; [8 u4 D: k    C* cp = &c;
    9 y8 I! q5 d3 U    cp->m = 10;  j- e4 n. s) l# E5 S1 a0 a
        std::cout << cp->*p << '\n'; // prints 10
    4 O7 i1 |$ t8 A- q& r  ^2 b: J3 h5 a% h8 H: |; X9 v1 X' _
    ————————————————2 m) \5 M- [) K0 V. C8 v- O
    版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。* e! Q' n3 F" f8 \2 @
    原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115
    ( Q4 ~* u; t  r$ ]5 J
    3 f) X5 I0 \& d0 K# x: r% n; s% Z; M+ u
    zan
    转播转播0 分享淘帖0 分享分享0 收藏收藏0 支持支持0 反对反对0 微信微信
    您需要登录后才可以回帖 登录 | 注册地址

    qq
    收缩
    • 电话咨询

    • 04714969085
    fastpost

    关于我们| 联系我们| 诚征英才| 对外合作| 产品服务| QQ

    手机版|Archiver| |繁體中文 手机客户端  

    蒙公网安备 15010502000194号

    Powered by Discuz! X2.5   © 2001-2013 数学建模网-数学中国 ( 蒙ICP备14002410号-3 蒙BBS备-0002号 )     论坛法律顾问:王兆丰

    GMT+8, 2026-5-26 05:17 , Processed in 0.413431 second(s), 51 queries .

    回顶部