QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2466|回复: 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++数组指针、函数指针、成员函数指针8 e' b) f. z1 S. p# ^- M) L4 `
    C++数组指针、函数指针、成员函数指针: I/ V8 i  d& k, Y" d' P
    ) H( h3 j0 o0 y( f9 k
    ( M+ J+ S4 q' C0 {5 h9 Y& k; p
    操作符名称
    + u, ?+ _" K1 _4 l# W+ v1 e. o- n& 取地址符(Address-Of operator)3 v: R4 _5 R: ]4 y
    * 间接寻址运算符(Indirection operator)
    ' s& Y) F* W/ A; m$ d7 T: k.和-> 成员访问运算符(Member-Access operators),用于取对象的成员。
    $ @, v, |2 _9 x.*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。
    ( e+ j3 @% u6 f() 函数调用运算符(Function-Call operator)
    3 }8 ?1 H7 A3 _6 G; Y. ^8 x:: 范围解析运算符(Scope-Resolution operator)7 R+ S, u; P% p/ z( D2 j
    如何定义一个指针变量
    ( C9 u- g# E/ }. |# z3 @假设类型T, 变量名称name, 指针的定义如下:
    1 a6 S2 N* b1 k1 r! e5 M- k+ U' P* w$ b6 F3 V
    T* name;
    % [" S/ L1 J8 X& Q标识变量名字name, 它是T类型的指针。例如; [) X- ~/ T) a7 B" [8 [" `

    8 K: C8 @9 {5 ]5 }  Pint n = 0;  a. C6 z- J0 E# @
    int* p_n = &n;: x$ g. H) w: X; U( y% F
    p_n是int指针类型, 指向某个int型的对象。% _1 `5 v6 l# z# \
    ! i0 s/ r3 l6 S, E# L9 J! p
    指针变量的修饰
    3 Q( D# Z9 H  ~* P! b4 N指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。
    7 O# J6 A5 t+ C# f  n* z: @5 \, E3 q$ H6 g" H
    指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:  e3 X! [7 z0 N6 _
    ; t; z& K- d* j7 Y' y5 s
    const int a = 1;6 N( |9 C* F9 l7 V; n( p
    int const b = 2;
    ' C9 V6 ~, j) b7 J既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:
    7 i+ c* Y* i, B2 S0 @+ [% Y7 H: o, {) m; E6 m, j
    int a = 1;9 w) ^6 k" z8 t" t4 Z
    int b = 2;
    + X" z; A" P) y3 ^% k5 bint* const cp_a = &a; // 指针的修饰词,放在*号后面。
    0 E3 p+ J/ }0 {6 N*cp_a = 10; // 指针指向的值可以修改
    % ^: D: w& e7 A9 q8 n3 Wcp_a = &b; // 指针不能被修改,报错!
    1 _. p- q7 Z7 b  z总结带修饰的指针的格式:
    3 K* q% |7 Y! r! Y: `只要记住修饰词总是放在被修饰的内容后面。6 K  s0 T) l, m* N% |7 r6 L

    6 ^! N% L; `" z+ h& s7 Zcv表示const / volatile修饰词。指针定义形式如下:
      `' c8 U$ C/ a! G7 ~- L) Z* \5 X7 Q6 R8 N3 v7 y
    T [cv for T] * [cv for pointer] name
    & @$ b6 s6 N- V' m5 ]/ Z2 z! f' A注意对T的修饰放在T的前面也是合法的写法。2 ~6 G, K. W" F/ g

    2 T) ^0 `( B( Hconst int const c = 2;+ C9 U1 [: h0 p$ Z3 @7 {
    在mscv编译器下也不会报错。  G* T) X: i  p- I! s0 g% d, q! B

    : _" F3 S. ?" F6 X$ c) H; i完整的格式:
    9 q) {$ V7 O; I- y; B9 g% u8 m+ h[cv for T] T [cv for T] * [cv for pointer] name$ r6 F7 m$ t! M) f+ V

    # Q. U6 V6 h; p$ kSyntax        meaning
    & M* J2 O! K; m. `) L* M( c. O2 zconst T*       
    $ e7 F6 w; h; w3 w, v# e. mpointer to constant object
    $ o7 @) f9 u: H3 V- ~' ?: ?  N& B8 J% E
    T const*        pointer to constant object
    ! S, S- G( o- U' sT* const        constant pointer to object
    5 t5 O2 E' h+ o2 k: O" ?* m) Dconst T* const        constant pointer to constant object
    ( q: w/ ^( K0 \! G. ]; a# [/ QT const* const        constant pointer to constant object. V) i) P. i2 n" ?
    上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。
    ! L7 A; B! U" X5 m% g% x
    1 W5 @3 |; Z* C* k" N( Y% Rint a = 1;4 r* ^7 p3 g% b; f) i
    int b = 2;
    9 L( ]- E7 L- g! n  T; [+ C+ d/ A' {7 I- n! J
    int* p_a = &a;
    6 Y1 y; C0 P  [! v# S8 h5 h! |*p_a = 10; // 合法
    : q! t: _3 o$ i/ r8 J$ V. sp_a = &b; //合法( p6 W* ~4 P) o* n) `& ?0 |
    % n9 _1 x; q0 O4 R9 }$ c
    const int* cp_a = &a; // const修饰int类型, 并非修饰指针
    ) U# b- @, C2 x- m*cp_a = 11; //报错! const int类型不能修改6 e' z7 h1 S. `8 J9 o
    cp_a = &b; // 合法, 指针没有const修饰,指针可以修改。/ r' r3 ^( B- K
    " n  _5 s' X; U& h
    int* const pc_a = &a; // const修饰指针。类型没有const修饰- i3 `. r; @0 `) ?  R+ O. b
    *pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。
    ) f5 D8 D) k8 L" j+ {pc_a = &b; //报错! 指针被const修饰, 不能修改指针。
    4 a1 B' x( |- {+ v' T
    2 Q/ j7 l( a) c5 i! x( w: M- |int const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰
    4 s2 e5 @" D9 Q8 ]*cpc_a = 13; // 报错! 类型被const修饰,不能修改。
    9 Y. A- g" S( p& |5 Kcpc_a = &b; // 报错! 指针被const修饰,不能修改。
    6 Z( |9 _% i, G4 V  U1 B* z; ~1 U* r9 d1 I; G  V
    更复杂的指针的指针
    : K$ t4 Q; n: z  S$ o5 m/ K# S7 j* u7 P4 v  `. z
    int a = 1;+ g! ?7 H$ R; ~9 S- Q3 D0 G
    int b = 2;
    ! [; `/ V3 ?; T4 F  ^+ z- v* pint* p1 = &a;; a% D% {; k* @! b0 j
    int* p2 = &b;" Y" o+ h3 ^5 l" B0 |* g/ s
    const int* ct_p1 = &a; // ct for const type5 W8 }/ [5 w1 v6 t/ |! g4 d+ R
    const int* ct_p2 = &b; // ct for const type5 b$ l' M% O2 h' o  ^/ F0 O
    9 H; E' {- y) a0 v* \
    // int * * pp1; 指向(int*)类型的指针
    ) G: Y3 K0 s, u4 m; |int** pp1 = &p1;  5 d) J  }( A! g# R1 W; H" J! Q+ P
    pp1 = &p2; // 合法, ' y  a: b4 i3 S2 u' y. T
    pp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)
    . \# B8 l: E: {/ H3 a: n
    1 `# F6 a6 Z3 d& _1 S& a7 _// (const int) * * pp1; 指向((const int) *)类型的指针
    ; |1 B1 N' b$ t  @; S& n0 p3 g+ Gconst int** ct_pp1 = &ct_p1;  
    ; L' O/ [  k  c5 l' r  Jct_pp1 = &ct_p2; // 合法
    ! ^' w  t$ ^2 q) V! Wct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。, y) |" ^' h& |8 i; a2 D/ s) W
    ! M( i7 J* j8 j' X& G; X9 h
    // (const int) (*const)
    ( T" B" l) R1 q% J- h, \const int * const ct_cp1 = &a; // 指针也不能修改1 ~. P* L* @" M0 k  {. |
    const int * const ct_cp2 = &b; // 指针也不能修改
    $ ^: L8 {2 T: [" ~, Xct_cp1 = &b; // 报错!指针有const修饰& L# ~, _% i! w3 O% w
    6 z3 x! I& j4 o" E& R
    // (const int) (* const) *  指向((const int) (*const))的指针
    & a4 y. m, h' f* z, Z4 Q2 t0 E, J* Nconst int* const * ct_cp_p1 = &ct_p1;  " Z& k8 K/ X# Q* F: @1 k0 }/ L
    ct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰, Q% c0 P& m. |. d6 t) p- |
    *ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改
    1 L6 X. Y' p2 `) O" ]) [2 ]( H' i/ h( C* J
    // (const int) (* const) (*const)  
    7 x- m) G9 j8 w9 g3 |6 c* q// 指向((const int) (*const))的指针,且该指针被const修饰$ q; X: B( _8 }( c
    const int* const * const ct_cp_cp1 = &ct_cp1;
    + i' j7 m% k/ ]8 C- e' W" p1 mct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。$ b+ t' J* ^/ D+ l0 B. B1 ~9 h
    1 [! b. D8 K6 C$ r8 C
    一行声明多个变量
    7 T+ u' |: K# c* ]类型 + 名称定义一个变量。. }0 t1 @- x9 B! h+ f2 _/ m
    变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。; t1 ^" g* G5 w' w0 x

    $ X( j, y) [& e8 Gint a, *b, *c, d, **e;% l6 b2 X; }0 U/ }6 b
    a = 0;
    6 I+ Q/ M% b3 h0 td = 1;4 A% p0 v# u4 D, [7 o8 |& D
    b = &a;
    % Y3 f% h- R+ D9 I" N1 u3 ?5 k# b$ sc = &d;
    7 X& s; E0 ]* [4 \& }6 He = &b; // e为int**类型 指针的指针
    . L0 k1 R) z8 N, Ie = &c; // e为int**类型 指针的指针* ]7 g5 U8 @0 N. q
    也可以用括号包围变量和*号。
    5 c) A$ }& J0 m8 n( B
    ; P& b3 _& t( }7 Vint (a), (*b), (*c), (d), (**e); // 合法定义。, _" L$ ?7 y4 b7 e6 z. b! e
    括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如& \# E: A. `/ l( @
    5 @1 f* \' M6 ?2 G# {# t8 S
    int (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;  R9 e. v* H& u+ E" V' b( o, u
    写成9 o5 O5 l& u: Y2 B6 w
    : N, E3 t8 P0 `& o3 `  G
    int a, const *b, *const c = &a, const d, const* const* const e = &c;
    6 }; @0 y! b3 ^4 M更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。& G/ |: ~6 I  T! i' V; h$ W' S/ x: j

    ( S& C0 {# i+ _' Z4 W' K数组指针  C/ i& |+ P, w; I* [/ r
    数组基本表达
    8 i; n- C+ \! a7 ~* uint a[10];  // 定义了类型是int, 元素个数是10的一个数组。- z; l. E" S8 p+ C" {
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得
    1 E1 x+ G; q" ]  l) x. \
    ( I+ N" C3 D' o  ^9 _int[10] a;
    3 q4 n) l0 E& r4 w) m这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:6 d, f5 U+ A) R- C& @1 q' |  h# I

    9 ?, ?4 w( ]* Q# ^int a = 0, *b = nullptr, c[20], **d = nullptr;
    0 x1 A5 C7 c4 K2 s/ O# H/ D1 Nc++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。5 R6 {* O2 b+ k# ?9 j6 ~8 r# N

    1 F1 j7 u$ V, iint a = 0;
    . k( d( {' J; c3 z% x0 N' x4 {int* b = nullptr; // 指针int*
    - |/ ?) G. J+ E' Qint c[20];
    6 H# y% S; ~  o/ g9 z2 H1 X' Bint** d = nullptr; // 指针的指针int**. g5 O4 A* v6 e: z& k" m
    数组的名称是什么类型1 E# E2 b  O5 E8 b) v" g2 c
    数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。
    8 M0 ]5 `$ J. Z. r( M" @% Y5 R# s/ s4 B5 c/ R9 Y
    int a[10];6 T3 h' V0 m( r6 q$ d
    int* p = a; // 指向a数组的第一个元素, }: i0 T6 S) B
    a[1] = 1;* J1 l: @3 O: J& D/ A! j
    p[1] = 1; // 效果与a[1] = 1一样。3 d* I- u  J% q9 y: q: Z; o2 K) H! o
    数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。
    + s% J5 H. _. \# x  a. w! D2 I' ]; o5 `  G" c
    int a[10];4 T0 O2 ], D. X. T
    int b[10];% s+ ^/ H2 B: m% o* T5 R
    int* const p_a = a;' P) X8 H/ m8 c
    a[0] = 1; // 合法。 数组的元素可以修改。
    # S1 N6 k, Y# N7 G9 x/ mp_a[0] = 1; // 效果与a[0] = 1一样。: C0 k0 l1 g. a: W% N

      J+ r- Q8 }" D1 ~  u4 `6 e$ o5 h7 Ha = b; // 报错! 数组本身的指向不能修改。
    6 _2 Q( S# G) N
    1 M/ @: d4 _2 ~' |// 所以数组a可以当作int* const来使用, H! I6 e5 H" u9 S, k
    int *const& ref1 = a; //正确。4 U, v) d4 f+ K$ ]* K1 K  W: j
    int *& ref2 = a; // 报错!7 o% }; U' j: w" T$ T9 B4 B3 F1 v* n
    # G$ w1 J, Q  M( {. b: V
    // 但是又跟int* const有些区别。6 ~% T& {6 I" _$ V  i5 p! m
    assert(sizeof(p_a) == 4); // 32bit程序。6 w3 E" s4 x. g! c
    assert(sizeof(a) == 4*10); // 32bit程序9 H  h$ s4 h" ^3 A1 T9 ?0 k
    3 ^0 B. E( z; A/ l2 t
    数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。# F! P- n: {  d$ j
    ; i% q8 E: K! p$ e1 U: f" H
    以下3个函数翻译成汇编以后,汇编代码是一样的。$ P( }- N  E: T. X8 F% u

    . _9 m$ j) n% W/ i/ svoid Func1(int* p_ary); c: w! s9 Y+ \1 E( p
    {
    ) \1 q" Q/ @3 X8 }# G    assert(sizeof(p_ary) == 4); // 32-bit
    ; Y+ N3 `' [6 H: q, [  Q, z8 D    p_ary[1] = 1;# y/ @. T  C) b! P
    }0 d& o, ]8 g- @# U
    2 A# A/ j% |5 [4 _1 V( F
    void Func2(int ary[])" ^3 G8 U$ n+ C) M: T) A$ S
    {, N7 P- z$ @! K+ u9 D
        assert(sizeof(ary) == 4); // 32-bit
      |/ F* M( w: X5 Y. K, |    ary[1] = 1;8 i; i  N( A6 W/ X% |& F
    }
    & G: P3 y3 R, }. b# |/ c
    $ B: G$ E8 t; ~; Dvoid Func3(int ary[10])
    + d& q: J5 A  e- \1 G: Y: U& }6 [0 B* C{+ D: i) W0 Y3 g; h+ S% i% Y
        assert(sizeof(ary) == 4); // 32-bit& Y% q3 |3 C! A6 Q
        ary[1] = 1;$ `" N# a$ s2 T6 C/ B: V
    }
    ' u3 p( [  Z: j% `! r. I2 e
    + e9 F4 r$ W& q9 Iint main(int argc, char** argv)* e9 L) ^0 u. ?- G" y
    {
    3 O$ ^8 K4 b3 N4 b6 f5 o    int a[10];
    % i# S+ X8 |* g& a8 D    int b[20];- N/ Q9 q2 R: F
        Func1(a);
    5 T. J8 Y0 t  N9 g$ w5 ^2 J( F/ O    Func2(a);
    & S! W5 F8 t  K/ E1 N    Func3(a);
    , ]% W$ A. \  F, o7 r9 b7 U4 S    Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。
    : W6 Z2 ^2 J' |* o, |  N    return 0;
    . _% r2 \6 U4 ]1 p8 h! {2 C. f}+ D  l9 R0 j) M3 _" m& s

    . B. ?$ [7 p/ p- j0 X9 `( O, o+ ]1 f, J6 z/ D5 s) K
    ; T, _& j  _- M) H$ N
    多维数组
    % L; h# `: Y; b0 P一个3行,4列的数组, 结构如下:) w& B4 a$ }/ f
    2 _. ^3 ~+ ?: W7 _  Z. T- l
    int a[3][4];1 ~9 V4 F3 ^8 Y' e6 ?0 s6 A
    column 0        column 1        column 2        column 3
      E0 P6 Q0 Q* g( [- Xrow 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]) C9 f6 j: e; J4 J* e  i
    row 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]1 f( Z9 q$ U/ I# d+ Z0 h' M
    row 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]
    ; P2 L5 h8 G5 N数组初始化
    " S# h7 h' O# `3 y8 a" B
    6 t* J0 R8 T" y) Pint a[3][4] = {
    + W8 o! i; b! i5 W+ `: L$ I    {0, 1, 2, 3},
    - Z( w* I3 O& }& Q2 {4 S    {4, 5, 6, 7},# H; M0 H$ [5 G; i$ W( H
        {8, 9, 10, 11}8 S$ [: A3 r7 d" i- Q2 |5 S$ I
    };! V9 r, l# L/ P+ v% \8 A$ I2 y
    实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。
    " I  i! R+ d% u2 L8 b# y% V- T6 v2 t, a' a1 f
    void Print(int* p_ary);" X7 E5 \0 J' g

    * K3 }! U1 w8 G$ p) S* Dvoid Test1()
    1 i0 n/ r8 ?0 G! `+ T1 G* j{" G' f& p' r' O( V
        int a[10];
    5 |7 U  X3 n3 S; d" h    a[3] = 3;) M" ]0 B# R, N% o$ C9 Q7 `
        a[7] = 7;
    8 g' A3 o5 q3 ~    Print(&a[0]);
    3 o1 E1 {0 [2 x; {}
    4 `4 ?# K6 w1 r5 m' E, h
    6 j0 v: v/ A8 k; G% t8 ?3 yvoid Test2(), ?( W& m* ^- I
    {
    2 H7 ~+ h2 w; @: A    int a[2][5];4 I/ J. ]; z& U8 Q9 Q
        a[0][3] = 3;
    . @% ^$ k) q% s" m3 t. X    a[1][2] = 7;
    % f8 L/ j' F) ^. Q- x    Print(&a[0][0]);
    . v( B$ Y% b3 Q}) v' o" I- _7 _

    9 W% j9 L! @9 D) x
    ; p/ D0 ]# g# Q; B
    : \6 t. u# N- F( s( ?" w. P8 g很自然地,多维数组也支持用1维数组的方式去初始化。
    * `5 M' n; @+ h( n0 d9 H( j9 J  ?  k) A4 b
    int a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    4 d$ C% {) Y5 E7 b9 e既然多维数组与1维数组没什么区别, 为什么还需要多维数组?; @8 |9 K5 e+ }& z  [

    ' b$ V6 E" u" M% q3 ?. k假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:
    & H4 `' |; N, T7 E/ x
    2 ~9 `/ Z) c% N. e- Q5 A3 hunsigned char* p_rgb_image;) j9 u4 F8 M" j* C: S0 I8 e8 O' W, N
    unsigned char r = p_rgb_image[40*720*3 + 50 + 0];( H+ A+ [* U; y; M
    unsigned char g = p_rgb_image[40*720*3 + 50 + 1];
    . T& _$ r" ~! h$ Uunsigned char b = p_rgb_image[40*720*3 + 50 + 2];
    3 M) ^/ d( x3 U+ S类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。4 M' q& _4 l4 r& Q8 B1 p
      B, L+ `; u6 u* M# G9 |/ q  Y4 g
    enum. I+ \" U) F" L& `3 S' U; p9 v( ], K
    {, |* T$ e& H' M0 n) R) N, p
    Red = 0,
    % b, A  G6 k: c) g Green = 1,
    : U3 i/ j7 }: o" o3 E Blue = 2
    1 T3 _( I" _% @, C, Y& P' ]};) P$ I2 a+ I# B9 A; t0 Z8 H6 e
    unsigned char rgb_image[576][720][3];% w% Y2 a; h3 f& ]' }9 W3 T& k
    int row = 40;
    2 l8 k9 ]$ o9 h0 u5 M7 gint col = 50;& H; H- V/ N( x4 e9 }; |  w8 ~
    unsigned char r = rgb_image[row][col][Red];% D/ t- s  p$ g9 }3 Q4 N) E
    unsigned char g = rgb_image[row][col][Green];& ]% d# ?: z6 L* D; y. ~& k
    unsigned char b = rgb_image[row][col][Blue];
    ) A. G9 C- @- t- `: \) L  m2 B数组指针以及与指针数组的区别  }6 Q* L: D: p! |* u5 f( Y8 i
    数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。" n# M$ F1 Y* D# s: @4 D* E0 G+ Z
    指针数组,是一个数组, 数组保存的元素的类型是指针。# \/ B3 Y# C5 Z* e
    数组指针的定义; C+ B$ }" ^7 Q6 a( J
    数组指针定义先定义一个数组。7 D6 X4 Z# |0 W
    int a[10];8 Q# ~( T/ d" k6 w; q  R! O" P4 U
    然后对数组里的名称用括号括起来后再在变量名称前面加个*号6 C+ p! P4 _5 \$ N/ c/ _+ F$ B3 Q4 S6 |
    int (*a)[10];
    ' P, H9 ~; b. ?+ Y: G后面你会发现函数指针定义类似。# o$ Q2 L- I. f! \1 s8 I; ~
    // 各类定义对比
    $ N) o9 a& D) ]/ wint a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];% Z6 i% h! ]& v7 Z! X/ j# n4 n

    # E! U0 D. D) \) M& R) _int *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int*1 u1 B, b2 g/ `3 y1 V* a: W
    int *(f2[10]); // 指针数组。另外一种定义方式。
    / [( u- i) T% C# eint(*f3[10]); // 指针数组。另外一种定义方式。
    ( f# x3 O1 F( }4 }& jint(f4)[10]; // int数组7 }; ]. J: n% _; w6 d1 a% }
    int(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组6 c  ?* M/ M2 i8 f1 E
    int* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组
    ( c8 Q" k7 m. L/ T0 M* D. j: P; ?+ C2 R) b. c! R
    int d[10];
    % S1 V$ a5 S/ dg = &d;2 J- H* l! v5 l, L9 Q- T
    8 k( E/ P+ y2 a/ Q% d$ s# Y$ P) z
    int* e[10];
    , U  b, m) }: _8 a$ T  sh = &e;/ U& g5 {. n! A. ~
    数组指针的使用$ }9 y: h& k/ H+ V7 S3 D
    数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。! z/ x# a' |2 u( ~4 J* h8 \# [# |, X$ |
    ( _6 ?3 ]) O& R% f# [2 X* R
    int a[10];
    - S8 g+ {6 O  h0 w, Aint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组2 @# z3 W. s. V. E* S: i" H5 D* l
    for (int i = 0; i < 10; i++) {
    3 z0 H5 y, q% G9 l( x // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象
    ! }5 b+ X) l% u) V8 f" h* Y  ~ // 再通过下标操作访问元素。
    6 e9 p2 N6 T" a0 p4 m (*p_ary) = i; ; b: @' j9 z0 n
    }, a; @- Q' g# m% F& `
    , q& O3 I& b# m! K$ `
    int b[10];* o4 `! \7 c& W6 ]" L
    int c[20];
    ' c( ~; d/ t" V/ L$ w' Fp_ary = &b; // 合法# k, z' j, ]& U
    p_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体
    . @+ y. v* q+ W, i数组指针指向多维数组的子数组
    ) E! g' U1 G8 W; T: O5 h4 pint a[10];
    & o1 f$ D/ z5 x% Y1 j& y  kint b[4][10];9 v; H4 P: m5 {, i' d
    int(*p_ary)[10] = &a;
    ) }; A, d8 T# U  n$ M. efor (int i = 0; i < 10; i++) {" S, p5 S) o; u
    (*p_ary) = 1;
    4 o- m/ S7 y& d4 z7 Q0 ?' O}
    % y/ f, _) B0 ?% t- b% h
    " z8 V( J* R: {* y* M3 }p_ary = &b[2]; // 多维数组,可以看作数组的数组,2 h& Y9 [- {4 k9 h! l, ?$ ~1 ^* C4 Q
    // b[2][0] ~ b[2][9]的值都被改成2了7 p3 V) q% Z& M$ L& K3 H
    for (int i = 0; i < 10; i++) {0 r. g, j; {6 U
    (*p_ary) = 2;& g/ k, r( `$ s3 t9 ^# W4 c
    }
    7 S8 K! B6 h2 j! K, b, N8 H多维数组指针5 T/ [- ]3 i" G( q
    多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。4 F" Y) o+ r: K7 e, [

    9 w3 L5 I; K- m/ @$ h1 `int a[2][5][10];
    : M  p8 ^& g! Z% {4 b$ ^int(*p_ary1)[10] = &a[1][2]; // 1维数组指针; }' d. O. x# H3 k% f' w6 }
    int(*p_ary2)[5][10] = &a[1]; // 2维数组指针
    $ `1 e* a1 r/ `; ~; R" c$ gfor (int row = 0; row < 5; row++) {
    5 Z9 @/ ?! x# ?9 [: a    for (int col = 0; col < 10; col++) {
    ' {  m- t6 u/ r' E        (*p_ary2)[row][col] = row * col;
    5 S/ f$ u& M3 K    }
    - k' k7 }4 g4 G5 P+ p3 n& [}  E. `" ]4 K9 Z3 z, u
    数组指针和指针数组对比实例8 F) ^, U, p8 D- T' L0 S
    数组指针还是记住两步法即可
    # {+ |% W/ I) d+ [5 P1 h7 t) P% ?: U2 m4 A7 |0 X  s
    定义一个数组& i' J- W& \3 G8 `
    括号包围1中定义的名称,再在名称前加个*号。* r5 s/ s5 n6 @
    int a[10];6 ^" l) W" `) ]& g2 q5 O+ b
    int(*ary_pointer1)[10] = &a; // 数组指针
    " F! E) {, U% M2 R8 f- _int* pointer_ary1[10]; // 指针数组。元素类型是int*% t) z, N; Q% D6 u3 }% t1 }
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。
    . i  q! B, c! Z$ o  X. R5 `7 Kint (*ponter_ary3[10]); // 指针数组。另外一种定义方式。- p. z9 X' U0 y) H
    int c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。
      g# D, t' T" G% J
    ; c4 y; A, ^7 e* G: `5 q// 指针数组可以把每个元素指向数组对应位置的地址。
    , s9 I( d! Y7 A* A// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,; H/ n. ^& @' m% |" h
    // 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。# z8 W* B( o; }2 \4 k% Q2 P
    int* pointer_ary[10];
    * h: Z( @+ \1 ^. S: lfor (int i = 0; i < 10; i++) {/ }% F1 C6 s5 v4 O2 I- {% j" ^
        pointer_ary = &a;
    9 \- C* S8 T0 ]}
    ) c8 v3 c/ c1 [// 类似遍历原数组效果。
    # t3 ?3 _9 ~& L+ Vfor (int i = 0; i < 10; i++) {
    * G' }; m7 ^4 e) b( W    *pointer_ary = i; // 修改原数组。% ?# _/ M$ ^% g! m8 q
    }, p% l, D+ Z+ @1 d8 \) A# F9 A/ S
      d1 F3 J& t. r9 z" ~  B3 V. S
    函数指针
    6 z! q  C% V7 S1 z6 e取得函数地址
    6 i. ?  K  M$ I& R0 N6 @! r, ~函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。
    , t# w" H$ M4 s2 H) C6 @( \7 @4 e8 O$ ?: \  t& {5 }; ^
    void f(int);
      c0 z- }9 v8 v) c; n6 {) `1 T" nint main()5 C# W4 Q( \& l& t, L
    {
    2 W2 f. y- E+ g0 J% o    void (*p1)(int) = &f;" E$ q; m+ |9 p8 [6 h2 D; {
        void (*p2)(int) = f; // same as &f6 c) z& }: b9 {: `3 d
        return 0;5 e* F' D% G. f/ D5 A. F7 w
    }
    4 q. A) O7 s" U* ~4 i7 P翻译成汇编代码, p1和p2的赋值是一样的。7 E6 I5 J6 x3 X+ s0 r. S
    - g3 j6 o. L+ v; g# u

    , `1 `# `8 f" X4 f, B% A2 V
    + e" F, y) w( Y5 r; j; ]! V函数指针的声明1 `! j, U. v) ~8 j  |& w. _
    单个函数指针变量定义步骤
    / D# Z: R( w9 y0 e. S$ r+ S定义一个函数。void fun1(int a, int b); int fun2(double a);& @4 Y9 ?6 Q; p
    用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);3 f8 K6 O0 }3 T: X0 {( p: \8 b  ]0 C
    如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);; v5 R$ r2 E, M
    typedef定义函数指针; ~: B2 e! X0 q2 _4 j
    可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。0 C6 p3 y. T$ l+ P* h; ?2 j8 e

    8 `7 x' Z* T4 }0 q/ G4 B9 ^. A5 rtypedef定义函数指针的语法# A, Q3 O/ P  S* F8 X3 F
    typedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。0 `: H, ^$ T, ]1 a2 ~, F8 x

    4 D5 R; ]8 [& _7 U! n* h' j7 \typedef int FuncObject(int a, int b); // FuncObject类型是函数对象6 {- n6 u, J, u9 o/ c4 B& T3 h7 f
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    # \5 U) C7 A" l5 C  a' a" V* J' WFuncObject* f1 = &Add;; \' V1 X  l" U. F
    FuncPointer f2;8 e; F, t6 \5 N. E
    f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。0 q$ X6 S0 Z6 C2 S$ M' c1 x2 O
    FuncObject f3 = Add; // 报错! 函数对象不支持拷贝
    / U3 D, D( R- z0 b% l+ i4 o. VFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配
    & m; F4 `# R2 r  ^5 j- u( HFuncObject& f5 = Add; // 正确
    $ V; X3 N) s: y$ a" p! qint ret = f5(2, 3); // 正确
    & L5 a# w' B+ Q. }6 cFuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配/ U% F* t3 L! z4 g- i
    如何记住typedef定义函数指针的步骤. F: X1 z2 [, w9 L  e% e
    像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);
    5 @. B# |$ K$ E1 P6 e7 N在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);: A1 a  h/ }; A8 P" x& P
    完整例子0 B7 Z5 b* p" }& t  E
    typedef int(*CalFun)(int a, int b);
    6 `5 }* E/ X+ B, p
    ; @) R) }. A4 D( @: Jint Add(int a, int b)& r; q+ Q- O: h
    {
    # r. |: n* J. t7 X" j/ R+ i, v( G    return (a + b);$ I' m7 }8 F* a
    }
    1 r5 L% W3 k( G: q6 ?+ A2 v1 f2 L1 @/ o+ I4 r+ @6 h
    int Sub(int a, int b): F7 r$ T1 a' k
    {
    . n& o* `# n; N( R    return (a - b);
    $ k) W$ R' r/ ?$ d}
    " d+ `; n& {' C; ]# {- O9 }. Q* a* P2 r9 J4 C2 l2 G9 b
    int main(int argc, char** argv); u3 r0 e6 |) c& x1 l
    {" s2 F- q3 ~3 J2 X+ r$ V. z7 b
        CalFun f1 = Add;
    ! z- Q8 t9 j/ q    CalFun f2 = Sub;$ ~, G* Z2 ?4 a9 T8 O! c, F0 t
        int a = f1(2, 3);) j8 b0 t. x8 i* v. r6 {$ l
        int b = f2(10, 5);
    / f% X4 ^) ~8 m' E9 `) |
    , S) Q9 j% x: l0 W) I    // typedef定义的函数指针数组。2 P) K+ p, u. i" M. `3 D
        CalFun f_ary[2];
    : \. K, R+ |  {& v  a+ J    f_ary[0] = Add;4 d: U. Q3 p& c" t( K, N
        f_ary[1] = Sub;
    & q7 s7 d9 ]1 x* t
    ! U# S# |' z1 h    // 单个定义的函数指针数组。! r0 `+ l3 |4 X/ w) e
        int(*f_ary2[2])(int a, int b);
    ' R" D  l) j9 n, n' A" K    f_ary2[0] = Add;1 S6 R1 f$ ^  Y) I6 L& ?
        f_ary2[1] = Sub;
    0 ?! W  H& W# ~0 O' t) e
    , I1 u" {6 r8 G. t# i( j    return 0;
    9 |6 d# q! w8 @+ m% v5 K. a. f* f}
    ( }% }2 F5 M  S3 @# t! h9 e$ B/ y  S% m  S
    using别名定义函数指针
    4 O: A, M. Z1 zc++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。
    4 j. c+ b6 G0 x) q) Z# Y1 J7 i) {. l5 S0 W0 w
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象# q& h6 x9 k7 J7 H
    using FuncObject = int(int a, int b);7 k8 C- b1 e. b! p" }
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    ' F  q, A! A  L) o  P* y$ z) |using FuncPointer = int(*)(int a, int b);, Y2 X5 X" I7 {# X. Y' i
    函数指针的调用
    + D' _0 F' Z0 J. {函数指针和函数对象都可以直接后加括号调用  P/ a* l2 I) }* `4 {3 ?
    int f();
    ; r7 d- [( p; _) E/ Iint (*p)() = f;  // pointer p is pointing to f
    1 X& j4 p' L+ {# Q, G- rint (&r)() = *p; // the lvalue that identifies f is bound to a reference- v1 P& K" s; Y5 `' n9 F7 d
    r();             // function f invoked through lvalue reference2 D) @$ z* a5 u  d1 q( x4 z' t
    (*p)();          // function f invoked through the function lvalue# {0 n( k2 P: [, j: ?! a
    p();             // function f invoked directly through the pointer& b! }3 p2 p3 s( M2 F4 m
    如果函数有重载, 函数指针会指向匹配的那个版本。' x( a: d5 P; L
    template<typename T>$ I" ?% G! T+ B  V
    T f(T n) { return n; }- A# ~' P, V/ c$ R7 o: f# }7 [

    ; F7 r/ H" T" Y8 tdouble f(double n) { return n; }
    1 ?6 G9 _  b; K: ^$ l9 q& `
    - ]& p$ [/ [; N% O" Zint main()
    & r; j5 l4 R+ Q* n{9 B- l: K5 e7 T7 o% c- V9 j
        int (*p)(int) = f; // instantiates and selects f<int>2 R) E2 B) u3 m' }& ]& i
    }
    % X) v# I) e5 f7 _3 _4 E成员函数指针
    % ]4 N& a' r) \" x/ B5 M! J静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。: }+ M: a, |3 o5 k
    1 ]/ M* K% x3 o5 g9 m6 A5 C
    成员函数指针定义。7 D& \( I4 y/ S& |0 D1 j
    像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);$ C' d) Z3 Z& b! k5 I! U3 [. E0 e5 i
    括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);6 d" @7 b: M' {% h% Q5 |8 L
    在名称的前面加个*号void (ClassName::*func)(int);
      T9 B' M, T0 M成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);! t4 f) B: X: g
    成员函数指针如何调用。. q9 Z7 y0 u! u2 ^1 Q
    假设成员函数指针名字为func( R# m+ Q, t9 E5 H( \" w

    3 n. B" {9 U# N, Qvoid (ClassName::*func)(int);
    4 y, h' f( L1 j1 }对象式调用。$ m8 {, b6 j, c* Z6 A) |
    ClassName c; // 被调用的对象5 o$ G% ~$ T0 T- i/ `
    成员函数指针名字当作正常函数那样写。
    + e' V; |4 I0 D# H$ u, k* I5 u! oc.func(3);
    & s* f$ m0 e& y, P3 j* j& o8 `  _成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    : X+ l# l* z; v( V* S$ Dc.*func(3);
    ) f( `6 N6 ^4 @最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
    7 O& k/ `  Q* ^* @3 e* L8 M5 K(c.*func)(3);( \' o% R1 U* \; ]! S
    为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外
    - X! A" p6 q2 v, e. N* I+ d" r(c.(*func))(3);3 _6 e6 G9 a. T4 _! R/ a
    这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。
    & ^2 j  y0 G0 g1 E, p4 ?8 ]% X( l5 m指针式调用
    ' p. \  f  J: u% VClassName* p; // 被调用的对象的指针; {/ W& E; Z) i1 R. a
    成员函数指针名字当作正常函数那样写。9 A/ c6 d" S/ W4 q, t$ }
    p->func(3);
    ; z. \( e' g2 L; a7 U成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    ; N: m9 l' z+ R$ lp->*func(3);" Z8 D/ X2 x" q2 L# A5 E" O
    最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。( T5 G& j6 \6 @5 N
    (p->*func)(3);& `: G2 }* ~7 ]
    函数指针使用完整例子
    6 B2 J- x7 D# O# l- H  m* qstruct Cal
    - }  c8 r2 Y, v{ 2 G- ^/ k0 n  h% i. I2 Z) L
        int add(int a, int b); 4 f/ z6 {9 P% D, H# y1 s
        int sub(int a, int b);, d3 A& q2 m- z
    };7 H, q) K! H* d& m1 T( J
    ( A  T* a* T. O% w, S% ?3 m
    int main()
    6 ^/ U5 `' a8 t# b{- ^' @  l# k: a
        int (Cal::*fun)(int, int) = &Cal::add;
    , d+ t% C) W7 W0 `% |, C    fun = &Cal::sub;% ^4 \" @4 L4 P) B: O: |5 T0 \
    / Q+ j( u, _2 G! f
        Cal* p_cal = new Cal();9 M4 m  b. Z6 i, ?* o
        int r1 = (p_cal->*fun)(2, 3);
    " ?. }7 s4 N' E% V6 H    delete p_cal;& b3 Z$ c# Y5 x7 G( b4 ^

    % O6 a8 c- G4 j) b    Cal local_cal;
    8 W/ }( p% B. V2 i% u0 b# D    int r2 = (local_cal.*fun)(8, 6);
    0 H# ?" S$ ~3 E8 w$ K, ~, N8 k$ F, `; v" v}
    ! Z) T: O0 |* Y4 H8 j! V
    # U* _4 k6 ~: Q7 w! D( J; N成员变量指针
    , m' N/ `9 X. P# O) Y& O成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。
    6 ?8 E! T' n7 w) }& ~& {
    ( Q" l# @, a  W. _; n( q成员变量指针的定义5 y( G& q9 K2 v0 F8 o/ H
    假如以下结构体C。
    - q5 j* X( w3 g# k9 Z7 K0 K7 i9 E, m8 d
    struct C
    5 O* b( l( i" ?# c: ^1 Y{
    ' W' E. g# Q' a    int m;
    . i5 R8 F2 J4 {+ V& n};
    2 I& w$ r. B% u5 q* N1 e( y* D单个成员变量指针定义 3 F' r( m$ n3 o: D
    假设名称为p, 类似静态成员变量定义那样声明
    * R! n( v6 o9 e' O. Sint C::p;, |* i# L, h0 p6 Z) v( I# p) R' |
    在名称前面加上指针标识号*
    7 {# N# D  E% ]" m1 V! Dint C::*p;9 t7 Q2 s3 K1 J' ]' e) U
    typedef或using方式定义3 z( M; S: h4 p7 b; [5 r
    typedef int C::*MemberPointer;. b7 n1 I6 F9 Q; _# {+ I
    using MemberPointer = int C::*;0 O: s) m$ r8 l/ E  k
    成员变量指针的使用。
    ) c: g. q1 ]3 t0 t1 r类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。/ a2 N& g" ]0 |4 a" G* S& G
    成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。
    - b$ o& ?8 t: ^! i) m* P) T- I2 _9 A( Q# Z- e
    完整例子$ m4 I0 j. e- ?  M) N  ]7 K
    struct C { int m; };' W1 r! w& k2 O4 x. X. O/ G
    int main()
    1 C: L# P/ _# e4 W+ h8 y4 b{
    - A' U9 b& h! Z: L0 q3 E    int C::* p = &C::m;          // pointer to data member m of class C" h2 r6 }& H* V4 ]
        C c = {7};
    6 L2 G2 A+ S! j    std::cout << c.*p << '\n';   // prints 7
    2 M: y# W2 K; r- Z% d    C* cp = &c;
    . z6 k- o5 `! [' i) K5 ?. S1 [    cp->m = 10;
      w9 J, _6 ]' ?) E9 c! n, q, G    std::cout << cp->*p << '\n'; // prints 10
    : I( x. x- O3 [- C  ]6 _( R. t6 Q  o
    ————————————————6 s4 a5 ?  [! \. a
    版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。3 a6 c6 Y' m( j+ ^
    原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115
    8 y/ |4 p) ]! i+ \0 L
    ; g( Y* S( I* A# O: o
    , o" J- R3 c4 Q* G; P2 G
    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-8-26 18:22 , Processed in 0.381471 second(s), 51 queries .

    回顶部