为什么我不能在此指针处打印值?

时间:2017-06-14 07:20:11

标签: c arrays pointers

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的间接运算符时,我可以得到它来打印第一个的值数组中的单元格。这是因为当我将&a分配给p时,它会将&arrint ( * ) [5])的类型转换为类型int * ,然后分配给p

3 个答案:

答案 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。 pq都有相同的值(但是类型不同),但是当使用带有`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,右边是指针。

此处也没有布尔。