QQ登录

只需要一步,快速开始

 注册地址  找回密码
查看: 2391|回复: 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++数组指针、函数指针、成员函数指针6 s3 f2 d2 t  i1 _+ o
    C++数组指针、函数指针、成员函数指针
    : m8 T( P% z& \) {  s# c; j; M4 {( w/ z! }

    ( `% ~0 D4 n% M! D7 C! B操作符名称
    7 q- K; {" z/ |- {& t2 D. L& 取地址符(Address-Of operator)
    - o1 _9 i9 t& N* 间接寻址运算符(Indirection operator)
    9 w8 Q3 v7 i, T1 n) F: C* ~9 ^.和-> 成员访问运算符(Member-Access operators),用于取对象的成员。
    7 U& k2 \; U; y8 m; z" R.*和->* 指向成员的指针运算符(Pointer-To-Member operators), 用于成员函数指针和成员变量指针的取对象。3 Y' A4 U' N/ l- z' u3 Q
    () 函数调用运算符(Function-Call operator)/ r' s3 k( l/ p: X
    :: 范围解析运算符(Scope-Resolution operator)
    ; c$ _: Y9 P9 D  {( m0 k, D9 N如何定义一个指针变量' q, Y4 v; }2 ?% I3 g/ `" J( r# z4 F
    假设类型T, 变量名称name, 指针的定义如下:, |, _, p" H/ q% P3 n5 h
    " ?. a2 n: q. A3 g* B  ~' o" b! R0 e
    T* name;5 Z  \4 L3 Z, X5 ^
    标识变量名字name, 它是T类型的指针。例如
    * `% ]$ O2 B: S! ~0 X* L
    3 R7 N2 {1 P; _$ tint n = 0;; ?, c& x1 M6 J' d) ]* G. [" }
    int* p_n = &n;
    - m3 n* P# q7 W. Z' N( f! }' }p_n是int指针类型, 指向某个int型的对象。, Z# c6 O- `4 u% L1 e! g
    $ m! b8 D: S2 C9 w
    指针变量的修饰- f$ Q' c0 b8 J# J, O
    指针实际上也是一种变量类型, 只是它保存的内容有些特别, 是指定类型的地址值,通过间接寻址运算符(indirection operator)*, 可以访问到指针指向地址上的指定类型。+ n& y# p8 _8 f  r( Q$ l! H

    " W7 z  n3 C$ R# U8 X指针也可以用const, volatile修饰。 const int或int const均表示一个变量类型是int, 且该变量不能修改。以下两种写法都可以:
    ' L2 [9 q9 T5 r' i0 d7 }2 e5 A/ p( K7 n
    const int a = 1;' t% D6 u4 E+ v  f4 T2 \7 ]. V. z
    int const b = 2;7 g/ b# o4 x* K8 d  D
    既然指针是也一种变量类型,同样支持被const修饰, 表示指针的值/指针的指向不允许修改, 指针所指向的那个变量是否允许修改, 那是另外修饰。写法如下:: J5 k/ m3 q8 b1 ~, N9 m" t

    3 B7 J( [, s- ~5 b9 a" O- i# v' fint a = 1;, O/ p0 o) ~. u9 x9 [" |6 m
    int b = 2;3 o# Q2 q; t6 c$ G
    int* const cp_a = &a; // 指针的修饰词,放在*号后面。' \9 T/ T) L3 Q8 `+ g" U
    *cp_a = 10; // 指针指向的值可以修改0 ?' j/ H" c# ~3 }$ j8 _9 [) d
    cp_a = &b; // 指针不能被修改,报错!7 M6 I& |8 O- m  `. L
    总结带修饰的指针的格式:( e' d6 v) J+ f% J, \2 @& ?
    只要记住修饰词总是放在被修饰的内容后面。) r# w+ H3 A% I# P% w; i/ }3 _* t" m* F3 s

    0 Y$ A5 L$ z. ucv表示const / volatile修饰词。指针定义形式如下:
    3 @7 ]7 {- l- U1 e2 W- |& }+ ]/ m5 f2 E
    T [cv for T] * [cv for pointer] name
    5 M2 e. j! N/ G. D注意对T的修饰放在T的前面也是合法的写法。5 _; {5 V9 ?( `, Z9 W/ m
    ; F& s( v+ j  k; {' A
    const int const c = 2;  y" G8 ]  m+ ?
    在mscv编译器下也不会报错。
    ( ]" U  G* u3 r/ j) A
    3 p# j& |7 _5 Y" Q2 J7 O5 |完整的格式:
    ; U# [# o( v" x$ I7 w[cv for T] T [cv for T] * [cv for pointer] name: E0 M& g% m  h$ ~: I6 D& W9 K
    / `$ J9 Y9 D- E' X4 l4 c
    Syntax        meaning
      b4 d8 l3 z! w! iconst T*       
    , x! h% d. F0 e( t5 f' J# \" b# Kpointer to constant object
    / J2 @0 P: f2 Z$ ]7 Q! M$ d- N1 N+ V# {4 t- S
    T const*        pointer to constant object$ O4 y& ?: m% ^; b
    T* const        constant pointer to object3 H9 v% o- p7 x9 E% N: s& |
    const T* const        constant pointer to constant object) z6 o5 D& f. e: e8 [- e
    T const* const        constant pointer to constant object+ b3 R" Q. n  f  B& d
    上面格式中T还可以是一种指针, 指针的指针仍然是按照修饰词总是修饰前面的标识(T或者*)来确定修饰的意图。* b7 @6 G" u; ~/ P8 n- w
    * y4 S- n: N" @9 @$ j* ^- V
    int a = 1;6 E) E  B, u- C) e$ d& n% `
    int b = 2;1 F, I' A% M4 s8 X
    ( _$ Z) i2 c; C$ Y+ O
    int* p_a = &a;
    " B: \! U: {1 B5 o6 x5 h*p_a = 10; // 合法3 P3 ~+ Q3 t8 D9 L7 Y. t
    p_a = &b; //合法  I& X  w/ X0 f
      |9 }* L, V5 N( c+ T" H
    const int* cp_a = &a; // const修饰int类型, 并非修饰指针; r  ]+ T. p7 i& n
    *cp_a = 11; //报错! const int类型不能修改+ _) ^# H$ g( P8 L8 A
    cp_a = &b; // 合法, 指针没有const修饰,指针可以修改。
    - b$ K2 Y0 ]+ u- ~
    $ n# Y; H) C: s1 q7 H. C6 D8 J; ^int* const pc_a = &a; // const修饰指针。类型没有const修饰' m/ o" I; Q+ [# a
    *pc_a = 12; // 合法, 因为类型没有const修饰,可以修改。5 _4 \7 p6 [8 l& `& l8 L
    pc_a = &b; //报错! 指针被const修饰, 不能修改指针。# _+ [& ^1 Y: [2 D2 Z
    ) O9 x% l6 N! u
    int const* const cpc_a = &a; // int类型被它后面的const修饰, 指针符号*后面也有const修饰
    ! T0 o2 c" ^7 f' P% A' D*cpc_a = 13; // 报错! 类型被const修饰,不能修改。4 A$ a; C! d0 u2 @; q4 _, }* D
    cpc_a = &b; // 报错! 指针被const修饰,不能修改。' e8 q5 E; x0 F! n7 G" M

    * ~8 G" g4 V0 X2 I, d, k. J 更复杂的指针的指针/ R! W7 i5 R7 ?! u

    * v  r! u$ q+ q4 J7 m& v1 Bint a = 1;
    % `. x$ j1 y; Jint b = 2;# x7 W  _" h8 {: a2 |- F3 ]/ M- _, n2 W
    int* p1 = &a;
      n# s: s6 {6 w% V8 @& W" [/ hint* p2 = &b;
    ; d$ `# n: j+ G4 X" Hconst int* ct_p1 = &a; // ct for const type
    ( F4 Q) E1 `0 Qconst int* ct_p2 = &b; // ct for const type
    0 v2 r8 G' {9 N* t# ^4 c/ h
    ( n. k8 j3 ?5 I3 w: F: @// int * * pp1; 指向(int*)类型的指针4 e4 I! }0 w- D+ k" \; u/ V% {
    int** pp1 = &p1;  * h1 R0 H( J( o7 T$ U8 S
    pp1 = &p2; // 合法, ! W5 f9 w  W- G4 M8 ^, h3 ^2 c/ X% Q* u
    pp1 = &ct_p1; // 报错! 类型不匹配。 (int*)不能指向(const int*)7 `( l% S, l1 ^+ J8 R

    / p4 I+ T- p  O" }4 K// (const int) * * pp1; 指向((const int) *)类型的指针! E9 U5 [# I! P7 l" E& Z
    const int** ct_pp1 = &ct_p1;  . M+ C) [. {0 C% F0 Z
    ct_pp1 = &ct_p2; // 合法) Y' v7 L; U% ~, Y
    ct_pp1 = &p1; // 合法!(const int*) 可以指向(int*)类型。
    # S( W  g3 h" {7 w/ T' W1 Y; V5 A4 U, P7 k0 a1 _7 Y
    // (const int) (*const)
    4 \/ P; B3 T* _const int * const ct_cp1 = &a; // 指针也不能修改
    ) o$ L* S# d4 U9 h. l1 v9 `const int * const ct_cp2 = &b; // 指针也不能修改
    - c" A# Z/ B# F5 P* V3 Sct_cp1 = &b; // 报错!指针有const修饰
    & y7 M# C: b* m% ]$ f; c" }& o" I
    // (const int) (* const) *  指向((const int) (*const))的指针$ h) {. }" j% b
    const int* const * ct_cp_p1 = &ct_p1;  
    0 O* K% C6 Q) U: c; ect_cp_p1 = &ct_cp2; // 合法, 指针的指针并没有const修饰, 指向的指针有const修饰
    8 p/ Z9 V" Z* L8 p, _7 j*ct_cp_p1 = &a; // 报错!等价于操作ct_cp2,  指向的指针是带const修饰的不能修改8 W0 o9 |! e  s# n3 E, [
      d& m+ `1 n- V; y6 W
    // (const int) (* const) (*const)  
    ! Z* D, ^+ W3 p" o$ E- b// 指向((const int) (*const))的指针,且该指针被const修饰
    9 \& Z# j8 W: `" C- d2 Nconst int* const * const ct_cp_cp1 = &ct_cp1;
    8 \# I% E9 k+ O/ b. Oct_cp_cp1 = &ct_cp2; // 报错! 指针的指针被const修饰, 不能修改指针指向。
    , k$ B/ h/ j  z, w) b+ X$ F
    ' H$ M; D* a. T! g4 q( ], W2 m: \/ R一行声明多个变量, Z% a: D0 z( t$ C
    类型 + 名称定义一个变量。
    + J. G+ j* z# l1 u. S2 N变量的前面可以加*号修饰, 表示指针, 一个星号代表一层间接。**表示指针的指针。
    / h! l4 w& f: H1 K" l" D/ k3 E, z5 [! O* k* i6 W1 [8 p
    int a, *b, *c, d, **e;( y6 t2 a- ]/ \' ]0 |; F' u2 m
    a = 0;
    / I( o  H  u6 D- h1 H6 a* _d = 1;
    * l* |9 n) c( f: k6 f, v$ D, eb = &a;
      q) [1 K6 S# V; L" Kc = &d;+ o0 I6 K) T0 u
    e = &b; // e为int**类型 指针的指针1 c" D. e- Q- M% V0 X3 [* ]
    e = &c; // e为int**类型 指针的指针$ }+ i' |7 d  N# A* x6 F
    也可以用括号包围变量和*号。3 A8 a: M+ o" ]
    * L: N" M) B8 c& w4 X. I+ a
    int (a), (*b), (*c), (d), (**e); // 合法定义。$ S+ n0 e- z% c: X( ]
    括号可以省略,某些情况, 个人感觉加上括号更清晰一些。例如( F. `- ~* C, R
    - t6 ~# V5 C4 [  a: ]1 ]
    int (a), (const *b), (*const c) = &a, (const d), (const* const* const e) = &c;& J! z* v7 A5 v
    写成) Q& j0 y/ J# ^+ y8 o# ?  @
    - o9 O& @, G/ s: j( y
    int a, const *b, *const c = &a, const d, const* const* const e = &c;
    / a& A  X8 C  w3 T- r更重要的是, 后面我们表达数组指针,以及函数指针时,括号是不可缺少的, 带括号的表达更加统一。
    ' x; M7 }8 i# m' c2 A, ]  t4 _" V+ K; k
    数组指针0 _4 a$ m! a7 x: |- x* L, E( O
    数组基本表达
    * ]; ^) V1 _& g* ?2 [& Bint a[10];  // 定义了类型是int, 元素个数是10的一个数组。& m7 p! b2 u$ K0 P' ]
    由于c++要支持一行定义一个类型的多个变量。 所以数组的[]时放在名称后面的。虽然我觉得1 W$ x4 h3 q: u2 e5 S

    - d/ v; F' X: v" \) M  M" p8 oint[10] a;- w  z2 Y# O! {) q8 J; l) H
    这样的写法更符合类型 名称的思维, 但是如果类型都这么写的话, 没法兼容以下的写法:
    9 Y- s/ D# d# O7 ^
    & r" f6 f) |6 d0 d. oint a = 0, *b = nullptr, c[20], **d = nullptr;" X2 [* k  R& F$ w' A' K
    c++标准规定如此,但我们可以通过每一行只定义一个变量的写法, 类型会更加清晰。7 {# X* Z- g% m

    5 s* m( r7 v" Y1 J7 H( M: ]+ h# n  ^int a = 0;
    : w3 b3 p9 M% F/ z& b# d! {6 \" \int* b = nullptr; // 指针int*
    ; l5 G. E( J2 a. b% q$ C* {int c[20];
    " E- ]# G7 E# ?( P9 p$ Uint** d = nullptr; // 指针的指针int**1 y% q! z' }8 B* p& C; n3 P  {. X
    数组的名称是什么类型+ |' g0 q7 Q% h
    数组元素类型的指针,可以直接指向数组。 并且数组跟指针一样,可以通过下标去访问元素。
    4 p" W$ d+ p  E0 ]& i
    : A; e; D' {5 b. [2 g. cint a[10];( g! ~( n* p1 r5 s$ ?* H# J
    int* p = a; // 指向a数组的第一个元素& l' l' ~& p" o
    a[1] = 1;4 N! M* K% C% P; n* a/ X1 z
    p[1] = 1; // 效果与a[1] = 1一样。
    5 I6 ~' D/ @) Y- l" W数组可以当作T* const来使用, 但是又与T* const有些不同。sizeof()的结果不一样。
    ! U0 H' F8 c5 N4 Z2 K8 w: J# b  J/ [/ z
    int a[10];
    & k6 S  t6 j) Z3 G$ A- s. ?int b[10];! d1 I6 v* }$ D! g
    int* const p_a = a;& c9 V' ~' i# C( I% o+ P8 R
    a[0] = 1; // 合法。 数组的元素可以修改。
      ^; x% Z' x  c+ v) y5 ^; j8 Ep_a[0] = 1; // 效果与a[0] = 1一样。) [$ t, J1 _$ ]' J

    # U, Y! o3 v% ?a = b; // 报错! 数组本身的指向不能修改。
    ! O* n3 b( T9 Z9 u% \1 u$ j! K  f9 R5 A/ {- R* T
    // 所以数组a可以当作int* const来使用
    7 t% o: P; A& M. }) C' ]3 ~int *const& ref1 = a; //正确。
    % Y/ [/ S/ ^9 n# w7 O# N2 uint *& ref2 = a; // 报错!
      t6 P" X- ?% s. f2 i$ _8 q/ Y- R1 I9 J+ I0 `; i
    // 但是又跟int* const有些区别。
    4 e: f4 @2 L0 M; k( _4 x# Zassert(sizeof(p_a) == 4); // 32bit程序。' Z' f4 E! ~* M+ s! j8 O5 ]3 |
    assert(sizeof(a) == 4*10); // 32bit程序* p! |+ b* D; e& g
    0 e% R0 \6 f1 m
    数组跟元素指针的作用很相似,都可以通过下标去访问元素, 但调用sizeof()函数的结果不一样。元素指针的sizeof()返回值是4(32-bit应用)或者8(64-bit应用), 数组的sizeof()返回值是数组实际占用的空间。数组可以当作指向第一个元素地址的T* const来用其实就是我们常说的数组到指针的隐式转换。当数组作为函数参数传递后,会自动退化成T* const, 在被调用的函数内部调用sizeof()的返回值跟T* const指针大小一样。 数组传递作为函数参数后, 在被调用函数的内部与T* const是没有任何区别,只有在数组定义的可见范围内sizeof()才有获取数组占用空间大小的效果。4 w; s# A, R) H$ x& m1 v4 ^5 M" T1 m

    & D1 ]6 R" F. n/ X以下3个函数翻译成汇编以后,汇编代码是一样的。5 h$ Z% s# q6 \, Z

    + Z: W+ x+ g: `# evoid Func1(int* p_ary)
    ! ?; U8 l) s  i! v* x/ E( j{
    / s6 _' F" ?9 |+ `' o* i8 }    assert(sizeof(p_ary) == 4); // 32-bit( f+ j$ s, A) H/ F6 j- n! y% Z
        p_ary[1] = 1;
    7 N( i4 n: V% q) L% U8 Y6 G}0 z! X8 _9 L3 S/ I  V/ {2 w

    7 D( V* w9 C' l2 e: ivoid Func2(int ary[])9 _( W' z. u- |" |7 X
    {3 Q; L2 O# H7 P1 B$ l6 {8 ?
        assert(sizeof(ary) == 4); // 32-bit* A2 C0 o0 G* o6 M4 K
        ary[1] = 1;' G; R, o. ~* C) c
    }" X" L3 F8 t/ ^' t0 l
    . F6 V0 V/ ]  k/ l8 j* v6 o
    void Func3(int ary[10])
    4 w- w- t- v7 U6 v2 T{
    ( s7 S# l) E! C( a4 O    assert(sizeof(ary) == 4); // 32-bit
    0 I7 h( U8 S/ x- e% `  c    ary[1] = 1;" t; g/ \4 T2 P7 `  q2 m
    }3 v; q, g2 u4 H; w2 z& y

    ' x& X7 [0 y0 [9 T8 ?) nint main(int argc, char** argv)1 D* U3 u$ w# ?* `/ j
    {; c1 o3 a6 H: D" o2 ^1 R6 ]+ ?4 O
        int a[10];
    6 S5 E4 V, L' F8 b/ S    int b[20];; U/ k: X' l9 l$ y7 V( F
        Func1(a);
    4 e$ b5 @4 M1 h0 A$ M    Func2(a);8 @/ l9 }5 E  F! t
        Func3(a);5 }6 U( X, P! C1 X
        Func3(b); // 退化成int* const了, 即使数组长度不匹配也不会报错。
    " d  M; W- e* M    return 0;3 A2 m1 S" L. {4 H
    }7 L" o( s  T2 G* l% L
    6 A. Q/ X$ T+ u8 H  s" x9 u0 k
    - B# a' U) h8 x) A* M' n
    1 u1 u) {4 z' N7 l- i! ~: W' i
    多维数组1 f8 B1 m' W% @" {0 U! U- t
    一个3行,4列的数组, 结构如下:. R/ s, @  D* w8 s
    7 |- S6 O# y9 U* x% v* b
    int a[3][4];7 E4 y9 v, W8 s# V: P+ `) s
    column 0        column 1        column 2        column 3
    : L. L  m. G( I+ r( p: Q6 grow 0        a[0][0]        a[0][1]        a[0][2]        a[0][3]
    ( F* c0 p( R3 W, F: ~row 1        a[1][0]        a[1][1]        a[1][2]        a[1][3]
    , v( I6 n$ q& o% y: Hrow 2        a[2][0]        a[2][1]        a[2][2]        a[2][3]2 j* g6 |; `  N: ~
    数组初始化
    * Q9 e' r% s+ A
    / z* y! n2 v, v# x' j2 O. K7 \int a[3][4] = {
      t$ n! J- z7 r1 q% y4 q8 z    {0, 1, 2, 3},$ F0 `' r# p1 w' _: z
        {4, 5, 6, 7},
    $ g2 i! e9 T+ x2 Y    {8, 9, 10, 11}
    ! z& H8 `2 W7 |. C: x" w7 {};
    : B4 U; G' m( y( Y& y# g 实际上多维数组和1维数组在开启速度优化后,翻译成汇编代码是一样的。
    5 E6 P7 j  F: K: c% l
    " t7 n2 i9 q# a# s- Xvoid Print(int* p_ary);
    6 Q+ w( e$ x9 h5 {- X' l% b) P$ d2 G
    void Test1()
    ; K6 A3 G  ^2 R; H* L9 C! _% q{
    0 Q; U6 D0 O$ U8 W' ?7 I. l    int a[10];
    ( n6 J+ x3 H) Z) P    a[3] = 3;; ^  k6 [4 p! z: d) p  x3 k" N
        a[7] = 7;
    ( X: J- u. ]( x: ?, ^; e9 W    Print(&a[0]);
    7 y; a1 y% c4 Q# R+ g}7 t8 r1 x5 ^  a* R. A

    ! V# p' o6 M% ]# p. r  P$ Svoid Test2()! w' q+ M; c; ]2 `1 d1 v; Y1 n
    {
    8 I3 b: L- R' m3 ?9 {# W- x6 Q    int a[2][5];, g2 ]# V4 A4 Q( C( A
        a[0][3] = 3;8 R" v1 _9 q" Z7 M' M
        a[1][2] = 7;4 q7 d  r+ Y  r) y5 l  F
        Print(&a[0][0]);. s& b; Z; y" V$ B
    }8 E& {4 k. z  H: i
    0 J" m! o! U+ D

    # p; ~  R5 h: M3 B# I$ k- j3 Y9 _$ m; C3 j, }* R8 [
    很自然地,多维数组也支持用1维数组的方式去初始化。
    + Q. x1 C2 @. x; t* w6 k$ |8 D7 U
    - g* v2 v1 N$ S# w* Pint a[3][4] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11 };
    3 ]3 q% c2 `- ~' n% ?既然多维数组与1维数组没什么区别, 为什么还需要多维数组?
    4 S" N+ m6 e) I# I+ p% o8 ?/ x1 Z6 C$ l& s- P% s! N8 u, Z
    假设有一幅RGB图像720*576个像素,每个像素有RGB三个通道,每个通道的值是8bit大小。给出图像的首地址p_rgb_image, 我们要取第40行,第50个像素的R,G,B值。代码如下:
    9 a% a4 v/ ?3 T3 L6 A: E
    0 f; T( g8 V) P" F& cunsigned char* p_rgb_image;
    ' ]% r; q  \& H0 u: a9 e/ g& a: dunsigned char r = p_rgb_image[40*720*3 + 50 + 0];( P& b. r: O. b0 `
    unsigned char g = p_rgb_image[40*720*3 + 50 + 1];
    3 @8 W1 X; D/ R9 B9 }  Zunsigned char b = p_rgb_image[40*720*3 + 50 + 2];5 C3 G- M; A4 M* i1 j3 }4 e
    类似这样的场景, 采用多维数组的写法, 有点类似以索引为参数,可读性更高。相当于程序员和编译器打了一个配合。+ x' b: _* @) N+ ?$ u/ M  [5 c

    - {1 ?8 S: E0 k/ oenum2 P6 l0 e( T8 }+ ]9 q5 G
    {  F+ F$ N/ q7 a3 M; o
    Red = 0," m, W; l8 M  ^) b  O8 p2 e
    Green = 1,
    & H. d1 e0 b; q* ]/ }8 r Blue = 25 {: f5 R# n" Y& f2 l
    };9 H$ L% l  P0 ~3 |& ^
    unsigned char rgb_image[576][720][3];! _" ~5 X9 }3 r/ `/ a0 n2 `
    int row = 40;
    1 u3 C5 F8 L6 ?" y, B+ vint col = 50;
    + k" T  `$ g* Z+ C- X0 Y4 Funsigned char r = rgb_image[row][col][Red];, _4 q0 h1 R1 e& t
    unsigned char g = rgb_image[row][col][Green];6 d* z' k% j' c- B# v6 \  o% T
    unsigned char b = rgb_image[row][col][Blue];+ G& W9 n2 N& ~/ x2 F1 X" H
    数组指针以及与指针数组的区别
    0 u' P, ^0 @- N( u数组指针,是一个指针, 指向的对象是数组。 数组指针的赋值,要求数组的长度匹配,否则会报错。当指向1维数组时, 需用用*取得数组对象的引用,再用下标来访问数组元素。
    - Y5 [! j8 j+ ^' h2 G指针数组,是一个数组, 数组保存的元素的类型是指针。
    2 t- X; ^- O# ~# X5 ~数组指针的定义
      k1 k) y4 U7 |. |' Y6 c4 y数组指针定义先定义一个数组。8 \3 G9 M: s! o8 K$ P
    int a[10];2 [$ k8 K" d; Q; _6 r
    然后对数组里的名称用括号括起来后再在变量名称前面加个*号$ `( x' c" ?7 B
    int (*a)[10];
    ( l# N7 q  y0 v9 K* a; x, r  |后面你会发现函数指针定义类似。: \! G" B# ^, a* P' y3 @6 j
    // 各类定义对比: \. M8 ^  U, H  h5 w  a
    int a, *b, **c, d[10], e[10][20], *f[10], (*g)[10], *(*h)[10];. ^$ x6 h: S3 {8 v( C
    , g- p; C6 F: ~% [) B, W, ]
    int *f[10]; // 指针数组, f是包含10个元素的数组, 数组里每一个元素的类型都是int*( {: D$ P2 ?9 X0 y0 e
    int *(f2[10]); // 指针数组。另外一种定义方式。
    + d# H- i/ ^9 J. dint(*f3[10]); // 指针数组。另外一种定义方式。0 ^, s; B, q$ d: ?- }% D
    int(f4)[10]; // int数组% |1 K+ Y# Z5 ]) d& R5 p: \
    int(*g)[10]; // 数组指针, g是一个指针, 这个指针可以指向类型是int,元素个数是10的数组8 g. `. f0 v( `# _2 f
    int* (*h)[10]; // 数组指针, h是一个指针, 这个指针可以指向类型是int*,元素个数是10的指针数组
    : ~1 ^0 C- x) \( C' R
    ) }$ I; E. d9 m" A/ Aint d[10];
    ' ]& ?' i4 j4 R' y% G4 \g = &d;
    3 a& S3 [4 v7 v" F2 b! l
    ; x  w2 c9 p1 h6 F2 l! qint* e[10];& ~) @5 Q3 K* y6 s6 @
    h = &e;+ n- L( M: S  Y4 l  P
    数组指针的使用0 D  ~# z  C; ?) V, u+ q2 a1 G
    数组指针一般先通过*号取得指针指向的数组对象, 然后再用下标操作访问元素。
    , V4 K8 K3 @  X! E  F! e1 ~' N) S* e+ [" l
    int a[10];
    , y; V' k. n4 m* n6 @9 J. e4 D0 v* Yint(*p_ary)[10] = &a; // p_ary是一个指针, 指向"int (*)[10]"类型的数组
    0 C  `( n7 w2 q( Dfor (int i = 0; i < 10; i++) {
    # ?5 u4 r$ R' ]" M; u // p_ary是一个指向数组的指针, 需要先通过间接寻址运算符*(indirection operator)取得数组对象/ ~9 o% p' j4 |1 p+ F% C
    // 再通过下标操作访问元素。0 U4 Y  p& \% H, A$ `; _% W1 Z
    (*p_ary) = i;
    & J1 d) ?% [3 m3 u+ ]# I}, U  K, w' |2 j

    9 Q, C/ d8 X+ r% ^9 ^5 P$ S0 ]" eint b[10];
    & |; d- P" f  d( lint c[20];
    ( g" A' d9 v. {. z. \p_ary = &b; // 合法
    ' N$ d7 s* z" f4 o% d- F9 \p_ary = &c; // 报错! 不能将 "int (*)[20]" 类型的值分配到 "int (*)[10]" 类型的实体& V* d" c1 O" h  F  y$ K; s
    数组指针指向多维数组的子数组
    8 x8 d$ V6 U% oint a[10];& }2 |1 X- i) }' c7 n: b
    int b[4][10];! U9 n) R. g; v" u7 [
    int(*p_ary)[10] = &a;
    * @# |+ @3 P8 a: c% F- f7 a( Pfor (int i = 0; i < 10; i++) {
    4 d7 j. k! y6 f+ r9 ^  N+ t (*p_ary) = 1; * C$ I9 c* n; D6 J: J8 a1 }
    }: [" ^9 ]# E/ S! ~  X
    % g$ Q( ]' b; L' j5 R
    p_ary = &b[2]; // 多维数组,可以看作数组的数组,: {) |) C2 I2 E3 A  R& }, z& z
    // b[2][0] ~ b[2][9]的值都被改成2了
      K0 \  N' l. }  V* lfor (int i = 0; i < 10; i++) {
    + F5 j& M( T. [9 E( m3 e$ G (*p_ary) = 2;$ h' M' O* H% n; V, E  v
    }
      Y$ L6 F3 r8 @  u0 q  N多维数组指针
    1 J* B- r  ]$ l) k: e' F$ g5 w多维数组指针,是一种指针,指向的对象是个多维数组,支持多个下标操作。; G- h+ i8 y2 h4 H8 r
    0 b4 l7 @* R. k8 J
    int a[2][5][10];5 S" D8 n7 C/ S9 R5 E
    int(*p_ary1)[10] = &a[1][2]; // 1维数组指针
    8 N5 B$ V% E) g1 U9 H$ |2 J: Nint(*p_ary2)[5][10] = &a[1]; // 2维数组指针
    ) {7 L! k% h/ \# _" x# Cfor (int row = 0; row < 5; row++) {9 f" s6 ]8 ~& \$ C" z
        for (int col = 0; col < 10; col++) {5 k4 Z* X; R" M) s% j
            (*p_ary2)[row][col] = row * col;+ @# ~; j) m. ]; ]( q" o- U# g
        }
    4 z+ U' ~0 v8 V* J( g! C}
    1 U# O; [; d6 H数组指针和指针数组对比实例8 H% t. Q1 }5 k3 a$ W7 T' r
    数组指针还是记住两步法即可
    . i/ v6 O( h9 N7 V/ A
    2 p7 M% d+ L9 I) s# B定义一个数组
    1 @: M' U$ [& T4 w1 N9 u括号包围1中定义的名称,再在名称前加个*号。/ |' M9 Y0 B- f5 \, i' x4 ~
    int a[10];9 H7 t% k; m) d3 N0 f; @" L
    int(*ary_pointer1)[10] = &a; // 数组指针' [9 G6 |/ o# I# F) b
    int* pointer_ary1[10]; // 指针数组。元素类型是int*5 F9 N- U# y7 L) F# J
    int *(ponter_ary2[10]); // 指针数组。另外一种定义方式。" Q. u2 M2 [; \! L4 Y/ C* t
    int (*ponter_ary3[10]); // 指针数组。另外一种定义方式。& z& ?) m3 r7 G
    int c, *d, (*ary_pointer2)[10], *pointer_ary3[10]; // 排列定义比较。
    : Q, ?# p9 [' R
    ( O6 Y1 T: \0 h3 b9 l$ M// 指针数组可以把每个元素指向数组对应位置的地址。
    ! U0 [9 F+ b+ n! w4 ~2 e// 这样遍历指针数组, 可以达到遍历数组元素的效果,但是注意每个元素都是指针,% \: S7 s1 i+ Q0 S9 I% i6 f
    // 需要访问原数组的值的话, 需要对指针用*间接寻址运算符。$ \0 O( x. [' L# V+ C
    int* pointer_ary[10];
    & M9 h& r: P) }9 I  Ifor (int i = 0; i < 10; i++) {8 b0 g$ o% |: O. {' u( [8 |
        pointer_ary = &a;
    & y, e8 M. n& l4 v, j, ?}4 p7 [3 {$ S2 z' K1 [5 y
    // 类似遍历原数组效果。2 F& s# F: r7 G# j: g/ x6 z
    for (int i = 0; i < 10; i++) {8 d4 j) W3 d* O9 u+ B* n5 ]- B
        *pointer_ary = i; // 修改原数组。
    - H3 S; T8 R3 v0 d}
    / x$ k9 |. H) W) O+ M2 m2 l$ U# J! Y! w5 `2 u
    函数指针
    + J; D2 J+ d9 N4 B& x# l取得函数地址
    0 i( `  p2 r: }3 m4 B函数的名称作为参数被传递时,会隐式转换成函数指针, 和在函数名称前加取地址符&等价。建议带上更加统一和清晰。
    2 {& H9 y# b9 p# ]5 t; |, l) m2 |5 S& J2 ?1 A0 Y* t4 r
    void f(int);
    3 D/ O7 K3 }( I% u4 x7 W. y) o6 i7 W) j" Jint main()
    * d4 l2 s8 c9 v{: I" M# a, _, r, l" l
        void (*p1)(int) = &f;& b3 ?! R6 v$ y/ {6 n
        void (*p2)(int) = f; // same as &f
    % x0 r# w( r& h* f& ]    return 0;' [7 \  Y0 N8 q9 R5 {
    }
    8 L, m8 P" B: R+ z+ s) U* l) e翻译成汇编代码, p1和p2的赋值是一样的。1 v! ]( }! q, W$ d
    . c4 N: I! z/ F

    5 [, j, ]8 _# y& z4 l3 I5 _; w. g1 D* x& h" j
    函数指针的声明
    % s- T+ c/ @; u, D: ?. {5 N4 f单个函数指针变量定义步骤# h" n/ W; b' k; Z& O) v1 C! f
    定义一个函数。void fun1(int a, int b); int fun2(double a);
    : O+ w* F9 o- ^! T, v0 L! y, ?用括号把函数名称包围起来,然后在名称前面加*号。void (*fun1)(int a, int b); int (*fun2)(double a);
    * d0 ~" @0 \) f如果要定义函数指针数组,在定义单个函数指针的基础上,在名称后面加上[数组长度]void (*fun1[2])(int a, int b); int (*fun2[10])(double a);8 e2 O" ?- T4 J3 O7 h. l
    typedef定义函数指针
    ) K: ~1 [0 Z; H. `$ b可读性高比单个定义要高,特别是声明多个同类型的函数指针,或者函数指针数组。
    ; D+ O; l# O: S! ], `, H8 z1 n: L; B: h
    typedef定义函数指针的语法, A5 U# b/ J6 M$ L# i! B$ [/ Z  t
    typedef有两种做法, 一种就是定义一种函数对象,另外一种就是定义函数指针。用法稍稍不同,效果是一样。其中函数对象不支持赋值, 但是支持引用。
    $ k4 P. H8 s* Z, W2 }4 j9 @4 {8 Y; V' V
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象9 G: X" Q$ g0 f/ Q1 G4 l4 }$ o
    typedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    2 S& i& i4 v6 ]! }4 ]0 @( hFuncObject* f1 = &Add;
    + B# E3 W0 T+ |FuncPointer f2;' R2 g& n0 q: `: |2 ^* w$ r) g
    f2 = f1; // f1, f2类型一样, 都是形式为int(int, int)的函数的指针。. A! x! n4 `# u! r& l
    FuncObject f3 = Add; // 报错! 函数对象不支持拷贝' n' l! v: x) {" ^, P
    FuncObject f4 = &Add; // 报错!&Add是函数指针,与函数对象类型不匹配
    / h- J. K  _) FFuncObject& f5 = Add; // 正确2 B) O# X6 t$ x
    int ret = f5(2, 3); // 正确1 l; e/ `: s, L0 {% d0 U
    FuncObject& f6 = &Add; // 报错!&Add是函数指针,与函数对象引用类型不匹配
    5 L5 `1 l! U8 \9 M+ V/ R如何记住typedef定义函数指针的步骤
    9 `  s. ]* W. U4 D; |- l像定义一个函数指针那样, 指定一个名称。int (*CalFun)(int a, int b);1 ~* u% C& L5 I
    在这个函数指针变量声明前面加上typedef。typedef int (*CalFun)(int a, int b);
    / N) z- u0 |; }3 x+ z& w完整例子5 }/ f- K9 [6 m3 i6 s) p5 i0 j
    typedef int(*CalFun)(int a, int b);% v! b# V1 k+ x1 t. s

    2 Z, b0 j/ J( z# [4 Y* K' Bint Add(int a, int b)3 N$ `8 m* v- B% Y
    {
    # k1 n: f3 g; f  R) u  m/ {# t6 i' R    return (a + b);7 e$ d* w' H8 O# b: |/ ]
    }; k2 U% v3 R, r% D1 q

    9 Q7 I: E' O! C8 O9 E" |$ L. Eint Sub(int a, int b): Q4 D/ t9 M* f: B* f/ o/ M. ^- O+ f+ ^
    {- q; i# s& c. T8 j. X# g
        return (a - b);+ u1 T" B3 |* ?$ M: j, }
    }/ y/ g5 Q7 @' Z5 U4 p" C

    ; M; i' T0 U" j9 ^" ~int main(int argc, char** argv)
    ' w# y9 D4 z2 D& j' R$ ~{* o# N8 C% z: A3 ]$ _# y
        CalFun f1 = Add;
    8 f. U3 f# p+ h8 u0 H6 @1 e: [' h    CalFun f2 = Sub;
    ' u# Q4 w- a  `# G+ p+ _" w& ~  K    int a = f1(2, 3);
    5 |" K3 Y/ K7 n1 v$ j    int b = f2(10, 5);3 h* G& x1 R: _

    ) E: _& r9 {) Z+ m7 h    // typedef定义的函数指针数组。+ A/ z; _! P/ ~
        CalFun f_ary[2];3 `+ U2 E% i$ g5 `+ f" d% \
        f_ary[0] = Add;& t3 I( k3 L& U3 N: s6 J
        f_ary[1] = Sub;! X5 m0 l2 T3 Y; z/ C

    % O! @9 p& e8 t! m: `# \9 E, N$ O    // 单个定义的函数指针数组。
    $ W1 {8 a8 a- G    int(*f_ary2[2])(int a, int b);
    4 Q, T' N2 n7 C    f_ary2[0] = Add;
    8 W8 s8 R* |7 s( \  y1 g    f_ary2[1] = Sub;
    ( x* }4 X# r  I( c7 ^
    . i7 a( {" f6 o  A" o    return 0;
    % \* v" d% v+ R% e$ G9 h# O: c}4 |% P: H* }: J% e: Q3 C
    4 N# }5 Q. a1 h( o" b# q! V; u
    using别名定义函数指针
    1 v- Q1 m! E" b% E9 O) Kc++11以后的类型别名定义--using也可以用于定义函数指针, typedef的好处它都有,个人感觉比typedef更直观。using类型别名同样分函数对象和函数指针两种方式。
    3 f' Y  o& f- x( v2 s, f+ b) [( x; |
    typedef int FuncObject(int a, int b); // FuncObject类型是函数对象. V  {) o3 G% |5 b. n, ?
    using FuncObject = int(int a, int b);
    ! K  r. g5 d' ?( z0 @& H' P. E0 Utypedef int (*FuncPointer)(int a, int b); // FuncPointer类型是函数指针
    : Z( @, |6 r5 _. W# [7 gusing FuncPointer = int(*)(int a, int b);
    % j3 j' r. T+ ]% R( j函数指针的调用: w! N/ h* h4 @# I. h
    函数指针和函数对象都可以直接后加括号调用
    ) \  \# z' b, C  sint f();0 a- x8 [  d" P4 c6 z# C
    int (*p)() = f;  // pointer p is pointing to f
      }% K; H% r9 Nint (&r)() = *p; // the lvalue that identifies f is bound to a reference
    ; B$ a4 `' n( C6 yr();             // function f invoked through lvalue reference
    3 x6 m5 d0 \+ h. h(*p)();          // function f invoked through the function lvalue
    ) u- h5 `9 N; v3 _; T0 J# I! i8 {p();             // function f invoked directly through the pointer8 b. \2 A) e: U9 h' \
    如果函数有重载, 函数指针会指向匹配的那个版本。& O0 v3 l6 [$ M/ I3 n
    template<typename T>7 e3 ]5 m% h1 D$ `$ ]
    T f(T n) { return n; }) A( e1 R- _' z6 w7 m; n
    " v) D  y8 E0 }5 F
    double f(double n) { return n; }
    # |6 J9 n0 w; ?& ?! |8 D0 j) O8 V% v( B- r5 \1 x
    int main()
    ) w8 A9 k& G, k- v, Z, D0 s{
    ' N8 B+ Z2 V. f1 X; [    int (*p)(int) = f; // instantiates and selects f<int>
    8 L- H& [+ D% Z9 b8 ?  n' @}
    / |* V( s. Z$ g成员函数指针3 d: y' w6 {! _
    静态成员函数,除了增加了访问控制以外,跟普通的函数指针没什么区别,所以普通函数指针可以直接指向类的静态成员函数。但非静态的成员函数与普通函数指针不太一样,声明时需要指定函数归属的类名,并且调用需要指定对象实例。- e$ q4 j; T7 X' W( V

    ( R! Q4 |% R- G! e8 c成员函数指针定义。1 |1 B% m' z- z1 D
    像定义类成员函数实现那样写, 并任意指定名称,这里作func。void ClassName::func(int);
    : S% V/ i% N. I" c3 ~括号把类名、范围解析运算符::、名称包围起来。void (ClassName::func)(int);6 n" ~3 y6 O. R) V3 O
    在名称的前面加个*号void (ClassName::*func)(int);! n6 G' O8 t8 \% _
    成员函数也支持typedef和using的定义方式。typedef void(C::* MemberFunc)(int); using MemberFunc = void(C::*)(int);' L( E( E1 T; _* Y+ d5 Z) q* p6 ~
    成员函数指针如何调用。
    0 P6 p$ Y& n; o) Z假设成员函数指针名字为func# o- B& `! c2 D  B  t9 T

    6 V" u3 {+ c7 mvoid (ClassName::*func)(int);
    3 q9 R) }+ Q" q" g对象式调用。
    1 e5 D& ^8 K/ {0 N+ rClassName c; // 被调用的对象3 \" m# ]' S' Y# P9 p0 z
    成员函数指针名字当作正常函数那样写。3 x9 a' c. x! X1 a7 m( V
    c.func(3);
    6 m) y, Z& P& ]* \1 F成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。7 q$ d4 e) K* i' D
    c.*func(3);
    " d! E) H, U' Z( \最后用括号把调用对象、成员访问运算符.、间接寻址运算符*、和成员函数指针的名称包围起来。
    & @# B$ f" V2 E8 m(c.*func)(3);. ?4 V7 P$ W1 M9 n
    为何要加上括号? 根据c++的优先级标准,取成员运算符. > 函数调用() > 间接引用符*。 *号优先级比函数调用要低, 成员函数指针还没取得对象就被调用了,自然报错。 另外, p* l% M( K6 T; i
    (c.(*func))(3);
    ) ~; [* B" T; q2 {这样的写法也不行。 .*和->*是整体作为一个运算符的,中间不能用括号隔开。
    ; k% }* b5 P' B0 _指针式调用
    ) t$ P7 \% t. S6 U) MClassName* p; // 被调用的对象的指针
    0 e, j& n+ ?4 b+ \5 d成员函数指针名字当作正常函数那样写。
    3 C$ ^6 ^, b4 N( E4 V# xp->func(3);3 P4 ^5 N0 |) n& i6 }2 c5 n* X
    成员函数指针是指针, func名称前面需加上间接寻址运算符*,变成函数对象。
    1 Y% Z, q1 ]4 K8 {p->*func(3);
    & E% u! n' p" ]4 k7 G& U0 d最后用括号把调用对象、成员访问运算符->、间接寻址运算符*、和成员函数指针的名称包围起来。6 K! T* K. r' B7 V6 o9 l+ _7 {! I
    (p->*func)(3);
    : i% n5 _5 s$ J4 X- C9 E! L5 ~% k函数指针使用完整例子
    5 E0 N/ B2 C6 m% I* f9 _0 kstruct Cal ) d# \2 N3 q/ J
    { 0 O1 U8 z: k9 w& n8 A5 c9 v1 \
        int add(int a, int b);
    7 I8 r" d3 ~$ @4 s8 S    int sub(int a, int b);; N5 q5 W  W3 t, q+ C# ?
    };' x0 ]' c! a* @( a% s% L
    0 K2 E4 ^; ^7 O7 ^* o
    int main()' o" [2 H: p1 J  r& |; h" Q
    {' r5 _, p( T; Q+ `
        int (Cal::*fun)(int, int) = &Cal::add;8 A3 m* U5 }7 {( \5 U, I! R
        fun = &Cal::sub;. E( [4 ]& R4 k+ V( t1 Y! G5 |7 o

    ( T" c1 y. e& P; c- S* D+ Y    Cal* p_cal = new Cal();% J. B& [+ q# }
        int r1 = (p_cal->*fun)(2, 3);& `- ]0 J, W) [6 j' l$ P
        delete p_cal;
    & }/ H3 g- D6 X7 W7 }4 Y% i/ a3 w8 F8 F
        Cal local_cal;
    6 ^6 I) t6 |; q2 D! H# a    int r2 = (local_cal.*fun)(8, 6);: q0 p  k) f4 v( V
    }5 g9 p" v3 R9 a/ a% @! r& S3 e

    * h* D2 d8 H& ~& F成员变量指针+ }  F* V- h% \) C8 V5 y6 O1 R
    成员变量指针比成员函数指针还要简单些,没有函数调用, 无需考虑函数调用和间接引用符*的优先级问题。
    " M: G+ Q0 E. n& U( m" ~, U1 y5 Y
    成员变量指针的定义
    2 ]& z9 N4 S  s# R5 S假如以下结构体C。
    4 R; l6 L3 Q- t  J& H$ o# G/ C, g( r9 H3 j6 Y1 }
    struct C
    $ k1 l" P/ p) \2 M' w{ 3 e' l7 Z! k( }$ b
        int m; 1 S+ P. w! r8 {/ ~0 G9 [+ x$ [
    };; ?9 a4 S9 ?- y) j1 f* z4 P
    单个成员变量指针定义
    * k& b  |% G+ S7 r( K- [假设名称为p, 类似静态成员变量定义那样声明, G) _" L0 z, c0 [
    int C::p;  E- o) P1 E, T/ b, a  w; q
    在名称前面加上指针标识号* / f; [" z' ^5 Z' _1 H
    int C::*p;4 a) d' d: R9 x3 ~9 q, B
    typedef或using方式定义% X6 r7 E  _8 h1 Q
    typedef int C::*MemberPointer;
    ' W! p6 P  r! G, b0 o2 o4 W- Vusing MemberPointer = int C::*;
    ; H& l' K7 m, U5 @6 U" Q$ s+ D成员变量指针的使用。
    # T& Y! ]: ?( w& |/ M* L. v类似成员函数指针那样,直接使用指向成员的指针运算符:.* 和->*即可。5 Z: Y2 f. X# T: `6 e! t
    成员变量指针, 能让我们实现一些遍历成员的动态功能。 例如把一个类/结构体的多个同类型的成员变量放进一个容器里,然后遍历访问这些成员变量。, f5 ?9 E2 d7 U5 `! i
    ( ?$ G6 S( b8 q8 v8 q
    完整例子4 X& Y- u" n- _/ h8 S
    struct C { int m; };
    : l) e6 t, b  f. `- }4 |! ]int main()
    $ _2 H; P$ \& I8 d{
    5 i9 _! ?" D$ L$ a. g9 z% h' p, b    int C::* p = &C::m;          // pointer to data member m of class C* `2 u2 _0 b2 y) s
        C c = {7};
    9 w# u. G" I  v% B# K    std::cout << c.*p << '\n';   // prints 7
    : S6 L# x/ y# S; [# S    C* cp = &c;, R5 ]' v, R- f/ X1 s
        cp->m = 10;
    6 |$ R- R' V% s* w( F0 _    std::cout << cp->*p << '\n'; // prints 107 x: X  P. I; i" W
    * [7 f/ U( S( \2 s
    ————————————————2 s% w& Q' ]/ }' D
    版权声明:本文为CSDN博主「南风fahaxiki」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。; D1 T7 r/ o8 P0 ]# r" u# r
    原文链接:https://blog.csdn.net/m0_64407685/article/details/126788115
    ; _4 @$ q3 Q% Q* G+ ]9 l1 J6 `' J3 `  @! \
    ! T2 |: r* B3 b9 }# J- \& h
    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 05:32 , Processed in 0.433803 second(s), 51 queries .

    回顶部