-2

我期待第一个 printf() 语句的垃圾值,但我得到了地址。为什么?

#include<stdio.h>
int main()
{
    int (*p)[10];
    int a[10]={1,2,3,4,5,6,7,8,9,0};
    p=a+10;
    printf("%d\n",*(p));
    printf("%u",(p));
}
4

3 回答 3

1

对于初学者来说,这个声明

p=a+10;

是无效的。

赋值的左侧具有类型int( * )[10],而赋值的右侧具有类型int *,并且没有从一种类型到另一种类型的隐式转换。

你必须写

p = ( int( * )[10] )( a + 10 );

其次,这些呼吁printf

printf("%d\n",*(p));
printf("%u",(p));

具有未定义的行为,因为使用了无效的转换说明符du指针类型的参数,因为*pp都是指针。

所以一个有效的程序可以如下所示

#include <stdio.h>

int main(void) 
{
    int (*p)[10];
    int a[10]={1,2,3,4,5,6,7,8,9,0};

    p = ( int( * )[10] )( a + 10 );

    printf("%p\n",( void * )*p );
    printf("%p\n", (void *)p );

    return 0;
}

指针可能指向对象(或数组)的最后一个元素之后的内存。

所以在这个声明之后

p = ( int( * )[10] )( a+10 );

指针p指向想象的二维数组元素的第一个元素之后,其中的元素依次是一维数组的类型int[10],并且想象的二维数组的第一个元素对应于数组a

表达式*p也是一个指针。它的类型是int *因为表达式 *p 给出了该类型的左值,int[10]并且在表达式中,该左值被隐式转换为指向其第一个元素的指针(在这种情况下为 type int)。

所以这两个表达式p*p指向相同的内存并且具有相同的值。

程序输出证明了这一点

0x7ffcfd46ea48
0x7ffcfd46ea48

这两个指针p和之间的区别在于*p它们可能指向的对象的类型。

考虑以下演示程序

#include <stdio.h>

int main(void) 
{
    int (*p)[10];
    int a[10]={1,2,3,4,5,6,7,8,9,0};

    p = ( int( * )[10] )( a+10 );

    printf("%zu\n",sizeof( *p ) );
    printf("%zu\n",sizeof( **p ) );

    return 0;
}

它的输出是

40
4

如果你写了,你可能会得到未定义的行为

printf( "%d\n", **p );

在这种情况下,表达式**p具有类型int,并且可以访问数组之外​​的内存a

于 2019-08-14T13:58:00.407 回答
1

鉴于此声明:

    int (*p)[10];

p是指向数组的指针。因此,表达式*p指定了指向数组本身。但在几乎所有 C 上下文中,尤其是当它是函数调用表达式中的参数时,数组类型的表达式的值会自动转换为指向数组第一个元素的指针。该元素的地址与数组本身的地址相同,所以如果p实际上是一个有效的指针,那么它是完全合理的

    printf("%p\n", (void *) (*p));
    printf("%p\n", (void *) (p));

打印两条相同的行。

但是请注意,使用%pformat 指令代替%uor%d来打印(void)指针值,以及强制转换为void *. 对原始代码的这些更改对于printf明确定义调用行为是必要的。

确实,就 C 语言而言,*p在您的情况下并未指定对象,因此从这个意义上说,评估表达式*p会产生未定义的行为。这就是为什么我只说代码发出两条相同的行是“合理的”的原因之一。然而,在实践中,实现可能会将表达式*p等同于(int *) p,在这种情况下,其行为是明确定义的。

您还应该知道 - 并且您的编译器应该警告您 - 表达式a + 10具有错误的类型 ( int *) 以直接分配给您的p(an int (*)[10])。允许在这两种类型之间进行转换,但需要强制转换:

    p = (int (*)[10])(a + 10);

或者

    p = (void *)(a + 10);

有充分的理由期望您的编译器正在编译原始赋值表达式,就好像存在所需的强制转换一样,特别是因为它在许多实现中是无操作的,可能包括您的实现,但您不应该依赖它。

于 2019-08-14T13:37:04.963 回答
0

该数组可以被视为(衰减到)指向第一个元素的指针。

当你获取一个数组的地址时,你会生成一个指向数组的指针,通常指向与第一个元素相同的地址

当您取消引用指向数组的指针时,您将获得指向元素的指针,该元素通常具有相同的实际数值。

我一般说,因为一些奇怪的 C 模拟器,或者一些在所有指针对象中放置额外间接的系统,可能会决定数组和数组的内容是 2 个不同的对象。

于 2019-08-14T13:31:42.507 回答