我期待第一个 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));
}
我期待第一个 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));
}
对于初学者来说,这个声明
p=a+10;
是无效的。
赋值的左侧具有类型int( * )[10],而赋值的右侧具有类型int *,并且没有从一种类型到另一种类型的隐式转换。
你必须写
p = ( int( * )[10] )( a + 10 );
其次,这些呼吁printf
printf("%d\n",*(p));
printf("%u",(p));
具有未定义的行为,因为使用了无效的转换说明符d和u指针类型的参数,因为*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("%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。
鉴于此声明:
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);
有充分的理由期望您的编译器正在编译原始赋值表达式,就好像存在所需的强制转换一样,特别是因为它在许多实现中是无操作的,可能包括您的实现,但您不应该依赖它。
该数组可以被视为(衰减到)指向第一个元素的指针。
当你获取一个数组的地址时,你会生成一个指向数组的指针,通常指向与第一个元素相同的地址
当您取消引用指向数组的指针时,您将获得指向元素的指针,该元素通常具有相同的实际数值。
我一般说,因为一些奇怪的 C 模拟器,或者一些在所有指针对象中放置额外间接的系统,可能会决定数组和数组的内容是 2 个不同的对象。