Sabit ifade ile başlatıcı C99'da olası taşma olabilir

Bu geçerli bir C99 kodu mu? Eğer öyleyse, uygulama tarafından tanımlanmış bir davranış tanımlar mı?

int a;
unsigned long b[] = {(unsigned long)&a+1};

C99 standardını anladığımdan, ISO C99 standardında §6.6'dan bu geçerli olabilir:

  1. An integer constant expression shall have integer type and shall only have operands that are integer constants (...) Cast operators in an integer constant expression shall only convert arithmetic types to integer types, except as part of an operand to the sizeof operator.

  2. More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:

    • an arithmetic constant expression,
    • (...)
    • an address constant for an object type plus or minus an integer constant expression.

Bununla birlikte, ilave taşma olasılığı olduğu için, bu sabit bir ifade olarak kabul edilmeyebilir ve bu nedenle geçerli bir C99 kodu olmayabilir.

Biri lütfen nedenimin doğru olup olmadığını onaylayabilir mi?

Hem GCC hem de Clang’ın, -std = c99 -pedantic kullanırken bile uyarı vermeden kabul ettiğini unutmayın. Ancak, imzasız uzun yerine imzasız int öğesine basarken aşağıdaki kodu kullanın:

int a;
unsigned long b[] = {(unsigned int)&a+1};

Sonra her iki derleyici de ifadenin derleme zamanı sabiti olmadığından şikayet eder.

6
Bunu dosya kapsamında mı yoksa blok kapsamında mı istediğinizi açıklayabilir misiniz?
katma yazar M.M, kaynak
Bunu dosya kapsamında mı yoksa blok kapsamında mı istediğinizi açıklayabilir misiniz?
katma yazar M.M, kaynak
Bunu dosya kapsamında mı yoksa blok kapsamında mı istediğinizi açıklayabilir misiniz?
katma yazar M.M, kaynak
long yerine int konumuna atıldığından (veya tanımsız davranışa yol açtığından), derleyicilerin "sabit bir ifade değil" olarak reddettiğini varsaydım; bu nedenle, aynı mantığı ilk davaya uygulamaları ve reddetmeleri gerektiğini kabul ettim.
katma yazar anol, kaynak
long yerine int konumuna atıldığından (veya tanımsız davranışa yol açtığından), derleyicilerin "sabit bir ifade değil" olarak reddettiğini varsaydım; bu nedenle, aynı mantığı ilk davaya uygulamaları ve reddetmeleri gerektiğini kabul ettim.
katma yazar anol, kaynak
long yerine int konumuna atıldığından (veya tanımsız davranışa yol açtığından), derleyicilerin "sabit bir ifade değil" olarak reddettiğini varsaydım; bu nedenle, aynı mantığı ilk davaya uygulamaları ve reddetmeleri gerektiğini kabul ettim.
katma yazar anol, kaynak
Bu değişkenler dosya kapsamında ilan edildi (küresel olarak).
katma yazar anol, kaynak
Bu değişkenler dosya kapsamında ilan edildi (küresel olarak).
katma yazar anol, kaynak
Bu değişkenler dosya kapsamında ilan edildi (küresel olarak).
katma yazar anol, kaynak
Taşan tamsayı ifadesinin sabit bir ifade olmasını engellediği neresinde?
katma yazar unwind, kaynak
Taşan tamsayı ifadesinin sabit bir ifade olmasını engellediği neresinde?
katma yazar unwind, kaynak
Taşan tamsayı ifadesinin sabit bir ifade olmasını engellediği neresinde?
katma yazar unwind, kaynak
'a' ikinci baytının 'adresini' bir diziye atamak. Bir adresin uzun bir imzasız int dizi girişine yerleştirilmesinde bazı problemler yaşanıyor gibi görünüyor.
katma yazar user3629249, kaynak
'a' ikinci baytının 'adresini' bir diziye atamak. Bir adresin uzun bir imzasız int dizi girişine yerleştirilmesinde bazı problemler yaşanıyor gibi görünüyor.
katma yazar user3629249, kaynak
'a' ikinci baytının 'adresini' bir diziye atamak. Bir adresin uzun bir imzasız int dizi girişine yerleştirilmesinde bazı problemler yaşanıyor gibi görünüyor.
katma yazar user3629249, kaynak

