QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2441|回复: 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++数组指针、函数指针、成员函数指针
    ; u5 D5 s5 K" u. Z* @! E* DC++数组指针、函数指针、成员函数指针
    7 ]- Q: c+ W0 j- q$ M
    ; ^1 C5 U8 I2 Q0 O4 B8 l
    ; y. s( [3 B$ J' w/ i) v操作符名称
    5 J, L$ ~; K4 E  J( d6 @& 取地址符(Address-Of operator)# i8 G7 n! I4 k" O. ~
    * 间接寻址运算符(Indirection operator)+ C! V( N7 G2 x+ q7 J2 _
    .和-> 成员访问运算符(Member-Access operators),用于取对象的成员。
    - u- {6 F3 `- o4 i! T.*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。
    $ r) y) y( g5 Q) V, Y+ I& p() 函数调用运算符(Function-Call operator)) J; A* w3 F/ A+ ~0 E. ]+ u& w
    :: 范围解析运算符(Scope-Resolution operator)
    * X4 ^% x6 n. ~6 |) [8 ]如何定义一个指针变量
      L- F* {1 D. m: C" t9 o" m0 P假设类型T, 变量名称name, 指针的定义如下:8 R- O* T7 o; z7 J. F' ~8 N' y% \
    4 u2 p7 W! u6 g1 G) S
    T* name;
    0 ]9 f# B5 M* p7 K; o标识变量名字name, 它是T类型的指针。例如. R$ W# ?) g* z. N' h/ a
    9 ?4 v- I3 `4 v4 L( ]- M( B
    int n = 0;9 A8 W% g4 P4 L& n" y$ c; }
    int* p_n = &n;% ]8 p! R6 ~2 m) \! r  W
    p_n是int指针类型, 指向某个int型的对象。3 ?/ e  j' b2 [3 m5 S) }

    + \2 o) r8 f7 n6 m- {" |4 G% r指针变量的修饰7 t2 Z7 k# e7 r& ]' R
    指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。. n' \+ B, M$ J9 S+ f

    # W2 Y* Y9 a; r# Y( {6 B指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:; U; P/ c7 M* T) h5 X$ ]# ]
    % I" j2 Y$ n# f; s/ i; m
    const int a = 1;
    1 @1 f+ W5 }  \1 d8 h3 rint const b = 2;( [9 `+ |& t" X+ t* z/ p
    既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:
    % |$ o( l% M) A6 j  N8 \- j0 X7 T3 a; g5 e. t6 o
    int a = 1;
    ' p2 Q8 _2 N# O, b  Mint b = 2;* i* m. e: s6 W2 z& n+ y+ C3 C1 X
    int* const cp_a = &a; // 指针的修饰词,放在*号后面。
    " l$ G/ P1 \$ O- j*cp_a = 10; // 指针指向的值可以修改
    9 A% L# p1 d" Vcp_a = &b; // 指针不能被修改,报错!
    6 ]! z- J' x; c2 [' D  Z; O总结带修饰的指针的格式:
    1 l5 q0 \, \2 Q只要记住修饰词总是放在被修饰的内容后面。
    5 s4 S2 s, S4 R8 n; J3 E
    6 B* r/ b2 t7 G( }0 X) {1 m" ocv表示const / volatile修饰词。指针定义形式如下:) ?2 L. ~, @; z% x, |( Y

    ; l: ~, p' c1 D; S! T. DT [cv for T] * [cv for pointer] name
    + K/ B& _1 p. \: M( O. q+ Z注意对T的修饰放在T的前面也是合法的写法。
      g+ w  W2 d; w0 h# k' j
    , C7 L8 R8 b5 Dconst int const c = 2;; S7 I+ X' V/ m
    在mscv编译器下也不会报错。
    4 `: ^# `) N5 P) u" ~. y* {% O* c# v) }# A- @" l
    完整的格式:" T& s5 [' P4 q9 E7 T: ?* p2 T3 v) Y
    [cv for T] T [cv for T] * [cv for pointer] name! P: I' X7 {' f" O7 f
    . h4 h! i& X$ y; W7 t! c
    Syntax        meaning, y. D& {- w5 @3 k0 k. @. g1 L
    const T*       
    , j# D/ C5 H6 P& C# {pointer to constant object% Y9 C3 d# M, ?) \
    : [, J3 ?& M) B# ^" h
    T const*        pointer to constant object7 p. V, L& z4 y" G7 i6 Z4 A/ W. g8 ?
    T* const        constant pointer to object. m/ d0 E! F( a  S
    const T* const        constant pointer to constant object
    8 j: S( g7 x( D0 o5 XT const* const        constant pointer to constant object
    / U! h% j: ~  Y8 f+ ?上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。
    9 P: ?  e) f6 h# y- k7 n: Q- ^- ^* H0 V6 Z
    int a = 1;
    " Z/ R# l& @/ Hint b = 2;# c- \: [6 C( \2 r: y5 p. m
    . T% ~" |8 @# X5 E
    int* p_a = &a;8 |3 e3 {; Z4 R- b/ u9 M4 F& B3 ]
    *p_a = 10; // 合法% U, t. K, o3 u: F7 \
    p_a = &b; //合法% T: T! d. U- a1 e2 r: F$ g
    / c7 D. h6 J# B! F: R
    const int* cp_a = &a; // const修饰int类型, 并非修饰指针
    ' N* I2 b$ P! g+ F& [*cp_a = 11; //报错! const int类型不能修改" e5 c: R5 V: a( V1 L3 p
    cp_a = &b; // 合法, 指针没有const修饰,指针可以修改。. [: O* H. w# r: S8 o1 O

    1 W( [5 h) D  y! _5 A. w5 g& I9 ]" eint* const pc_a = &a; // const修饰指针。类型没有const修饰  S* d! s* U7 M9 T; p* s& d+ F
    *pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。
    4 X$ [9 }5 t2 c, a+ @pc_a = &b; //报错! 指针被const修饰, 不能修改指针。& J2 P$ C3 N8 F* p, |

    ( ~' X8 M& V' Tint const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰; j- k  W. i( l, O& c) G
    *cpc_a = 13; // 报错! 类型被const修饰,不能修改。
    5 N0 L! f3 a* Y! v% e+ Y5 Y4 ~' qcpc_a = &b; // 报错! 指针被const修饰,不能修改。
    8 y3 Z8 Q. l* z7 X, U) ^3 ^. B& I" v7 T* P! u( Z; |( E0 ~
    更复杂的指针的指针4 S4 {5 r: ?! c2 b8 l6 h# C7 I' P
    1 o' ]1 v7 ]8 ~! S+ ^" f8 ?
    int a = 1;
    4 F4 u4 \. [2 zint b = 2;3 V9 I4 ?; g) K$ J9 K- {2 K
    int* p1 = &a;
    8 c& }* w6 q! Y0 p( h0 ^int* p2 = &b;
    $ n: H5 i5 M  y3 L) @const int* ct_p1 = &a; // ct for const type# X, T' E5 V+ t2 J6 I( l8 c3 a, R1 b
    const int* ct_p2 = &b; // ct for const type
    - n5 {) c+ V: ]" r! b" L8 N* ~  F# O3 O, l9 F" U
    // int * * pp1; 指向(int*)类型的指针! F& \. V' V8 z/ D4 L/ L7 W
    int** pp1 = &p1;  
    , ?+ i2 D0 @. o; q7 w7 N$ W& b1 Spp1 = &p2; // 合法, . g2 o& e* {# r( ?  E0 O0 J
    pp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)
    6 m# E& x+ M, Q7 X$ {0 z* q
    8 H7 H$ l( ~% X+ ^( v// (const int) * * pp1; 指向((const int) *)类型的指针
    * h/ a6 s3 F3 j2 u2 Z  K6 vconst int** ct_pp1 = &ct_p1;  
    , X6 D& `# P; L* Y) ]ct_pp1 = &ct_p2; // 合法
    & A$ r, J% |" ^, L1 @ct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。
    0 M! n6 C6 N/ U- v% w5 E( @: V. J1 V0 r3 p
    // (const int) (*const)
    : V/ T, R8 k1 h3 y5 R. [  ?const int * const ct_cp1 = &a; // 指针也不能修改0 S* L1 K) P# e2 U3 B: \6 g
    const int * const ct_cp2 = &b; // 指针也不能修改
    4 Z6 X# |' p  H- j) Z7 V1 X& T9 t& Y4 z4 fct_cp1 = &b; // 报错!指针有const修饰
    5 Y/ P- e- G3 C, }
    & i* x  s! N" h0 t9 @// (const int) (* const) *  指向((const int) (*const))的指针* h# c; r) S! F/ j+ y
    const int* const * ct_cp_p1 = &ct_p1;  
    ( r, D1 I- Z0 [9 f/ C9 J9 J& s' Uct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰
    8 ~8 [8 l- w* ~4 G1 M: a& t*ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改
    * q" p" i2 ]& l5 I, I2 Q' _; H3 R# o( X/ E; B  m) j
    // (const int) (* const) (*const)  ! x. }) X" w! }! l# f0 ~* _
    // 指向((const int) (*const))的指针,且该指针被const修饰; j, A9 [" ?) p2 z; t+ y/ o0 N
    const int* const * const ct_cp_cp1 = &ct_cp1; ) ^- l6 T! b3 q6 X" l' b( I
    ct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。
    . K8 x2 L5 M) E# @* N
    0 A" Z5 b$ }7 Y( w9 L1 Q一行声明多个变量
    ( V" n1 N" n4 H( _类型 + 名称定义一个变量。
    * K2 A5 a1 R% J/ ?( e变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。
    0 }# a+ o( k- A7 c6 ~  ?# o
    3 k& h' u9 J( \int a, *b, *c, d, **e;2 N  L0 f, S/ F( |
    a = 0;5 }7 @0 d6 K7 x; U$ H2 D: x+ O
    d = 1;
    6 l3 d' |5 V; tb = &a;
    ! T- k1 V2 {$ {% r: F( Xc = &d;5 E4 a2 [3 m( A; d3 H
    e = &b; // e为int**类型 指针的指针; n  E+ U. W( @5 b6 ~) Z: M
    e = &c; // e为int**类型 指针的指针/ Q* l& B1 w! K! k
    也可以用括号包围变量和*号。
    9 R, f5 [0 T3 Y' Q  w: a) v; V8 |2 Y7 ~; L
    int (a), (*b), (*c), (d), (**e); // 合法定义。
    ! v7 }3 c8 W; r7 F0 h4 S- e括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如/ J) u0 U+ {. D( a. R4 _

    4 b! `. L! q- Y% Hint (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;
    + ]3 R/ s! y5 Y( e% m' [8 D写成* R: B2 y) R/ s; I; X# N8 k* W
    , L& Q; S8 ?) w# o  f8 C
    int a, const *b, *const c = &a, const d, const* const* const e = &c;) w$ h+ ~$ b' m' L
    更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。
    4 r, N" t$ P" I7 j- ]
    % x& b' I( c. F& r数组指针* u# g: H% v- Q+ X' Y9 G* J
    数组基本表达# l2 Q' t9 p. c) N
    int a[10];  // 定义了类型是int, 元素个数是10的一个数组。% c" U7 b1 _$ {2 g
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得& N# i8 m% u+ d3 j8 |: F! W
    8 A! A+ k( t: T! g
    int[10] a;
    7 r+ G- a9 w, ~. G8 p这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:
    5 u, e$ p; U( h( a1 i& Y/ m/ ~% R+ O
    int a = 0, *b = nullptr, c[20], **d = nullptr;
    ) T  C: o4 W: V& E+ F# cc++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。
    & H/ l' k, t9 _- d" r& D$ n5 y, L! p" n) Q6 C- o* C3 G0 e# y! U
    int a = 0;
    / I# X3 s6 K1 i% @; h) a# }, wint* b = nullptr; // 指针int*
    7 S6 x+ _/ L& k$ r5 W% P& bint c[20];) b/ ]) @8 E+ q; t; x7 |5 w
    int** d = nullptr; // 指针的指针int**& t: \4 e! i) C
    数组的名称是什么类型
    ) x! R/ ^* W5 q4 k0 a) s% g数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。: L/ I0 p! ?; n0 n* O1 k

    / p" F. E7 r# C7 fint a[10];4 N9 C0 K* v  z$ T4 `- T! ^& q
    int* p = a; // 指向a数组的第一个元素
    ' N1 O) q. q- u+ H$ y5 ~. v5 Ga[1] = 1;3 P* V. W3 r+ v  s
    p[1] = 1; // 效果与a[1] = 1一样。! y8 r6 U7 F3 Q8 \+ E( o
    数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。  V1 B2 a( b$ N, b

    2 d, V% m0 b9 Cint a[10];
    0 y# Y, o; D; ~$ i/ iint b[10];% f. _" o0 }+ h  I, u5 o
    int* const p_a = a;, M5 n0 S& B5 e; x: t
    a[0] = 1; // 合法。 数组的元素可以修改。3 n1 e: m3 }3 F; A. p. k+ u
    p_a[0] = 1; // 效果与a[0] = 1一样。
    ; w; q# X% u2 B9 ^/ x3 J/ P! m! B' K7 Q8 U
    a = b; // 报错! 数组本身的指向不能修改。
    0 _: b7 s2 o1 J- g# \
    $ K1 t# d0 t- b% P' Q8 d. D// 所以数组a可以当作int* const来使用$ Z+ `  W4 `# S) b6 I5 _4 k' r/ R
    int *const& ref1 = a; //正确。
    " A6 K; `! Y' B' }int *& ref2 = a; // 报错!
    ; ?! B3 j+ j1 H& p
    ( Z% Y7 `8 W7 S7 _& _// 但是又跟int* const有些区别。8 S( V, H$ H0 u) c( A9 L3 k
    assert(sizeof(p_a) == 4); // 32bit程序。
    + `" D7 F; d& h7 n+ Wassert(sizeof(a) == 4*10); // 32bit程序" b$ c( w. d% u* ?# E" O2 p+ J
    8 ?0 M: D0 ]# c5 j, Z. }
    数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。
    0 K8 ^$ _! o: z% W' B  A) z, ]! K3 q/ R6 Z5 {# G5 U
    以下3个函数翻译成汇编以后,汇编代码是一样的。: z7 d# X* T/ ^6 V, i! g5 `3 |6 C. h

    % H$ t4 D0 g3 q2 z2 ~0 @void Func1(int* p_ary)
    ) X  G$ O/ L, ?1 S$ _. \; N: i{# a) R" [" K' r: U# d' l
        assert(sizeof(p_ary) == 4); // 32-bit
    . B0 t9 Z7 Q2 N* U$ u    p_ary[1] = 1;
    ! l1 Y4 Z0 I( ]4 U3 k}
    $ P  e0 V, Y. N# Z/ X3 d5 J/ p( p7 h- G5 D# l; X6 O. m
    void Func2(int ary[])+ W! A( m! E7 g6 T( S& [1 u
    {9 X& y: w/ ~0 ]9 A' @2 z* q4 [
        assert(sizeof(ary) == 4); // 32-bit* t4 G" G% D+ l% L6 A4 n
        ary[1] = 1;7 \7 Y2 \$ H8 \% P) R5 t  L
    }
    , j9 [! V& y- X  i) W- i- m0 A4 C" x; M1 |* d1 ]- t# {# T! f
    void Func3(int ary[10])1 }& `4 _* x. k3 z  p5 o
    {
    " U6 h+ f. q5 {# G3 d  I    assert(sizeof(ary) == 4); // 32-bit/ s4 I. [9 I- E# k$ y
        ary[1] = 1;4 k6 `! G' b# S# z) K9 M
    }5 ]5 F% I. h& p; k

    8 R: z1 m6 E  g% Eint main(int argc, char** argv)
    * }' {' _# D; p& Y9 O0 A2 u{3 u) @- s5 G% g4 q  \
        int a[10];6 l( I' i; {5 G" M6 J
        int b[20];
    ' Z& d& [7 C: S6 A+ I    Func1(a);
    * n5 t* S  E* B" N    Func2(a);# h  d7 H5 m( N
        Func3(a);/ \3 U! Q1 P& [( G
        Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。
    6 S3 S8 I2 e# E' N, L    return 0;- @: w  M) Q  J6 F+ {2 M
    }
    0 d$ U; p- f- M* t# p. N+ ~' O, m6 m) \9 k
    4 c! ^2 z7 M% [$ G5 T, D
    . p: \9 E6 [' l8 `* T  R
    多维数组
    6 T! C" R0 `# I" |一个3行,4列的数组, 结构如下:
    " G, f8 H6 I8 N
    % H- Q5 U! f& ^2 r) l8 Z" [int a[3][4];
    3 C5 \( M/ s' G6 ?& M3 Q" g7 q/ Ucolumn 0        column 1        column 2        column 3
    $ b1 z' ~& M. |& S# n! Urow 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]1 [; h, p& F, S2 H
    row 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]
    , g& q+ o8 d& }( E% m, B+ Brow 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]. ?$ W: i" D9 C: d9 @  [8 ]
    数组初始化% b  ], `" d; i: t" O
    0 I7 W- M+ N% \8 |8 k. L4 }: \
    int a[3][4] = {: |3 e6 |. m% Z# K; I9 k% V4 U4 X
        {0, 1, 2, 3},& S$ Y! g, j( Z7 N$ e/ P" _
        {4, 5, 6, 7},
    # L- d. u' H5 P    {8, 9, 10, 11}: q( D$ _$ f2 u% U# A
    };3 @, D4 g% N! E. E  s0 `
    实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。- g4 W  M0 y9 H7 l$ d, D9 f* w8 f

    - M8 F$ z/ w- ^8 `void Print(int* p_ary);
    5 k' f4 y- L" ^
    3 r7 b5 c7 z  d# }; _+ fvoid Test1()" m3 M5 V: }/ {$ k' Z2 n6 _
    {1 W! e- X4 d3 P4 P. ~; w. Z
        int a[10];  y& H& T" F% e) D1 Y+ K
        a[3] = 3;+ L3 z* m+ N8 k; l; [7 k1 u
        a[7] = 7;
    3 Z# s* Z' ]2 H! [2 w5 W9 [    Print(&a[0]);
    1 f, D( F  m4 f: B}
    - N) G: g- a4 c% B6 s9 m
    / a8 @3 m1 A: z. Z# A& Avoid Test2()' v- Z  n  Z+ f7 G0 h% @" d
    {
    6 l, l4 j9 r% e8 `& _    int a[2][5];5 T, |7 R* y# i" U/ i9 ]
        a[0][3] = 3;1 K1 }* {( x( e$ ]/ `
        a[1][2] = 7;7 N9 W- m5 d! L5 g) l
        Print(&a[0][0]);& ]1 X4 D9 P: D% z7 z+ b
    }3 t  f) B; w4 R! a- X8 j  R+ ?( \

    * I8 T* ?& C, d4 V9 @; y+ W) C: ~% Q5 c3 c7 w

    3 D( I- `1 ^4 W8 P- O6 P% `很自然地,多维数组也支持用1维数组的方式去初始化。
    % u  J7 ?: ^( {- r- ?# J1 n8 H; }8 v$ `- I
    int a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    1 a6 u; s7 F3 l. i  a既然多维数组与1维数组没什么区别, 为什么还需要多维数组?
    ; o" y* H3 a7 H3 b+ c& F6 ]* S* F1 C  ^* u
    假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:
    ( \8 `8 z: F8 C; d6 a+ c, X  M/ O  J& T  |( x
    unsigned char* p_rgb_image;) [' S0 E0 @; }
    unsigned char r = p_rgb_image[40*720*3 + 50 + 0];3 _5 p) B- }/ @0 p, y
    unsigned char g = p_rgb_image[40*720*3 + 50 + 1];' k/ d# D$ D3 r
    unsigned char b = p_rgb_image[40*720*3 + 50 + 2];
    8 m* C. M( L$ z, {% G* A! H' \类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。
    ' f3 A6 ?( h$ q
    - `& a. M+ Q0 B9 b5 Renum% I, }+ h1 t! I4 _$ Z' n, R
    {% s" T0 u$ t8 L. s6 N8 w
    Red = 0,9 x5 b5 e- n$ a- `5 D- i# p1 r1 f
    Green = 1,! p, W$ c; x2 k2 U7 }' Z* t5 F7 F
    Blue = 20 i# n' F" {* I  M
    };
    * c9 n/ j6 F) o; Uunsigned char rgb_image[576][720][3];) b7 P" U, ]) J3 b9 t
    int row = 40;4 P* ?# Z7 F, ]9 q5 }
    int col = 50;; u+ {6 g$ H* s3 @; f
    unsigned char r = rgb_image[row][col][Red];
    ! S) z3 v% x3 u; |7 W# eunsigned char g = rgb_image[row][col][Green];
      H* M0 l5 }) @+ e, ]4 P' B' @unsigned char b = rgb_image[row][col][Blue];
      ?. m5 x6 [6 I- M5 Q" @+ j& U$ s数组指针以及与指针数组的区别. O2 f3 t6 n* E8 q" [7 U
    数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。! L0 Q4 E  I5 S# X: ]" O: j0 K1 H
    指针数组,是一个数组, 数组保存的元素的类型是指针。, m$ Q: g; D% W* v/ E
    数组指针的定义
    " }' w- l6 M, f; g数组指针定义先定义一个数组。
      d* a3 m& f6 }7 ]( f( I0 Kint a[10];
      _( x$ |5 ?6 x; Q4 D# E然后对数组里的名称用括号括起来后再在变量名称前面加个*号6 G: l' i$ u  K& X0 R/ s' t6 O4 \
    int (*a)[10];7 ?) j2 {& R2 ~9 E1 u. h! b
    后面你会发现函数指针定义类似。
    " J7 d- w3 p, P/ E// 各类定义对比
    7 ]! D: k% x. iint a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];
    4 C  @5 u, j% }7 {3 z( @4 ~# @" n$ y
    int *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int*& h( [  I9 i: g. y
    int *(f2[10]); // 指针数组。另外一种定义方式。5 K4 z; |& Z3 J9 @& t' q. A/ c: W
    int(*f3[10]); // 指针数组。另外一种定义方式。: j0 C0 n% R7 P0 `+ }
    int(f4)[10]; // int数组
    / ^3 A! ~/ D1 Cint(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组! @) \8 ?9 }, w, `' o
    int* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组
    6 \6 }# W2 f, X: F/ a5 V6 F( g, c# s" W* O( C/ l
    int d[10];
    / ?; |& w3 h% Q# \- qg = &d;
    2 |. Z$ }; P* U5 c$ H. [+ G* c0 }
    ( ?  F; b( G# g2 qint* e[10];
    # ?- Q2 G& L9 t/ }! z9 Eh = &e;
    4 H3 c  j, t9 @8 N数组指针的使用
    " l  ]( y7 C8 K7 f数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。
    / J+ Q6 ]" M' A* s9 Y3 n3 G. Q6 o) ]# I* K- D9 c8 u
    int a[10];/ j: A* G, X' k, z% M  L) u
    int(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组
    ( U( R5 J. J  R1 w  C4 ]( J  ]! tfor (int i = 0; i < 10; i++) {& |# B0 a3 H/ R  [" C! {& i! f
    // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象1 b2 D& Y8 W: L$ t. |
    // 再通过下标操作访问元素。
    0 U' N4 Q* M3 k- e1 S  U, f+ p (*p_ary) = i; & h6 g4 R6 L  E& i( T+ Z
    }- H& h+ a: J3 B
    - ~( {( c& K! s4 ~  G6 g2 I; V
    int b[10];
    9 x/ f9 L9 n/ W7 }9 `0 D9 b+ z2 T) bint c[20];
    , m# w0 B& t) b2 \0 I  t: Wp_ary = &b; // 合法
      }  K6 e% W$ l5 y& hp_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体+ ?- A  b4 W: y; |/ b; d& P0 r& h
    数组指针指向多维数组的子数组
    ' L6 G& \; t, S, hint a[10];
    : m+ T  i9 B: U7 D7 `int b[4][10];
    ! n( G/ d4 I* T' K3 Gint(*p_ary)[10] = &a;
    ' j* m5 G9 O0 _$ A" v$ f6 }for (int i = 0; i < 10; i++) {
    0 O8 E1 [1 Z1 i$ f (*p_ary) = 1; 7 ?6 C# X/ S3 ]" V
    }6 E1 @' i/ H; u& L
    : z0 A$ ~5 W1 R" Q! _
    p_ary = &b[2]; // 多维数组,可以看作数组的数组,. o! b* V& P& A/ m, A& D
    // b[2][0] ~ b[2][9]的值都被改成2了
    8 Q7 S+ z5 {# {: l$ rfor (int i = 0; i < 10; i++) {2 E& _1 A' p& T! j2 x
    (*p_ary) = 2;
      _. }7 _8 M1 h) O) v}
    % i0 B1 k( i; Z+ d, E6 u多维数组指针3 g+ D6 V' m( J  K/ A3 K) N/ t
    多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。' x' P  l2 f4 _

    " Y8 H1 M8 ]1 q; B: g- tint a[2][5][10];
      b* y6 ^  g) L  E4 ^3 T+ Q' {int(*p_ary1)[10] = &a[1][2]; // 1维数组指针% M5 K# y4 o+ H# b6 f* l
    int(*p_ary2)[5][10] = &a[1]; // 2维数组指针
    ) _! o6 P8 _9 f+ t- S) Yfor (int row = 0; row < 5; row++) {
    + C7 q4 x$ F1 L: M    for (int col = 0; col < 10; col++) {* I0 s  K0 A7 {5 u: Z  _1 G
            (*p_ary2)[row][col] = row * col;
    + d$ P- p* j; m, y" d) m    }
    % y3 J: k3 Q# \. i! B, ~}
    7 H- Q8 |) N0 n  G数组指针和指针数组对比实例
    / v/ E, V: ^- j- R数组指针还是记住两步法即可
    7 I" x9 E  B$ U3 D
    6 h$ D* W( E! w定义一个数组9 U+ Q) M# V' ^0 J" J" T& H/ G0 a
    括号包围1中定义的名称,再在名称前加个*号。
    8 }! h# a. o* D- `$ Bint a[10];
    4 B4 z8 x1 f* H2 a% U! Y0 l, v1 {  {7 Kint(*ary_pointer1)[10] = &a; // 数组指针
    ( ^6 [1 j1 j0 ~9 n5 x! ^int* pointer_ary1[10]; // 指针数组。元素类型是int*  W1 w; K, t$ c4 i
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。
    " P1 w! ?; g3 m/ ^+ c9 |- j1 kint (*ponter_ary3[10]); // 指针数组。另外一种定义方式。& Y8 G! }+ s5 N+ @
    int c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。% N9 J* L0 u/ G* h! J. T% i

    # E9 K. s: E5 \! X: [/ a- I// 指针数组可以把每个元素指向数组对应位置的地址。$ E6 [; n7 b6 c- d
    // 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,
    , a, U1 n* N6 M: }// 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。: W7 ]' h9 ^9 N. R% f: w
    int* pointer_ary[10]; - Z/ ]* f  p/ }2 o+ U4 Y8 i
    for (int i = 0; i < 10; i++) {+ _3 w6 I, |/ E# F- C1 @+ G
        pointer_ary = &a;6 _4 T* V( q* Z% T6 B# A
    }
    & B2 o- Y2 p5 M: S- M8 M// 类似遍历原数组效果。2 u- u2 W- `! _0 ^1 b
    for (int i = 0; i < 10; i++) {2 x* S8 ^5 Z6 I% n5 E2 |* }% O
        *pointer_ary = i; // 修改原数组。
    : {. Z3 \/ P! r$ u2 f3 C9 Z}
    ' Q% O. P; Q6 X( f; C- @  X/ b; V3 K$ j
    函数指针$ `' l. K# W1 g8 w
    取得函数地址
    ' x0 J$ h" `, ^- I, P0 l6 T函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。. `& K$ b" u% o8 |/ I) {

    5 H& Z1 ]/ H& |/ {+ E2 u0 i, Evoid f(int);
    2 E- }! O# p4 e/ N, q. Pint main()5 S% \5 D# [% A1 P
    {, Q2 g2 e! n( x  i2 U/ ^
        void (*p1)(int) = &f;
    % W& }+ D6 E2 V8 T) g    void (*p2)(int) = f; // same as &f
    ' X# W; Y& c) |9 C    return 0;
    0 G* Q) C5 i( R9 ]8 R- E7 n}
    : J1 @& W: }* o# O' b, F8 d4 g翻译成汇编代码, p1和p2的赋值是一样的。
    6 F4 |$ O" ^3 s2 m2 n
    * Z8 b  K5 l9 B' N8 v! m% a: H/ q) D9 P6 j/ E9 o1 T

    5 p( g; {; e9 m& y( c5 ^函数指针的声明3 c. w7 [* H6 _5 t+ l
    单个函数指针变量定义步骤3 ~7 j0 P9 u1 \, X
    定义一个函数。void fun1(int a, int b); int fun2(double a);+ _# o1 h9 w0 ~  O, M' j) u
    用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);# }' D: W9 W% Y% h/ n8 y! \; [
    如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);
    " d- e3 Y# ]/ Q' }6 |/ |; `: Mtypedef定义函数指针
    " p- l# W0 K3 q1 m! v' V可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。
    $ Z7 T! Y* j% G1 o
    * x. {* W" i' u% v, ~typedef定义函数指针的语法8 p5 P$ W  A, n" r/ V: j2 `
    typedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。  z3 M; g8 {" ?' ~7 L! k- @
    7 Y, w, a( H2 h+ ]0 k
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象
    ' ]0 `: Y' A. W9 A$ B- ztypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针/ l7 h: c2 t# C& V4 g0 J
    FuncObject* f1 = &Add;2 ^# Z8 ?# \  x5 e
    FuncPointer f2;6 R& M4 \3 C0 x2 {/ M, e
    f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。
    ) ]  b+ d. D# B% {0 ?FuncObject f3 = Add; // 报错! 函数对象不支持拷贝0 k  o+ [6 g6 |% D
    FuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配
    " o: r, M+ N/ CFuncObject& f5 = Add; // 正确
    % S% @& F6 K0 s% D, R( Sint ret = f5(2, 3); // 正确
    ! |' K& O% j7 a" d3 IFuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配' W3 f1 V4 J3 A
    如何记住typedef定义函数指针的步骤
    1 R  E; l4 ]4 n% o5 U2 F像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);! \0 N- o8 c9 S9 K. ?9 \2 J
    在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);3 f6 s$ ~& ?1 p) ]
    完整例子: h- g) k: n& d0 n7 c! {$ F
    typedef int(*CalFun)(int a, int b);
    3 V$ ]3 q- \5 B! {2 Z0 G4 F4 e
    2 g1 N4 i) v) S9 l" X4 U7 y4 R8 Bint Add(int a, int b)  H3 \7 u* l4 B3 p. [" N, }
    {5 h: O; u  u! b* }; d2 I
        return (a + b);
    + @0 j. _/ a% h! X% D2 `}' N5 B% ?; ^6 O5 e# D) b. ~
    7 i; B4 A* C0 z
    int Sub(int a, int b)/ u# ~* [% g- F' w& s% M! |
    {/ m& E0 }) r9 b1 J
        return (a - b);
    ' X2 g1 W2 z6 O6 I}
    ' o6 k0 C  b0 f% k8 j5 ?
    $ ^  ~% [" S7 Kint main(int argc, char** argv)* g) M' j9 B; A6 k! e
    {
    % f7 b/ T! r% p" F% P) K    CalFun f1 = Add;% C, E; H2 W, @: \
        CalFun f2 = Sub;, p* w+ A/ i+ E1 a8 ]
        int a = f1(2, 3);4 q' L0 v4 f1 W
        int b = f2(10, 5);
    , z8 w3 Z4 K5 p0 P* X; s
    ) F( M7 v# d! O) T( O    // typedef定义的函数指针数组。! J* a/ w; h# R  b* N, T& G6 A
        CalFun f_ary[2];; P/ y$ I. \1 Y5 q8 J8 w, Q% k
        f_ary[0] = Add;$ ^: A7 ]- Y9 }# [' v
        f_ary[1] = Sub;3 B# h7 }" E# h& U
    # h. _; l' w  M2 h2 I8 u
        // 单个定义的函数指针数组。
    + y+ P' u* K% ?$ W) j/ K7 Y    int(*f_ary2[2])(int a, int b);
    : ?  L. [# X- E7 |    f_ary2[0] = Add;
    - f; a- l6 L' a  s6 |3 k    f_ary2[1] = Sub;1 h% \0 r( H: {( U9 U7 f

    & g  G: y4 m1 B% R: q! h2 m. h7 B    return 0;& q+ x% m5 U9 \
    }
    7 I* X) ~0 ]( d3 l  ~" u/ I
    3 y8 Y% H" ?1 [3 w7 G0 |7 ausing别名定义函数指针$ G& g" T# J4 Z1 a
    c++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。
    ) Z8 f5 t. X" Q
    - I) w& F# ]0 n8 [( X# c1 @0 Z" ytypedef int FuncObject(int a, int b); // FuncObject类型是函数对象
    ' @8 k; q! ^- Uusing FuncObject = int(int a, int b);- A8 Y% n: Y9 Z* w- Z
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针9 p; Q! G, A4 m* `
    using FuncPointer = int(*)(int a, int b);; J3 e  K$ M+ A* p
    函数指针的调用" D; V' n- _9 `. H' w2 f6 q
    函数指针和函数对象都可以直接后加括号调用6 j; |" M3 |: I3 ~
    int f();% x) z& K0 J, ]+ Y. b" X
    int (*p)() = f;  // pointer p is pointing to f
    + S# c* S$ {6 R; p/ D" Q- [% vint (&r)() = *p; // the lvalue that identifies f is bound to a reference  t/ q# z/ l4 f+ \1 ?
    r();             // function f invoked through lvalue reference
    # l# Y+ \2 y* ?3 b! p8 U(*p)();          // function f invoked through the function lvalue
    2 h7 e" z9 v" N9 [2 \; mp();             // function f invoked directly through the pointer; i7 _: x  S5 C4 J; f% U2 ~  H8 ?
    如果函数有重载, 函数指针会指向匹配的那个版本。: y) b9 r7 r4 p. u0 z
    template<typename T>( s( c& y: A) Y2 F$ p( F
    T f(T n) { return n; }
    + O; R7 F4 ]4 E3 e; @# G0 I, y5 p: @" U5 z. ?0 z
    double f(double n) { return n; }
    , D+ e0 s( M- h# q9 D$ W
    - T, J7 U* }! n' g+ |- Sint main()# }8 @! c- x9 \# [# w1 V. V
    {; p& y! J8 d7 q3 o
        int (*p)(int) = f; // instantiates and selects f<int>
    9 l4 }( `* q; w5 V: g% p* P}/ i) }# t  o+ m0 e# N+ `" k& y5 z$ I
    成员函数指针* R. p) Z1 H- K
    静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。
    & e" b* u5 R2 ~+ v/ b. a# ^$ f; i; h- Z; [& I5 K
    成员函数指针定义。
    3 J3 [4 D# w2 L" t8 t像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);
    ; g* q8 z" L% {括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);
    & }3 ?! c' i  {+ `6 Y7 v. e! o在名称的前面加个*号void (ClassName::*func)(int);" l6 @; K( l9 l6 ^$ j4 @4 @" H/ x
    成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);" B4 A- \6 P' h
    成员函数指针如何调用。/ n1 `& Z3 u) R- u  ^4 b* [
    假设成员函数指针名字为func
    + Z" ^% f% ?  N: \, s; \  a  A. v* o$ Z
    void (ClassName::*func)(int);2 j' E; l( r  C, J
    对象式调用。0 i2 @" M" i( ~1 T9 X$ ]' d! j4 v
    ClassName c; // 被调用的对象% A9 q0 O& x+ C
    成员函数指针名字当作正常函数那样写。" L5 J4 J8 A' s3 C* i# t; S
    c.func(3);
    ( x" q: O/ x4 p' n- W成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    # R3 C. a+ ]3 d  U" J+ j9 M, G1 [c.*func(3);8 v  A, n" K6 d6 m! r3 S
    最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
      _! w$ |" x  C% O' @(c.*func)(3);, i$ Q# @. N: U
    为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外
    8 u) m5 C- N7 ]( b% D! `* {(c.(*func))(3);
    ! F, Q' z# Z8 F这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。/ W& p5 r7 z. M) I0 F! a
    指针式调用
    ( K4 K; D! J8 M2 q* O0 z$ `ClassName* p; // 被调用的对象的指针
    : G' {2 F' `0 P7 ?) t4 }7 L  _成员函数指针名字当作正常函数那样写。
    4 W& z7 N5 I6 O, T* T1 g- Dp->func(3);
    ( n+ i- Y" t7 j5 c成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    & ?  U* n: \# j1 ip->*func(3);0 w* c; Q' l1 d1 c, C7 o- f- Q
    最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。
    5 N0 k8 l1 z. Q9 u9 o8 Y' ]0 s+ O7 E(p->*func)(3);: y3 G# p! B% w9 V& B
    函数指针使用完整例子
      f5 O/ t4 ]  s0 J2 ~struct Cal
      Y2 f! T  S4 e{
    % u; W3 M9 `5 F: r9 [" Q( c    int add(int a, int b); 1 c* G2 I6 J3 ]3 {2 y* B
        int sub(int a, int b);
    5 E$ p! }. x. x};- m. S, A* G$ z

    + l+ b' _; X0 P$ r2 Yint main()5 Z3 x9 Q* T" y0 u: \) |
    {( Q3 ~5 K, `6 A" ~* @0 \
        int (Cal::*fun)(int, int) = &Cal::add;( b, f7 S% j( K, ]- Q1 i% [
        fun = &Cal::sub;
    5 I' k, |# f+ B9 P8 z3 l' W" C$ r
    - s5 |3 n6 C3 D5 I7 r0 _    Cal* p_cal = new Cal();
    ; ~. F5 h5 k0 S5 F1 \, Z' I    int r1 = (p_cal->*fun)(2, 3);
    . t( g" A/ t: z3 ?    delete p_cal;
    0 W. K# {: V8 M  `( B8 {  j, w: s% V! @
        Cal local_cal;
    & ?- ^6 E6 d2 {) a# Y# u    int r2 = (local_cal.*fun)(8, 6);
    8 y3 K) K( \) F, h$ z0 Q% m$ q/ Y}
    ; }& o5 l; m7 W- l4 y2 y$ K* a- ?/ [2 v3 P& z
    成员变量指针6 P8 N8 c  V8 {& I2 r* L
    成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。
    , z2 q( e! @& K- O( A, Q( A5 K' q
    % D- X: ]9 d( p( w# r成员变量指针的定义/ H9 U- e* t; B5 n0 t8 l1 \" v
    假如以下结构体C。- u5 S+ z; |, Y$ `/ p$ r" P

    % ^* E2 i+ \7 F( X+ Fstruct C - B- [) {) l# D0 U
    {
    , d: z7 C5 E$ u    int m; . u5 d* q8 G1 t6 p2 t8 x
    };3 V7 T( B6 Q/ X& }1 h+ ?$ l; S
    单个成员变量指针定义
      |7 `& P. i4 t/ P% j( Z假设名称为p, 类似静态成员变量定义那样声明0 R" N# l$ M" P" P% U* l: v( E
    int C::p;: Y1 ]9 D" {! T& v$ H
    在名称前面加上指针标识号*
    " l& A# N# ~& D, w2 I0 Uint C::*p;$ y1 ~! D: E  X$ M
    typedef或using方式定义
    8 `! s" G' v- D# ^typedef int C::*MemberPointer;7 U) n2 @2 \- p  B
    using MemberPointer = int C::*;" a# d) D& v% Z- [% m" ]
    成员变量指针的使用。0 N& u1 e" L1 y) F) p. S: J
    类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。. C, W' T6 z5 J; ?
    成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。
    / E3 m1 k5 j! s# I
    & }, d5 V" d1 L& |! I 完整例子0 `* i. [8 E1 m% E# E
    struct C { int m; };* V! g. _. E& m' j4 d
    int main()
    $ ?2 _: _/ w* Y; L+ G' ^  l{
    6 p. S% ~; d  i0 \    int C::* p = &C::m;          // pointer to data member m of class C8 x3 o8 \' X, }" E
        C c = {7};
    ; c. N; n" @: F8 y/ w& ~+ v8 h    std::cout << c.*p << '\n';   // prints 75 d/ \( X4 I; s* r$ b
        C* cp = &c;+ o) |- ^( s, {* D6 C2 g
        cp->m = 10;1 j' c/ q' y% K' b+ D
        std::cout << cp->*p << '\n'; // prints 100 \1 h% C; i  F: E7 R
    % G! a: H/ W% T) W
    ————————————————
    ! W( `" e4 \1 O( A6 ]. S7 j版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。/ X6 e, R6 y% l4 A2 s' _
    原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115- ~. D5 J1 P% ^3 ~. W" I. ^. z

    # H" k! b& y& x* N) q4 H) u3 S" l! G
    ; j$ p) J; n. E8 i
    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-7-10 09:26 , Processed in 0.378502 second(s), 51 queries .

    回顶部