2017-06-14 74 views
6

正在关注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

  • 两个pq使用与p间接运算符时的值相同(但不同类型的),然而,我不会在数组的第一个单元格中获得的价值,而不是我得到的值p本身打印出来。为什么是这样?当我使用int *p = &a时,它引发了一个警告(由于不同类型的指针,但后来当我使用间接运算符与p,我可以得到它打印数组中的第一个单元格的值)。这是因为当我给你&ap,其转换的&arr的类型int *类型(这是int (*) [5]),然后分配给p

+1

你的代码是未定义行为;你必须使用'%p'指定符打印地址,并将参数转换为'void *'。 –

+0

注意:不要使用'%d'来打印指针,'int'对于指针来说可能太小(并且它在典型的64位系统上是*) –

+0

对于简单的分配约束违规,gcc给出了很差的诊断。确保使用'gcc -std = c11 -pedantic-errors'进行编译,以使其正常运行。 – Lundin

回答

4

指针p在增加时跳跃20。这是因为它是int(*)[5]类型的指针,因此按数组中的sizeof(int)*列数跳转?

是。使用具有p间接运算符时

p和q两者具有相同的值(但不同类型的),然而,我并不在所述阵列的所述第一小区中获得的价值,而不是我得到的值OF打印出来。为什么是这样 ?

p指向一个数组,因此使用*p,您可以访问此数组。在没有索引的情况下评估一个数组会得到一个指向其第一个元素的指针。您的*p在此处评估为类型int *

附注中,*通常被称为解除引用运算符。使用不同的术语可能会让其他人感到困惑。

当我使用int * p = &一个,它抛出一个警告(因为不同类型的指针,但后来当我使用与对间接运算符,我可以得到它打印的价值阵列中的第一个单元。这是因为当我分配& a至p,将其转换的& ARR类型(其为int(*)[5])与int类型*,然后分配至p?

我有一个答案在这里误解这个像int *q = (int *)p这将创建一个别名指针,并可能undefined行为,所以留下这个小提示以防万一有人会想到这个的想法。但是你在这个问题上提出的建议只是一个无效的任务。 Lundin's answer有一个完整的解释。


有在你的代码更多的问题,您的指针印刷应该像:

printf("Original Pointers: \t%20p%20p", (void *)p, (void *)q); 

指针可以比int更大。需要投射到void *是因为printf()是一个可变参数函数。如果声明为void *,转换将是隐含的,但事实并非如此,您必须自己做。

+0

我正在从K.N.King的书(C编程:现代方法)学习C,它调用*'间接'操作符。 – Nathu

+0

那么,它不会在C标准有明确的名称,并描述为“*一元\ *运算符表示间接。*“,所以*有一些使用该名称的理由,但我仍然认为它是一个坏主意,因为它执行的操作通常称为*指针解除引用*,因此大多数C程序员将它称为* dereference * –

+0

@NathuramGoatse仍然我会改写我的提示,因为*间接*不是*错误* –

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])。指针算术荣誉datd类型

§2。两者pq当使用间接运算符与`P [....]

请注意,类型存在具有相同的值(但不同类型),和爱好。 p的类型是int (*) [5],因此*p的类型是int [5]。而已。所有你应该有一个数组作为解引用的产物。 (但是阅读.....)现在

,同时使阵列型作为函数参数,它衰减到指针的第一个元素的阵列,因此是类似于int *,指针。所以,最终你会打印一个指针值。

§3。当我使用int *p = &a,它抛出一个警告[...]

等待。停止。这是违反约束的。严格来说,这是无效的C代码。类型int *int (*) [5]不兼容,并且不存在使其成为有效表达式的转换(隐式或显式)。不要这样做,使用适当的类型。

3

除了答案由菲利克斯:

当我使用int *p = &a,它抛出一个警告

这是因为这段代码是无效C.它是一个所谓的违反约束(大致意味着严重的语言违规)。所以编译器需要提供诊断信息。更好的编译器会给出错误,而不是警告。

没有指针转换发生。除非其中一个操作数是void*,否则指针转换不会隐含在C中。


它无效的原因C是因为该表达式不是有效的简单赋值形式。有效形式列在C11 6.5.16.1:

6.5.16.1简单赋值
约束

下列情况之一的应持有:

- 左操作具有原子,合格,不合格或算术类型,右边有 算术类型;

不是这里的情况,两个操作数都是指针。

- 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;

不是这里的情况。

- 左操作数具有原子,合格,或不合格的指针类型;以及(考虑类型左操作数将具有左值变换后)两个操作数都指向兼容的类型的合格或不合格的版本,类型指向左侧的所有限定符都是右侧指向的类型的限定符;

左操作数是(非限定)指针类型。但是右操作数不是兼容类型。所以这是不符合条件的。

- 左操作数具有原子,合格,或不合格的指针类型;以及(考虑 左操作数将具有左值转换后的类型)一个操作数是一个指针 到的对象类型,而另一个是一个指针 空隙的合格或不合格的版本和类型指向左边有指向 由右侧的类型的所有的限定符;

不,这里没有void指针。

- 左操作数是原子,限定或非限定指针,右是空指针常量;或

没有空指针常量无论是。

- 左操作数具有原子型,限定型或非限定型_Bool,右边是一个指针。

这里也没有布尔。

+0

在违反约束的情况下,行为是未定义的(因为代码尽管如此)? – Nathu

+0

@NathuramGoatse是的,这样的程序完全没有被C标准定义,因为它不再是C程序,而是其他的东西。 – Lundin

相关问题