【问题标题】:Why I cant declare a constant in a function?为什么我不能在函数中声明常量?
【发布时间】:2021-09-14 06:37:11
【问题描述】:

当我尝试执行以下函数时出现此错误 src\main.c(41): error: #259: constant value is not known

代码:

uint8_t checksum_tx(uint64_t Data, uint8_t dataLength, uint8_t checksum_len )   {

  const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;

    //** splitting data into 6-bits subunits . . .
    
    uint8_t res = 0U;   
    uint8_t dataSubUnit[length];

导致错误的行 const uint8_t length = (dataLength + checksum_len - 1) / checksum_len;

有人可以澄清发生了什么问题吗? 我在某处读到C中的常量必须直接声明,不允许初始化它们,我想这就是我所做的。

【问题讨论】:

  • 常量在编译期间进行评估。在您的情况下,您使用函数参数来定义它,但编译器不知道它们的值
  • 您使用的是什么编译器/操作系统?在 C17 中,const 变量不是其值在编译时已知的变量;相反,const 基本上意味着它的值一旦被初始化就不能被修改。这就是为什么dataSubUnit 不是静态数组,而是 VLA(即:在运行时知道其维度的数组,并将在堆栈上分配),但并非所有编译器都支持 VLA。
  • 欢迎来到 StackOverflow!请拨打tour 并阅读“How to Ask”以了解本网站的运作方式。 -- 我们真的需要知道你使用的是哪个编译器。标准编译器对您的初始化常量变量没有问题,即使是带有“-std=c90”的 GCC。最好edit您的问题,并提供minimal reproducible example,重点是“完整”。
  • 请尝试发布语法正确的代码,最好是How to create a Minimal, Reproducible Example中推荐的完整示例所以我们不需要更正代码来查看编译器产生的错误(可能我们得到您犯的错误,或其他错误,或根本没有)。这意味着您的函数头必须通过左花括号等与主体分开。
  • 感谢您的反馈,以后我会尝试发布更易于理解的问题。

标签: c constants


【解决方案1】:

这是语言的限制...或const 标记对象的功能(取决于阅读者)。我认为您对const 关键字的解释,适用于生命周期有限的数据对象,应该使其在对象的生命周期内不可变,即定义它的块的执行。但是编译器使数据有效地成为一个常量(如常量 PI),并且它必须在整个程序生命周期内保持相同的值,而不是在每个函数调用时。在您发布的情况下,常量将具有的表达式的值在函数启动并且参数接收它们的值之前是未知的(只有这样才能评估初始化表达式),所以常量实际上不是常量(您可以在不同的调用中为函数参数提供不同的值,并使常量值与上一次调用不同)。

例如,在 java 中,有一个 final 关键字来将数据值标记为常量,并且您必须在对象的生命周期内保留(您不能为其分配/更改值)(这意味着因为它被创建直到被销毁),但初始值是在创建时动态确定的。这在 C 语言中不成立。const 声明的数据对象实际上必须是一个常量,并且必须由一个表达式(称为 const 表达式)初始化,该表达式的值可以在编译时确定。

【讨论】:

    【解决方案2】:

    常量不是真正的变量:一旦代码被编译,常量就会被转换成硬编码的值(立即操作数),或者程序中不能写入的部分(通常是 .rodata)中的引用。

    因此,不能在运行时为常量赋值。您必须设置一个在编译时已知的值,因此该值不依赖于其他变量。 (例如const int length = 42;)。 如果您在编译代码之前无法知道length 的值,那么您需要一个变量,而不是const

    我猜你需要一个常量来声明数组dataSubUnit。解决这个问题的常用方法是声明一个足够大的数组以包含任何可能的length

    #define MAX_DATA_SUBUNIT_LEN 42
    
    uint8_t checksum_tx(uint64_t Data, ... ) {
      uint8_t dataSubUnit[MAX_DATA_SUBUNIT_LEN];
    

    ...并防止可能的溢出:

    uint8_t length = (dataLength + checksum_len - 1) / checksum_len;
    
    if ( length < MAX_DATA_SUBUNIT_LEN ) {
       ... // proceed 
    }
    else {
        // return some relevant error code
    }
    

    【讨论】:

    • 对不起,这是完全错误的。在前两段中,您将constconstexpr 混淆了(顺便说一句,仅存在于C++ 中),并且您提出的解决方案不是很好;这充其量是一种解决方法。但是,如果您的数组中需要超过 42 个元素怎么办?真正的解决方案是使用malloc 创建一个大小合适的数组。
    • 正如目前所写,您的答案尚不清楚。请edit 添加其他详细信息,以帮助其他人了解这如何解决所提出的问题。你可以找到更多关于如何写好答案的信息in the help center
    • 嗯,C 编译器过去常常将“const”符号映射到 .rodata 部分并阻止那里的任何存储操作,还是在我不注意的时候发生了变化?另外,我同意 malloc() 将是最节省内存的解决方案,但编码规则有时会禁止使用动态分配或可变长度数组。
    • 通用(非标准化!).rodata 部分仅包含 static 常量。自动变量不会去那里。
    • @fabiencasas:禁止动态分配的环境确实非常具体,值得 OP 提及。如果没有提到任何此类限制,可以安全地假设(恕我直言)malloc() 是允许的。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-01-21
    • 2012-11-23
    • 1970-01-01
    • 2011-08-27
    • 2019-03-05
    • 2021-06-11
    相关资源
    最近更新 更多