【问题标题】:Can representation and alignment be assumed for qualified types within a family是否可以为族中的合格类型假定表示和对齐方式
【发布时间】:2015-12-30 00:29:50
【问题描述】:

我已阅读this post
从那,我读到了:
从 C99:6.2.7.27

指向 void 的指针应具有相同的表示和对齐方式 要求作为指向字符类型的指针。39) 类似地,指针 兼容类型的合格或不合格版本应具有 相同的表示和对齐要求。所有指向的指针 结构类型应具有相同的表示和对齐方式 互相要求。所有指向联合类型的指针都应具有 相同的表示和对齐要求。 指向其他类型的指针不需要具有相同的表示或对齐要求。 (强调我的)

我对这个问题的重要部分的解释似乎是说
如果我有:

int *a, **b;   

保证注册和对齐,并且所有这些陈述都是真实的;

sizeof(a)==sizeof(*a)&&
sizeof(int *)==sizeof(b)&&
sizeof(*b)==sizeof(**b);// all essentially int pointers,
                        // and would be equal

但如果我有:

int *a;
float*b;  

注册和对齐是保证的。即:

sizeof(a)!=sizeof(b)&&
sizeof(float *)!=sizeof(int *)&&
sizeof(*b)!=sizeof(*a);//all pointers, but not of compatible types
                       //therefore not guaranteed to be equal.

我问的原因是因为this讨论,
我在其中发布了一个答案,显示了一个创建 3D 数组的函数:

int *** Create3D(int p, int c, int r) 
{
    int ***arr;
    int    x,y;

    arr = calloc(p, sizeof(arr)); 
    for(x = 0; x < p; x++)
    {
        arr[x] = calloc(c ,sizeof(arr)); 
        for(y = 0; y < c; y++)
        {
            arr[x][y] = calloc(r, sizeof(int));
        }
    }
    return arr;
}

就使用 sizeof() 而言,以下语句是否安全?

arr = calloc(p, sizeof(arr)); 

或者,即使只使用了int 类型,也应该是:

arr = calloc(p, sizeof(int **));

或:

arr = calloc(p, sizeof *arr);

问题:
给定 arr 被声明为 int ***:
对于分配内存,只要类型保持int,使用 int 指针 (int *, int **, arr, *arr, int ***) 的任何变体作为 sizeof 的参数是否有任何危险?

一种形式优于另一种形式吗? (请给出风格以外的理由)

