【问题标题】:const and typedef of arrays in CC中数组的const和typedef
【发布时间】:2015-09-15 12:29:05
【问题描述】:

在 C 中,可以使用以下构造 typedef 一个数组:

typedef int table_t[N];

这里,table_t 现在定义为 N 个 int 的数组。任何声明的变量,例如table_t t;,现在都将表现为int 的普通数组。

这种构造的重点是用作函数中的参数类型,例如:

int doSomething(table_t t);

一个相对等价的函数原型可能是:

int doSomething(int* t);

第一个构造的优点是它强制N 作为表的大小。在许多情况下,强制执行此属性比依靠程序员正确计算出此条件更安全。

现在一切都好了,只是为了保证table的内容不会被修改,必须使用const限定符。

下面的说法比较容易理解:

int doSomething(const int* t);

现在,doSomething 保证它不会修改作为指针传递的表的内容。 现在,这个几乎等效的结构呢? :

int doSomething(const table_t t);

这里的const 是什么?表的内容,还是指向表的指针? 如果它是const 的指针,是否有另一种方式(C90 兼容)来保留定义表格大小并告诉其内容将是 const 的能力?

请注意,有时还需要修改表格的内容,因此const 属性不能嵌入到 typedef 定义中。

[编辑] 感谢您迄今为止收到的出色答案。 总结一下:

  • typedef 强制大小 N 的初始假设是完全错误的。它的行为基本上与普通指针相同。
  • const 属性的行为也与指针一样(与指向指针类型的 typedef 形成鲜明对比,如下面的 @random 下划线)
  • 要强制执行大小(这不是最初的问题,但现在最终变得非常重要......),请参阅Jonathan's answer

【问题讨论】:

标签: c arrays constants typedef


【解决方案1】:

表格的内容是不变的。使用此代码轻松检查。

#include<stdio.h>

typedef int table_t[3];
void doSomething(const table_t t)
{
    t++;    //No error, it's a non-const pointer.
    t[1]=3; //Error, it's a pointer to const.

}

int main()
{
    table_t t={1,2,3};
    printf("%d %d %d %ld",t[0],t[1],t[2],sizeof(t));
    t[1]=5;
    doSomething(t);
    return 0;
}

【讨论】:

  • 函数参数和普通的本地数组不是一回事。
  • @Lundin:我已经编辑了代码。是现在吗?还是我还是不明白?
  • 是的,现在它是正确的,虽然你没有解释为什么它会这样。
【解决方案2】:

首先,你错了,函数原型

int doSomething(table_t t);
int doSomething(int* t);

完全等价。对于函数参数,第一个数组维度总是被重写为指针。所以不能保证接收到的数组的大小。

const-qualification on arrays 总是适用于数组的基类型,所以两个声明

const table_t a;
int const a[N];

是等价的,对于函数参数我们有

int doSomething(const table_t t);
int doSomething(int const* t);

【讨论】:

    【解决方案3】:

    标准 6.7.6.3 说:

    将参数声明为“类型数组”应调整为“限定类型指针”

    这意味着当您将函数参数声明为const int 数组类型时,它会衰减为指向const int(数组中的第一个元素)的指针。在这种情况下相当于const int*

    还要注意,由于上述规则,指定的数组大小不会增加额外的类型安全性!这是 C 语言的一大缺陷,但事实就是如此。

    不过,最好像您一样声明具有固定宽度的数组,因为静态分析器或聪明的编译器可能会生成有关不同类型的诊断。

    【讨论】:

    • 好点。如今,静态分析器在发现溢出错误方面往往更加完善。大小的指示确实可以暗示它们。
    【解决方案4】:

    第一个构造的优点是它强制 N 作为表的大小。

    我不确定你在这里的意思。它会在什么情况下“强制执行”它?如果您将函数声明为

    int doSomething(table_t t);
    

    数组大小将强制执行。我为了强制执行大小,你必须走不同的路线

    int doSomething(table_t *t); // equivalent to 'int (*t)[N]'
    

    这里的 const 是什么?

    至于const...当const 应用于数组类型时,它一直“下拉”到数组元素。这意味着const table_t 是一个常量ints 的数组,即它等价于const int [N] 类型。这样做的最终结果是数组变得不可修改。在函数参数声明上下文中const table_t会被转换成const int *

    但是,请注意一个在这种情况下并不立即显而易见的特殊细节:数组类型本身仍然是非 const 限定的。 个别元素变成const。事实上,在 C 中不可能对数组类型本身进行 const 限定。任何这样做的尝试都会使 const 限定“筛选”到单个元素。

    这种特性会导致数组 const 正确性产生相当不愉快的后果。例如,这段代码不会在 C 中编译

    table_t t;
    const table_t *pt = &t;
    

    即使从 const 正确性的角度来看它看起来很无辜,并且可以为任何非数组对象类型编译。 C++ 语言更新了它的 const 正确性规则来解决这个问题,而 C 继续坚持它的旧方式。

    【讨论】:

      【解决方案5】:

      数组类型和指针类型不是 100% 等效的,即使在这种情况下,您最终会为函数参数获得 a 指针类型。你的错误在于假设const 如果它一个指针类型,它会以同样的方式行事。

      扩展 ARBY 的例子:

      typedef int table_t[3];
      typedef int *pointer_t;
      
      void doSomething(const table_t t)
      {
          t++;    //No error, it's a non-const pointer.
          t[1]=3; //Error, it's a pointer to const.
      }
      
      void doSomethingElse(const pointer_t t)
      {
          t++;    //Error, it's a const pointer.
          t[1]=3; //No error, it's pointer to plain int
      }
      

      它的行为与const int * 类似,但const pointer_t 等效于int * const

      (另外,免责声明,POSIX 不允许使用以 _t 结尾的用户定义名称,它们是为将来扩展而保留的)

      【讨论】:

      • 这很好。最初的问题仅限于typedef int table_t[N]; 示例(因此是第一个问题)。尽管如此,强调与typedef int *pointer_t; 的区别还是很有趣的。
      猜你喜欢
      • 2016-12-14
      • 1970-01-01
      • 2016-04-26
      • 2011-01-16
      • 2019-09-27
      • 2011-01-10
      • 2013-07-20
      • 2014-08-03
      相关资源
      最近更新 更多