Koşullu operatör kullanılırken C neden dizelerin birleştirilmesine izin vermez?


95

Aşağıdaki kod sorunsuz bir şekilde derlenir:

int main() {
    printf("Hi" "Bye");
}

Ancak, bu derlemez:

int main() {
    int test = 0;
    printf("Hi" (test ? "Bye" : "Goodbye"));
}

Bunun nedeni nedir?


95
Dize birleştirme, erken sözcük oluşturma aşamasının bir parçasıdır; C'nin ifade sözdiziminin bir parçası değildir. Diğer bir deyişle, "dizge değişmezi" türünde bir değer yoktur . Daha ziyade, dize değişmezleri, değerleri oluşturan kaynak kodun sözcük öğeleridir.
Kerrek SB

24
@KerrekSB cevabını açıklığa kavuşturmak için - dizelerin birleştirilmesi, kod metnini derlemeden önce ön işlemenin bir parçasıdır . Üçlü operatör çalışma zamanında değerlendirilirken, kod derlendikten sonra (veya her şeyin sabit olması durumunda derleme zamanında yapılabilir).
Eugene Sh.

2
Detay: Bu yazı, "Hi"ve "Bye"vardır dize hazır değil dizeleri C standart kütüphanesinde kullanılan. İle dizgilerin , derleyici sıralamak olacak "H\0i" "B\0ye". Aynı değilsprintf(buf,"%s%s", "H\0i" "B\0ye");
chux - Monica'yı eski durumuna getir

15
a (some_condition ? + : - ) b
Yapamamanla

4
Hatta printf("Hi" ("Bye"));çalışmayacağına dikkat edin - üçlü operatör gerektirmez; parantez yeterlidir (ancak printf("Hi" test ? "Bye" : "Goodbye")derlenmez). Bir dizge hazır bilgisini takip edebilecek yalnızca sınırlı sayıda simge vardır. Virgül ,, açık köşeli parantez [, köşeli parantezi kapat ](olduğu gibi 1["abc"]- ve evet, dehşet verici), yuvarlak parantezi )kapat, kıvrımlı parantezi kapat }(bir başlatıcı veya benzer bağlamda) ve noktalı virgül ;meşru (ve başka bir dize hazır); Başkalarının olduğundan emin değilim.
Jonathan Leffler

Yanıtlar:


121

C Standardına göre (5.1.1.2 Çeviri aşamaları)

1 Çeviri sözdizimi kuralları arasındaki öncelik, aşağıdaki aşamalarla belirlenir.6)

  1. Bitişik dize değişmez belirteçleri birleştirilir.

Ve sadece ondan sonra

  1. Jetonları ayıran beyaz boşluk karakterleri artık önemli değil. Her ön işleme jetonu bir jetona dönüştürülür. Ortaya çıkan simgeler sözdizimsel ve anlamsal olarak analiz edilir ve bir çeviri birimi olarak çevrilir .

Bu inşaatta

"Hi" (test ? "Bye" : "Goodbye")

bitişik dize değişmez belirteçleri yoktur. Yani bu yapı geçersizdir.


43
Bu sadece C.'de buna izin verilmediği iddiasını tekrarlar. Nedenini açıklamıyor , soru buydu . Neden 5 saatte 26 olumlu oy topladığını bilmiyorum .... ve kabul et, daha az değil! Tebrikler.
Orbit'te Hafiflik Yarışları

4
Burada @LightnessRacesinOrbit ile aynı fikirde olmalısınız. Neden olmamalı (test ? "Bye" : "Goodbye")dize hazır birine değerlendiririz esasen yapma "Hi" "Bye" ya "Hi Goodbye"? (sorum diğer cevaplarda cevaplandı)
Insane

48
@LightnessRacesinOrbit, çünkü insanlar normalde bir şeyin neden C'de derlenmediğini sorduklarında, hangi kuralı çiğnediğini soruyorlar, Standards of Antiquity'nin onu neden böyle seçtiğini değil.
user1717828

4
@LightnessRacesinOrbit Açıkladığınız soru muhtemelen konu dışı olacaktır. Bunu uygulamanın mümkün olmaması için herhangi bir teknik neden göremiyorum , bu nedenle şartnamenin yazarlarından kesin bir cevap olmaksızın, tüm cevaplar fikir temelli olacaktır. Ve genellikle "pratik" veya "cevaplanabilir" sorular kategorisine girmez ( yardım merkezinin gerektirdiğini belirttiği gibi ).
jpmc26

12
Bu var @LightnessRacesinOrbit açıklıyor neden : "C standart öyle dedi diye". Bu kuralın neden tanımlandığı gibi tanımlandığı ile ilgili soru konu dışı olacaktır.
user11153

135

C11 standardına göre, bölüm §5.1.1.2, bitişik dize değişmezlerinin birleştirilmesi:

