作为背景,我不久前给出了这篇文章的答案:
它无意中开启了关于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
与x = z
时会发生什么?我仍然能够使用x[]
并访问最初在y
或z
中的内容,但这真的只是因为指针算法恰好让我在仍然存在的内存空间中有效?
我已经在SO上挖掘了所有类似的数组/指针问题,而我无法找到明确的解释,一劳永逸地为我解决这个问题。
答案 0 :(得分:6)
C ++是静态类型的,因此编译器当然理解x
和z
不是同一类。它们有不同的类型 - z是数组,x和y是指针。
z = x
无法编译的原因不是(只是)类型不兼容,但是,根本不能分配给数组变量。永远。 x = z
分配给x,指向z的第一个元素的指针。 x = y
将y
的值分配给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的向后兼容性)。在以下情况下,数组“是”指针:
sizeof
) (ANSI C标准,6.2.2.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 );
另一方面,指针永远不会转换回数组 - 这就是你最后两行不能编译的原因。
答案 4 :(得分:1)
当我将数组传递给函数时 int * in而不是[]中的int,我是什么 输或输?是一样的 将数组作为int *返回时?是 这样做会产生不良副作用 此?
AFAIK,一个是另一个的语法糖,它们的意思完全一样。
带[]
的版本可能只是强烈暗示该函数需要指向数组的指针,而不是指向单个对象的指针。
当涉及到真正的多维数组与指针数组(到数组)时,你会注意到一个区别,因为在这种情况下,只有第一个维度衰减到具有多维数组的指针。这些东西在内存中有一个完全不同的布局(一个大的连续块与一个指向不同内存块的小块指针)。
如果我问你y的数据类型是什么 是,你会说指向int的指针, 整数或其他什么?
y的类型是指向int的指针。事实上,在动态分配数组的情况下,你永远不会看到数组!也就是说,与实际数组不同,无法用sizeof确定分配的大小。
同样,当我说x =时会发生什么 y与x = z?我仍然可以使用x [] 并访问那些东西 最初在y或z,但是这个 真的只是因为指针算术 碰巧让我进入记忆空间 那仍然有效吗?
这是因为x是一个指针。您将无法执行z = x;
,因为您无法分配给数组。
答案 5 :(得分:1)
int *in
等函数参数与int in[]
之间没有区别(根本没有)。对于函数参数,这些只是拼写pointer to T
的不同方式。它们可以完全不同的唯一方法是(可能)类似可读性(例如,如果您打算始终传递数组的基址,您可能会发现数组符号更合适,而如果您打算传递单个地址对象,你可能会发现指针符号更有品味)。y
显然属于pointer to int
。x
和y
是指针,可以分配。 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,只是指向第一个元素值的指针。
此外,免费分配内存。