9 cevap

Bu argo geliştiriciden benzer bir konuda konuya: İşlev işaretçisi, çok uzun sürdüğünde derleme zamanı sabittir, ancak int değil mi? , standart, derleyicinin bunu desteklemesini gerektirmediği anlamına gelir. ( bu senaryo, 6.6p7 'daki hiçbir madde işaretine dahil edilmemiştir) ve bu destekleyici kısaltılmış adresleri desteklemesine izin verilse de, külfetli olacaktır:

I assume that sizeof(int) < sizeof(void(*)()) == sizeof(long) on your target. The problem is that the tool chain almost certainly can't express a truncated address as a relocation.

C only requires the implementation to support initializer values that are either (1) constant binary data, (2) the address of some object, or (3) or an offset added to the address of some object. We're allowed, but not required, to support more esoteric things like subtracting two addresses or multiplying an address by a constant or, as in your case, truncating the top bits of an address away. That kind of calculation would require support from the entire tool chain from assembler to loader, including various file formats along the way. That support generally doesn't exist.

Bir işaretleyiciyi bir tamsayı türüne dönüştüren durumunuz, 6.6 paragraf 7 altındaki davaların hiçbirine uymuyor:

Başlatıcılarda sabit ifadeler için daha fazla enlem izin verilir.   Böyle sabit bir ifade,   aşağıdaki gibidir:

     
      
  • bir aritmetik sabit ifade,
  •   
  • anull işaretçi sabiti,
  •   
  • bir adres sabiti veya
  •   
  • bir nesne tipi artı veya eksi bir tamsayı sabiti ifadesi için bir adres sabiti.
  •   

ancak post derleyicide belirtildiği gibi diğer sabit ifade biçimlerini desteklemeye izin verilir:

Bir uygulama diğer sabit ifade biçimlerini kabul edebilir.

ancak ne clang ne de gcc bunu kabul etmez.

3
katma

Bu argo geliştiriciden benzer bir konuda konuya: İşlev işaretçisi, çok uzun sürdüğünde derleme zamanı sabittir, ancak int değil mi? , standart, derleyicinin bunu desteklemesini gerektirmediği anlamına gelir. ( bu senaryo, 6.6p7 'daki hiçbir madde işaretine dahil edilmemiştir) ve bu destekleyici kısaltılmış adresleri desteklemesine izin verilse de, külfetli olacaktır:

I assume that sizeof(int) < sizeof(void(*)()) == sizeof(long) on your target. The problem is that the tool chain almost certainly can't express a truncated address as a relocation.

C only requires the implementation to support initializer values that are either (1) constant binary data, (2) the address of some object, or (3) or an offset added to the address of some object. We're allowed, but not required, to support more esoteric things like subtracting two addresses or multiplying an address by a constant or, as in your case, truncating the top bits of an address away. That kind of calculation would require support from the entire tool chain from assembler to loader, including various file formats along the way. That support generally doesn't exist.

Bir işaretleyiciyi bir tamsayı türüne dönüştüren durumunuz, 6.6 paragraf 7 altındaki davaların hiçbirine uymuyor:

Başlatıcılarda sabit ifadeler için daha fazla enlem izin verilir.   Böyle sabit bir ifade,   aşağıdaki gibidir:

     
      
  • bir aritmetik sabit ifade,
  •   
  • anull işaretçi sabiti,
  •   
  • bir adres sabiti veya
  •   
  • bir nesne tipi artı veya eksi bir tamsayı sabiti ifadesi için bir adres sabiti.
  •   

ancak post derleyicide belirtildiği gibi diğer sabit ifade biçimlerini desteklemeye izin verilir:

Bir uygulama diğer sabit ifade biçimlerini kabul edebilir.

ancak ne clang ne de gcc bunu kabul etmez.

3
katma

Bu argo geliştiriciden benzer bir konuda konuya: İşlev işaretçisi, çok uzun sürdüğünde derleme zamanı sabittir, ancak int değil mi? , standart, derleyicinin bunu desteklemesini gerektirmediği anlamına gelir. ( bu senaryo, 6.6p7 'daki hiçbir madde işaretine dahil edilmemiştir) ve bu destekleyici kısaltılmış adresleri desteklemesine izin verilse de, külfetli olacaktır:

I assume that sizeof(int) < sizeof(void(*)()) == sizeof(long) on your target. The problem is that the tool chain almost certainly can't express a truncated address as a relocation.

C only requires the implementation to support initializer values that are either (1) constant binary data, (2) the address of some object, or (3) or an offset added to the address of some object. We're allowed, but not required, to support more esoteric things like subtracting two addresses or multiplying an address by a constant or, as in your case, truncating the top bits of an address away. That kind of calculation would require support from the entire tool chain from assembler to loader, including various file formats along the way. That support generally doesn't exist.

Bir işaretleyiciyi bir tamsayı türüne dönüştüren durumunuz, 6.6 paragraf 7 altındaki davaların hiçbirine uymuyor:

Başlatıcılarda sabit ifadeler için daha fazla enlem izin verilir.   Böyle sabit bir ifade,   aşağıdaki gibidir:

     
      
  • bir aritmetik sabit ifade,
  •   
  • anull işaretçi sabiti,
  •   
  • bir adres sabiti veya
  •   
  • bir nesne tipi artı veya eksi bir tamsayı sabiti ifadesi için bir adres sabiti.
  •   

ancak post derleyicide belirtildiği gibi diğer sabit ifade biçimlerini desteklemeye izin verilir:

Bir uygulama diğer sabit ifade biçimlerini kabul edebilir.

ancak ne clang ne de gcc bunu kabul etmez.

3
katma

Bu kodun uygun bir uygulama tarafından kabul edilmesi gerekli değildir. Sorunuzdaki ilgili bölümden alıntı yaptınız:

  1. More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
    • an arithmetic constant expression,
    • a null pointer constant,
    • an address constant, or
    • an address constant for an object type plus or minus an integer constant expression.

(unsigned long)&x is none of those things. It's not an arithmetic constant because of C11 6.6/8:

Aritmetik bir sabit ifadedeki döküm işleçleri yalnızca dönüştürme   aritmetik türlerine aritmetik türlerini

(işaretçi türleri aritmetik değildir, 6.2.5/18); ve bir adres sabiti değil, çünkü tüm adres sabitleri işaretçilerdir (6.6/9). Sonunda bir işaretçi artı veya eksi bir ICE başka bir işaretçidir, bu yüzden o da değildir.


Ancak 6.6/10, bir uygulamanın diğer sabit ifade biçimlerini kabul edebileceğini söylüyor. Bunun orjinal kodun kötü biçimlendirilmiş olarak adlandırılması gerekip gerekmediğinden emin değilim. Açıkçası, derleyiciniz burada başka bazı sabit ifadeleri kabul ediyor.


Bir sonraki konu göstericiden tamsayıya döküm uygulamasının tanımlanmasıdır. Belirli bir işaretçiye karşılık gelen bir tamsayı gösterimi yoksa, tanımsız da olabilir. (6.3.2.3/6)

Finally, the + 1 on the end makes no difference. unsigned long arithmetic is well-defined on addition and subtraction, so it is OK if and only if (unsigned long)&x is OK.

2
katma

Bu kodun uygun bir uygulama tarafından kabul edilmesi gerekli değildir. Sorunuzdaki ilgili bölümden alıntı yaptınız:

  1. More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
    • an arithmetic constant expression,
    • a null pointer constant,
    • an address constant, or
    • an address constant for an object type plus or minus an integer constant expression.

(unsigned long)&x is none of those things. It's not an arithmetic constant because of C11 6.6/8:

Aritmetik bir sabit ifadedeki döküm işleçleri yalnızca dönüştürme   aritmetik türlerine aritmetik türlerini

(işaretçi türleri aritmetik değildir, 6.2.5/18); ve bir adres sabiti değil, çünkü tüm adres sabitleri işaretçilerdir (6.6/9). Sonunda bir işaretçi artı veya eksi bir ICE başka bir işaretçidir, bu yüzden o da değildir.


Ancak 6.6/10, bir uygulamanın diğer sabit ifade biçimlerini kabul edebileceğini söylüyor. Bunun orjinal kodun kötü biçimlendirilmiş olarak adlandırılması gerekip gerekmediğinden emin değilim. Açıkçası, derleyiciniz burada başka bazı sabit ifadeleri kabul ediyor.


Bir sonraki konu göstericiden tamsayıya döküm uygulamasının tanımlanmasıdır. Belirli bir işaretçiye karşılık gelen bir tamsayı gösterimi yoksa, tanımsız da olabilir. (6.3.2.3/6)

Finally, the + 1 on the end makes no difference. unsigned long arithmetic is well-defined on addition and subtraction, so it is OK if and only if (unsigned long)&x is OK.

2
katma

Bu kodun uygun bir uygulama tarafından kabul edilmesi gerekli değildir. Sorunuzdaki ilgili bölümden alıntı yaptınız:

  1. More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
    • an arithmetic constant expression,
    • a null pointer constant,
    • an address constant, or
    • an address constant for an object type plus or minus an integer constant expression.

(unsigned long)&x is none of those things. It's not an arithmetic constant because of C11 6.6/8:

Aritmetik bir sabit ifadedeki döküm işleçleri yalnızca dönüştürme   aritmetik türlerine aritmetik türlerini

(işaretçi türleri aritmetik değildir, 6.2.5/18); ve bir adres sabiti değil, çünkü tüm adres sabitleri işaretçilerdir (6.6/9). Sonunda bir işaretçi artı veya eksi bir ICE başka bir işaretçidir, bu yüzden o da değildir.


Ancak 6.6/10, bir uygulamanın diğer sabit ifade biçimlerini kabul edebileceğini söylüyor. Bunun orjinal kodun kötü biçimlendirilmiş olarak adlandırılması gerekip gerekmediğinden emin değilim. Açıkçası, derleyiciniz burada başka bazı sabit ifadeleri kabul ediyor.


Bir sonraki konu göstericiden tamsayıya döküm uygulamasının tanımlanmasıdır. Belirli bir işaretçiye karşılık gelen bir tamsayı gösterimi yoksa, tanımsız da olabilir. (6.3.2.3/6)

Finally, the + 1 on the end makes no difference. unsigned long arithmetic is well-defined on addition and subtraction, so it is OK if and only if (unsigned long)&x is OK.

2
katma

Her şeyden önce, başlatıcı mutlaka sabit bir ifade değildir. a yerel kapsam içeriyorsa, yığına itildiğinde çalışma zamanında bir adres atanır. C11 6.6/7, bir işaretçinin sabit bir ifade olması için, 6.6/9'da aşağıdaki gibi tanımlanan bir adres sabiti olması gerektiğini söyler:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type.

(Vurgu madeni)


Kodunuzun standart C olup olmadığına gelince, evet. İşaretçilerin tamsayılara dönüşümüne izin verilir, ancak bazı kötü tanımlanmış davranış biçimleriyle gelebilirler. 6.5/6'da belirtilenler:

Herhangi bir işaretçi türü bir tamsayı tipine dönüştürülebilir. Hariç   önceden belirtilen, sonuç uygulama tarafından tanımlandı. Eğer   Sonuç tamsayı tipinde gösterilemez, davranış   Tanımsız. Sonuç, herhangi bir değer aralığında olmamalıdır.   tamsayı türü.

İşaretçinin tamsayıya sığabileceğinden emin olmak için uintptr_t kullanmanız gerekir. Ancak tamsayı dönüşümü için göstericinin bu soruyu yayınlamanızın nedeni olduğunu sanmıyorum.


Bir tamsayı taşmasının derleme zaman sabiti olmasını engelleyip engellemeyeceği konusunda, bu fikri nereden aldığınızdan emin değilim. Mantığınızın doğru olduğuna inanmıyorum, örneğin (INT_MAX + INT_MAX) bir derleme zamanı sabitidir ve ayrıca taşması garanti edilir. (GCC size bir uyarı verir.) Taşması durumunda tanımsız davranışa neden olur.


Neden bir derleme zamanı sabiti olmadığı ifadesinde hatalar gelince, bilmiyorum. Gcc 4.9.1'de çoğaltamıyorum. Hem statik hem de otomatik saklama süresi olan ancak a ilan etmeyi denedim, ancak hiçbir fark yoktu.

Bir şekilde yanlışlıkla C90 olarak derlenmiş gibi görünüyorsun, bu durumda gcc size "hata: başlatıcı elemanı yükleme sırasında hesaplanamaz" diyecektir. Veya belki de benim gcc versiyonumda düzeltilen bir derleyici hatası vardı.

1
katma
INT_MAX + INT_MAX sabit bir ifade değil. Bkz. 6.6/4 "Her sabit ifade, türü için temsil edilebilir değerler aralığında olan bir sabiti değerlendirir." Bu maddeyi, sabit ifadelerin herhangi bir biçimde UB içeremeyeceği anlamına geldiğini de kabul ediyorum.
katma yazar M.M, kaynak
@ Lundin UB bir çalışma zamanı özelliğidir. Derleyici çalıştırıldığında UB'ye neden olması garanti edilirse bir programı çevirmeyi reddedebilir; ancak bu durumda mutlaka doğru değildir (örneğin, void foo() {int x [] = {INT_MAX + INT_MAX};} ve program gerekli değildir; >). Tanılama üretilmezse, bu Kısıtlamalar bölümünde olduğundan derleyici hatasıdır.
katma yazar M.M, kaynak
@Lundin Bu kodu web sitesine yapıştırdım: int a; imzasız uzun b [] = {(imzasız int) & a + 1}; , Derleme tuşuna basın ve hem bir uyarı hem de bir hata oluştu: uyarı: göstergeden farklı boyuttaki tam sayıya [-Wpointer- to-int-cast] ve error: initializer öğesi sabit değil .
katma yazar anol, kaynak
gcc yalnızca ifadenin ikinci durumda sabit olmadığından şikayet eder (ilk durumda değil, uzun yerine int kullanırsam). Sadece gcc'yi trunk'tan (5.0.0) derledim ve ikinci sürümü de reddediyorum. GCC 4.9.2 kullandığını iddia eden Bu çevrimiçi C99 derleyicisi de aynı hatayı üretir.
katma yazar anol, kaynak
@ anol Bu kod "göstericiden tamsayıya al ..." uyarısı veriyor, ancak başlatıcı ile ilgili hiçbir şey sabit değil.
katma yazar Lundin, kaynak
@ anol Hayır, bu uyarıyı da yapmaz. Bu bize gönderdiğiniz kodun kullandığınız kod olmadığı sonucunu veriyor.
katma yazar Lundin, kaynak
@MattMcNabb Tamsayı taşması durumunda, derleyicinin bir şeyin sabit bir ifade olup olmadığına karar vermesinden önce UB almayı tercih ederim. Bitlerin herhangi bir ikili birleşimi, ikinin tamamlayıcı sayısı olarak ifade edilebildiğinden, uygulamada, derleyici taşma UB ile karşılaştığında büyük olasılıkla bir miktar saçma sayı üretecek ve ardından bunu sabit ifade olarak kullanacaktır. Sanırım bunu orada herhangi bir derleyicide göreceksiniz. Örneğin, INT_MAX + INT_MAX sabit ifadesi için GCC size -2 değerini ve 4 bayt boyutunu verir.
katma yazar Lundin, kaynak
Yukarıda üç yorum yazan kod ve gcc 4.9.1-16ubuntu6 ile (şu andan itibaren Ubuntu 14.10'dan bir tane), 64'te derlerken bir uyarı ve hata alıyorum (@anol gibi) -bit modu ve 32 bit modunda hiçbir şey (@lundin gibi) ( gcc -m32 file.c ).
katma yazar Virgile, kaynak

Her şeyden önce, başlatıcı mutlaka sabit bir ifade değildir. a yerel kapsam içeriyorsa, yığına itildiğinde çalışma zamanında bir adres atanır. C11 6.6/7, bir işaretçinin sabit bir ifade olması için, 6.6/9'da aşağıdaki gibi tanımlanan bir adres sabiti olması gerektiğini söyler:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type.

(Vurgu madeni)


Kodunuzun standart C olup olmadığına gelince, evet. İşaretçilerin tamsayılara dönüşümüne izin verilir, ancak bazı kötü tanımlanmış davranış biçimleriyle gelebilirler. 6.5/6'da belirtilenler:

Herhangi bir işaretçi türü bir tamsayı tipine dönüştürülebilir. Hariç   önceden belirtilen, sonuç uygulama tarafından tanımlandı. Eğer   Sonuç tamsayı tipinde gösterilemez, davranış   Tanımsız. Sonuç, herhangi bir değer aralığında olmamalıdır.   tamsayı türü.

İşaretçinin tamsayıya sığabileceğinden emin olmak için uintptr_t kullanmanız gerekir. Ancak tamsayı dönüşümü için göstericinin bu soruyu yayınlamanızın nedeni olduğunu sanmıyorum.


Bir tamsayı taşmasının derleme zaman sabiti olmasını engelleyip engellemeyeceği konusunda, bu fikri nereden aldığınızdan emin değilim. Mantığınızın doğru olduğuna inanmıyorum, örneğin (INT_MAX + INT_MAX) bir derleme zamanı sabitidir ve ayrıca taşması garanti edilir. (GCC size bir uyarı verir.) Taşması durumunda tanımsız davranışa neden olur.


Neden bir derleme zamanı sabiti olmadığı ifadesinde hatalar gelince, bilmiyorum. Gcc 4.9.1'de çoğaltamıyorum. Hem statik hem de otomatik saklama süresi olan ancak a ilan etmeyi denedim, ancak hiçbir fark yoktu.

Bir şekilde yanlışlıkla C90 olarak derlenmiş gibi görünüyorsun, bu durumda gcc size "hata: başlatıcı elemanı yükleme sırasında hesaplanamaz" diyecektir. Veya belki de benim gcc versiyonumda düzeltilen bir derleyici hatası vardı.

1
katma
@ Lundin UB bir çalışma zamanı özelliğidir. Derleyici çalıştırıldığında UB'ye neden olması garanti edilirse bir programı çevirmeyi reddedebilir; ancak bu durumda mutlaka doğru değildir (örneğin, void foo() {int x [] = {INT_MAX + INT_MAX};} ve program gerekli değildir; >). Tanılama üretilmezse, bu Kısıtlamalar bölümünde olduğundan derleyici hatasıdır.
katma yazar M.M, kaynak
INT_MAX + INT_MAX sabit bir ifade değil. Bkz. 6.6/4 "Her sabit ifade, türü için temsil edilebilir değerler aralığında olan bir sabiti değerlendirir." Bu maddeyi, sabit ifadelerin herhangi bir biçimde UB içeremeyeceği anlamına geldiğini de kabul ediyorum.
katma yazar M.M, kaynak
gcc yalnızca ifadenin ikinci durumda sabit olmadığından şikayet eder (ilk durumda değil, uzun yerine int kullanırsam). Sadece gcc'yi trunk'tan (5.0.0) derledim ve ikinci sürümü de reddediyorum. GCC 4.9.2 kullandığını iddia eden Bu çevrimiçi C99 derleyicisi de aynı hatayı üretir.
katma yazar anol, kaynak
@Lundin Bu kodu web sitesine yapıştırdım: int a; imzasız uzun b [] = {(imzasız int) & a + 1}; , Derleme tuşuna basın ve hem bir uyarı hem de bir hata oluştu: uyarı: göstergeden farklı boyuttaki tam sayıya [-Wpointer- to-int-cast] ve error: initializer öğesi sabit değil .
katma yazar anol, kaynak
@ anol Bu kod "göstericiden tamsayıya al ..." uyarısı veriyor, ancak başlatıcı ile ilgili hiçbir şey sabit değil.
katma yazar Lundin, kaynak
@ anol Hayır, bu uyarıyı da yapmaz. Bu bize gönderdiğiniz kodun kullandığınız kod olmadığı sonucunu veriyor.
katma yazar Lundin, kaynak
@MattMcNabb Tamsayı taşması durumunda, derleyicinin bir şeyin sabit bir ifade olup olmadığına karar vermesinden önce UB almayı tercih ederim. Bitlerin herhangi bir ikili birleşimi, ikinin tamamlayıcı sayısı olarak ifade edilebildiğinden, uygulamada, derleyici taşma UB ile karşılaştığında büyük olasılıkla bir miktar saçma sayı üretecek ve ardından bunu sabit ifade olarak kullanacaktır. Sanırım bunu orada herhangi bir derleyicide göreceksiniz. Örneğin, INT_MAX + INT_MAX sabit ifadesi için GCC size -2 değerini ve 4 bayt boyutunu verir.
katma yazar Lundin, kaynak
Yukarıda üç yorum yazan kod ve gcc 4.9.1-16ubuntu6 ile (şu andan itibaren Ubuntu 14.10'dan bir tane), 64'te derlerken bir uyarı ve hata alıyorum (@anol gibi) -bit modu ve 32 bit modunda hiçbir şey (@lundin gibi) ( gcc -m32 file.c ).
katma yazar Virgile, kaynak

Her şeyden önce, başlatıcı mutlaka sabit bir ifade değildir. a yerel kapsam içeriyorsa, yığına itildiğinde çalışma zamanında bir adres atanır. C11 6.6/7, bir işaretçinin sabit bir ifade olması için, 6.6/9'da aşağıdaki gibi tanımlanan bir adres sabiti olması gerektiğini söyler:

An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary & operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type.

(Vurgu madeni)


Kodunuzun standart C olup olmadığına gelince, evet. İşaretçilerin tamsayılara dönüşümüne izin verilir, ancak bazı kötü tanımlanmış davranış biçimleriyle gelebilirler. 6.5/6'da belirtilenler:

Herhangi bir işaretçi türü bir tamsayı tipine dönüştürülebilir. Hariç   önceden belirtilen, sonuç uygulama tarafından tanımlandı. Eğer   Sonuç tamsayı tipinde gösterilemez, davranış   Tanımsız. Sonuç, herhangi bir değer aralığında olmamalıdır.   tamsayı türü.

İşaretçinin tamsayıya sığabileceğinden emin olmak için uintptr_t kullanmanız gerekir. Ancak tamsayı dönüşümü için göstericinin bu soruyu yayınlamanızın nedeni olduğunu sanmıyorum.


Bir tamsayı taşmasının derleme zaman sabiti olmasını engelleyip engellemeyeceği konusunda, bu fikri nereden aldığınızdan emin değilim. Mantığınızın doğru olduğuna inanmıyorum, örneğin (INT_MAX + INT_MAX) bir derleme zamanı sabitidir ve ayrıca taşması garanti edilir. (GCC size bir uyarı verir.) Taşması durumunda tanımsız davranışa neden olur.


Neden bir derleme zamanı sabiti olmadığı ifadesinde hatalar gelince, bilmiyorum. Gcc 4.9.1'de çoğaltamıyorum. Hem statik hem de otomatik saklama süresi olan ancak a ilan etmeyi denedim, ancak hiçbir fark yoktu.

Bir şekilde yanlışlıkla C90 olarak derlenmiş gibi görünüyorsun, bu durumda gcc size "hata: başlatıcı elemanı yükleme sırasında hesaplanamaz" diyecektir. Veya belki de benim gcc versiyonumda düzeltilen bir derleyici hatası vardı.

1
katma
INT_MAX + INT_MAX sabit bir ifade değil. Bkz. 6.6/4 "Her sabit ifade, türü için temsil edilebilir değerler aralığında olan bir sabiti değerlendirir." Bu maddeyi, sabit ifadelerin herhangi bir biçimde UB içeremeyeceği anlamına geldiğini de kabul ediyorum.
katma yazar M.M, kaynak
@ Lundin UB bir çalışma zamanı özelliğidir. Derleyici çalıştırıldığında UB'ye neden olması garanti edilirse bir programı çevirmeyi reddedebilir; ancak bu durumda mutlaka doğru değildir (örneğin, void foo() {int x [] = {INT_MAX + INT_MAX};} ve program gerekli değildir; >). Tanılama üretilmezse, bu Kısıtlamalar bölümünde olduğundan derleyici hatasıdır.
katma yazar M.M, kaynak
@Lundin Bu kodu web sitesine yapıştırdım: int a; imzasız uzun b [] = {(imzasız int) & a + 1}; , Derleme tuşuna basın ve hem bir uyarı hem de bir hata oluştu: uyarı: göstergeden farklı boyuttaki tam sayıya [-Wpointer- to-int-cast] ve error: initializer öğesi sabit değil .
katma yazar anol, kaynak
gcc yalnızca ifadenin ikinci durumda sabit olmadığından şikayet eder (ilk durumda değil, uzun yerine int kullanırsam). Sadece gcc'yi trunk'tan (5.0.0) derledim ve ikinci sürümü de reddediyorum. GCC 4.9.2 kullandığını iddia eden Bu çevrimiçi C99 derleyicisi de aynı hatayı üretir.
katma yazar anol, kaynak
@ anol Hayır, bu uyarıyı da yapmaz. Bu bize gönderdiğiniz kodun kullandığınız kod olmadığı sonucunu veriyor.
katma yazar Lundin, kaynak
@MattMcNabb Tamsayı taşması durumunda, derleyicinin bir şeyin sabit bir ifade olup olmadığına karar vermesinden önce UB almayı tercih ederim. Bitlerin herhangi bir ikili birleşimi, ikinin tamamlayıcı sayısı olarak ifade edilebildiğinden, uygulamada, derleyici taşma UB ile karşılaştığında büyük olasılıkla bir miktar saçma sayı üretecek ve ardından bunu sabit ifade olarak kullanacaktır. Sanırım bunu orada herhangi bir derleyicide göreceksiniz. Örneğin, INT_MAX + INT_MAX sabit ifadesi için GCC size -2 değerini ve 4 bayt boyutunu verir.
katma yazar Lundin, kaynak
@ anol Bu kod "göstericiden tamsayıya al ..." uyarısı veriyor, ancak başlatıcı ile ilgili hiçbir şey sabit değil.
katma yazar Lundin, kaynak
Yukarıda üç yorum yazan kod ve gcc 4.9.1-16ubuntu6 ile (şu andan itibaren Ubuntu 14.10'dan bir tane), 64'te derlerken bir uyarı ve hata alıyorum (@anol gibi) -bit modu ve 32 bit modunda hiçbir şey (@lundin gibi) ( gcc -m32 file.c ).
katma yazar Virgile, kaynak