可以为一个家庭中的合格类型假设表示和对齐

时间:2015-10-01 20:25:32

标签: c pointers types

我已阅读 this post 从那以后,我读到了这一点:
来自C99:6.2.7.27

  

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

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

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参数的危险?

一种形式优先于另一种吗? (请说明风格以外的理由)

2 个答案:

答案 0 :(得分:4)

  

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

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;           int 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的参数的危险? / p>

是。给定任何类型TT *是不同的,不兼容的类型。无论资格或指针性如何,标准都不保证两者具有相同的表示或对齐。没有相同表示保证的部分没有相同大小的保证。特别是,如果Tint,则常见情况TT *的大小不同。

  

一种形式优先于另一种吗? (请说明风格以外的理由)

虽然这是一种风格点,但这种形式可以论证:

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

的优势在于,如果您更改arr的类型,则无需修改calloc来电。正确的大小来自arr的声明。如果没有查看arr的声明,也很容易判断大小是否合适。如果您希望这样做,可以很容易地围绕该表单编写宏。

答案 1 :(得分:2)

考虑一个幻想编译器,只根据需要制作指针。

代码使用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 *的物理地址。

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

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

当前数据嵌入式处理器有时使用一个指针大小用于常量数据,另一个大小用于可变数据。

C支持许多体系结构,而不仅仅是可以指向任何位置的64位指针的平面模型。