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등), 열거 형 멤버 및와 같은 연산자의 결과를 나타 sizeof냅니다. Const-qualified 객체 (모든 유형)는 C 언어 용어에서 상수 가 아닙니다 . 유형에 관계없이 정적 저장 시간을 갖는 객체의 이니셜 라이저에는 사용할 수 없습니다.

예를 들어, 이것은 상수 가 아닙니다

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

위의 NC ++에서는 상수이지만 C에서는 상수가 아닙니다. 따라서 시도하면

static int j = N; /* ERROR */

상수가 아닌 정적 객체를 초기화하려고 시도하면 같은 오류가 발생합니다.

이것이 C 언어에서 주로 #define명명 된 상수를 선언하고 #define명명 된 집계 이니셜 라이저를 만드는 데 사용하는 이유 입니다.


2
좋은 설명은 +5이지만 놀랍게도이 프로그램은 ideone : ideone.com/lx4Xed 에서 잘 컴파일됩니다 . 컴파일러 버그입니까 아니면 컴파일러 확장입니까? 감사합니다
— 소멸자

2
@ meet : 나는 IDE에서 컴파일러 옵션의 조합이 무엇인지 알지 못하지만 그 결과는 종종 설명 이상으로 이상합니다. Coliru ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) 에서이 코드를 컴파일하려고 시도했지만 사용하는 C 언어 방언 설정에 관계없이 예상되는 오류가 발생했습니다. GCC 웹 사이트에 C 언어 확장으로 나열된 것과 같은 것이 보이지 않습니다. 다시 말해, 어떻게 그리고 왜 IDE에서 컴파일되는지 전혀 모른다. 언어 확장으로 컴파일 되더라도 여전히 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에서 :

정적 저장 시간을 갖는 객체에 대한 이니셜 라이저의 모든 표현식은 상수 표현식 또는 문자열 리터럴이어야합니다.

섹션 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
여기에 일정하지 않은 것으로 초기화 된 정적 저장 기간이있는 변수가 표시되지 않습니다.
— 안녕 안녕

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 : 오류 : 초기화 요소가 일정하지 않습니다 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 항 : ". 식 또는 문자열 리터럴 상수한다 정적 또는 스레드 저장 기간을 가진 개체에 대한 초기화의 모든 표현"
— Andrew Henle
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.