QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2426|回复: 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 h" x, d2 k4 T5 Y9 O7 }5 ]: {9 H5 [
    C++数组指针、函数指针、成员函数指针
    9 Q/ c. v% {' q. Z; _- b; ~$ ?
    4 g& _5 h4 [) f9 N4 }; @- t# Y5 {) S3 h" _
    操作符名称
    - F& p: g; _" p* \& 取地址符(Address-Of operator)
    4 M( B$ S0 \* J# u; ]  A: Z5 @- F* 间接寻址运算符(Indirection operator)  \/ \! G. B" B+ S, g1 ^
    .和-> 成员访问运算符(Member-Access operators),用于取对象的成员。* a6 p  |' [3 T4 k4 U" J
    .*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。) _0 j, l$ k- X/ a4 p
    () 函数调用运算符(Function-Call operator); o+ [$ o. _/ S, ?8 u; w' w
    :: 范围解析运算符(Scope-Resolution operator): K2 ?$ D$ t3 ^, K, y
    如何定义一个指针变量
    ; N6 |9 z" }% M/ ?  |/ ^- g: d假设类型T, 变量名称name, 指针的定义如下:
    , a; G' v0 [6 g$ V# ]3 Q0 E5 k1 |( c- T) ]! v  p; x
    T* name;4 A( W3 b: Q, D) z
    标识变量名字name, 它是T类型的指针。例如& F5 D. h  ~+ O2 n5 o
    . O; b' i& L- ]) H
    int n = 0;+ Q- T# I& O/ x
    int* p_n = &n;" g0 ^2 j$ l" G4 a8 O$ ]' n
    p_n是int指针类型, 指向某个int型的对象。8 T& C! p  R* P- D( V
    1 @: d7 t/ Q5 R# U7 d, X
    指针变量的修饰
    - P# z! R) M" P" q0 X; f& X$ V& f指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。
    : L; n9 p) r3 R: N  l1 b" c+ f* a* _
    指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:
    - E  v3 y) @& o  ?# Q- ]2 p
    " ?/ Z4 t5 p7 W3 lconst int a = 1;
    ) u: D- \: ~" ^2 C+ Uint const b = 2;
    ' c! D9 }. f  Z既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:" |' e8 @, e4 V

    + |8 d6 R& T( Gint a = 1;
    , @" n: I. ^6 Y$ ]9 a8 _% Eint b = 2;
    " B& c9 S8 h' o/ x3 uint* const cp_a = &a; // 指针的修饰词,放在*号后面。
    8 P  V* u1 h" x3 [*cp_a = 10; // 指针指向的值可以修改8 I  @5 N* G. r7 m5 \
    cp_a = &b; // 指针不能被修改,报错!
    3 F  \; x# L, \总结带修饰的指针的格式:
    3 I# _. _, u* K5 n只要记住修饰词总是放在被修饰的内容后面。
    & \/ q4 V9 W0 f8 \# |/ U9 K
    % w6 K+ B5 k8 tcv表示const / volatile修饰词。指针定义形式如下:, c- H1 y$ a. J) j" Z
    0 I: u7 T3 Q4 \% K. @
    T [cv for T] * [cv for pointer] name& `' Y: n  b( D( k
    注意对T的修饰放在T的前面也是合法的写法。8 \9 _( z  z2 i

    ) j+ C6 k; u# @2 ]0 o8 `const int const c = 2;8 o5 A- T* }" c2 o
    在mscv编译器下也不会报错。
    - e1 s( K6 u  S. s" S, J; f
      s; a  k- X( L) U. A( |完整的格式:
    % O( J- j6 J( G. i3 r- R[cv for T] T [cv for T] * [cv for pointer] name0 h; o; k, w! }! g; ]/ E& R/ x1 p
    - b, p$ @9 ~5 z% p# i0 x' Z2 ~
    Syntax        meaning+ r2 e) J5 r0 G$ S9 A% l# Y5 B+ Z! A& T
    const T*       
    3 {: C: w, ]: u6 P5 N5 F4 [pointer to constant object
    1 j" K3 a9 N& s  I7 |% B6 ?
    + C! Q5 i+ `! k3 @T const*        pointer to constant object7 Z# P0 R* R( J. Q+ N/ E
    T* const        constant pointer to object( c& I3 }+ L6 L; E
    const T* const        constant pointer to constant object
    & C1 T) [# G  x" ^1 s6 }% |T const* const        constant pointer to constant object' s+ Z* c: }2 s% h0 h& m# K: f
    上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。, Z( g1 d' l: C0 y' k; H( @
    2 a) ~8 m. n6 ?; H$ C7 e
    int a = 1;9 \3 [2 p6 g! p
    int b = 2;8 }% [' m: s' o# M  o! d; h

    % o1 B# q! o: t8 ~" J( M$ mint* p_a = &a;% Q% s" A! n$ D& n' G2 ]4 Z  v
    *p_a = 10; // 合法7 o# a' L7 D" [; z6 d
    p_a = &b; //合法; v- i2 O( \- M2 X

    ( Y: E3 P! p* V0 p2 R2 Z3 w' I  mconst int* cp_a = &a; // const修饰int类型, 并非修饰指针  c) r  T6 [" T0 n6 w( v
    *cp_a = 11; //报错! const int类型不能修改/ A. K% d7 A0 b
    cp_a = &b; // 合法, 指针没有const修饰,指针可以修改。. ]1 ]( C+ x: V! Y6 O+ ]) c

    5 K& A- p! {) hint* const pc_a = &a; // const修饰指针。类型没有const修饰9 N7 b! F' F* X: _  V) }* G9 g) x
    *pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。# h  v1 Y$ r  O4 b5 v
    pc_a = &b; //报错! 指针被const修饰, 不能修改指针。
    % R* O- a  L5 ?. d: f: j+ g( ]+ |! v0 K4 q- I
    int const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰
    & a6 s8 K8 C- I$ Y4 G*cpc_a = 13; // 报错! 类型被const修饰,不能修改。
    : G  v1 l+ z1 S/ [0 h- rcpc_a = &b; // 报错! 指针被const修饰,不能修改。8 y$ ]/ y) H2 h, x% W

      N8 s+ L0 J1 S9 F0 b: m3 q2 q) I 更复杂的指针的指针. c/ i6 l. l  Q  K/ S6 |  p

    3 [. u3 |% \6 tint a = 1;
      `3 `/ ]. _& Aint b = 2;2 ?9 m. A' d& D  G2 S7 r  M: Z
    int* p1 = &a;5 E; r5 @& x" g0 ]6 J/ R) D
    int* p2 = &b;" ^4 T! T' {; T+ H
    const int* ct_p1 = &a; // ct for const type
    * ^( q" l- ~4 H. ^! }2 \8 v1 tconst int* ct_p2 = &b; // ct for const type
    ' [2 d* t" V2 Q# ~" E) l' q0 f
    . {2 C5 V4 K% s) p: a/ k- |, E// int * * pp1; 指向(int*)类型的指针
    3 G' R4 i  {& K7 Xint** pp1 = &p1;  4 j, M9 q6 w2 x9 A( U
    pp1 = &p2; // 合法,
    ! `/ l2 [7 a' K+ Q" `! J  c1 w* epp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)# {# z; U$ ^, ^% ~

    , Y, P* d, i! k4 _* @// (const int) * * pp1; 指向((const int) *)类型的指针
    , C$ i# H. ]4 L2 F% J8 i7 Rconst int** ct_pp1 = &ct_p1;  
    6 a- U% I7 E7 z; B; gct_pp1 = &ct_p2; // 合法
    + k% o6 b+ v% l. _! E+ Wct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。
    5 g) q4 i2 d: ?/ ?' q( B4 Y
    / |6 x1 }5 ^5 I8 [// (const int) (*const)
    ! D: o, V) n8 R$ U6 g3 z. B# \' Pconst int * const ct_cp1 = &a; // 指针也不能修改, I3 b9 }3 j0 g+ R
    const int * const ct_cp2 = &b; // 指针也不能修改9 l2 n# v7 F% {$ }: F
    ct_cp1 = &b; // 报错!指针有const修饰
    9 E0 n; F( [% s0 W+ }$ x$ o& l! C5 v8 }; s5 \& T
    // (const int) (* const) *  指向((const int) (*const))的指针, t) E) l3 q& b2 ^: U" t
    const int* const * ct_cp_p1 = &ct_p1;  2 f0 R% p! f1 q' U) w8 }. F
    ct_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰. H4 v0 O& M& G6 a. H7 K* r4 d
    *ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改5 l6 n- [5 N8 I" G5 K

    : Z/ N( n3 Y- p" r// (const int) (* const) (*const)  * j1 Q6 g: M% t7 w4 j
    // 指向((const int) (*const))的指针,且该指针被const修饰. Q3 ^/ N2 b' _1 Y7 f
    const int* const * const ct_cp_cp1 = &ct_cp1;
    / l# h- f* }6 T2 x4 J$ D, X$ E. [ct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。- T0 b7 {2 H  r/ b7 ?

    2 h8 o5 @! p, r$ g. u' ]一行声明多个变量
    5 ?- B4 p( c6 y! \" _: \类型 + 名称定义一个变量。) n; ?! O" X8 F# H( C
    变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。( Y# x% X9 J+ E$ |

      ]( J/ `2 N7 m& L+ w4 qint a, *b, *c, d, **e;1 h9 n/ X. {8 P0 a
    a = 0;) S- q/ ~  m3 t& q- W; R
    d = 1;
    , l8 M, ~) _$ W5 T; o0 C3 Nb = &a;
    % ^2 ^; ]# m) _( G. B1 Sc = &d;! X+ y7 Y# L. T  K; Z: \* m
    e = &b; // e为int**类型 指针的指针
    6 g4 r1 V8 K/ I3 j- Ie = &c; // e为int**类型 指针的指针
    * @+ M  Z, C) o也可以用括号包围变量和*号。0 Q5 v1 W$ i8 l7 n; P# P! w

    2 L: z$ Q/ J5 L0 }/ }% m+ Xint (a), (*b), (*c), (d), (**e); // 合法定义。0 N) ~6 J/ F  Z9 P* ]
    括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如
    4 c- a. f6 h5 g+ H: s
    2 ?6 E8 Z9 Q! Jint (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;7 [4 Q1 Z+ j/ o6 @$ F# Z# A
    写成0 F. O3 u( Q9 [3 `& f7 t2 ^
    ( t+ X/ m" K4 D. J  w  V
    int a, const *b, *const c = &a, const d, const* const* const e = &c;8 A5 I- O6 m# a8 G5 W
    更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。
    ( t9 w# l9 O- s; v9 T
    7 ^. W' l, v; K" z6 a) F数组指针
    1 ]0 B) I& D9 n$ |- X1 W: V数组基本表达
    ! t1 x' H6 O1 o- ]9 U" k0 Z2 Jint a[10];  // 定义了类型是int, 元素个数是10的一个数组。& @/ L( P" E: U6 i, H' ^% y! n) `
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得9 I9 N$ O. e9 X7 v! N7 k3 D5 K
    - Q% d9 H' F% U: T' f. o3 ]3 Z2 A3 d
    int[10] a;: X$ b, B/ h; _. X* [
    这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:
    9 p! g& L( g. [% }5 k7 v( T, Y+ V
    ; @2 r# J) c& Q) F. Oint a = 0, *b = nullptr, c[20], **d = nullptr;$ u$ s, z8 {) j
    c++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。
    ( a* |+ Y# f7 f) M1 x0 R
    ) `" y2 I) Q+ P7 Z9 o/ Jint a = 0;
    8 v6 }& @% c# x) `& I  @8 {8 iint* b = nullptr; // 指针int*
    ( b9 x, C  i; u& B- P0 X) S: Sint c[20];
    , L4 U- \# \' Jint** d = nullptr; // 指针的指针int**& |9 `6 R, q! h( u/ q% C. ?+ ?
    数组的名称是什么类型- M! i' S3 f2 {- c5 l/ C
    数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。
    9 F0 n/ `" l' y, a
    ! w  f, e  h3 y5 K$ {& r0 K# Iint a[10];
    6 `8 c+ x5 N' o" f/ Iint* p = a; // 指向a数组的第一个元素7 [/ \3 M& P7 W5 C% P
    a[1] = 1;% L4 F  E1 d( D' {5 e) i
    p[1] = 1; // 效果与a[1] = 1一样。7 K1 A+ h9 j, G# P; L
    数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。/ I3 F# g, I/ H- q
    . K" f2 r! Q. M+ e2 [% [) J* ?0 H
    int a[10];
    # i" B( i9 |- s% q' ^3 xint b[10];
    ' `1 M+ n# A% w# \int* const p_a = a;
    3 Q" o$ E1 q7 }7 S. p! Za[0] = 1; // 合法。 数组的元素可以修改。
    0 k( r0 Q+ y1 x2 a! V* a* Pp_a[0] = 1; // 效果与a[0] = 1一样。6 `) S/ j; O# i

    : W) U9 F  K. H9 q. oa = b; // 报错! 数组本身的指向不能修改。
    , j, `( u" g8 @
    8 R5 u' s$ @) b  J// 所以数组a可以当作int* const来使用
    6 k' ]7 i7 D+ h/ l) k* e( S+ Tint *const& ref1 = a; //正确。2 l# M# F/ U' L5 ~' Z
    int *& ref2 = a; // 报错!. b! v% [; n4 Y% b1 T' f

    $ b! Z/ }1 s# `2 i// 但是又跟int* const有些区别。
    ( F; Q! {1 s8 X( P' Y5 i+ ]assert(sizeof(p_a) == 4); // 32bit程序。' k+ C- `( L1 E
    assert(sizeof(a) == 4*10); // 32bit程序( n; C* e  C& `

    ' M6 U% i# [( k3 c, y数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。" c( _6 L0 _% O9 k2 x2 [

    ' w6 g4 l. C1 Y6 s! D1 E以下3个函数翻译成汇编以后,汇编代码是一样的。: W/ i+ e. j) T/ k
    8 o% `& o) I+ ~$ e$ g, Z1 ~4 t
    void Func1(int* p_ary)
    / ~+ ~* U: f6 d( t% N8 {$ v3 R{3 o8 Q( B; M& i% I4 T0 e+ t0 |
        assert(sizeof(p_ary) == 4); // 32-bit
    7 j! [' {) P& e3 c8 |) h# d    p_ary[1] = 1;; V3 O8 B# n4 }, g# ]0 R; \8 [
    }# x' B* o: G# c

    6 A1 ~! m9 H6 b" X) lvoid Func2(int ary[])
    4 K5 H, J" W+ _{
    % x1 m2 p; @5 v- h    assert(sizeof(ary) == 4); // 32-bit; Z3 {) J4 e0 W! I0 }
        ary[1] = 1;
    # L$ ]1 m- T- K& D( U3 }}
    5 g9 v# ^( j5 X! C& s
    # z# c& f( @1 {" t5 fvoid Func3(int ary[10])
    / b0 i  @0 @" V& V. V{
    . r' [) U4 m: k7 K    assert(sizeof(ary) == 4); // 32-bit" t! K& y$ r$ ?; p
        ary[1] = 1;% P0 ]2 b/ H! }. g, g! m
    }
    ; y) M% k! \5 ^0 r5 P
    1 C# u7 ^( k* L# T0 D; e7 ~int main(int argc, char** argv)& ]+ u, ^5 G- A1 k( R6 [* }
    {
    . m+ X; C+ ~( F& L( N    int a[10];& b1 K# y9 G' @6 H. X8 E% w0 U
        int b[20];# {. x) c8 I5 d; v5 O
        Func1(a);
    " Q2 G2 R7 e' ]" Q8 L    Func2(a);
    - J/ M! {3 ~9 r' J+ T8 T7 V    Func3(a);4 _9 a+ t5 n: B. W- r/ c
        Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。) A5 b" O! e7 ~' A. t5 l* A  j
        return 0;
    ) ~6 t! P, f- w8 A: f6 l% |}
    4 _  Z1 Z: }3 x9 s0 g2 T7 Z( C5 t  y2 p( F/ Z  J" O
    ! I5 v9 z" c, s; K6 u
    ' n" ^% ~: ?6 [* J
    多维数组
    & J4 f/ {3 Z& P/ W$ \9 g" m+ f一个3行,4列的数组, 结构如下:
    0 v% K1 i2 {; u) G: R, _. I$ x
    " T- A/ y2 h8 @/ A; gint a[3][4];
    . L4 ^. ^6 @: Icolumn 0        column 1        column 2        column 3
    $ Z" a, J6 [% \; K% b/ Wrow 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]
    ) H4 e# ]- T0 L% M" ~1 brow 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]
    & c3 k7 t( c/ g  c& Wrow 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]
    9 p6 r3 \# D$ k9 ~6 @数组初始化+ h: ^! x* Z$ F# ~

    - [2 W! q. a) _3 V' _int a[3][4] = {) L/ p( g6 y& F! {0 O$ v; `1 b$ ]
        {0, 1, 2, 3},
    : J) Y- H  `- o# E* m    {4, 5, 6, 7},
    ( o. z# l" W/ n: q! c    {8, 9, 10, 11}
    7 l# a/ p' X. h2 k; b) D};! K& ]* g9 Y1 g' ~
    实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。- {' ?. Y: s' k% D" z5 D# r4 m
    : A+ y, q! r) g, `4 H
    void Print(int* p_ary);
    3 x" n( N9 {' c: s
      K1 I/ c9 S' y' G  }8 Z5 I) xvoid Test1()
    0 t0 _  |( K% X" b7 I0 L' h{. Q* `, H$ A3 h% h% \
        int a[10];
    . k6 V! f: Z0 p. G2 p  W    a[3] = 3;
    + P% S" u: G0 i- @  R+ x6 e2 _6 x    a[7] = 7;
    6 `6 l1 M# u( A6 {    Print(&a[0]);) m( {$ x7 o6 |2 ?& S3 ~
    }
    : ]9 D) n; q- G& V
    ' p- u7 c; A  B( X6 uvoid Test2()
    ' p5 H, w1 b/ Q/ ^. ^3 ~{
      I& N# i* D( T3 l" M( O( {  n- }    int a[2][5];/ O& W$ V. b& D% J" Z' q
        a[0][3] = 3;
    7 |6 t. [0 \$ Y5 N' G: v- x: t5 J    a[1][2] = 7;
    / F% |/ {$ E$ M& D  a    Print(&a[0][0]);* }! h1 K# ?. a" G! i5 W  \
    }
    0 W4 v# l) n( G  N. E* U
    ; \1 D' |' x. ?1 w! K1 Y+ j! J
    0 K# }, @) H. k1 _% C+ o
    很自然地,多维数组也支持用1维数组的方式去初始化。
    , l! J' A: y, m# M
      e" e0 j0 Z8 \. S8 e6 ?8 P7 Gint a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };, b) ~( s' S, y$ V5 p
    既然多维数组与1维数组没什么区别, 为什么还需要多维数组?1 |) R; L1 I$ m2 p$ e) Q. T, Q' N, l" p
    ! N) H( Z6 T1 L# R
    假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:
    : D5 Q9 T  S) h1 |+ b: b0 y
    9 V2 Z3 X7 ~6 g2 y0 q# \& U( aunsigned char* p_rgb_image;6 c& V# u9 L7 E. z- o$ p$ d
    unsigned char r = p_rgb_image[40*720*3 + 50 + 0];9 H7 \  [& [7 [6 d, X3 o
    unsigned char g = p_rgb_image[40*720*3 + 50 + 1];& p1 E# w' E# N. U" }0 J
    unsigned char b = p_rgb_image[40*720*3 + 50 + 2];
    6 V  p, S! E4 s) d3 r8 ?/ e3 X7 S$ Q0 d! K, ^类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。. Z- R( {# e% p. A) y

    , B4 v) v. e% ]# v5 L! ~' Y' uenum
      K: x% }7 G" h$ Y/ l, o: z{$ x/ S) N$ @" X: b
    Red = 0,
    & P4 e4 U* }' {0 X Green = 1,( t0 u7 M1 R- I# F* g4 N4 V5 ?
    Blue = 2
    " S) v  v& r% {$ K$ m};
    / U. p- O# o/ a% S, T8 i( ?0 I7 Funsigned char rgb_image[576][720][3];
    ! s4 s$ x# Z& d) p& Vint row = 40;
    - t+ i# n+ u) p# b- U) |! S4 ^int col = 50;
    0 }7 \, \& H3 V4 F. P4 g8 }3 H# t, xunsigned char r = rgb_image[row][col][Red];/ p% o  U6 j1 A/ J, n
    unsigned char g = rgb_image[row][col][Green];
    2 z* p' o2 O0 G$ ^! o. ]unsigned char b = rgb_image[row][col][Blue];
    ' @3 H" ?" A! E# B数组指针以及与指针数组的区别+ d+ ^1 C0 P& q- p* A- k
    数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。
    $ {. q+ N1 R& ]; r- P4 U指针数组,是一个数组, 数组保存的元素的类型是指针。5 G! Y- h: V- W. T3 H
    数组指针的定义9 ]1 q/ K) b- Z; x" F( F
    数组指针定义先定义一个数组。* o! m6 K# H& Z
    int a[10];
    " r1 b. z+ t( Y" Y然后对数组里的名称用括号括起来后再在变量名称前面加个*号
    * B$ q7 G. y/ R# p2 L' {7 ?int (*a)[10];
    + ~3 U% ?3 z% w! L: }# G后面你会发现函数指针定义类似。
    / c+ X( D" D8 Y6 H9 x: t// 各类定义对比
    5 z9 ^! i+ q- ?5 c4 F0 gint a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];' r4 q, A/ U0 _3 f1 o' Q% L

    9 C) `1 o/ g1 ^7 ^8 `int *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int*
    & k7 M# J% R  j3 t6 H4 X* K: Mint *(f2[10]); // 指针数组。另外一种定义方式。+ ]  L+ O/ i5 n1 p
    int(*f3[10]); // 指针数组。另外一种定义方式。
    7 ?5 w; j7 _( S7 \* @9 Rint(f4)[10]; // int数组
    4 _6 c' z. a' gint(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组- {, s5 y4 ~, Z. }! Q( T
    int* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组0 ~6 H9 J/ f3 ^- y4 f0 H1 M

    + ^/ d* M! O3 C6 Nint d[10];. n0 u+ A* U" _; q7 x0 l7 F) C# f, {- A
    g = &d;
    8 \4 |: U# t# ?! w0 d1 e" T& N) _1 Z; s+ r3 @9 N+ Z, A1 T
    int* e[10];
    / G% ^# ]* J7 hh = &e;+ I: m9 w3 v* c" F0 s' y: `
    数组指针的使用/ \& }$ O" ]/ s/ S9 F( l
    数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。+ i- _  k' ^" D4 Q

    ) t4 S5 ^) V, pint a[10];
      r% P6 d* W0 m# v0 i* Y+ K6 Kint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组& J6 w0 v) t* ~& Y
    for (int i = 0; i < 10; i++) {
    / M8 O8 k' _: R; _ // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象
    7 G$ y8 |; M& Y, u // 再通过下标操作访问元素。
    % v4 G4 ?+ a  t) S+ Q# Z (*p_ary) = i;
    3 ?( a: B* e  ^, {" _9 W}) w6 D+ }& A! p) R: N! G
    , w9 n" F& d/ x- E( Z
    int b[10];# A( e$ v: @9 B0 t( h
    int c[20];, `' B( B3 A0 E, \
    p_ary = &b; // 合法
    ! X$ O! ~% z# Q+ yp_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体
    1 a' @1 k/ w3 g7 E( t: M数组指针指向多维数组的子数组
    ; D, g! m7 }  z2 i6 vint a[10];. L" N: n8 Y0 _
    int b[4][10];0 {/ @4 ]& }, a2 I
    int(*p_ary)[10] = &a;4 d0 G; S* ^: n8 V
    for (int i = 0; i < 10; i++) {  D5 |% @. R( a& [  ?: M, M; B
    (*p_ary) = 1; ; Y: z3 q) z; I  @' R1 v2 {" j  T
    }, j$ z: x: [0 O. x' X/ v3 U7 ?
    ( X, _. `* n& p/ |
    p_ary = &b[2]; // 多维数组,可以看作数组的数组,$ c; X  b+ l# z& f- X
    // b[2][0] ~ b[2][9]的值都被改成2了
    ; Z) z1 h# Q- ]# u5 Pfor (int i = 0; i < 10; i++) {9 Q! _, _, |& E6 n* {6 n/ H
    (*p_ary) = 2;0 h# r: W+ X7 D6 w( A1 @+ G
    }$ z. q! E* N( W% Y% m  A! Q" l
    多维数组指针8 a0 Q/ q" d9 N# j5 C4 P
    多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。
    4 m- U  @% A/ ?% G3 a7 L" j! A- m4 t5 M0 @1 I. }0 E
    int a[2][5][10];2 P0 m# ^4 @, H
    int(*p_ary1)[10] = &a[1][2]; // 1维数组指针
    ; j1 D1 [( h: R0 x% k6 wint(*p_ary2)[5][10] = &a[1]; // 2维数组指针
    - o  S. M+ Q8 S# ^2 a+ {for (int row = 0; row < 5; row++) {; C0 o+ m* `2 u* k4 h  H
        for (int col = 0; col < 10; col++) {! k: {/ D# d8 Q" `) p: j
            (*p_ary2)[row][col] = row * col;. O, T8 Z9 l6 `
        }1 P5 x. w) w) w0 a4 J; R
    }
    9 T+ K, X' w0 b5 e; d* G( X数组指针和指针数组对比实例2 c" v# F" f% w9 u& c) O
    数组指针还是记住两步法即可8 u1 ^! @& x6 R2 ^, F; L2 ?: {

    . L# o' `7 ]3 k' a$ U定义一个数组
    4 Q" I5 ?/ P% u* R) S% ]5 S6 a括号包围1中定义的名称,再在名称前加个*号。
    + f8 e% p: W8 vint a[10];
    1 I7 V; u, R* l& Rint(*ary_pointer1)[10] = &a; // 数组指针7 U! M$ U1 E* P( l' R6 L0 I
    int* pointer_ary1[10]; // 指针数组。元素类型是int*1 P( f' S3 j$ v
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。
    7 B( X1 T6 ~& l; Y. C. g! tint (*ponter_ary3[10]); // 指针数组。另外一种定义方式。
    2 R; w" E& p+ f" Dint c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。, u4 [! j! e; k7 P& t+ L9 y
    ( D8 C9 |' T9 Y" i
    // 指针数组可以把每个元素指向数组对应位置的地址。
    ! i; D# D2 L: V4 B7 V// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,4 {$ `* y$ g% g  ~
    // 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。. ^' O/ i+ i; N
    int* pointer_ary[10]; % }& H+ m3 n$ n7 ?/ [/ ~
    for (int i = 0; i < 10; i++) {
    0 c+ J1 e$ M* e+ T8 {    pointer_ary = &a;
    % ?: a/ z# \8 a/ r: p}
    / L, |/ H+ E, R( a( H: c// 类似遍历原数组效果。  e/ i/ x9 {: Q
    for (int i = 0; i < 10; i++) {+ b( i+ ]' d6 L+ u1 t- x1 e
        *pointer_ary = i; // 修改原数组。
    ' g6 B- S! n1 p! l}
    8 F3 _% d$ K+ n' J# B' B- X% C. ^  [3 z
    函数指针3 {/ X! S; ~( i7 Q
    取得函数地址
    + F! ^+ }1 ?1 J) w" ~( o函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。
    # v, m$ i4 [; @
    * W9 K4 `5 G0 X+ I3 _/ gvoid f(int);
    0 Z. |5 ?1 ?8 i8 _! {* t6 d$ ]int main()
    6 [2 t1 B8 @5 m) ]& `- u& Q{
    1 @, `3 I4 P% C" D+ Q% N  `6 h, C    void (*p1)(int) = &f;
    $ T6 p+ o: K- a% \. {. E1 f* l6 R    void (*p2)(int) = f; // same as &f
    9 i. D9 e' e- `: q, C0 y    return 0;
    1 G) x1 _0 a/ O( o+ ~4 }}
    ( N. h$ y, f+ t. p% T, j) ^翻译成汇编代码, p1和p2的赋值是一样的。( P) T# _3 Z7 o
    1 a, U' I! X; t  `4 r$ {, V- w
    ' z. U# k; b! O0 f3 m& @( T

    - V$ E  o9 f' R$ g4 ~- K函数指针的声明
    / o1 v1 e( ?! B! |& b  k单个函数指针变量定义步骤3 C# k% b" c% t: X) S$ W7 d9 \- k4 `
    定义一个函数。void fun1(int a, int b); int fun2(double a);
    ! U/ F! d6 f# Z0 y( Z# U# T! m用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);9 N! a- U) W% Q( e  D/ H# H  A
    如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);% ]4 ~! A# P; s
    typedef定义函数指针! ^. x9 f: b6 E, r, b* A) c6 Y! R) h
    可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。3 i4 W: y8 i7 [8 @6 Y3 z
    4 q" e7 A+ b( I# }
    typedef定义函数指针的语法
    , \8 X  y9 ?  u( [/ O8 k: ftypedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。3 y# A1 A5 l$ T9 d* d2 c

    ( z1 Z/ q$ a3 |- f2 t5 O3 V" |typedef int FuncObject(int a, int b); // FuncObject类型是函数对象
    5 g. x" H6 l4 A! t. o2 J$ Otypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针$ f: x# Y+ h$ D) B7 N/ k, y
    FuncObject* f1 = &Add;# y3 t7 ^6 H1 A+ l' V
    FuncPointer f2;
      w6 D$ P  v5 ^: e- P7 Qf2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。4 @+ G. I/ l$ C% f* Q/ S
    FuncObject f3 = Add; // 报错! 函数对象不支持拷贝
    1 V) w" L% L& l. I  m. I. l2 NFuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配1 w6 x1 k; }$ F$ s( ^: Q
    FuncObject& f5 = Add; // 正确0 n2 v& O/ R) p- y9 u0 L
    int ret = f5(2, 3); // 正确. Q2 t; `1 U. Q7 Y
    FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配
    9 R0 c$ c# f2 L0 P+ X- T如何记住typedef定义函数指针的步骤
    % N# T% |  s% X2 o8 k" I  ~) G像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);, F( Q2 X+ d* y$ X
    在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);  x5 T1 H; U  R
    完整例子
    9 [( a1 q9 x* s6 O* X) Ctypedef int(*CalFun)(int a, int b);
    9 V/ [* C- I; \9 Z( f, L! m( o& G  d  {
    int Add(int a, int b)
    " I# m/ `% p/ y{( ^: Q  M' }- X+ G
        return (a + b);
      v# W2 T9 I8 x+ N}4 m& t  p' D' }# d/ M" E
    % Z# q: I  [! p. I
    int Sub(int a, int b)
    6 o# b5 V( d1 \* I* K" k* l7 |{: ^" G: z" b5 b& U8 }
        return (a - b);+ t# L& c2 i; v9 K$ O- [# _0 G
    }
    ' ?5 ~! Y& C% [& F" G+ c9 Y
    3 s  X" C  ~" b7 X& K+ tint main(int argc, char** argv)
    3 _4 [% O$ E% L  C1 O{
    " g0 k5 i& c$ R. l2 Z% E" v+ f    CalFun f1 = Add;$ B6 z7 j' r1 d# H5 M3 a
        CalFun f2 = Sub;0 d& l, Z8 S& Q1 k
        int a = f1(2, 3);7 m; G7 g) O* H5 o8 j$ k
        int b = f2(10, 5);1 [  T$ D& h5 d
    , |+ ^' m* _) n* S
        // typedef定义的函数指针数组。
    - o- \/ {5 {4 u8 D  |  R3 O# A    CalFun f_ary[2];; {4 h9 `/ a# a: K1 v6 c
        f_ary[0] = Add;% y. K# z1 @8 c' p/ i0 X
        f_ary[1] = Sub;* D$ \2 C1 S3 q9 D: i' f

    * G# g0 L3 y. n; ~9 d    // 单个定义的函数指针数组。
    . c0 \' C# U/ F    int(*f_ary2[2])(int a, int b);1 e8 U$ V# w' I% H
        f_ary2[0] = Add;
    8 t4 ^0 F: Z$ Y( _    f_ary2[1] = Sub;- K+ U3 a; x( M. T; c

    : g: U. @/ @. j0 K1 ?) ?! o3 `    return 0;8 L- y  X; f1 V2 o
    }8 l1 r  H. V$ T; x

    5 {" u+ l( e& nusing别名定义函数指针  W8 M% s% U1 q: K6 B) S
    c++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。' ?, s$ m0 d' O4 K. G& h, ~
    8 {. g5 p, T7 C: c
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象2 x. t3 Q4 W5 e' a3 M( ]# E
    using FuncObject = int(int a, int b);" x. J0 f2 N/ {( V+ M1 W
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    , X& b7 u0 N7 [% C1 Rusing FuncPointer = int(*)(int a, int b);
    5 D. O+ x" S6 o函数指针的调用
    ) ~; n2 E" x: V+ Q- J$ q函数指针和函数对象都可以直接后加括号调用$ {+ x* I. t2 F* h
    int f();
    3 \  X' v$ M. e& x3 r, tint (*p)() = f;  // pointer p is pointing to f
    6 E' {4 w9 j2 q4 A7 J' X( fint (&r)() = *p; // the lvalue that identifies f is bound to a reference0 h4 M6 z9 D9 d. ~9 ~0 e
    r();             // function f invoked through lvalue reference
    . W' b  Q0 m& G' ^7 o/ g+ A(*p)();          // function f invoked through the function lvalue9 ]( E; h, J: O2 T' X. _. f
    p();             // function f invoked directly through the pointer
    9 B' c6 F5 Q+ J# U. w如果函数有重载, 函数指针会指向匹配的那个版本。) N/ d4 Y: a: C  e
    template<typename T>* _6 o. M5 g' |7 Z% W  M1 j
    T f(T n) { return n; }
    , N# D& o  A& q0 X# b; t; B) Z' p
    7 U3 e+ @  N' N3 O3 U) R! gdouble f(double n) { return n; }) |9 B$ [' @7 `/ T+ _

    + F  t# m( q6 c4 `4 bint main()/ ^# |: }5 i( c# a
    {2 }# }( A, i% z, X
        int (*p)(int) = f; // instantiates and selects f<int>
    1 |) u) ?. M( @- A( N7 t; |# g}
    6 V! d1 s3 K9 G成员函数指针, c3 P* C9 T4 p& u% @" r
    静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。
    ' m/ k; S+ |. l0 m' e" K- `+ @7 ^$ w0 [
    成员函数指针定义。
      ]# b& g' t5 n$ D5 Q" ]/ G像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);& W% t6 A+ a( C& D& `" Q! ~% f
    括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);: k- S6 X4 O! n( Y8 y5 T
    在名称的前面加个*号void (ClassName::*func)(int);( a+ b" G7 P5 F$ M/ k
    成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);
    ( K& v1 J3 [! z/ {成员函数指针如何调用。
    % \% l. ^$ W" d5 s1 z, h假设成员函数指针名字为func
    - f/ e" a. V, ?$ w# Y& {% }0 s# g+ S: `/ {, ?& h% \
    void (ClassName::*func)(int);
    8 B2 D$ L! U$ F, v  j, S对象式调用。0 T' @- o  f* r! L
    ClassName c; // 被调用的对象
    " {; h% q/ W, b  a2 K成员函数指针名字当作正常函数那样写。
    5 n* e  T( T4 Y( s3 @c.func(3);
    , |9 y+ u* H6 _5 {+ U% M# b! D5 O成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。& `  B- u  H& B- F, M5 P0 P
    c.*func(3);7 `1 x( ]7 r9 c$ }+ Z3 T4 ]
    最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
    0 i' c! s3 T2 {+ q8 F- p6 Y4 R) l+ r(c.*func)(3);
    # w, ~$ u% V9 z3 R- Q  {5 G$ Z为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外% q" P$ ~$ i, t  X* Z
    (c.(*func))(3);
    5 P7 y& q4 e' v5 f* A  N% F这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。
    # e3 J/ h3 y- m8 z指针式调用8 [# z$ @) p. w- z$ i
    ClassName* p; // 被调用的对象的指针
    / u' {0 y8 S! [( E; N+ X成员函数指针名字当作正常函数那样写。; V2 p5 A- a* Q# J+ s* V* E
    p->func(3);
    9 {  Y: h0 S. A1 K成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。9 S& J( Z+ a' J" x
    p->*func(3);
    5 d+ M# u: H4 V: A7 K; [6 C9 a  \- q最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。
    % y6 G  r6 f4 z(p->*func)(3);
    7 ^2 N& l: Q: ~) t+ D& c9 [函数指针使用完整例子
    4 K& s8 B- m  m& D3 o, ]7 h. D# |struct Cal   j- |+ m$ c. K: M
    {
    ! a2 S  k5 h% }& i1 [2 P    int add(int a, int b); ' r% w# k) n* A1 ?0 ~, S
        int sub(int a, int b);
    ( k- F+ t# e& ^3 T7 O% C/ \- E};
    : i0 J, [* q+ W+ p. v
    ) ]' B3 C% K, ~; _$ i. r) A! Dint main()
    . j) I8 N- u8 Y6 T  d7 W{
      x7 d1 F% B) y/ a# S    int (Cal::*fun)(int, int) = &Cal::add;
    4 v0 e: Y5 f! r/ m* S    fun = &Cal::sub;
    1 Q+ K" C! h6 ^8 r9 D+ J
    . y6 E. n0 b: d: J" K/ v, {; W8 K' K    Cal* p_cal = new Cal();, D* f6 d5 W! k- P. ^0 ]' ?" V
        int r1 = (p_cal->*fun)(2, 3);( u+ o- T0 G# S9 g5 h
        delete p_cal;
    3 d" S! m. q. f. @- w+ W( ]) J9 W) P( _! i
        Cal local_cal;
    / g5 Z8 ~$ O1 D5 g    int r2 = (local_cal.*fun)(8, 6);, Q/ `: l  H: v: H* Y
    }" ~8 N! i  A! R- _1 s
    8 }  M9 \% x& r; v
    成员变量指针
    2 Q7 P, a( q0 V4 N* H( {% t! ^成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。
    + v( r" O* Q6 I7 m: A2 k, w
    5 i+ d0 \9 v0 T成员变量指针的定义
    1 e4 m# H5 {7 V, {) b& j假如以下结构体C。& z" Q' r, @' I$ e8 s- S6 Z

    9 q- |5 M/ K- D* cstruct C ) I( c" h4 ]6 p: S! v: C
    {
    $ B) x. R" W3 P# J. }, y2 T    int m; , D" X0 i/ U1 P3 Q
    };
    ) P% {, t; x7 K. i6 S. A+ C单个成员变量指针定义
    " s4 _5 u8 ?9 g/ O( V8 x! L假设名称为p, 类似静态成员变量定义那样声明, L$ y/ B: T: \* [4 N' F
    int C::p;# `1 A4 A% U/ |  S. Z8 @
    在名称前面加上指针标识号* # n8 I  N3 s! z  S
    int C::*p;5 F3 Q6 b6 F" p
    typedef或using方式定义
    % u# I( ^& d4 U$ t& Btypedef int C::*MemberPointer;% x- x' H" p9 w( n  a3 I  r# V
    using MemberPointer = int C::*;
    7 ^3 v; t, V' i& V/ y0 |4 c" w成员变量指针的使用。
    ) Z; f+ T/ \: P- w! h( s% K* v类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。
    . \# u. t3 r9 `4 r0 K$ x成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。
    ' n$ d* D6 B* z" |8 Q; z  d5 C1 n0 F
    5 q& c. G$ P( `$ b+ N 完整例子6 N9 l. J3 d+ }) P( V# U+ {
    struct C { int m; };# J4 }3 z0 |2 i/ ]' a- r
    int main()
    . L0 f8 `4 V( a' k) U2 h" T{
    % F6 |) s9 b3 h( Q% u" q% r; p    int C::* p = &C::m;          // pointer to data member m of class C
    6 x9 P: D! _8 `    C c = {7};
    ' E& ]3 p9 M6 I5 A/ B, |8 ~    std::cout << c.*p << '\n';   // prints 7! M( Y- U; F) D4 ^1 p+ p3 ~
        C* cp = &c;
    , P, G6 J+ |/ A    cp->m = 10;
    . J3 N' E! V$ I    std::cout << cp->*p << '\n'; // prints 10* [. K. L% G* S) G. X

    3 g: d' H* h" b, k) g————————————————; g1 j* ^5 S' T+ r& k
    版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。+ V+ Z0 c% _* a% z4 M+ q
    原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115$ [' S4 \0 b8 V3 U/ ^
    " Q. d* O1 i1 E. r' A9 T

    & V, X1 L" V. @, q- {- V/ A
    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-6-18 01:49 , Processed in 0.416042 second(s), 51 queries .

    回顶部