Bitişik dize değişmez belirteçleri birleştirilir.

olur dönüşüm aşamasından . Diğer yandan:

printf("Hi" (test ? "Bye" : "Goodbye"));

çalışma zamanında değerlendirilen koşullu operatörü içerir . Bu nedenle, derleme sırasında, çeviri aşamasında, bitişik dizeler mevcut değildir, dolayısıyla birleştirme mümkün değildir. Sözdizimi geçersizdir ve bu nedenle derleyiciniz tarafından rapor edilir.


Neden bölümünde biraz ayrıntı vermek için , ön işleme aşaması sırasında, bitişik dize değişmezleri birleştirilir ve tek bir dize değişmezi (jeton) olarak temsil edilir . Depolama buna göre tahsis edilir ve birleştirilmiş dize değişmezi tek bir varlık (bir dize değişmezi) olarak kabul edilir .

Öte yandan, çalışma zamanı birleştirme durumunda, hedef, birleştirilmiş dizeyi sabit tutmaya yetecek kadar belleğe sahip olmalıdır, aksi takdirde, beklenen birleştirilmiş çıktıya erişilemeyecektir. Şimdi, söz konusu dize hazır , onlar zaten derleme sırasında bellek tahsis edilir ve olamaz genişletilmiş bir daha gelen girişteki uyum için içine veya eklenen orijinal içerik. Başka bir deyişle, birleştirilmiş sonuca tek bir dize hazır bilgisi olarak erişilmesi (sunulması) mümkün olmayacaktır . Dolayısıyla, bu yapı doğası gereği yanlıştır.

Bilginize, çalışma zamanı dizesi ( değişmez değerler değil ) birleştirme için, strcat()iki dizeyi birleştiren kitaplık işlevine sahibiz . Dikkat, açıklama şunlardan bahsediyor:

char *strcat(char * restrict s1,const char * restrict s2);

strcat()Fonksiyon ile gösterilen dizinin bir kopyasını ekler s2sonuna (sonlandırıcı boş karakter dahil) dizesi tarafından işarets1 . İlk karakter s2sonundaki boş karakterin üzerine yazar s1. [...]

Böylece, s1bir dizge olduğunu görebiliriz, değişmez bir dize değil . Bununla birlikte, içeriği s2herhangi bir şekilde değiştirilmediğinden, çok iyi bir dizge olabilir .


1
hakkında fazladan bir açıklama eklemek isteyebilirsiniz strcat: hedef dizi, karakterleri s2artı orada zaten mevcut olan karakterlerden sonra bir boş sonlandırıcı alacak kadar uzun olmalıdır .
chqrlie

39

Dize değişmez birleştirme, derleme zamanında önişlemci tarafından gerçekleştirilir. Bu birleştirmenin test, program fiilen çalıştırılıncaya kadar bilinmeyen değerinden haberdar olmasının bir yolu yoktur . Bu nedenle, bu dize değişmezleri birleştirilemez.

Genel durum, derleme zamanında bilinen değerler için böyle bir yapıya sahip olmayacağınızdan, C standardı, otomatik birleştirme özelliğini en temel durumla sınırlandırmak için tasarlanmıştır: değişmez değerler birbirinin yanında tam anlamıyla doğru olduğunda .

Ancak bu kısıtlamayı bu şekilde ifade etmemiş olsa veya kısıtlama farklı bir şekilde oluşturulmuş olsa bile, birleştirme işlemini bir çalışma zamanı süreci yapmadan örneğinizin gerçekleştirilmesi yine de imkansız olacaktır. Ve bunun için, gibi kütüphane işlevlerine sahibiz strcat.


3
Sadece varsayımları okudum. Söyledikleriniz oldukça geçerli olsa da, hiçbiri olmadığı için kaynak sağlayamazsınız. C ile ilgili tek kaynak, (çoğu durumda göze batmayan) bazı şeylerin neden olduğu gibi olduğunu belirtmeyen, ancak sadece bu şekilde olması gerektiğini belirten standart belgedir. Dolayısıyla, Moskova'nın cevabından Vlad hakkında o kadar seçici olmak uygunsuzdur. OP, "Neden bu şekilde?" -Tek doğru kaynaklı yanıtın "Çünkü C olduğu ve C'nin tanımlanma şekli budur" olduğu yerde, kelimenin tam anlamıyla düz doğru yanıt budur.
dhein

1
Bu (kabul edilmektedir) açıklama eksikliğidir. Ama burada tekrar söylenen, Vlad'ın cevabının asıl soruna bir açıklama olarak hizmet ettiğini söyledi, o zaman seninki. Tekrar söyledim: Doğrulayabileceğim bilgiler ilgili ve doğru olsa da şikayetlerinize katılmıyorum. ve senin offtopik olduğunu düşünmezsem de, benim bakış açımdan daha fazla offtopik, o zaman Vlads aslında öyle.
dhein

