在yesterday's question之后,我用指针进行了更多实验。特别是int(*)[n]
类型的指针这是我写的一些代码:
#include <stdio.h>
int main(void)
{
int a[5] = {1, 2, 3, 4, 5};
int (*p) [5] = &a;
int *q = a;
printf("\t\t\t\t\tp\t\tq\n\n");
printf("Original Pointers: \t%20d%20d", p, q);
printf("\n\n");
printf("Incremented Pointers:\t%20d%20d", p+1, q+1);
printf("\n\n");
printf("Pointer values: %20d%20d", *p, *q);
printf("\n\n");
return 0;
}
这是输出:
p q
Original Pointers: 132021776 132021776
Incremented Pointers: 132021796 132021780
Pointer values: 132021776 1
指针p
在递增时跳过20。这是因为它是int(*)[5]
类型的指针,因此在数组中跳过sizeof(int) * number of columns
吗?
p
和q
都有相同的值(但是类型不同),但是当使用带有p
的间接运算符时,我得不到值数组的第一个单元格,而不是我打印出的p
本身的值。这是为什么?
当我使用int *p = &a
时,它会抛出一个警告(因为不同类型的指针,但后来当我使用p的间接运算符时,我可以得到它来打印第一个的值数组中的单元格。这是因为当我将&a
分配给p
时,它会将&arr
(int ( * ) [5]
)的类型转换为类型int *
,然后分配给p
?
答案 0 :(得分:4)
指针p在递增时跳过20。这是因为它是int(*)[5]类型的指针,因此跳过sizeof(int)*数组中的列数?
是
p和q都有相同的值(但是类型不同),但是当使用带p的间接运算符时,我没有得到数组的第一个单元格的值,而是得到值p p打印出。这是为什么?
p
指向一个数组,因此使用*p
,您可以访问此数组。在没有索引的情况下评估数组会获得指向其第一个元素的指针。您的*p
在此处被评估为int *
类型。
在旁注中,*
通常称为取消引用运算符。使用不同的命名法可能会让其他人感到困惑。
当我使用int * p =&amp; a时,它会抛出一个警告(因为不同类型的指针,但后来当我使用p的间接运算符时,我可以让它打印第一个单元格的值这是因为当我将&amp; a分配给p时,它将&amp; arr(int(*)[5])的类型转换为int *类型,然后分配给p?
我在这里得到了一个答案,误解为像int *q = (int *)p
那样会产生别名指针并且可能是未定义的行为,所以留下这个小提示以防万一有人会得到这个的想法。但你在这个问题上的建议只是一个无效的任务。 Lundin's answer对此有完整的解释。
您的代码中存在更多问题,您的指针打印应如下所示:
printf("Original Pointers: \t%20p%20p", (void *)p, (void *)q);
指针可能大于int
。转换为void *
的需要是因为printf()
是一个可变函数。如果它被声明为void *
,则转换将是隐含的,但事实并非如此,您必须自己完成。
答案 1 :(得分:3)
您的代码会导致undefined behavior,因此无法验证任何输出。
首先,一些通用信息:
根据标准,使用格式说明符的不匹配类型的参数会导致UB。要使用printf()
打印指针,必须使用%p
格式说明符并将相应的参数强制转换为void *
。
相关,引用C11
,章节§7.21.6.1/ P8
p
参数应是指向
void
的指针。 [...]
和P9,
[....]如果有任何论据 不是相应转换规范的正确类型,行为是 未定义。
由于printf()
是一个可变参数函数,并且没有默认参数提升,因此需要转换为void *
。
现在,提出更直接的问题。
§1。指针
p
在递增时跳过20。 [...]
你是对的,检查数据类型。类型为int (*) [5]
的指针将根据您的平台递增/递减sizeof(int [5])
,即指向类型。 Pointer arithmatic尊重datd类型
§2。
p
和q
都有相同的值(但是类型不同),但是当使用带有`p [....的间接运算符时]
请注意那里的类型。 p
的类型为int (*) [5]
,因此*p
的类型为int [5]
。而已。你 所拥有的只是一个数组作为解除引用的产物。 (但请继续阅读..... )
现在,在将数组类型作为函数参数传递时,它会衰减到指向数组的第一个元素的指针,因此类似于int *
,一个指针。所以,最终,你将打印一个指针值。
§3。当我使用
int *p = &a
时,它会发出警告[....]
<强>等待。停止即可。这是违反约束的行为。严格来说,它是无效的C代码。类型int *
和int (*) [5]
不兼容,并且不存在使其成为有效表达式的强制转换(隐式或显式)。不要这样做,使用适当的类型。
答案 2 :(得分:3)
除了Felix的回答:
当我使用
int *p = &a
时,它会抛出警告
这是因为此代码无效C.这是所谓的约束违规(大致意味着严重的语言违规)。因此编译器需要提供诊断消息。更好的编译器会给出错误,而不是警告。
不会发生指针转换。指针转换不是在C中隐式完成的,除非其中一个操作数是void*
。
它不是有效C的原因是因为表达式不是简单赋值的有效形式。有效表格列于C11 6.5.16.1:
6.5.16.1简单分配
约束以下其中一项应成立:
- 左操作数具有原子,限定或非限定算术类型,右边具有 算术类型;
这里并非如此,两个操作数都是指针。
- 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;
不是这里的情况。
- 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,以及指向的类型左边有所有右边指向的限定词;
左操作数是(非限定的)指针类型。但右操作数不是兼容类型。所以这是条件不符合。
- 左操作数具有原子,限定或非限定指针类型,并且(考虑到 左值操作数在左值转换后将具有的类型)一个操作数是一个指针 到对象类型,另一个是指向合格或非限定版本的指针 void,左边指向的类型具有指向的类型的所有限定符 在右边;
不,这里没有void
指针。
- 左操作数是原子,限定或非限定指针,右边是空 指针常数;或
没有空指针常量。
- 左操作数的类型为atomic,qualified或nonqualified _Bool,右边是指针。
此处也没有布尔。