代码之家  ›  专栏  ›  技术社区  ›  Brent Writes Code

在C++中传递和分配数组作为指针的后果是什么?

  •  11
  • Brent Writes Code  · 技术社区  · 14 年前

    作为背景,我在不久前回答了这篇文章:

    Return array in a function

    它无意中启动了一个关于C++中指针与数组的很长的注释链,因为我试图过简单化,并且我声明“数组是指针”。虽然我的最终答案听起来相当不错,但只是在对我收到的许多评论进行了大量编辑之后。

    这个问题并不意味着是巨魔诱饵,我知道指针和数组不是同一回事,但是C++语言中的一些可用语法肯定会使他们在很多情况下表现得非常相似。(仅供参考,我的编译器是 i686-apple-darwin9-g++-4.0.1 OS X 10.5.8 )

    例如,这段代码对我来说编译和运行都很好(我意识到 x[8] 是一个潜在的细分错误):

      //this is just a simple pointer                                                                                                                                                            
      int *x = new int;
      cout << x << " " << (*x) << " " << x[8] << endl; //might segfault                                                                                                                          
    
      //this is a dynamic array                                                                                                                                                                  
      int* y = new int[10];
      cout << y << " " << (*y) << " " << y[8] << endl;
    
      //this is a static array                                                                                                                                                                   
      int z[10];
      cout << z << " " << (*z) << " " << z[8] << endl;
    

    这个特定的代码片段使它看起来像指针和数组可以几乎相同地使用,但是如果我将其添加到代码的底部,最后两行将无法编译:

      x = y;
      x = z;
      y = x;
      y = z;
      //z = x; //won't compile
      //z = y; //won't compile
    

    很明显,编译器至少理解 z x 是不同的东西,但我可以互换 X y 很好。

    当您看到将数组传递给函数和从函数返回数组时,这会更加令人困惑。考虑这个例子(同样,我在传递时也注意到了这里潜在的分段错误 X ):

    void foo(int in[])
    {
      cout << in[8] << endl;                                                                                                                                                                                      
    }
    
    void bar(int* in)
    {
      cout << in[8] << endl;                                                                                                                                                                     
    }
    
    int main()
    {
      //this is just a simple pointer                                                                                                                                                            
      int *x = new int;
      foo(x);
      bar(x);
    
      //this is a dynamic array                                                                                                                                                                  
      int* y = new int[10];
      foo(y);
      bar(y);
    
      //this is a static array                                                                                                                                                                   
      int z[10];
      foo(z);
      bar(z);
    }
    

    所有这些代码在我的机器上正确地编译和运行。

    我觉得我对这里发生的事情有一个很好的内部理解,但是如果你让我确切地解释发生了什么,我觉得我不能令人满意地解释。我想说的是:

    • 当我将数组作为 int* in 而不是 int in[] ,我的得失是什么?返回数组时与 int* ? 这样做有没有副作用?

    • 如果我问你 Y 是,您会说指向int的指针、int数组还是其他什么?

    • 同样,当我说 x = y VS x = z ?我仍然可以使用 x[] 并访问最初在 Y Z 但这真的是因为指针算术恰好把我放在仍然有效的内存空间中吗?

    我已经研究了所有关于so的类似数组/指针问题,并且我很难找到一个明确的解释来一次性解决这个问题。

    8 回复  |  直到 14 年前
        1
  •  6
  •   Steve Jessop    14 年前

    C++是静态类型的,所以编译器当然理解这一点。 x z 不是同一种东西。它们有不同的类型-Z是数组,X和Y是指针。

    原因 z = x 不编译不是(只是)类型不兼容,而是根本无法分配给数组变量。曾经。 x = z 指定给x,指向z的第一个元素的指针。 x = y 指定的值 y X [*]

    当我将数组传递给函数int*in而不是int[]时,我获得或失去的是什么?

    他们做的完全一样,所以你别无选择。可能你被C++语法允许的事实误导了。 int in[] 作为函数参数。参数的类型 in 不是任何类型的数组,它是 int* .

    如果我问你Y的数据类型是什么

    它是 INT* . 这就是它所宣称的,所以这就是它。

    这个 它所拥有的价值 是指向数组(第一个元素)的指针。在我想说“指向数组的指针”的情况下,我经常使用这个公式:“指向(的第一个元素)”,但不能,因为涉及到的类型是否是指向数组的指针可能存在歧义。

    但是,数组中的指针很少在C++中使用,因为数组的大小是类型的一部分。C++中没有这样的“int数组指针”,即“1 int数组的指针”、“指向2 int数组的指针”等等。这通常不是很方便,因此使用一个数组的第一个元素的指针,该数组的大小在编译时可能不知道。

    这真的是因为指针算术恰好把我放在仍然有效的内存空间中吗?

    差不多,是的。数组的大小是z类型的一部分,但不是x或y类型的一部分,也不是z衰减到其第一个元素的指针的结果类型的一部分。所以 Y 可以是指向10个元素中第一个元素的指针,也可以是指向1个元素的指针。你只知道不同的上下文,并要求你的调用者你的值指向它应该指向的地方。

    不过,“发生”是留给机会的太多——使用数组时的一部分工作是确保您不会偏离它们的界限。

    [*] Z=X 不允许,即使在你做完之后 x= z ,因为z是(并且始终是)内存中10个整数的特定数组。回到设计C时,有一个问题是数组变量原则上是否可以“可重新密封”,这意味着您可以这样做:

    int z[10];
    int y[10];
    z = y; // z is now an alias for y
    y[0] = 3;
    // z[0] now has the value 3
    

    丹尼斯·里奇决定不允许这样做,因为这样做会妨碍他以他需要的方式区分数组和指针。所以 Z 不能引用与其声明为的数组不同的数组。在这里阅读所有相关信息: http://cm.bell-labs.com/cm/cs/who/dmr/chist.html 在“胚胎C”下。

    另一个似是而非的意思是 z = y 可以是 memcpy(z,y,sizeof(z)) . 它也没有这个意思。

        2
  •  6
  •   Amardeep AC9MF    14 年前

    指针和数组的根本区别在于指针具有唯一的内存地址 持有 数组数据的地址。

    数组名虽然被视为基于上下文的指针,但它本身没有可以获取其地址的内存位置。当它被视为指针时,它的值在运行时作为第一个元素的地址生成。

    这就是为什么您可以将其值赋给另一个指针,但不能将其赋给另一个指针。没有可作为l值处理的指针内存位置。

        3
  •  5
  •   sbi    14 年前

    数组不是指针,但 数组容易衰减到指针 到他们的第一个元素。此外,C(和C++)允许 用于指针的数组访问语法 .

    当我将数组传递给函数int*in而不是int[]时,我获得或失去的是什么?当返回一个数组作为int*时是否相同?这样做有没有副作用?

    你什么也得不到,因为 int[] 只是另一种写作方式 int* . 如果要传递数组,必须根据 参考 完全匹配它的大小。非类型模板参数可以通过精确的大小缓解问题:

    template< std:::size_t N >
    void f(int (&arr)[N])
    {
       ...
    }
    

    如果我问你Y的数据类型是什么,你会说指向int的指针,int数组或其他什么东西吗?

    它是指向动态分配数组的第一个元素的指针。

    同样,当我说x=y和x=z时会发生什么?

    将不同类型的不同对象的地址分配给同一指针。(你漏了一个 int 堆上。 :) )

    我仍然可以使用x[]并访问原来在y或z中的内容,但这真的只是因为指针算术恰好将我放在仍然有效的内存空间中吗?

    是的。如我所说,指针方便而混乱地允许数组语法应用于它们。但是,这仍然不能使指针成为数组。

        4
  •  2
  •   Nikolai Fetissov    14 年前

    这是一段来自 this book (C++语义遵循与C的向后兼容性)。在以下情况下,数组“are”指针:

    1. 数组名 在表达式中 (与声明相反)被编译器视为指向数组第一个元素的指针(这不适用于 sizeof )(ANSI C标准,6.2.2.1)
    2. 下标始终等于指针的偏移量(6.3.2.1)
    3. 数组名 在函数参数的声明中 被编译器视为指向数组第一个元素的指针(6.7.1)

    这基本上意味着:

    int arr[20]; int* p = arr;
    

    相当于:

    int arr[20]; int* p = &arr[0];
    

    然后

    int arr[20]; int x = arr[10];
    

    相当于:

    int arr[20]; int x = *( arr + 10 );
    

    void func( int arr[] );
    

    相当于:

    void func( int* arr );
    

    另一方面,指针永远不会转换回数组——这就是最后两行不能编译的原因。

        5
  •  1
  •   UncleBens    14 年前

    当我将数组作为 我是什么? 得失?也是这样吗 当以int*返回数组时?是 做会有不良的副作用 这个?

    阿法克,一个只是另一个的句法糖,它们的意思完全一样。

    版本 [] 可能只是给出了一个强烈的提示,该函数需要一个指向数组的指针,而不是指向单个对象的指针。

    当涉及到真正的多维数组与指针数组(指向数组)时,您会注意到一个区别,因为在这种情况下,只有第一个维度衰减到具有多维数组的指针。这些东西在内存中具有完全不同的布局(一个大的连续块与一个指向不同内存块的小指针块)。

    如果我问你Y的数据类型 是,你会说指向int的指针吗? 整数数组或其他什么?

    Y类型是指向int的指针。事实上,如果是动态分配的数组,则根本看不到该数组!也就是说,与实际数组不同,没有办法用size of来确定分配的大小。

    同样,当我说x时会发生什么= y=x=z?我仍然可以使用x[] 然后进入 原来是Y或Z,但这是 真的只是因为指针算术 正好把我放在记忆空间里 这仍然有效?

    这是因为x是一个指针。你不能这样做 z = x; 因为你不能分配给数组。

        6
  •  1
  •   Jerry Coffin    14 年前
    1. 像这样的函数参数没有区别 int *in int in[] . 对于函数参数,这些只是不同的拼写方式 pointer to T . 它们完全不同的唯一方法是(可能)可读性(例如,如果您打算总是传递数组的基地址,您可能会发现数组符号更合适,而如果您打算传递单个对象的地址,您可能会发现指针符号更雅致)。
    2. 在上面的代码中, y 很明显有这种类型 pointer to int .
    3. x Y 是指针,可以分配。 z 是无法分配的数组。
        7
  •  1
  •   decimus phostle    14 年前

    作为一个旁白(与主题有一定的相关性——可以添加这个作为注释,但没有足够的代表性)——你可以使用指针来衡量数组中元素的数量。或者以不同的方式声明size of返回size of(array_type)*array中的num_元素,而返回指针的大小。Glib提供 this macro 为此目的。

        8
  •  0
  •   Joe D    14 年前

    当我将数组传递给函数int*in而不是int[]时,我获得或失去的是什么?当返回一个数组作为int*时是否相同?这样做有没有副作用?

    你没有得到或失去任何东西

    如果我问你Y的数据类型是什么,你会说指向int的指针,int数组或其他什么东西吗?

    我称Y为指向整数数组的指针

    同样,当我说x=y和x=z时会发生什么?我仍然可以使用x[]并访问原来在y或z中的内容,但这真的只是因为指针算术恰好将我放在仍然有效的内存空间中吗?

    x = y 复制Y指向的数组,只复制Y中的指针。

    x = z 复制数组z,只创建指向第一个元素值的指针。

    也, 可用分配内存 .