11
@Zaibis: Kaynak benim. Voyvoda'nın cevabı hiçbir şekilde bir açıklama değildir; bu sadece sorunun öncülünün bir teyididir. Kesinlikle ikisi de "konu dışı" değildir (bu terimin ne anlama geldiğine bakmak isteyebilirsiniz). Ama fikrinize hakkınız var.
Orbit'te Hafiflik Yarışları

Yukarıdaki yorumları okuduktan sonra bile, bu cevaba kimin olumsuz oy verdiğini merak ediyorum ᶘ ᵒᴥᵒᶅ OP bu cevapla ilgili daha fazla açıklama istemedikçe bunun mükemmel bir cevap olduğuna inanıyorum.
Mohit Jain

2
Bu cevabın neden sizin için kabul edilebilir olduğunu ve @ VladfromMoscow's'un neden aynı şeyi söylediklerini ve onun bir alıntıyla desteklendiğinde ve sizinki olmadığında neden olduğunu ayırt edemiyorum.
Marquis of Lorne

30

Çünkü C'nin stringtipi yoktur . Dize değişmezleri, charbir char*işaretçi tarafından başvurulan dizilere derlenir .

C, ilk örneğinizde olduğu gibi , bitişik değişmez değerlerin derleme zamanında birleştirilmesine izin verir . C derleyicisinin kendisi dizeler hakkında biraz bilgi sahibidir. Ancak bu bilgi çalışma zamanında mevcut değildir ve bu nedenle birleştirme gerçekleşemez.

Derleme işlemi sırasında, ilk örneğiniz şu dile "çevrilir":

int main() {
    static const char char_ptr_1[] = {'H', 'i', 'B', 'y', 'e', '\0'};
    printf(char_ptr_1);
}

Program çalıştırılmadan önce, iki dizenin derleyici tarafından tek bir statik dizide nasıl birleştirildiğine dikkat edin.

Ancak, ikinci örneğiniz şöyle bir şeye "çevrilir":

int main() {
    static const char char_ptr_1[] = {'H', 'i', '\0'};
    static const char char_ptr_2[] = {'B', 'y', 'e', '\0'};
    static const char char_ptr_3[] = {'G', 'o', 'o', 'd', 'b', 'y', 'e', '\0'};
    int test = 0;
    printf(char_ptr_1 (test ? char_ptr_2 : char_ptr_3));
}

Bunun neden derlenmediği açık olmalıdır. Üçlü operatör ?, "dizeler" artık mevcut olmadığında, yalnızca işaretçiler chartarafından başvurulan basit diziler olarak mevcut olduğunda derleme zamanında değil, çalışma zamanında değerlendirilir char*. Bitişik dize değişmezlerinin aksine , bitişik karakter işaretçileri sadece bir sözdizimi hatasıdır.


2
Mükemmel cevap, muhtemelen burada en iyisi. "Bunun neden derlenmediği açık olmalı." Bunu "çünkü üçlü operatör derleme zamanında değil çalışma zamanında değerlendirilen bir koşullu olduğundan" ile genişletmeyi düşünebilirsiniz .
kedi

Olmamalı static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};olmak static const char *char_ptr_1 = "HiBye";ve benzer işaretçiler geri kalanı için?
Spikatrix

@CoolGuy Yazdığınızda static const char *char_ptr_1 = "HiBye";, derleyici satırı 'ye çevirir static const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};, bu yüzden hayır, "bir dizge gibi" yazılmamalıdır . Cevap söylediği gibi dizeleri karakterlerin dizisi için derlenmektedir ve bunu en "ham" şeklinde karakter dizisi atama olsaydı, tıpkı bir virgül karakter listesi ayrılmış kullanırsınızstatic const char *char_ptr_1 = {'H', 'i', 'B', 'y', 'e', '\0'};
Ankush

3
@Ankush Evet. Ancak her ne kadar static const char str[] = {'t', 'e', 's', 't', '\0'};aynıdır static const char str[] = "test";, static const char* ptr = "test";olduğu değil aynı static const char* ptr = {'t', 'e', 's', 't', '\0'};. İlki geçerlidir ve derlenecektir, ancak ikincisi geçersizdir ve beklediğinizi yapar.
Spikatrix

Son paragrafı çözdüm ve kod örneklerini düzelttim, teşekkürler!
İmzasız

13

Her iki dalın da çalışma zamanında seçilecek derleme zamanı dize sabitleri üretmesini gerçekten istiyorsanız, bir makroya ihtiyacınız olacaktır.

#include <stdio.h>
#define ccat(s, t, a, b) ((t)?(s a):(s b))

int
main ( int argc, char **argv){
  printf("%s\n", ccat("hello ", argc > 2 , "y'all", "you"));
  return 0;
}

