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.