C&C ++:指针数组和数组地址有什么区别?

6

C++11代码:

int a[3];
auto b = a;       // b is of type int*
auto c = &a;      // c is of type int(*)[1]

C 代码:

int a[3];
int *b = a;
int (*c)[3] = &a;

bc的值是相同的。

bc有什么不同?为什么它们不是同一种类型?

更新:我将数组大小从1更改为3。


auto 不太适用于指针,通常对于这种类型不够可靠。 - user2485710
1
@user2485710 你能详细说明一下吗?有任何参考文献或引用吗? - Domi
auto 与指针一起使用非常好,不知道你从哪里得到这个想法。 - Baum mit Augen
@BaummitAugen,我绝不是在暗示auto和指针之间存在任何问题。你能详细说明一下吗? - Domi
1
你没有暗示任何事情,我只是在质疑第一条评论。 - Baum mit Augen
在这种情况下,也许你可以添加一个带有相应用户的 @ 符号来进行提及 :P - 对于误解我感到抱歉。 - Domi
3个回答

7
< p > sizeof 运算符应该有所不同,特别是如果您更改 a 的声明为不同数量的整数,例如 int a [7]

int main()
{
    int a[7];

    auto b = a;
    auto c = &a;

    std::cout << sizeof(*b) << std::endl;  // outputs sizeof(int)
    std::cout << sizeof(*c) << std::endl;  // outputs sizeof(int[7])

    return 0;
}

对我来说,这将打印出:
4
28

那是因为这两个指针是非常不同的类型。一个是指向整数的指针,另一个是指向7个整数数组的指针。第二个确实有指向数组的指针类型。如果你解引用它,当然,在大多数情况下它会退化为指针,但它实际上并不是指向指针的整数指针。第一个是指向整数的指针,因为衰减发生在赋值时。其他出现的地方是如果你真的有两个指向数组的指针变量,并尝试将一个分配给另一个:
int main()
{
    int a[7];
    int b[9];

    auto aa = &a;
    auto bb = &b;

    aa = bb;

    return 0;
}

这会让我收到错误信息:
xx.cpp: In functionint main()’:
xx.cpp:14:8: error: cannot convert ‘int (*)[9]’ to ‘int (*)[7]’ in assignment
     aa = bb;

然而,这个例子是有效的,因为对于 bb 进行解引用会使其衰减为指向整数的指针:

int main()
{
    int a;
    int b[9];

    auto aa = &a;
    auto bb = &b;

    aa = *bb;

    return 0;
}

请注意,衰减不会在赋值的左侧发生。以下代码无法正常工作:
int main()
{
    int a[7];
    int b[9];

    auto aa = &a;
    auto bb = &b;

    *aa = *bb;

    return 0;
}

它会为您赚取这个:

xx2.cpp: In functionint main()’:
xx2.cpp:14:9: error: incompatible types in assignment ofint [9]’ toint [7]’
     *aa = *bb;

sizeof 也会丢弃第二个 cout 中的 c 变量的 const 部分。这就是它们产生相同输出的原因。 - user2485710
@user2485710:什么会导致相同的输出?我的两个cout行输出了两个不同的数字。const根本没有涉及到。我无法将您的评论与我的帖子对齐。 - Joe Z
很棒的答案。强调了许多我以前没有考虑过的概念!我可能应该更经常地使用数组类型而不是指针类型! - Domi
@KerrekSB:我20多年前第一次学习C语言。那部分已经深入我的骨髓,就像当我看到void main(void)时会出现的痉挛一样。而且由于我仍然需要编写C程序,至少这一部分不会对我造成实质性的伤害。此外,如果您使用一个通过#definemain包装在其他东西中的库(我在看你,SDL),最好小心谨慎... - Joe Z

6

C++中,任何对象的身份由其类型和地址的配对确定。

在您的示例中,存在两个具有相同地址的不同对象:数组本身和数组的第一个元素。前者的类型为int[1],后者的类型为int。如果一个对象是另一个对象的子对象,比如数组元素、类成员和类基类子对象,则两个不同的对象可以有相同的地址。

如果您将示例改为以下写法,会更加清晰:

int a[5];
int (*ptr_to_array)[5] = &a;
int * ptr_to_array_element = &a[0];

但是您利用了这样一个事实,即数组的 id-expression a 会衰减为指向数组第一个元素的指针,因此在您的上下文中,a 的值与 &a[0] 相同。


谢谢!我在我的问题中添加了一个稍微修改过的版本您的代码。 - Domi

1
考虑以下例子:
#include<stdio.h>

int main()
{
    int myArray[10][10][10][10]; //A 4 Dimentional array;

    //THESE WILL ALL PRINT THE SAME VALUE
    printf("%d, %d, %d, %d, %d\n",
            myArray,
            myArray[0],
            myArray[0][0],
            myArray[0][0][0],
            &myArray[0][0][0][0]
          );

    //NOW SEE WHAT VALUES YOU GET AFTER ADDING 1 TO EACH OF THESE POINTERS
    printf("%d, %d, %d, %d, %d\n",
            myArray+1,
            myArray[0]+1,
            myArray[0][0]+1,
            myArray[0][0][0]+1,
            &myArray[0][0][0][0]+1
          );
}

你会发现第一个例子中打印的所有5个值都相等,因为它们指向同一初始位置。
但是当你将它们加1时,你会发现不同的指针现在跳转(指向)不同的位置。这是因为myArray [0] [0] [0]+1将跳过10个整数值,即40个字节,而myArray [0] [0]+1将跳过100个整数值,即400个字节。同样地,myArray [0]+1将跳过1000个整数值或4000个字节。
因此,这些值取决于您所引用的指针级别
但现在,如果我使用指针来引用它们:
#include<stdio.h>

int main()
{
    int myArray[10][10][10][10]; //A 4 Dimentional array;

            int * ptr1 = myArray[10][10][10];
            int ** ptr2 = myArray[10][10];
            int *** ptr3 = myArray[10];
            int **** ptr4 = myArray;

    //THESE WILL ALL PRINT THE SAME VALUE
    printf("%u, %u, %u, %u\n", ptr1, ptr2, ptr3, ptr4);

    //THESE ALSO PRINT SAME VALUES!!
    printf("%d, %d, %d, %d\n",ptr1+1,ptr2+1,ptr3+1,ptr4+1);
}

所以你可以看到,不同级别的指针变量的行为方式与数组变量不同。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接