【问题讨论】:

  • 为什么int 和指针的大小应该相同?引用甚至没有提到非指针类型。而int ***不是 3D 数组,而是指向指向int 的指针的指针!注意:成为 3 星程序员并不是恭维。
  • @Olaf - 如果我建议 int 和指针具有相同的大小,那是无意的。 (顺便说一下,你从我的帖子的哪个确切部分向我指出了这一点?)我真正的问题是,如果在一个类型中,使用任何形式的 int 指针来初始化内存是否安全。即,int *, int *** and int **** 将为 sizeof() 产生相同的值。
  • 这不是建议,而是暗示:“所有这些陈述都是正确的;sizeof(a)==sizeof(*a) ...”。您正在将苹果与橙子进行比较。指向int 的指针与指向指针的指针等类型不同。* 没有限定符、说明符等,
  • @Olaf,请注意,在我使用的上下文中,a 是作为int *** 创建的,并且仍然是int ***,而不是int。因此,有趣的是,当我比较两者的大小时,sizeof(a)==sizeof(*a) 是一个真实的陈述。在大小方面,我希望aint *** 具有相同的大小。
  • 除非您使用非常奇怪的非标准“C”编译器,否则int *a 只能是“指向int”的指针。因此*aint,仅此而已。 (请注意,sizeof 并不暗示类型兼容性或身份。sizeof(char [4]) == sizeof(float)` 不会产生任何关于相同对齐或表示的信息,甚至不是关于可表示范围的信息,而只是关于 -好 - 大小)。例如。 sizeof(unsigned char) == sizeof(unsigned int) 很可能与 UCHAR_MAX == 255UINT_MAX == 65535 一起使用。

标签: c pointers types


【解决方案1】:

考虑一个幻想编译器,它只会根据需要使指针变宽。

代码使用int 的大集合(万亿)和int * 的小集合(2)。

int *ticket = malloc(sizeof *ticket * 1000ULL*1000*1000*1000);
int **purchase = malloc(sizeof *purchase * 2);

指针运算只需要在分配内存的 + 1 范围内工作。指向int 的指针需要具有 39+ 位的精度。指向 int * 的指针只需要 2+ 位。这样的编译器可以在代码中的某处维护所有int 的基础,并在需要时添加一个缩放的39 位指针以形成物理地址。指向int * 的指针可以使用它们的少数位以及隐藏在代码库中的代码并缩放以生成int * 的物理地址。

这是在printf("%p", (void *) ptr) 上强制转换为void * 的原因,因为这样的编译器需要将基数和缩放比例放在一起以形成一个通用的void * 指针,该指针可以指向内存中的任何对象。这也是为什么铸造(int **)((void *)ticket)是UB的一个原因。

DOS 段中存在类似系统:偏移天数,功能在一个地址空间(32 或 16 位)和数据在另一个地址空间(独立于代码 32 或 16 位)。

目前的数据嵌入式处理器有时对常量数据使用一种指针大小,对可变数据使用另一种大小。

C 支持许多架构,不仅是可以指向任何地方的 64 位指针的平面模型。

【讨论】:

  • 指向 int 的指针需要具有 39+ 位的精度。指向 int * 的指针只需要 2+ 位。很好的插图。
  • @ryyker 我应该使用数组而不是malloc() 来说明,因为这个幻想编译器需要在运行前知道最大使用量。然而希望这个想法很清楚:不同的指针类型可以有不同的大小。
  • 开始明白了。对我有耐心,我最终会得到它的。
  • @ryyker 帮助我的是假设每种不同的类型都存在于自己的地址空间中,每个空间的大小都不同。所有int 居住在Red:0Red:999999double 居住在Blue:0Blue:99 当转换为void * 时,某些类型特定的地址空间前缀以及缩放的指针值被放在一起形成宽 void *。从void * 回到原点很容易。棘手的一点是从为一种类型生成的void * 转换为 another 类型 - 它只是不起作用 - 因此是 UB。
【解决方案2】:

我对这个问题的重要部分的解释似乎是说 如果我有:

int *a, **b;   

保证注册和对齐,

a 是指向int 的指针。 b 是指向 int * 的指针。这些不是兼容的类型,标准不要求指向这些类型的指针具有相同的表示或对齐方式。

并且所有这些陈述都是正确的[:]

sizeof(a)==sizeof(*a)&&
sizeof(int *)==sizeof(b)&&
sizeof(*b)==sizeof(**b);// all essentially int pointers,
                        // and would be equal

不,该标准不要求其中任何为真实。第一个在 64 位系统上经常是错误的。其他的通常是正确的,但是如果您正在寻找保证,那么标准不会提供它们。

但如果我有:

int *a;
float*b;  

不保证注册和对齐。即:

正确,float *int * 的表示和对齐方式不保证相同。

我问的原因是因为这个讨论, 我发布了一个答案,显示了一个创建 3D 数组的函数: [..] int ***arr; 整数 x,y;

    arr = calloc(p, sizeof(arr)); 

这通常会按预期工作,因为在大多数系统上,所有对象指针实际上确实具有相同的大小,但 C 并不要求它是正确的。应该是:

    arr = calloc(p, sizeof(*arr));

    arr = calloc(p, sizeof(int **));

同样,这个:

        arr[x] = calloc(c ,sizeof(arr)); 

应该是

    arr[x] = calloc(c ,sizeof(*arr[x])); 

    arr[x] = calloc(c ,sizeof(int *)); 

。不过这个没问题:

            arr[x][y] = calloc(r, sizeof(int));

对于分配内存,只要类型保持 int,在该语句中,使用 int 指针的任何变体 (int *, int **, arr, *arr) 作为 sizeof 的参数是否有任何危险?

是的。给定任何类型TT * 是不同的、不兼容的类型。无论限定性或指针性如何,该标准都不保证两者具有相同的表示或对齐方式。没有保证相同表示的部分原因是没有保证相同的大小。特别是,如果Tint,那么在常见情况下TT * 的大小不同。

一种形式优于另一种形式吗? (请给出风格以外的理由)

虽然这是否是一种风格值得商榷,但这种形式:

arr = calloc(p, sizeof(*arr));

的好处是,如果你改变arr的类型,你就不必修改calloc调用。正确的大小来自arr 的声明。无需查看arr 的声明,也很容易判断大小是否正确。如果您愿意,可以很容易地围绕该表单编写一个宏。

【讨论】:

  • 不是sizeof(*arr) 取消引用未知指针(并可能导致段错误)?
  • @RussSchultz - 不,sizeof 在编译时根据元素的类型(已知)进行评估,因为它实际上不需要值,只需要值大小。
  • int *** 不是数组。为什么这么多人在理解单星指针的同时,对多星指针有这种误解?
  • John - a 是一个指向 int 的指针。 b 是一个指向 int * 的指针。这些不是兼容的类型 ...这可能是我错误地解释标准的地方。我假设如果指针都是从同一类型创建的,那么它们之间会有兼容性,如果两个指针是使用两种不同的类型创建的,那么就没有兼容性。感谢您的澄清。
  • @ryyker:如果您的假设是正确的,那么将只有一种指针类型,并且指针类型之间的类型检查是无用/不可能的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-14
  • 1970-01-01
  • 1970-01-01
  • 2019-03-10
相关资源
最近更新 更多