QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2392|回复: 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++数组指针、函数指针、成员函数指针
    3 |: ^$ }' |3 o- w8 K1 \' vC++数组指针、函数指针、成员函数指针
    4 }2 O- ?3 e+ i8 b6 y+ L5 r
    6 p5 q; h! `/ x& K  ~6 W$ k
    * J5 T' w3 B# J操作符名称! F" L/ N5 V- b
    & 取地址符(Address-Of operator): c! c3 Q" C9 X6 [& ^
    * 间接寻址运算符(Indirection operator)
    : \/ K  {+ f* I+ m" @5 E.和-> 成员访问运算符(Member-Access operators),用于取对象的成员。
    5 ?+ B8 f5 [7 b- f7 U$ a/ U+ ~.*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。
    & H( t+ I) N- p: A' I! Y( W% p! U() 函数调用运算符(Function-Call operator)8 e0 |" S0 h) D/ D: Z) Q
    :: 范围解析运算符(Scope-Resolution operator)
    7 x+ d, A0 f: Y如何定义一个指针变量
    , @* [" ~1 h- n. C假设类型T, 变量名称name, 指针的定义如下:4 K  S! x, q  g9 e. a) b# O6 O
    / o0 ^  Y+ ?& Z* O+ X
    T* name;4 X( N# p1 l/ B. K0 b
    标识变量名字name, 它是T类型的指针。例如, l) U# X7 g0 E- G0 N/ P# y2 z
      i' G) r5 Y0 V: ]+ t
    int n = 0;* y( b1 t; Q/ }; e" b  q7 |, w/ z6 n: f
    int* p_n = &n;
    $ p2 b- t8 `5 {4 M6 gp_n是int指针类型, 指向某个int型的对象。' R* J* u: r8 F/ @4 U# O
    - [* D" X" d- D1 O) m( x
    指针变量的修饰
    7 c( U1 ]. N% o/ g9 X; O  W指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。1 {% K2 r( J5 m+ Z) r4 u
    ) R: w& W+ U( B' E3 P* }
    指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:8 @* s5 C9 h5 Z' K. M/ W: W" _

    8 n0 o" f. r0 f( C7 Yconst int a = 1;! O* ~9 v' f1 q" s* K
    int const b = 2;# x& y0 I; }8 Q
    既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:0 X, h- @- z3 P3 n5 }

    & G- w9 Q- t& p  q( `3 D5 v# cint a = 1;
    % Z) ^8 o: e) ?$ j8 |  {int b = 2;
    ; h  }9 S3 t4 [. Aint* const cp_a = &a; // 指针的修饰词,放在*号后面。/ X" d' b) I: Q$ Y3 `/ u
    *cp_a = 10; // 指针指向的值可以修改! `- v0 [9 g3 Y7 i9 T. m' b6 a
    cp_a = &b; // 指针不能被修改,报错!
    : Q& ]8 X& C, I* L) g- t) ]总结带修饰的指针的格式:: F6 J2 k- A) `  h: P) W- C
    只要记住修饰词总是放在被修饰的内容后面。9 a' e7 \4 _  z* R1 K& D! i+ V
    % u* \$ T) q. o5 o- a2 ~; @2 K
    cv表示const / volatile修饰词。指针定义形式如下:' s3 V6 a: k* P8 f

    % m5 j1 [' {8 dT [cv for T] * [cv for pointer] name
    4 l4 {( ?! E0 t  a& N+ e% [注意对T的修饰放在T的前面也是合法的写法。* e3 r& R$ n: s' @4 y, \. T# S
    - @* w2 W& o  ~" `3 S
    const int const c = 2;2 h( i; ~# P- P4 j5 m6 `% q
    在mscv编译器下也不会报错。
    7 h: c% e) T5 \% @. D9 h3 R) d( q  @& F1 t
    完整的格式:/ O+ L7 D/ M7 n( {% K, a: S
    [cv for T] T [cv for T] * [cv for pointer] name  k( Y6 D. \! \* K7 @2 u5 b
      R- E. m% Z7 }$ G! _+ z9 @
    Syntax        meaning7 Q0 u  u: e& |/ T7 X! |
    const T*       
    4 b# ^$ K' l, M6 v0 m1 O8 @pointer to constant object
    8 i. l" n/ A: O$ j- K% t
    % B3 I$ q" F: c! m+ _3 qT const*        pointer to constant object
    " M0 M% N7 t9 kT* const        constant pointer to object' L- A9 D5 c( `
    const T* const        constant pointer to constant object
    3 \- V7 r( B  l4 [; eT const* const        constant pointer to constant object
    # S9 j1 U7 i# K' A: X; R) ]上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。
    % m7 a, t' G7 B5 _0 f2 j( T6 d  @
    int a = 1;( ~- E; X1 D/ t* z
    int b = 2;/ ~) H$ v7 f. q! T+ u' B

    9 ~) Z( s! @5 \: _$ {- ?int* p_a = &a;
    2 t3 G  y+ p& X" t- D*p_a = 10; // 合法% t9 _; k- ~2 _  [; t* u
    p_a = &b; //合法
    , _& }, v, d" m% E0 X& U4 [, v* m" ~% [, M' e
    const int* cp_a = &a; // const修饰int类型, 并非修饰指针
    . Y# P) f. J. X' t3 S*cp_a = 11; //报错! const int类型不能修改
    ) {2 h# s5 G  u: D+ t2 Q6 rcp_a = &b; // 合法, 指针没有const修饰,指针可以修改。: G/ K! \2 P% }
    + N1 L* H% |& G8 C) |* Q; S
    int* const pc_a = &a; // const修饰指针。类型没有const修饰
    / ]/ _. g" L. F0 [*pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。" C7 j% }8 K1 s9 V: E) N) ?9 A+ _
    pc_a = &b; //报错! 指针被const修饰, 不能修改指针。. Q8 d* T% @  L. I( q4 L
    2 q0 r* g. {& `: k8 V
    int const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰
    . m6 d* ?$ B9 x$ x  a) s*cpc_a = 13; // 报错! 类型被const修饰,不能修改。
    . A8 G3 b' n/ }" ncpc_a = &b; // 报错! 指针被const修饰,不能修改。  W8 s2 P3 k8 E; Y4 ]) B( p

    # v) H1 C8 L" S. r( l# _& _ 更复杂的指针的指针  f& y) ?; j# C2 g

    8 g; x7 \4 M% E: T6 [3 Y+ ~' R+ q! Eint a = 1;4 v- h' M+ E% S
    int b = 2;
    . @2 n; M5 i. b6 Y+ `5 L' lint* p1 = &a;
    , ?2 X" e. e0 v+ L/ ^$ ?  Y0 B4 Hint* p2 = &b;( I  ^5 ?5 i# E- w+ ?$ L  D% X* F, j
    const int* ct_p1 = &a; // ct for const type# ^9 u/ ~, Y2 j
    const int* ct_p2 = &b; // ct for const type9 @  r9 f4 i9 r) F; \0 J# S
    * {# r' i- w. L4 j
    // int * * pp1; 指向(int*)类型的指针
    6 ^6 }5 u% M9 v: o/ p! b* gint** pp1 = &p1;  ) P7 y- h% G( E8 C# S' r% i' f2 J
    pp1 = &p2; // 合法, ' }1 G4 m$ M, z  J" ^+ `* @
    pp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)" i% ?# i5 \7 w9 d7 N% X+ Y
    1 M, j: S" J8 U! `' k
    // (const int) * * pp1; 指向((const int) *)类型的指针
      H- {  @0 t/ Z, P& Y- rconst int** ct_pp1 = &ct_p1;  
      ^1 W/ B. g% e7 tct_pp1 = &ct_p2; // 合法4 e8 |" c( }9 C: u% H! H% Y6 H# Z
    ct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。
    0 n. E: V  v! h" ]+ V5 d, T* G" D7 `0 X% L# [
    // (const int) (*const)
    & b( m) [, ]8 b: W" J& Fconst int * const ct_cp1 = &a; // 指针也不能修改8 Y0 _; Q. t% w5 {) M
    const int * const ct_cp2 = &b; // 指针也不能修改
    , S+ V2 E  Q+ x0 I$ H& C+ Mct_cp1 = &b; // 报错!指针有const修饰9 o$ H6 `+ v9 r2 k
    ' U2 P' h, I3 j0 a1 X
    // (const int) (* const) *  指向((const int) (*const))的指针
    # {# C2 x) @$ K; [, L4 hconst int* const * ct_cp_p1 = &ct_p1;  
    5 L) u" F6 A. B4 kct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰+ c: X, G9 p9 N; c
    *ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改6 E! R# p5 {7 `9 D5 a6 u
    $ {( ]+ u" k7 _7 l: C6 I: \8 Y$ M, {
    // (const int) (* const) (*const)  2 B* b1 u& }8 K& V0 }* X
    // 指向((const int) (*const))的指针,且该指针被const修饰
    ' G7 l! y0 ^  l- o) P9 O9 Y  Qconst int* const * const ct_cp_cp1 = &ct_cp1; ) S/ W; ?8 a$ |" C7 f9 U4 L( ?' I$ _
    ct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。
    3 h: N  }. U9 R) A9 ?% R8 y/ q3 o+ p% |" f# e
    一行声明多个变量
    $ h2 w+ |2 z% D. k# `9 M类型 + 名称定义一个变量。$ s! h- e* R5 i* {* J+ z) g" e1 u
    变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。4 Z- X1 f) N9 C. r( p1 m

    - u& X3 m% }' K, T7 Aint a, *b, *c, d, **e;
    9 m1 F+ @, ]% v* ~! y1 B) {a = 0;
    4 z" b) V) n* J& F1 vd = 1;
    8 T0 w5 i& S8 }1 Rb = &a;6 A# w+ N: K; g* c2 [# V0 q/ q4 F
    c = &d;' N4 M) T* `; v3 O$ s$ W% @# s
    e = &b; // e为int**类型 指针的指针
    ( K# Q! Y5 V) i4 H3 a" fe = &c; // e为int**类型 指针的指针
    ) s. c* }# F& ]! w6 e2 S3 u也可以用括号包围变量和*号。' ^- b, ^. A; F

    5 E" o4 Y" z  C6 \- R/ @6 w" \' Dint (a), (*b), (*c), (d), (**e); // 合法定义。/ h: d1 m/ b$ w
    括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如
    : {6 ~. r9 z- k8 t- B6 P- X8 ^
    ; d5 t( U3 ?, o- hint (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;
    , m+ X" f' I* O$ ]" h  E写成; d+ o( [# u0 c  D( P

    % R5 a7 P* I9 L* |int a, const *b, *const c = &a, const d, const* const* const e = &c;" J" n: h- m4 f6 M) ]
    更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。
    ' P2 v  ]; W) l7 l7 ?! V( a+ V8 c. [1 z# w9 @9 F1 H/ a
    数组指针3 F& y8 P7 \0 r- Q) T
    数组基本表达
    7 O1 K) L. z; V! M1 `3 V5 D; Wint a[10];  // 定义了类型是int, 元素个数是10的一个数组。8 F9 b8 ^2 ]. S; `; R  A* {- x
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得, t9 Y0 b* G8 }5 k' R; O4 H8 A
    ! o3 ~" W0 _- t$ f
    int[10] a;
    # S' r9 ]' t  ?( c这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:* B# n7 |* k" t- K/ b
    0 h7 `9 G9 U2 A7 ^2 H* c$ @$ Y; c
    int a = 0, *b = nullptr, c[20], **d = nullptr;
    3 Q2 o  i/ J* Wc++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。) d$ y3 M. r6 V

    * W) `  I+ h; R: c: u% `int a = 0;
    ; p- x$ s9 I6 r! i: u4 i, u4 Qint* b = nullptr; // 指针int*
    & f: Z) c9 P2 }- D$ h# zint c[20];4 i8 f+ {1 Q2 m; \. S) l
    int** d = nullptr; // 指针的指针int**% y4 O, l8 C, {& y7 B. q1 I$ l
    数组的名称是什么类型
    1 z5 w4 A- \8 ~% s) A: Y% |  H- k' n2 p数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。+ w- i0 l. b' ~2 p. ~2 H" z- T9 E

    * `# [7 H2 X: }1 O! s& iint a[10];
    7 q& W9 |& i& e3 ^" j0 zint* p = a; // 指向a数组的第一个元素
    0 K- @. x' c  j$ Q5 ^. ^a[1] = 1;
    0 {  U: O- o$ d: g" h4 J) p# bp[1] = 1; // 效果与a[1] = 1一样。8 h# U* A, p# U3 X) o& I
    数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。
    % y3 F3 y% w8 N
    9 Z- W1 T4 A; d5 ?2 s9 B) z+ cint a[10];
    ; l; e9 A9 {! G1 g5 zint b[10];
    7 l' M6 o* ^# l* `& @int* const p_a = a;
    ' A& l+ ?" F/ m" ^a[0] = 1; // 合法。 数组的元素可以修改。
    / y- @% n$ [8 D4 f) ap_a[0] = 1; // 效果与a[0] = 1一样。
    0 X( [% P+ R4 _$ F6 O- |3 r
      |8 c. @. m" M% B  xa = b; // 报错! 数组本身的指向不能修改。7 }9 U& U8 H; Y/ w$ K

    ! o, O$ V1 {5 F, t# X# M, I// 所以数组a可以当作int* const来使用
    : a% @% }; `+ l1 }1 w1 qint *const& ref1 = a; //正确。$ o' X; v* W0 I$ S' o' F4 e$ b; h) e
    int *& ref2 = a; // 报错!. @: x) f4 [0 K  O& l
    5 {; N+ o. _! p* D7 ^/ D
    // 但是又跟int* const有些区别。9 E; a4 v1 Z% {7 z, P, P
    assert(sizeof(p_a) == 4); // 32bit程序。. I7 A& ?4 `( S9 W- ^
    assert(sizeof(a) == 4*10); // 32bit程序9 Q+ T: O' y1 X5 i4 \0 E
    2 k0 L5 V$ K) U/ ~: S
    数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。
    : q9 t( |* f' y3 Y% i
    & W' G7 o% l: _1 k% ~, ]& q0 e. Y以下3个函数翻译成汇编以后,汇编代码是一样的。6 S9 e8 h/ r9 q9 q$ w/ J# ^; |

    , t2 h4 _+ V. G  l) [void Func1(int* p_ary)9 X& W1 S8 {- }0 r' \4 Z
    {7 ^6 N( A' f% |$ w3 o' }
        assert(sizeof(p_ary) == 4); // 32-bit
    9 L; I  V+ n' F    p_ary[1] = 1;. P3 A; _0 L% B: j+ ~" |0 A! e' l3 ~
    }# X2 j( T- Z: i# Y. v

    ! l! W7 H4 P9 V- |void Func2(int ary[])+ `( _/ p" i, d6 U9 u' [; w
    {
    ' }% o' \( D* |# V9 c( n! p: e    assert(sizeof(ary) == 4); // 32-bit
    + L) L$ Z' c% [) @4 e6 ^# a) _# F    ary[1] = 1;
    4 _! L+ h$ K) H4 l; M9 V8 z}2 z- S& W+ b' A9 W! Z6 b' w! o
    # G5 C) Q- J2 V: w# }* P, m
    void Func3(int ary[10])
    $ ~- ^8 j% Q! E9 G3 ]{
    ( O- {& @* r" U% w0 B    assert(sizeof(ary) == 4); // 32-bit  L) j/ N( ?, u5 ~" {
        ary[1] = 1;& {1 S" q! _2 g& Q1 L9 Z% h
    }# E8 F6 `' v! v

    1 F, v9 c1 B: `1 Nint main(int argc, char** argv), }+ c7 O- p9 V  s( F3 H: ^
    {& |* z7 g& i+ N
        int a[10];& D- N; q+ S0 d/ E
        int b[20];
    & ]6 G& t0 }! C6 b- F    Func1(a);5 ?* y# Y" y! L+ S/ L( W
        Func2(a);5 @# z: C% m  z9 G
        Func3(a);
    1 i: t# N- K$ Q/ J$ _: Z  f    Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。
    ) R) s' ~/ ]/ {0 w% t    return 0;# D1 I8 S/ a- U0 d8 z  `4 R" ~4 f8 D! I
    }+ P( k! X. N/ i8 |

    1 D, m$ j$ ]1 g6 ?) N$ U( O: f# b& y$ x/ Y& e
    ! z0 g- }, k* w3 R
    多维数组
    , k4 J8 x+ v# d- Y- Y8 }" Y) k一个3行,4列的数组, 结构如下:
    2 Z( o4 i$ e6 a9 ^, V8 @7 F* l! D* Q/ v4 |! B8 W: j3 B7 b
    int a[3][4];, N5 y6 |  b+ j& @
    column 0        column 1        column 2        column 3( o9 b# U$ f- D; `/ H8 `. I5 K6 e
    row 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]
    " p4 [0 |5 g& grow 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]! d3 E3 C+ H* {- {/ v
    row 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]. g" [' e3 Z8 r: s$ B% }! ~* k
    数组初始化7 {+ r( h# t7 x/ T! _3 j" z
    + [2 v, L- m; ?8 B3 v; i- A
    int a[3][4] = {; A5 E$ Z; p% K
        {0, 1, 2, 3},1 q+ n. b, w7 i* G; w2 `. t8 n
        {4, 5, 6, 7},# V6 X3 @- M: ^
        {8, 9, 10, 11}
    $ ]* t  L1 o2 d; M# E! u};
    6 a2 U4 X/ y1 A4 }& h( K 实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。
    - |( o: G) E! H' L" l  q
    % @. X; V" h: O. o7 E$ v( n8 mvoid Print(int* p_ary);7 D4 p0 h$ v4 T% G, W1 T% D

    5 F! J' s; i3 }* `7 b( G  b  xvoid Test1()
    $ C1 |0 R0 _4 K- S7 v! Q8 k+ `% \{
    + ?/ q. w! l& n6 R8 A6 K" R4 e: W  a9 b    int a[10];8 C0 M9 X" M6 H+ h. |7 N3 g
        a[3] = 3;
    # u5 j0 ~1 t; p& O* B    a[7] = 7;
    3 f9 {7 a+ x% H% s. H& k  G    Print(&a[0]);- |4 n. }4 q! @& _; J+ ~
    }
    ) Z; o& G7 z8 C5 I$ x, W
    0 `0 R$ G1 U6 Xvoid Test2()
    1 i4 z. z4 g" x: x* s/ ]7 R{7 D: {0 G9 X0 K, t9 K0 Y1 l) i
        int a[2][5];3 |1 D# k8 V- Y
        a[0][3] = 3;
    $ _% O# w) }  Z/ o' b% j' {1 C    a[1][2] = 7;! c8 }# M* Z" E1 D+ u
        Print(&a[0][0]);
    , C0 h; L- k: X1 y8 J}/ ^0 ~/ M4 ?& B% K  r& H/ g- k( F

    9 o( ]8 C" ]0 j/ u! i% ?
    - }2 D* A( N2 ]/ j. L& _9 z5 S
    ' M% h: B; d, z8 l+ {2 s$ |很自然地,多维数组也支持用1维数组的方式去初始化。
    ! k. k+ d) V3 |: _
    - m; W2 E! X0 I8 |$ T3 Qint a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    . ?! O. J2 u% @- y  ?6 \$ m; ~( f: r, d既然多维数组与1维数组没什么区别, 为什么还需要多维数组?3 g8 _1 F+ D9 [/ h0 }0 q! {

    7 F; ^! G# ?6 V& X假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:
    # x$ P- ]& E+ x2 x' n/ |: Y! a5 |9 Y9 H) ~
    unsigned char* p_rgb_image;
    5 v0 m: [( q, s& j3 {unsigned char r = p_rgb_image[40*720*3 + 50 + 0];
    ) ^" A/ X, M2 Y8 l  j! b( |3 Cunsigned char g = p_rgb_image[40*720*3 + 50 + 1];
    5 U7 Z' W) Y7 N& @* S, V9 y4 q: n( [unsigned char b = p_rgb_image[40*720*3 + 50 + 2];  p/ @+ o( T7 y  s  o
    类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。
    3 t( j3 ?/ {, s9 }5 K# }+ K! g# W, A' A8 I' ?. D! [
    enum5 e2 o; l6 o) p& _  B
    {
    ; {6 z! q+ g9 K# Q Red = 0,9 s  J/ T* [- a- J  Q: \# ~
    Green = 1,
    # v2 s+ e/ d1 F% Y, } Blue = 2% u4 |* x  n# ]$ l9 A# P  p8 s9 K
    };5 U$ w+ ]. h+ X) Z4 L. h$ t
    unsigned char rgb_image[576][720][3];
      j# C- i* e9 z/ v4 J5 [6 F7 e2 f7 xint row = 40;
      m3 p; e& t& J* g% \* wint col = 50;( F9 @! u  j7 V, V
    unsigned char r = rgb_image[row][col][Red];
    + e$ ^. J0 R3 [2 Vunsigned char g = rgb_image[row][col][Green];+ I; ]+ W: E5 v0 y5 g' b# [- ^
    unsigned char b = rgb_image[row][col][Blue];
    ' ~6 L3 ?0 D' j+ M数组指针以及与指针数组的区别
    $ q4 \" L5 d! T! S+ H  c: }9 a8 d2 `. u数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。
    % I* Z# _# C' Q1 e' m, d  ~指针数组,是一个数组, 数组保存的元素的类型是指针。
    4 ]/ `( u; Q% z5 S数组指针的定义& l3 g+ s; Y" x2 T
    数组指针定义先定义一个数组。
    3 c% g7 w( t5 H; Z5 z6 U5 n. G; Mint a[10];! N/ O- R! i2 ]6 I
    然后对数组里的名称用括号括起来后再在变量名称前面加个*号& h4 N  g1 J. v) a
    int (*a)[10];1 S4 M, q( D$ x0 r" e( O
    后面你会发现函数指针定义类似。
    0 s# _2 [1 Z' \% k+ C// 各类定义对比
    9 {7 x' [0 |) b9 A: ^$ c: mint a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];- z& L0 o% V' d; D

    * F% J- B0 W: Z; Q  Dint *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int** A2 X2 ^6 y  D* P
    int *(f2[10]); // 指针数组。另外一种定义方式。
    & ?/ K2 d2 k; B: vint(*f3[10]); // 指针数组。另外一种定义方式。
    $ {4 }) v  P5 l7 q% Xint(f4)[10]; // int数组
    6 d0 _; S' ~, h# bint(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组0 w( F, c5 p" d) v. y
    int* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组7 r6 l, S- r; Y7 A. l" `9 `
    * \0 ~: O, d1 j5 X4 X: q$ r
    int d[10];, T! z8 o* d5 [5 r5 `( x2 R7 S
    g = &d;
    , Y5 r6 l- u0 H% {; n8 o8 a( P/ o: ~2 @9 P
    int* e[10];6 }8 }& c' }+ |0 y
    h = &e;
    0 z5 O8 w: \9 d7 v8 [) v! C数组指针的使用2 z6 i# p7 |; `' e; \
    数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。9 _: ~3 t2 u- W' |

    / q! C" o! w! ]3 tint a[10];
    , x% R. Q" I9 R% ~8 Q' H  g% oint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组* s2 ?/ S& s' f
    for (int i = 0; i < 10; i++) {
    2 i* B4 n9 d5 k1 _% q6 U // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象" F( X( I& J( l# i' h( E" ]
    // 再通过下标操作访问元素。
    & ~1 y! |7 \9 Z$ |% `  u' N (*p_ary) = i;
    ! U! x. m) {) J7 Z6 T4 Z! P}
    4 S$ A; s# f' H8 s8 o1 O3 W3 }6 d+ O( {. J$ Z1 Y' b# k. r
    int b[10];! x  x2 w2 B; J( h: r# X
    int c[20];
    & M0 y# ^. d1 f8 b& T# r! ep_ary = &b; // 合法0 |5 F/ _8 i5 C
    p_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体4 q& T" x/ k: D
    数组指针指向多维数组的子数组
    2 X8 r$ Y5 I5 ^% Lint a[10];, V+ Z* d$ E$ D
    int b[4][10];9 R* i9 h$ ^9 d9 g4 y
    int(*p_ary)[10] = &a;5 C: Z$ ?$ n/ M; @
    for (int i = 0; i < 10; i++) {+ M) v* g$ B3 N; Y
    (*p_ary) = 1;
    % @1 n; m& F& Z4 G) ~}
    - e% Z- ?) W* v( j1 H, k: b$ ?( J, f+ J; V9 F  v
    p_ary = &b[2]; // 多维数组,可以看作数组的数组,) ~* \, L, a9 Y. k
    // b[2][0] ~ b[2][9]的值都被改成2了6 r4 H6 ?( O8 C' r  y: B
    for (int i = 0; i < 10; i++) {! \1 U$ b; @) T6 L# W( Z
    (*p_ary) = 2;
    & Q  A) W4 Z: N4 j0 O# U}9 I* v: @8 I7 u$ X) z3 V
    多维数组指针
    * Q$ z1 ^$ Y6 v5 A3 Z0 }多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。7 s0 b, p3 \4 e8 z$ F
    : V$ a1 P# u3 _
    int a[2][5][10];, [/ q7 l/ Q+ q# R
    int(*p_ary1)[10] = &a[1][2]; // 1维数组指针
    & ]7 E! ?( V" i, h, }) S1 |* N" pint(*p_ary2)[5][10] = &a[1]; // 2维数组指针; Z$ E, @' K8 v1 ?% Z
    for (int row = 0; row < 5; row++) {7 L. D0 u; e/ m  n7 V' s
        for (int col = 0; col < 10; col++) {
    * `* Y4 ~! b" u8 ]% ]7 Q" T, m        (*p_ary2)[row][col] = row * col;
    / ~9 p9 h4 I$ d+ S- w* m    }
    . O* G# k" ^$ \! `5 A2 ~}, x  a- g, k+ H; V( m8 i+ s- h, `/ _
    数组指针和指针数组对比实例2 p9 S/ r7 _2 x6 ]3 W( g5 Q
    数组指针还是记住两步法即可: ~: p: i. _' w  K% \* z

    : I# T1 d- A' h2 l: H( a. N0 Y5 W6 v定义一个数组
    # L( O+ d: ^/ j, e" w# x) O括号包围1中定义的名称,再在名称前加个*号。; U# d, |( m; Y# _
    int a[10];
    7 F3 {* H5 T3 h, v3 N6 N; [# ^int(*ary_pointer1)[10] = &a; // 数组指针4 |: E8 I  H& t3 G
    int* pointer_ary1[10]; // 指针数组。元素类型是int*3 K1 q. e5 l. h$ ]) t  z' w1 d+ B
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。
    ' M6 b1 y5 b' `, X/ E% q. nint (*ponter_ary3[10]); // 指针数组。另外一种定义方式。4 J- e1 r5 p6 d7 j& X3 J' Z0 w7 S
    int c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。5 a( G4 b! f8 j0 O8 g; i
    ! x. [! O. |7 B2 h' Y/ z
    // 指针数组可以把每个元素指向数组对应位置的地址。; R4 ]8 e0 ]2 s+ s5 W! E; {
    // 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,
    ) h, Y$ @  }5 X2 ~! r' n// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。
    . u/ Z% c6 A2 S- S# {4 sint* pointer_ary[10];
    : [! V% }  O9 y5 ]5 I$ Efor (int i = 0; i < 10; i++) {: ^/ C0 z* H+ {. _& _* _' [
        pointer_ary = &a;
    , e% t, S: a0 \' o2 W* `}
    ( }; q2 f7 M7 ~5 T8 k& n3 e// 类似遍历原数组效果。. U4 t8 a  k0 P8 z' F- D. O( f
    for (int i = 0; i < 10; i++) {
    2 S- x8 ]6 ?1 p- s2 w& h# {4 K    *pointer_ary = i; // 修改原数组。
    : n0 j) z/ z9 N& v1 d, f# }}' u- m3 R8 u3 J  C5 Y0 Z- @

    ( Z, S, D, E) M7 |+ i: c# j# T1 _函数指针
    ; o- v) N- H0 M" Q. N( {9 @取得函数地址
      E, W2 |9 r& \/ d函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。+ E/ _# @0 [! @+ z* [6 d5 q2 Q
    3 C- }' C; s3 n* W  ~
    void f(int);, A/ H4 W; L- p; c" A
    int main()
    - C) z+ z" r8 ?' m- S3 _{- ?/ E9 |$ a  _! a
        void (*p1)(int) = &f;
    0 s9 t1 g, s3 W0 [9 x  o3 f    void (*p2)(int) = f; // same as &f
    % i& c: d  j% n# F7 M    return 0;% n/ l0 R; t! [" U7 L
    }
    " F4 b0 a. U' G  u/ J; Q. ^翻译成汇编代码, p1和p2的赋值是一样的。" y5 k1 a! L5 t' z, ]7 H

    ) L7 T# d+ r1 {" |* }" x; [
    2 I! T4 m- G) ^; N6 v3 c6 S* R+ ^1 \4 ?* Q% N+ z% K
    函数指针的声明0 o4 d& |% ]5 i) ?9 p9 l
    单个函数指针变量定义步骤
    6 E3 p6 Z1 k5 f) r定义一个函数。void fun1(int a, int b); int fun2(double a);3 h6 n" U6 A+ B5 y: `
    用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);
    " ?" K  @  y! J9 e如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);9 S0 i' U1 m. V! G1 ^
    typedef定义函数指针6 A5 k# x( u  D! g" v7 p
    可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。) R3 I7 m* ]; f' _2 n+ W
    , \  T) N, S' h$ i3 [
    typedef定义函数指针的语法
    1 L8 t, K' m& dtypedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。
    " I/ \# S: O, A9 ?; o1 n, a6 I) A# Q
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象
    0 j% d' E! u- d! q. v, Itypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针$ j/ {; T: z4 i- s
    FuncObject* f1 = &Add;
    3 g; e$ |! l8 b! Y' S7 U6 T* H; TFuncPointer f2;& `9 p- W* C& T+ G8 t
    f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。
    & i* F: {2 M+ dFuncObject f3 = Add; // 报错! 函数对象不支持拷贝
    6 G; c+ W8 V2 @" B" V+ L' O- FFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配
    - C5 m4 m0 b7 iFuncObject& f5 = Add; // 正确4 u2 v; B$ K7 y1 P3 P
    int ret = f5(2, 3); // 正确* R4 q4 f& b  P; p
    FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配
    + L! v& ?% ]) L: ^如何记住typedef定义函数指针的步骤
    * r  t- X8 B* F0 P# }像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);
    5 D3 B6 Y: V6 c' Z2 w1 g/ I在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);
    ' T$ a" Z. ?" ^2 ?2 j$ L完整例子
    2 O8 S9 A9 f/ p6 @9 dtypedef int(*CalFun)(int a, int b);
    / v2 g+ e& K1 \. M$ C
    1 P; L  L9 q8 M0 |- Kint Add(int a, int b)
    4 D) ^2 t+ w2 ~6 E- f{
    * Q6 w/ O4 f9 ?5 a3 a0 Y7 M' c    return (a + b);
    . f, \4 s4 X4 c, w6 _4 h! L}9 e- |8 E! x# _% u. G7 ^$ N& u8 m7 @
    8 x5 b/ N; X* n. S* i4 L. K
    int Sub(int a, int b)
    6 D: y1 T5 ~& _# E" Z3 F' f8 q{6 B; c1 X3 }/ h" W8 e
        return (a - b);+ `# R, ^; i- k: o- S
    }
    % \! m/ m5 A! E8 f3 S
    4 J4 C0 z! ]* j6 g' U2 V( E+ wint main(int argc, char** argv)
    8 T3 W; E8 @1 i* |{+ i% D7 W  L' Y" s' o9 X
        CalFun f1 = Add;
    7 V0 L- {) ~' y* n( y    CalFun f2 = Sub;
    2 A3 `7 c" k8 M! ~; j, V8 ~6 Z    int a = f1(2, 3);
    ( Z! T; n8 s+ }: r2 r+ M    int b = f2(10, 5);" Z7 [9 C1 S# C9 z: z6 L0 N: g

    " J: q# `' p: ?1 A    // typedef定义的函数指针数组。; W( e" ^7 z; d" I
        CalFun f_ary[2];( k$ o2 ~: l- M5 w
        f_ary[0] = Add;% ?# V0 X4 A/ r" X0 |5 t# n
        f_ary[1] = Sub;
    & G" q% V4 P* l& {5 |: W( `% g7 g$ w# o1 u/ @' B5 @
        // 单个定义的函数指针数组。8 n- b: N" E; u
        int(*f_ary2[2])(int a, int b);
    # ?; A4 Q+ @2 Z    f_ary2[0] = Add;: G, G+ ?* t( t
        f_ary2[1] = Sub;
    - u9 z1 N6 I  ?4 W
    4 k5 z6 Q: Q: q, Q: u' s. h7 \9 \    return 0;
    - B3 i6 j4 V' [3 W- T' w}  w- h* r' Z( Z! Z

    $ w( T, \$ J+ i* yusing别名定义函数指针
    3 V* [- J0 e3 v7 zc++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。1 @2 s" P1 t2 s; f3 Q! b2 v. [4 O
    7 ]1 p- k' ~: v* ^7 e3 m+ V2 [5 G
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象1 c6 X/ K6 y1 H7 m! V- G
    using FuncObject = int(int a, int b);9 m! N# _& a; _' y
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    7 x3 `, n* J4 Qusing FuncPointer = int(*)(int a, int b);' k0 m9 V- E! O7 W5 o3 f
    函数指针的调用( Y" @$ }7 ~: i0 q) a
    函数指针和函数对象都可以直接后加括号调用
    6 B7 v0 u. z) V7 [int f();& N; H6 m" ?5 j+ w/ ]
    int (*p)() = f;  // pointer p is pointing to f
    , [3 O* T8 C) ?9 Vint (&r)() = *p; // the lvalue that identifies f is bound to a reference2 ~8 P+ Z; o( ^) L. _1 t# U
    r();             // function f invoked through lvalue reference
      k/ C# a. a$ R, \# R! B5 a0 f(*p)();          // function f invoked through the function lvalue$ P$ B, M0 m" ?9 f. d) `7 ]
    p();             // function f invoked directly through the pointer) ], ~1 J+ ]1 s# Y- x0 |1 j
    如果函数有重载, 函数指针会指向匹配的那个版本。5 ~, ]' K! v. O* r( L
    template<typename T>
    7 e! S! F2 f9 d  C1 @; PT f(T n) { return n; }
    $ P5 v6 V9 B3 s4 {8 w3 S+ Q+ S, o  {5 \  }4 V2 }. V: _( s
    double f(double n) { return n; }
    4 X1 Q. q; u% m
    ! X: l: J  v8 b( Uint main()
    , Y, x% L" t" I3 w& S{- _8 _1 [& \  o; f# p
        int (*p)(int) = f; // instantiates and selects f<int>
    2 l$ t  x7 ^% F" }- E) w}
    ' o( U4 e$ F+ x' g6 j) {. a成员函数指针
    + L0 c8 b6 f# H静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。7 }5 R" z# h& [5 X% g' f

    4 X9 C4 R) r, q2 ?成员函数指针定义。$ k6 f3 R6 e9 D$ Y( i2 v
    像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);3 d, v, G* Y$ r2 N
    括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);
    0 P9 |+ Z5 M! R. T. a在名称的前面加个*号void (ClassName::*func)(int);/ b+ i3 ?; b" r
    成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);
    2 G* l, c3 r) ~+ Q7 X# I成员函数指针如何调用。
    8 \) W7 {2 Z! u% @假设成员函数指针名字为func/ X* P3 k2 a) P+ ?8 W" y% |1 g

    ( D7 k4 G4 K: p  ]% u) t  Tvoid (ClassName::*func)(int);
    6 w5 m8 W4 u0 v. e: u/ n' t对象式调用。( E" Q5 W$ J2 n+ Q- G7 n
    ClassName c; // 被调用的对象
    : \% g1 ]( Y: G. d成员函数指针名字当作正常函数那样写。
      o% E8 H/ _. l8 O" W2 C4 h0 @' qc.func(3);/ i' t8 ^4 ^% Z! d8 y" Q
    成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。  q, K( w' v, G0 O* q7 L6 N
    c.*func(3);, c$ F  n2 n* k1 R
    最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
    ; P' K9 t# P" Q* ?' x* }5 w(c.*func)(3);
    $ Z3 y9 K# q) K7 T1 _! Z3 @为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外! r( }  z. {' ~
    (c.(*func))(3);
    / m8 I. y/ Z$ `这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。0 F- j9 t2 D) v. g+ p( \$ L! Y4 \# g
    指针式调用) M6 l$ x) h; b: h+ D0 f
    ClassName* p; // 被调用的对象的指针. p* _. b( `& ?, M
    成员函数指针名字当作正常函数那样写。* r- C5 C, F1 `' k$ a  j1 Q; K" Q
    p->func(3);
    6 m- X) E! M* C1 }/ j6 ~2 W, X) O) x成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    2 H4 F! y) O; _p->*func(3);. g+ D8 `: [$ o( S# l: T
    最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。
      R1 {, }- f5 @& `(p->*func)(3);
    : h+ P# a  i7 X, T# v; `# _函数指针使用完整例子
    ( J" ^+ r; O4 D. n* u1 lstruct Cal
    , ~0 t' F8 }# g; H; G3 ]. f{
    + G! {& U7 Y4 s+ [7 z    int add(int a, int b);
    7 s* b4 `7 W8 @5 r. q" b, Z! ^8 z1 ?    int sub(int a, int b);0 p9 K) u. h/ O& R5 V3 l
    };* p# t  ?/ F8 q

    & T5 r6 K5 n" [/ Y0 J0 J" t: W) Q- |int main()
    8 V5 |3 s+ l2 x/ ^' x; @{
    - `' Z. H/ }% |+ r) i! w- p  o    int (Cal::*fun)(int, int) = &Cal::add;
    ' |  r0 i- K8 t# R# o8 O    fun = &Cal::sub;6 `! G8 x" g# \' z9 \

    9 U! ?& U! z; m* e7 j0 l4 Q    Cal* p_cal = new Cal();
    8 C- l9 R  E0 Q1 G    int r1 = (p_cal->*fun)(2, 3);: n- q$ B# J4 {) ^7 T9 M
        delete p_cal;1 R2 a1 j, s" K9 c7 E) P4 H2 p

    4 j! ?, R8 |9 V% ~4 }3 o    Cal local_cal;
    $ B7 Q7 Y) X5 X# j4 V    int r2 = (local_cal.*fun)(8, 6);
    5 o5 r) x  y0 Z6 Y4 `7 J}# K  g1 ^1 G! f7 N* s7 R- |& b

    1 ]1 f, @7 [* B. [8 I6 j2 R成员变量指针- i6 z: C+ V/ K" z% C+ W
    成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。
    2 D9 ?0 ]& ?5 _6 A- f. T: J3 C5 i; s( Z  K
    成员变量指针的定义
    ( `+ V/ u  S6 ]假如以下结构体C。/ T9 a* ?& X- W& H# D* L

    : O1 W7 k2 B' V3 K0 v; O# _$ Sstruct C
    ) U8 c1 h/ ~% I0 r{
    , F2 X# g' S9 ?& H    int m; ; ^5 l; o: Y. V* W6 `: Z
    };& S/ }, U/ e% y. |/ F
    单个成员变量指针定义 7 [4 p  A" E9 a3 b- Q, P; w
    假设名称为p, 类似静态成员变量定义那样声明1 }5 C" p& T$ m. Y7 ]( l# Q4 q% i
    int C::p;
    : H4 G; }* X) D% G在名称前面加上指针标识号* 7 j0 u, f$ ?2 s( g( \* S6 l
    int C::*p;
    + a! f; D1 e7 w) O" btypedef或using方式定义, Y6 h; b+ F8 T
    typedef int C::*MemberPointer;
    3 Q/ p9 Z2 G: ?. f' Musing MemberPointer = int C::*;
    4 U' q# V1 C. g  o  t9 \成员变量指针的使用。! m2 I% S; m0 Q
    类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。
    1 [6 c% V5 N% ~7 V成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。8 W# T% z& p/ L; Y% g

    + l% o. W% }3 e! F* u 完整例子  F4 k! _% a0 r* q& q' W/ F
    struct C { int m; };+ J# j8 e# V4 Y
    int main()
      u$ a* d) Q" o1 U{8 v/ R1 m4 i8 d" R9 ?% d8 l
        int C::* p = &C::m;          // pointer to data member m of class C
    / w% r  H3 Y( p7 U  Y8 }8 q    C c = {7};" g- r( h. z9 P; Y
        std::cout << c.*p << '\n';   // prints 74 a) U! O4 Q! N8 J1 h
        C* cp = &c;' f% ]' ^4 ]$ g! K9 L
        cp->m = 10;
    8 p- M0 x/ p; V9 D    std::cout << cp->*p << '\n'; // prints 10
    / y; X$ J; U" ~8 J3 Q, M" i1 P
    % i/ L2 ~) S5 ]8 _% X————————————————: `- g: v  f9 q2 I( ~, g9 R
    版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    ) {0 T- _6 Z, ]0 O) Q+ a  ?原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115  H( H3 _% Y* V; ~

    1 e( |8 m! c4 ?# T+ F0 q9 z2 V/ L% x1 X/ \. j% J% f/ t/ S/ z0 ~
    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-4-12 13:51 , Processed in 0.416843 second(s), 52 queries .

    回顶部