ข้อผิดพลาด“ องค์ประกอบเริ่มต้นไม่คงที่” เมื่อพยายามเริ่มต้นตัวแปรด้วย const


187

ฉันได้รับข้อผิดพลาดในบรรทัดที่ 6 (เริ่มต้น my_foo เป็น foo_init) ของโปรแกรมต่อไปนี้และฉันไม่แน่ใจว่าฉันเข้าใจว่าทำไม

typedef struct foo_t {
    int a, b, c;
} foo_t;

const foo_t foo_init = { 1, 2, 3 };
foo_t my_foo = foo_init;

int main()
{
    return 0;
}

โปรดทราบว่านี่เป็นเวอร์ชันที่เรียบง่ายของโครงการหลายไฟล์ขนาดใหญ่ที่ฉันกำลังทำงานอยู่ เป้าหมายคือการมีค่าคงที่เดียวในไฟล์อ็อบเจ็กต์ที่หลายไฟล์สามารถใช้เพื่อเริ่มต้นโครงสร้างสถานะ เนื่องจากเป็นเป้าหมายแบบฝังที่มีทรัพยากร จำกัด และโครงสร้างไม่เล็กฉันจึงไม่ต้องการสำเนาหลายชุด ฉันไม่ต้องการใช้:

#define foo_init { 1, 2, 3 }

ฉันกำลังพยายามเขียนโค้ดแบบพกพาด้วยดังนั้นฉันจึงต้องการโซลูชันที่ใช้ได้กับ C89 หรือ C99

สิ่งนี้เกี่ยวข้องกับ ORG ในไฟล์วัตถุหรือไม่? ตัวแปรเริ่มต้นนั้นไปเป็นหนึ่ง ORG และเริ่มต้นได้โดยการคัดลอกเนื้อหาของ ORG ที่สอง?

บางทีฉันอาจต้องเปลี่ยนชั้นเชิงของฉันและมีฟังก์ชั่นการเริ่มต้นทำสำเนาทั้งหมดเมื่อเริ่มต้น นอกเสียจากว่าจะมีความคิดอื่น ๆ ออกมา?

คำตอบ:


269

ในภาษา C วัตถุที่มีระยะเวลาการจัดเก็บแบบคงที่จะต้องเริ่มต้นด้วยการแสดงออกคงที่หรือด้วยการเริ่มต้นรวมที่มีการแสดงออกคงที่

วัตถุ "ใหญ่" จะไม่แสดงออกอย่างต่อเนื่องใน C แม้ว่าวัตถุจะถูกประกาศเป็น constแม้ว่าวัตถุที่ถูกประกาศให้เป็น

นอกจากนี้ในภาษา C, คำว่า "คงที่" หมายถึงค่าคงที่ตัวอักษร (เช่น1, 'a', 0xFFและอื่น ๆ ) สมาชิก enum sizeofและผลของผู้ประกอบการเช่น วัตถุที่ผ่านการรับรอง Const (ชนิดใด ๆ ) ไม่คงที่ในคำศัพท์ภาษา C พวกเขาไม่สามารถใช้ในการเริ่มต้นของวัตถุที่มีระยะเวลาการจัดเก็บแบบคงที่โดยไม่คำนึงถึงประเภทของพวกเขา

ตัวอย่างเช่นนี่ไม่ใช่ค่าคงที่

const int N = 5; /* `N` is not a constant in C */

ด้านบนNจะเป็นค่าคงที่ใน C ++ แต่มันไม่คงที่ใน C ดังนั้นถ้าคุณลองทำ

static int j = N; /* ERROR */

คุณจะได้รับข้อผิดพลาดเดียวกัน: ความพยายามในการเริ่มต้นวัตถุคงที่ที่ไม่คงที่

นี่คือเหตุผลว่าทำไมในภาษา C เราส่วนใหญ่ใช้#defineในการประกาศค่าคงที่ชื่อและยังหันไปใช้#defineเพื่อสร้าง initializers รวมที่มีชื่อ


2
5 สำหรับคำอธิบายที่ดี แต่ที่น่าแปลกใจโปรแกรมนี้รวบรวมได้ดีบน ideone: ideone.com/lx4Xed มันเป็นข้อผิดพลาดของคอมไพเลอร์หรือส่วนขยายคอมไพเลอร์? ขอบคุณ
Destructor

2
@ ตอบสนอง: ฉันไม่ทราบว่าการรวมกันของตัวเลือกคอมไพเลอร์ ideone ใช้ภายใต้ประทุน แต่ผลลัพธ์ของพวกเขามักจะแปลกเกินคำอธิบาย ฉันพยายามรวบรวมรหัสนี้บน Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) และได้รับข้อผิดพลาดที่คาดหวังไว้โดยไม่คำนึงถึงการตั้งค่าภาษา C ที่ฉันใช้ ฉันไม่เห็นอะไรเช่นนั้นที่ระบุไว้ในเว็บไซต์ของ GCC เป็นส่วนเสริมภาษา C ในคำอื่น ๆ ฉันไม่ทราบวิธีและทำไมมันรวบรวมใน ideone แม้ว่ามันจะรวบรวมเป็นส่วนขยายของภาษาก็ควรจะยังคงสร้างข้อความวินิจฉัยใน C.
AnT

15
enum { N = 5 };เป็นวิธีที่ไม่นิยมสำหรับการประกาศค่าคงที่โดยไม่ต้องหันไป#defineใช้
MM

