指针符号表示法

6

我不认为指针理论有什么特别棘手的地方,但是有时候我会被一些符号搞糊涂。在下面的例子中,有人能解释一下 p = (int*) a 这一行是如何工作的吗?我对这段代码的解释是,这一行只是将第一个数组的第一个元素的地址存储在指针p中,使得 printf("%u", *p) 可以输出 5。如果是这样的话,那这一行是否只是写成了 p = a[0] 的另一种更间接的方式呢?

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



    int *p; // create an integer pointer
    int (*q)[4]; // create a pointer to a four-element integer array

    p = (int*)a; // ?
    q = a;


    printf("%u %u\n", p, q);
    p++;
    q++;
    printf("%u %u\n", p, q);


    return 0;
}
5个回答

9

当在值上下文中使用表达式a时,它确实会计算为数组a的第一个元素的地址——即a[0]的地址,正如您正确理解的那样。

然而,请注意,数组a实际上是我们所谓的二维数组。它是一个数组的数组。数组a的第一个元素本身就是一个数组:一个类型为int [4]的数组。因此,考虑到以上内容,当在值上下文中使用表达式a时,等同于使用表达式&a[0],这是一个类型为int (*)[4]的指针,概念上指向整个一维数组a[0]

因此,试图执行以下操作:

p = a;

如果将 int (*)[4] 类型的值赋给 int * 指针对象,编译器会报错,因为这两种类型不兼容。为了避免这个错误信息,代码中使用了显式类型转换。

p = (int *) a;

这将强制将前面提到的int(*)[4]指针值传递给p。在典型的实现中,这保留了原始指针的数值,仅执行概念上的类型转换。

尝试访问*p的值通常会产生a[0][0]的值,因为从数字上来看,整个a的地址与a[0]的地址以及a[0][0]的地址相同。上述代码利用了这种数字身份识别,并使用显式转换来解决类型不兼容的问题。


感谢您提供如此详尽的答案。虽然我知道我在使用一个二维数组,但我没有意识到指针 &a[0] 是指向 [int 数组] 而不是简单的 [int] 指针。我也错过了一些强制类型转换的事实。这是一个非常有帮助的答案。 - Paul Patterson

7
我对这段代码的解释是,这行代码仅仅将第一个数组的第一个元素的地址存储在指针p中。
正确。
如果是这样的话,那么这行代码是否只是更间接地写作“p = a[0]”呢?
不,不是。 “这行代码仅仅存储了第一个元素的地址”,这更类似于……
p = &a[0];

但是上述陈述并不完全正确,因为&a[0]的类型是int (*)[4]。正确的赋值语句应该是不需要强制转换的,类似于:
p = &a[0][0];

尝试使用 -Wall 编译各种声明,并谷歌偶发的错误/警告 :)


@ruakh: 不是的,2D索引是从下标为1开始的 - 我已经扩展了解释。 - user529758

2
进一步解释H2CO3的答案,需要记住在大多数情况下,数组类型的表达式将被转换为指针类型的表达式,并且表达式的值将是数组的第一个元素的地址。
表达式a的类型是“由4个元素组成的3元数组”。除非它是sizeof、_Alignof或一元&运算符的操作数,否则a将被转换为类型为“指向4个元素的int数组”的表达式(int (*)[4]),其值将是a [0]的地址。问题在于,类型为int (*)[4]的值不能赋给类型为int *的变量;这些类型不兼容,因此我们需要将表达式a的结果强制转换为int *。
这是有效的,因为数组的地址和数组第一个元素的地址相同——表达式a、&a、a[0]、&a[0]和&a[0][0]都产生相同的值,它们只是具有不同的类型(int (*)[4]、int (*)[4][4]、int *、int (*)[4]和int *)。

谢谢。你说得很对;int ()[4]类型和int类型指针之间的差异让我感到新奇,也是我无法理解代码的核心所在。 - Paul Patterson

0

差不多。这就像写成p = &a [0] [0]


-2

a是一个 3 的数组,包含了 4int 数组。

a 的值是指向一个有 4int 的数组的指针。这个指针的值是数组开头的地址。

(int *) a 把指针转换成 int 类型的指针。指针的值并没有改变,但类型已经变了。

请注意,打印指针值时,正确的格式说明是 p


不是。a的值是一个数组。 - newacct
常见的术语误解。a是一个数组(请阅读我的第一行)。现在,数组的是指向其第一个元素的指针。对象的值是它的求值结果。对于数组,有一些运算符不会对其操作数进行求值(&sizeof),还有字符数组的字符串字面值初始化器的例外。 - ouah
不是。表达式 a 的类型是数组类型。因此,它的值是一个数组值。当数组值在某些上下文中使用时,可以将其隐式转换为指针值。但这并不意味着它们是相同的。如果您获取数组值的地址,则会得到指向数组的指针,而不是指向指针的指针。 - newacct
@newacct C语言没有数组值。数组的值被转换为指针类型。数组操作数的&是指向数组而不是指向指针的指针,因为&运算符的数组操作数不会被评估。我建议你阅读一些关于值和对象的好文章:Chris Torek的优秀著作"The Rule":http://web.torek.net/torek/c/expr.html#therule - ouah
不,是你不理解什么是值。表达式的类型被定义为它所评估的值的类型。 - newacct
呃。我认为Torek的术语“有时被称为'rvalue'”在这个国际标准中被描述为“表达式的值”,但它并不完全相同。最好坚持“(除了…)类型为T数组的表达式被转换为类型为T指针的表达式”。 - Daniel Fischer

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接