传球和传球的后果是什么?在C ++中将数组指定为指针?

时间:2010-08-18 18:19:52

标签: c++ arrays pointers

作为背景,我不久前给出了这篇文章的答案:

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

很明显,编译器至少理解zx是不同的东西,但我可以很好地交换xy

当您查看将数组传递给函数并从函数返回数组时,这会让您感到更加困惑。考虑这个例子(再次,我知道传递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 = yx = z时会发生什么?我仍然能够使用x[]并访问最初在yz中的内容,但这真的只是因为指针算法恰好让我在仍然存在的内存空间中有效?

我已经在SO上挖掘了所有类似的数组/指针问题,而我无法找到明确的解释,一劳永逸地为我解决这个问题。

8 个答案:

答案 0 :(得分:6)

C ++是静态类型的,因此编译器当然理解xz不是同一类。它们有不同的类型 - z是数组,x和y是指针。

z = x无法编译的原因不是(只是)类型不兼容,但是,根本不能分配给数组变量。永远。 x = z分配给x,指向z的第一个元素的指针。 x = yy的值分配给x。[*]

当我将一个数组作为int *而不是[]中的int传递给函数时,我获得或失去了什么?

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

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

这是int*。这就是它所宣称的,所以就是这样。

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

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

这真的只是因为指针算法发生在我仍然有效的内存空间

差不多,是的。数组的大小是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

Dennis Ritchie决定不允许这样做,因为这会阻止他以他需要的方式区分数组和指针。所以z不能引用与声明的数组不同的数组。在这里阅读所有相关内容:http://cm.bell-labs.com/cm/cs/who/dmr/chist.html,在“胚胎C”下。

z = y的另一个合理含义可能是memcpy(z,y,sizeof(z))。也没有给出那个含义。

答案 1 :(得分:6)

指针和数组之间的根本区别在于指针具有唯一的存储器地址,保存数组数据的地址。

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

这就是为什么您可以将其值分配给另一个指针但反之亦然。没有指针内存位置可以视为l值。

答案 2 :(得分:5)

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

  

当我将一个数组作为int *而不是int中的int传递给函数时,我得到了什么?将数组作为int *返回时是否相同?这样做会产生不良副作用吗?

你什么都没得到,因为int[]只是写int*的另一种方式。如果要传递数组,则必须按引用传递它,与其大小完全匹配。非类型模板参数可以使用确切的大小来缓解问题:

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

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

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

  

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

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

  

我仍然能够使用x []并访问最初在y或z中的内容,但这是否真的只是因为指针算法发生在我仍然有效的内存空间?

是的。正如我所说,指针方便且容易混淆地允许将数组语法应用于它们。但是,仍然没有使指针成为一个数组。

答案 3 :(得分:2)

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

  1. 表达式中的数组名称 (与声明相反)被编译器视为指向数组第一个元素的指针(这不适用于sizeof) (ANSI C标准,6.2.2.1)
  2. 下标总是等于指针的偏移量(6.3.2.1)
  3. 编译器将函数参数声明中的数组名称视为指向数组第一个元素的指针(6.7.1)
  4. 这基本上意味着:

    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 );
    

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

答案 4 :(得分:1)

  

当我将数组传递给函数时   int * in而不是[]中的int,我是什么   输或输?是一样的   将数组作为int *返回时?是   这样做会产生不良副作用   此?

AFAIK,一个是另一个的语法糖,它们的意思完全一样。

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

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

  

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

y的类型是指向int的指针。事实上,在动态分配数组的情况下,你永远不会看到数组!也就是说,与实际数组不同,无法用sizeof确定分配的大小。

  

同样,当我说x =时会发生什么   y与x = z?我仍然可以使用x []   并访问那些东西   最初在y或z,但是这个   真的只是因为指针算术   碰巧让我进入记忆空间   那仍然有效吗?

这是因为x是一个指针。您将无法执行z = x;,因为您无法分配给数组。

答案 5 :(得分:1)

  1. int *in等函数参数与int in[]之间没有区别(根本没有)。对于函数参数,这些只是拼写pointer to T的不同方式。它们可以完全不同的唯一方法是(可能)类似可读性(例如,如果您打算始终传递数组的基址,您可能会发现数组符号更合适,而如果您打算传递单个地址对象,你可能会发现指针符号更有品味)。
  2. 在上面的代码中,y显然属于pointer to int
  3. 类型
  4. xy是指针,可以分配。 z是一个无法分配的数组。

答案 6 :(得分:1)

暂时(与主题有一些相关性 - 会将其添加为注释但没有足够的代表。) - 您可以衡量数组中元素的数量与使用指针的数量。或者说明sizeof返回sizeof(array_type)* num_elements_in_array vs返回指针的大小。为此目的,Glib提供this macro

答案 7 :(得分:0)

  

当我将一个数组作为int *而不是int中的int传递给函数时,我得到了什么?将数组作为int *返回时是否相同?这样做会产生不良副作用吗?

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

  

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

我会称y为指向一组int的指针

  

同样,当我说x = y vs. x = z时会发生什么?我仍然可以使用x []并访问最初在y或z中的内容,但这是否真的只是因为指针算法发生在我仍然有效的内存空间?

x = y 使y指向的数组的副本只是y中指针的副本。

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

此外,免费分配内存