2
@PravasiMeet "ideone" นั้นไม่ได้แสดงข้อความการวินิจฉัยจำนวนมากที่คอมไพเลอร์สร้างขึ้นดังนั้นมันจึงไม่ใช่ไซต์ที่ดีมากที่จะใช้ในการพิจารณาว่ารหัสถูกต้องหรือไม่
MM

1
ฉันพบสิ่งที่น่าสนใจ ถ้า ptr เป็นตัวชี้คงที่ที่กำหนดไว้ภายในฟังก์ชั่นนี่เป็นข้อผิดพลาด: static int* ptr = malloc(sizeof(int)*5);แต่นี่ไม่ใช่ข้อผิดพลาดstatic int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

มันเป็นข้อ จำกัด ของภาษา ในส่วน 6.7.8 / 4:

นิพจน์ทั้งหมดใน initializer สำหรับวัตถุที่มีระยะเวลาการจัดเก็บแบบคงที่จะต้องเป็นนิพจน์คงที่หรือตัวอักษรสตริง

ในส่วนที่ 6.6 ข้อมูลจำเพาะจะกำหนดสิ่งที่ต้องพิจารณาว่าเป็นนิพจน์คงที่ ไม่มีที่ใดที่จะระบุว่าตัวแปร const ต้องพิจารณาว่าเป็นนิพจน์คงที่ มันถูกกฎหมายสำหรับคอมไพเลอร์ที่จะขยายนี้ (6.6/10 - An implementation may accept other forms of constant expressions ) แต่ที่จะ จำกัด การพกพา

หากคุณสามารถเปลี่ยนแปลงได้my_fooดังนั้นจึงไม่มีที่เก็บข้อมูลแบบคงที่คุณจะโอเค:

int main()
{
    foo_t my_foo = foo_init;
    return 0;
}

ฉันชอบที่คุณอ้างถึงข้อมูลจำเพาะ แต่สิ่งนี้ไม่ช่วยให้ฉันเข้าใจว่าเราควรทำอะไรหรือทำไมสิ่งต่าง ๆ เป็นอย่างที่เป็น
Evan Carroll

1
ดูเหมือนว่า GCC 8.1 (และใหม่กว่า) ได้ใช้ส่วนขยายบางส่วนตามที่อธิบายไว้ในคำตอบนี้ static const int x = 3; static int y = x;จะยอมรับ
Eric Postpischil

5

เพื่อภาพประกอบโดยการเปรียบเทียบและความคมชัดรหัสมาจากhttp://www.geeksforgeeks.org/g-fact-80/ / รหัสล้มเหลวใน gcc และส่งผ่านเป็น g ++ /

#include<stdio.h>
int initializer(void)
{
    return 50;
}

int main()
{
    int j;
    for (j=0;j<10;j++)
    {
        static int i = initializer();
        /*The variable i is only initialized to one*/
        printf(" value of i = %d ", i);
        i++;
    }
    return 0;
}

2

นี่เก่าไปหน่อย แต่ฉันพบปัญหาที่คล้ายกัน คุณสามารถทำได้ถ้าคุณใช้ตัวชี้:

#include <stdio.h>
typedef struct foo_t  {
    int a; int b; int c;
} foo_t;
static const foo_t s_FooInit = { .a=1, .b=2, .c=3 };
// or a pointer
static const foo_t *const s_pFooInit = (&(const foo_t){ .a=2, .b=4, .c=6 });
int main (int argc, char **argv) {
    const foo_t *const f1 = &s_FooInit;
    const foo_t *const f2 = s_pFooInit;
    printf("Foo1 = %d, %d, %d\n", f1->a, f1->b, f1->c);
    printf("Foo2 = %d, %d, %d\n", f2->a, f2->b, f2->c);
    return 0;
}

5
ฉันไม่เห็นตัวแปร aa ที่มีระยะเวลาการจัดเก็บแบบสแตติกที่เริ่มต้นด้วยค่าคงที่ที่นี่
ลา SE

0

gcc 7.4.0 ไม่สามารถคอมไพล์โค้ดได้ด้านล่าง:

#include <stdio.h>
const char * const str1 = "str1";
const char * str2 = str1;
int main() {
    printf("%s - %s\n", str1, str2);
    return 0;
}

constchar.c: 3: 21: ข้อผิดพลาด: องค์ประกอบ initializer ไม่คงที่ const char * str2 = str1;

อันที่จริงแล้วสตริง "const char *" ไม่ใช่ค่าคงที่เวลาคอมไพล์ดังนั้นจึงไม่สามารถเป็นตัวเริ่มต้นได้ แต่สตริง "const char * const" เป็นค่าคงที่เวลารวบรวมมันควรจะสามารถเริ่มต้นได้ ฉันคิดว่านี่เป็นข้อเสียเปรียบเล็ก ๆ ของ CLang

ชื่อฟังก์ชั่นเป็นค่าคงที่เวลาคอมไพล์ดังนั้นรหัสนี้ทำงาน:

void func(void)
{
    printf("func\n");
}
typedef void (*func_type)(void);
func_type f = func;
int main() {
    f();
    return 0;
}

ในรหัสที่คุณโพสต์str1ไม่ใช่นิพจน์ต่อ6.7.9 การเริ่มต้นย่อหน้าที่ 4 : "นิพจน์ทั้งหมดใน initializer สำหรับวัตถุที่มีระยะเวลาคงที่หรือการจัดเก็บข้อมูลในเธรดจะต้องเป็นนิพจน์หรือสตริงตัวอักษรคงที่"
Andrew Henle
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.