Değişkenin const ile başlatılmasına çalışılırken "başlatıcı öğesi sabit değil" hatası


187

Aşağıdaki programın 6. satırında bir hata alıyorum (my_foo için foo_init için başlat) ve nedenini anladığımdan emin değilim.

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

Bunun üzerinde çalıştığım daha büyük, çok dosyalı bir projenin basitleştirilmiş bir sürümü olduğunu unutmayın. Amaç, nesne dosyasında bir durum yapısını başlatmak için birden fazla dosyanın kullanabileceği tek bir sabitin bulunmasıydı. Sınırlı kaynaklara sahip yerleşik bir hedef olduğundan ve yapı o kadar küçük olmadığından, kaynağın birden fazla kopyasını istemiyorum. Kullanmamayı tercih ederim:

#define foo_init { 1, 2, 3 }

Ayrıca taşınabilir kod yazmaya çalışıyorum, bu yüzden geçerli C89 veya C99 bir çözüme ihtiyacım var.

Bunun bir nesne dosyasındaki ORG'lerle bir ilgisi var mı? Başlatılan değişkenler bir ORG'ye girer ve ikinci bir ORG'nin içeriği kopyalanarak başlatılır mı?

Belki sadece taktiğimi değiştirmem ve başlangıçta tüm kopyaları yapması için bir başlatma işlevine sahip olmam gerekir. Başka fikirler olmadığı sürece?

Yanıtlar:


269

C dilinde, statik depolama süresine sahip nesnelerin sabit ifadelerle veya sabit ifadeler içeren toplu başlatıcılarla başlatılması gerekir.

"Büyük" bir nesne, nesne olarak bildirilse bile C'de asla sabit bir ifade değildir const.

Ayrıca, C dilinde terimi "sabit" belirtir değişmez sabitleri (gibi 1, 'a', 0xFFve benzeri), numaralama üyeleri ve gibi operatörlerin sonuçları sizeof. Const niteleyici nesneler (herhangi bir türde) C dili terminolojisinde sabit değildir . Türlerine bakılmaksızın statik depolama süresine sahip nesnelerin başlatıcılarında kullanılamazlar.

Örneğin, bu bir sabit DEĞİLDİR

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

Yukarıdaki NC ++ 'da bir sabit olurdu, ama C' de bir sabit değildir. Yani, eğer

static int j = N; /* ERROR */

aynı hatayı alırsınız: statik olmayan bir nesneyi sabit olmayan bir şekilde başlatma girişimi.

Bu nedenle, C dilinde ağırlıklı olarak #defineadlandırılmış sabitleri bildirmek için kullanıyoruz ve aynı zamanda #defineadlandırılmış toplu başlatıcılar oluşturmak için başvuruyoruz .


2
Güzel açıklama için +5, ama şaşırtıcı bir şekilde bu program ideone: ideone.com/lx4Xed üzerinde iyi derler . Derleyici hatası mı yoksa derleyici uzantısı mı? Teşekkürler
Destructor

2
@meet: ideone'nin başlık altında hangi derleyici seçeneklerinin kombinasyonunu kullandığını bilmiyorum, ancak sonuçları genellikle açıklamanın ötesinde garip. Bu kodu Coliru'da ( coliru.stacked-crooked.com/a/daae3ce4035f5c8b ) derlemeyi denedim ve hangi C dili lehçe ayarını kullandığımdan bağımsız olarak beklenen hatayı aldım. GCC'nin web sitesinde C dili uzantısı olarak listelenen böyle bir şey görmüyorum. Başka bir deyişle, ideone'de nasıl ve neden derlendiğini bilmiyorum. Bir dil uzantısı olarak derlenmiş olsa bile, yine de C'de bir tanı mesajı
üretmelidir

15
enum { N = 5 };başvurmak zorunda kalmadan sabitleri bildirmenin az bilinen bir yoludur #define.
MM

2
@PravasiMeet "ideone" sadece derleyicinin ürettiği tanı iletilerinin çoğunu görüntülemez, bu nedenle kodun doğru olup olmadığını belirlemek için kullanmak için çok iyi bir site değildir.
MM

1
İlginç bir şey buldum. ptr bir işlev içinde tanımlanan statik bir işaretçi ise, bu hata: static int* ptr = malloc(sizeof(int)*5);ancak bu bir hata DEĞİLDİR static int* ptr; ptr = malloc(sizeof(int)*5);:: D
aderchox

74

Bu dilin bir sınırlamasıdır. Bölüm 6.7.8 / 4'te:

Statik depolama süresi olan bir nesne için bir başlatıcıdaki tüm ifadeler sabit ifadeler veya dizgi değişmezleri olacaktır.

Bölüm 6.6'da, teknik özellik sabit bir ifade olarak neyin dikkate alınması gerektiğini tanımlar. Hayır, bir const değişkeninin sabit bir ifade olarak değerlendirilmesi gerektiği anlamına gelmez. Bir derleyicinin bunu ( 6.6/10 - An implementation may accept other forms of constant expressions) genişletmesi yasaldır, ancak bu taşınabilirliği sınırlar.

Değiştirmek Eğer my_foostatik depolama birimi yok bu yüzden, iyi olacaktır:

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

Spesifikasyondan alıntı yaptığınızı sevdim, ancak bu ne yapmamız gerektiğini veya neden bu şekilde olduklarını anlamamı sağlamaz.
Evan Carroll

1
Görünüşe göre GCC 8.1 (ve üstü) bu cevapta açıklandığı gibi bazı uzantılar uyguladı; kabul eder static const int x = 3; static int y = x;.
Eric Postpischil

5

Sadece tarafından gösterim için karşılaştırma ve kod ila kontrast http://www.geeksforgeeks.org/g-fact-80/ / kod gcc giremiyerek g ++ geçer /

#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

Bu biraz eski, ama benzer bir sorunla karşılaştım. İşaretçi kullanıyorsanız bunu yapabilirsiniz:

#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
Burada bir sabit olmayan tarafından başlatılan statik depolama süresi olan bir değişken görmüyorum.
Elveda SE

0

gcc 7.4.0 aşağıdaki gibi kodları derleyemez:

#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: hata: başlatıcı elemanı sabit değil const char * str2 = str1;

Aslında, bir "const char *" dizesi derleme zamanı sabiti değildir, bu yüzden bir başlatıcı olamaz. Ancak bir "const char * const" dizesi bir derleme zamanı sabiti, bir başlatıcı olabilmelidir. Bence bu CLang'ın küçük bir dezavantajı.

Bir işlev adı elbette bir derleme zamanı sabitidir.Bu nedenle bu kod çalışır:

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

Gönderdiğiniz kodda, 6.7.9 Başlatma , paragraf 4 uyarınca str1bir ifade değildir : "Statik veya iş parçacığı saklama süresi olan bir nesne için bir başlatıcıdaki tüm ifadeler sabit ifadeler veya dize değişmezleri olacaktır."
Andrew Henle
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.