当前位置:  开发笔记 > 运维 > 正文

C中的指针与数组,非平凡差异

如何解决《C中的指针与数组,非平凡差异》经验,为你挑选了2个好方法。

我以为我真的理解这一点,并重新阅读标准(ISO 9899:1990)只是证实了我明显错误的理解,所以现在我问这里.

以下程序崩溃:

#include 
#include 

typedef struct {
    int array[3];
} type1_t;

typedef struct {
    int *ptr;
} type2_t;

type1_t my_test = { {1, 2, 3} };

int main(int argc, char *argv[])
{
    (void)argc;
    (void)argv;

    type1_t *type1_p =             &my_test;
    type2_t *type2_p = (type2_t *) &my_test;

    printf("offsetof(type1_t, array) = %lu\n", offsetof(type1_t, array)); // 0
    printf("my_test.array[0]  = %d\n", my_test.array[0]);
    printf("type1_p->array[0] = %d\n", type1_p->array[0]);
    printf("type2_p->ptr[0]   = %d\n", type2_p->ptr[0]);  // this line crashes

    return 0;
}

比较表达式my_test.array[0]type2_p->ptr[0]根据我对标准的解释:

6.3.2.1数组下标

"下标运算符[]的定义是E1 [E2]与(*((E1)+(E2)))相同."

应用这个给出:

my_test.array[0]
(*((E1)+(E2)))
(*((my_test.array)+(0)))
(*(my_test.array+0))
(*(my_test.array))
(*my_test.array)
*my_test.array

type2_p->ptr[0]
*((E1)+(E2)))
(*((type2_p->ptr)+(0)))
(*(type2_p->ptr+0))
(*(type2_p->ptr))
(*type2_p->ptr)
*type2_p->ptr

type2_p->ptr有类型"指向int的指针",值是起始地址my_test.*type2_p->ptr因此,求值为整数对象,其存储位于相同的地址my_test.

进一步:

6.2.2.1左值,数组和函数指示符

"除非它是sizeof运算符或一元&运算符的操作数,...,具有类型的左值array of type转换为一个表达式,其类型pointer to type指向数组对象的初始元素,而不是左值."

my_test.array具有类型"array of int"并且如上所述转换为"指向int的指针",其中第一个元素的地址作为值.*my_test.array因此,求值为整数对象,其存储位于与数组中第一个元素相同的地址.

最后

6.5.2.1结构和联合说明符

指向适当转换的结构对象的指针指向其初始成员...,反之亦然.在结构对象中可能存在未命名的填充,但不是在其开始处,以实现适当的对齐.

由于第一个成员type1_t是数组,因此该type1_t对象的起始地址和整个对象与上述相同.因此,我的理解是*type2_p->ptr求值为一个整数,其存储位于与数组中第一个元素相同的地址,因此与之相同*my_test.array.

但事实并非如此,因为程序在solaris,cygwin和linux上使用gcc版本2.95.3,3.4.4和4.3.2一直崩溃,因此任何环境问题都是完全不可能的.

我的推理在哪里错了/我什么不明白?如何声明type2_t使ptr指向数组的第一个成员?



1> Johannes Sch..:

如果我忽略了你的分析中的任何内容,请原谅我.但我认为所有这一切的基本错误都是错误的假设

type2_p-> ptr的类型为"指向int的指针",值为my_test的起始地址.

没有任何东西可以使它具有这种价值.相反,它很可能是指向某个地方

0x00000001

因为你要做的是将构成整数数组的字节解释为指针.然后你添加一些东西和下标.

另外,我非常怀疑你对其他结构的转换实际上是有效的(因为,保证可以工作).如果它们都是联合的成员,您可以强制转换然后读取任一结构的公共初始序列.但他们不在你的榜样.您也可以转换为指向第一个成员的指针.例如:

typedef struct {
    int array[3];
} type1_t;

type1_t f = { { 1, 2, 3 } };

int main(void) {
    int (*arrayp)[3] = (int(*)[3])&f;
    (*arrayp)[0] = 3;
    assert(f.array[0] == 3);
    return 0;
}



2> Chuck..:

数组是一种存储.从语法上讲,它用作指针,但在物理上,该结构中没有"指针"变量 - 只有三个整数.另一方面,int指针是存储在struct中的实际数据类型.因此,当你执行强制转换时,你可能正在使ptr取得数组中第一个元素的值,即1.

*我不确定这实际上是定义的行为,但至少它是如何在大多数常见系统上运行的.


"定义的行为"并不意味着"事情发生",它意味着"发生的事情是由标准定义的".类型惩罚是未定义的行为.如果你想在输入双关语时看到一些令人惊讶的事情,那么在你的编译器上将优化放一两个等级.
推荐阅读
虎仔球妈_459
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有