Lỗi phần tử khởi tạo lỗi không phải là hằng số khi cố gắng khởi tạo biến với const


186

Tôi gặp lỗi trên dòng 6 (khởi tạo my_foo thành foo_init) của chương trình sau và tôi không chắc tại sao tôi hiểu tại sao.

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;
}

Hãy nhớ rằng đây là phiên bản đơn giản hóa của một dự án nhiều tệp lớn hơn mà tôi đang làm việc. Mục tiêu là có một hằng số duy nhất trong tệp đối tượng, rằng nhiều tệp có thể sử dụng để khởi tạo cấu trúc trạng thái. Vì đó là mục tiêu được nhúng với tài nguyên hạn chế và cấu trúc không nhỏ, tôi không muốn có nhiều bản sao của nguồn. Tôi không muốn sử dụng:

#define foo_init { 1, 2, 3 }

Tôi cũng đang cố gắng viết mã di động, vì vậy tôi cần một giải pháp hợp lệ là C89 hoặc C99.

Điều này có liên quan đến ORG trong một tệp đối tượng không? Các biến được khởi tạo đó đi vào một ORG và được khởi tạo bằng cách sao chép nội dung của ORG thứ hai?

Có lẽ tôi chỉ cần thay đổi chiến thuật của mình và có chức năng khởi tạo để thực hiện tất cả các bản sao khi khởi động. Trừ khi có những ý tưởng khác ngoài kia?

Câu trả lời:


269

Trong ngôn ngữ C, các đối tượng có thời lượng lưu trữ tĩnh phải được khởi tạo bằng các biểu thức không đổi hoặc với các bộ khởi tạo tổng hợp có chứa các biểu thức không đổi.

Một đối tượng "lớn" không bao giờ là một biểu thức không đổi trong C, ngay cả khi đối tượng được khai báo là const.

Hơn nữa, trong ngôn ngữ C, thuật ngữ "liên tục" đề cập đến hằng đen (như 1, 'a', 0xFFvà vân vân), các thành viên enum, và kết quả của các nhà khai thác như sizeof. Các đối tượng đủ điều kiện (thuộc bất kỳ loại nào) không phảihằng số trong thuật ngữ ngôn ngữ C. Chúng không thể được sử dụng trong các bộ khởi tạo của các đối tượng có thời gian lưu trữ tĩnh, bất kể loại của chúng.

Ví dụ, đây KHÔNG phải là hằng số

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

Ở trên Nsẽ là một hằng số trong C ++, nhưng nó không phải là hằng số trong C. Vì vậy, nếu bạn thử làm

static int j = N; /* ERROR */

bạn sẽ nhận được cùng một lỗi: một nỗ lực để khởi tạo một đối tượng tĩnh với hằng số không đổi.

Đây là lý do tại sao, trong ngôn ngữ C, chúng tôi chủ yếu sử dụng #defineđể khai báo các hằng số được đặt tên và cũng sử dụng #defineđể tạo ra các công cụ khởi tạo tổng hợp có tên.


2
+5 cho lời giải thích hay, nhưng đáng ngạc nhiên là chương trình này biên dịch tốt trên ideone: ideone.com/lx4Xed . Là lỗi trình biên dịch hoặc phần mở rộng trình biên dịch? Cảm ơn
Kẻ hủy diệt

2
@meet: Tôi không biết sự kết hợp của các tùy chọn trình biên dịch mà ideone sử dụng dưới mui xe, nhưng kết quả của chúng thường kỳ lạ ngoài mô tả. Tôi đã thử biên dịch mã này trên Coliru ( coliru.stacked-crooking.com/a/daae3ce4035f5c8b ) và nhận được lỗi mong đợi cho nó bất kể cài đặt phương ngữ ngôn ngữ C nào tôi đã sử dụng. Tôi không thấy bất cứ điều gì giống như được liệt kê trên trang web của GCC dưới dạng phần mở rộng ngôn ngữ C. Nói cách khác, tôi không biết làm thế nào và tại sao nó biên dịch thành ideone. Ngay cả khi nó biên dịch thành một phần mở rộng ngôn ngữ, nó vẫn sẽ tạo ra một thông báo chẩn đoán trong C.
AnT

15
enum { N = 5 };là một cách đánh giá thấp các khai báo mà không cần phải dùng đến #define.
MM

2
@PravasiMeet "ideone" đơn giản là không hiển thị nhiều thông báo chẩn đoán mà trình biên dịch tạo ra, vì vậy đây không phải là một trang web rất tốt để sử dụng để xác định xem mã có chính xác hay không.
MM

1
Tôi đã tìm ra một điều thú vị. nếu ptr là một con trỏ tĩnh được xác định bên trong hàm, đây là lỗi: static int* ptr = malloc(sizeof(int)*5);nhưng đây KHÔNG phải là lỗi static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

Đó là một hạn chế của ngôn ngữ. Trong phần 6.7.8 / 4:

Tất cả các biểu thức trong bộ khởi tạo cho một đối tượng có thời lượng lưu trữ tĩnh phải là biểu thức không đổi hoặc chuỗi ký tự.

Trong phần 6.6, thông số kỹ thuật xác định những gì phải coi là biểu thức hằng. Không có nơi nào nói rằng một biến const phải được coi là biểu thức hằng. Nó là hợp pháp cho một trình biên dịch để mở rộng điều này ( 6.6/10 - An implementation may accept other forms of constant expressions) nhưng điều đó sẽ hạn chế tính di động.

Nếu bạn có thể thay đổi my_foođể nó không có bộ nhớ tĩnh, bạn sẽ ổn thôi:

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

Tôi thích rằng bạn đã trích dẫn thông số kỹ thuật, nhưng điều này không giúp tôi hiểu những gì chúng ta phải làm hoặc tại sao mọi thứ lại như vậy.
Evan Carroll

1
Dường như GCC 8.1 (và sau này) đã triển khai một số phần mở rộng như được mô tả trong câu trả lời này; nó chấp nhận static const int x = 3; static int y = x;.
Eric Postpischil

5

Chỉ để minh họa bằng cách so sánh và tương phản Mã được lấy từ http://www.geekforgeek.org/g-fact-80/ / Mã bị lỗi trong gcc và chuyển qua 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

Điều này hơi cũ, nhưng tôi gặp phải một vấn đề tương tự. Bạn có thể làm điều này nếu bạn sử dụng một con trỏ:

#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
Tôi không thấy biến aa với thời lượng lưu trữ tĩnh được khởi tạo bởi một hằng số ở đây.
Tạm biệt SE

0

gcc 7.4.0 không thể biên dịch mã như sau:

#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: error: phần tử khởi tạo không phải là hằng const char * str2 = str1;

Trong thực tế, một chuỗi "const char *" không phải là hằng số thời gian biên dịch, vì vậy nó không thể là một bộ khởi tạo. Nhưng một chuỗi "const char * const" là hằng số thời gian biên dịch, nó có thể là một bộ khởi tạo. Tôi nghĩ rằng đây là một nhược điểm nhỏ của CLang.

Tên hàm tất nhiên là hằng số thời gian biên dịch. Vì vậy, mã này hoạt động:

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

Trong mã bạn đã đăng, str1không phải là biểu thức trên 6.7.9 Khởi tạo , đoạn 4 : "Tất cả các biểu thức trong trình khởi tạo cho một đối tượng có thời lượng lưu trữ tĩnh hoặc luồng sẽ là biểu thức không đổi hoặc chuỗi ký tự."
Andrew Henle
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.