10

Bunun nedeni nedir?

Üçlü operatör kullanan kodunuz koşullu olarak iki değişmez dize arasında seçim yapar. Bilinen veya bilinmeyen koşul ne olursa olsun, bu derleme zamanında değerlendirilemez, bu yüzden derleme yapamaz. Bu ifade bile printf("Hi" (1 ? "Bye" : "Goodbye"));derlenmez. Nedeni, yukarıdaki cevaplarda derinlemesine açıklanmıştır. Bir başka olasılık derlemek için üçlü operatörü geçerli kullanarak bu tür bir açıklama yaparak , aynı zamanda bir yer alacağı format etiketi ve biçimlendirilmiş üçlü operatör ifadesinin sonucunu ek argüman için printf. O zaman bile, printf()çıktı bu dizeleri yalnızca çalışma zamanında ve bu kadar erken bir zamanda "birleştirmiş" izlenimi verirdi .

#include <stdio.h>

int main() {
    int test = 0;
    printf("Hi %s\n", (test ? "Bye" : "Goodbye")); //specify format and print as result
}

3
SO bir Eğitim sitesi değildir. Bir öğretici değil, OP'ye bir Cevap vermelisiniz.
Michi

1
Bu, OP'nin sorusuna cevap vermiyor. OP'nin temelindeki sorunu çözme girişimi olabilir, ancak bunun ne olduğunu gerçekten bilmiyoruz.
Keith Thompson

1
printfvermez gerektiren bir format tanımlayıcı; derleme zamanında sadece birleştirme yapılsaydı (ki bu değildir), OP'nin printf kullanımı geçerli olacaktır.
David Conrad

Yorumunuz için teşekkürler, @David Conrad. Özensiz ifadelerim gerçekten de ifade etmek printf()bir biçim etiketi gerektiriyormuş gibi görünecek , ki bu kesinlikle doğru değil. Düzeltildi!
user3078414

Bu daha iyi bir ifade. +1 Teşekkürler.
David Conrad

7

İçinde printf("Hi" "Bye");, derleyicinin tek bir dizi haline getirebileceği iki ardışık karakter dizisine sahipsiniz.

İçinde printf("Hi" (test ? "Bye" : "Goodbye"));bir dizi ve onu izleyen bir işaretçi (ilk elemanına bir göstericiye dönüştürülmüş bir dizi) var. Derleyici bir dizi ve bir işaretçiyi birleştiremez .


0

Soruyu cevaplamak için - printf'in tanımına gidecektim. Printf işlevi argüman olarak const char * olmasını bekler . "Hi" gibi herhangi bir dizge sabit bir karakterdir *; ancak böyle bir ifade (test)? "str1" : "str2"sabit karakter * DEĞİLDİR çünkü böyle bir ifadenin sonucu yalnızca çalışma zamanında bulunur ve dolayısıyla derleme zamanında belirsizdir, bu da derleyicinin gerektiği gibi şikayet etmesine neden olan bir gerçektir. Öte yandan - bu mükemmel bir şekilde çalışıyorprintf("hi %s", test? "yes":"no")


* ancak böyle bir ifade (test)? "str1" : "str2"DEĞİL const char*... Tabii ki öyle! Sabit bir ifade değildir, ancak türü öyledir const char * . Yazmak son derece iyi olurdu printf(test ? "hi " "yes" : "hi " "no"). OP'ın problemi ile ilgisi vardır printf, "Hi" (test ? "Bye" : "Goodbye")ne olursa olsun ifade bağlamının ne bir yazım hatasıdır.
chqrlie

Kabul. Bir ifadenin çıktısını ifadenin kendisiyle karıştırdım
Stats_Lover

-4

Bu derlenmez çünkü printf işlevi için parametre listesi

(const char *format, ...)

ve

("Hi" (test ? "Bye" : "Goodbye"))

parametre listesine uymuyor.

gcc bunu hayal ederek anlamlandırmaya çalışır

(test ? "Bye" : "Goodbye")

bir parametre listesidir ve "Hi" nin bir işlev olmadığından şikayet eder.


6
Stack Overflow'a hoş geldiniz. printf()Argüman listesiyle eşleşmediği konusunda haklısınız , ancak bunun nedeni ifadenin hiçbir yerde geçerli olmamasıdır - sadece printf()argüman listesinde değil. Başka bir deyişle, sorun için çok fazla özel bir neden seçtiniz; genel sorun, "Hi" (bir çağrı bir yana, C de geçerli olmamasıdır printf(). Bu yanıtı olumsuz oylamadan önce silmenizi öneririm.
Jonathan Leffler

C böyle çalışmıyor. Bu, PHP gibi bir dizge değişmezi çağırmaya çalışmak olarak çözümlenmez.
kedi
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.