Etkisi olmayan ifadeler neden C'de yasal kabul edilir?


13

Bu soru safsa özür dilerim. Aşağıdaki programı düşünün:

#include <stdio.h>

int main() {
  int i = 1;
  i = i + 2;
  5;
  i;
  printf("i: %d\n", i);
}

Yukarıdaki örnekte, ifadeler 5;ve i;gereksiz görünüyor, ancak kod varsayılan olarak uyarı veya hata olmadan derler (ancak, gcc warning: statement with no effect [-Wunused-value]ile çalıştırıldığında bir uyarı verir -Wall). Programın geri kalanı üzerinde hiçbir etkisi yoktur, neden ilk etapta geçerli ifadeler olarak kabul edilirler? Derleyici onları görmezden geliyor mu? Bu tür ifadelere izin vermenin herhangi bir faydası var mı?


5
Bu tür ifadeleri yasaklamanın faydaları nelerdir?
Mooing Duck

2
Herhangi bir ifade, arkasından koyarak bir ifade olabilir ;. İfadelerin ne zaman ifade edilemediğine ilişkin daha fazla kural eklemek dili zorlaştırır
MM

3
Kodunun döndürülmesini istemezsiniz çünkü dönüş değerini dikkate almaz printf()mısınız? İfadesi 5;temelde "ne gerekiyorsa diyor 5(hiçbir şey) yapar ve sonucu görmezden. İfadende printf(...)olan 'ne gerekiyorsa printf(...)yapar ve sonuçlar göz ardı (dönüş değeri printf())'. C aynı olanlar davranır. Bu aynı zamanda gibi kod için izin verir (void) i;nerede iolduğunu voidkasten kullanılmayan olarak işaretlemek için kullandığınız bir işleve parametre
Andrew Henle

1
@AndrewHenle: Bu tam olarak aynı değil, çünkü printf()sonunda döndürdüğü değeri görmezden gelseniz bile, çağırmanın bir etkisi var. Aksine 5;hiçbir etkisi yoktur.
Nate Eldredge

1
Çünkü Dennis Ritchie ve bize anlatmak için etrafta değil.
user207421

Yanıtlar:


10

Bu tür ifadelere izin vermenin bir yararı, insanlar tarafından yazılmak yerine makrolar veya diğer programlar tarafından oluşturulan koddan kaynaklanır.

Örnek olarak, int do_stuff(void)başarıda 0, başarısızlık durumunda -1 döndürmesi gereken bir fonksiyon düşünün . Bu "şeyler" için destek isteğe bağlı olabilir ve böylece bir başlık dosyası olabilir

#if STUFF_SUPPORTED
#define do_stuff() really_do_stuff()
#else
#define do_stuff() (-1)
#endif

Şimdi mümkünse bir şeyler yapmak isteyen bir kod düşünün, ancak başarılı olup olmadığını gerçekten umursamayabilir veya etmeyebilir:

void func1(void) {
    if (do_stuff() == -1) {
        printf("stuff did not work\n");
    }
}

void func2(void) {
    do_stuff(); // don't care if it works or not
    more_stuff();
}

Ne zaman STUFF_SUPPORTED0'dır önişlemci aramayı genişleyecektir func2sadece okur açıklamaya

    (-1);

böylece derleyici kartı sizi rahatsız edecek gibi görünen "gereksiz" ifadeyi görecektir. Yine de başka ne yapabilirim? Eğer sen #define do_stuff() // nothing, o zaman kod func1kırılacak. (Ve yine de func2sadece ;daha gereksiz olan bu okumalarda hala boş bir ifadeye sahip olacaksınız .) Öte yandan, do_stuff()-1 döndüren bir işlevi tanımlamanız gerekiyorsa , bir işlev çağrısının maliyetine maruz kalabilirsiniz. Iyi bir sebep olmaksızın.


No-op'un daha klasik bir versiyonu (ya da ortak versiyon mu demek istiyorum) ((void)0).
Jonathan Leffler

Bunun güzel bir örneği assert.
Neil

3

C'deki basit ifadeler noktalı virgülle sonlandırılır.

C'deki basit ifadeler ifadelerdir. İfade, değişkenlerin, sabitlerin ve işleçlerin birleşimidir. Her ifade, bir değişkene atanabilecek belirli türde bir değerle sonuçlanır.

Bazı "akıllı derleyicilerin" 5'i atabileceğini söyledikten sonra; ve ben; ifadeleri.


Bunları atmak dışında herhangi bir şey yapacak bir derleyici hayal edemiyorum. Onlarla başka ne yapabilirdi?
Jeremy Friesner

@JeremyFriesner: Çok basit, optimize edilmeyen bir derleyici, değeri hesaplamak ve sonucu bir kayıt defterine koymak için çok iyi kod üretebilir (hangi noktadan itibaren yoksayılır).
Nate Eldredge

C standardı "basit ifade" terimi değildir. Bir sentezleme ifadesi bir noktalı virgül, ardından bir (isteğe bağlı) ifade oluşur. Her ifade bir değerle sonuçlanmaz; tür ifadesinin voiddeğeri yoktur.
Keith Thompson

2

Etkisi olmayan ifadelere izin verilir, çünkü onları yasaklamak, izin vermekten daha zor olacaktır. C ilk tasarlandığında ve derleyiciler daha küçük ve daha basit olduğunda bu daha önemliydi.

Bir ifade ifadesi , ardından noktalı virgül içeren bir ifadeden oluşur. Davranışı ifadeyi değerlendirmek ve sonucu (varsa) atmaktır. Normalde amaç, ifadenin değerlendirilmesinin yan etkilere sahip olmasıdır, ancak belirli bir ifadenin yan etkileri olup olmadığını belirlemek her zaman kolay ve hatta mümkün değildir.

Örneğin, bir işlev çağrısı bir ifadedir, bu nedenle bir noktalı virgül ve onu izleyen bir işlev çağrısı bir ifadedir. Bu ifadenin herhangi bir yan etkisi var mı?

some_function();

Uygulamasını görmeden söylemek mümkün değildir some_function.

Buna ne dersin?

obj;

Muhtemelen hayır - ama objolarak tanımlanırsa volatile, öyle.

İzin herhangi bir içine yapılacak ifadesini ifadesi-deyimi noktalı virgül ekleyerek dil tanımı daha basit hale getirir. İfadenin yan etkilere sahip olmasını zorunlu kılmak, dil tanımına ve derleyiciye karmaşıklık katacaktır. C, tutarlı bir kurallar dizisi üzerine kurulmuştur (işlev çağrıları ifadelerdir, ödevler ifadelerdir, ifadeyi noktalı virgülle izleyen bir deyimdir) ve programcıların mantıklı olabilecek veya yapamayacakları şeyleri engellemeden istediklerini yapmalarını sağlar.


2

Etkisiz olarak listelediğiniz ifadeler , sözdizimi C standardının 6.8.3p1 bölümünde aşağıdaki şekilde verilen bir ifade ifadesine örnektir :

 expression-deyimi :
    expression opt  ;

Bölüm 6.5'in tamamı bir ifadenin tanımına ayrılmıştır, ancak gevşek bir ifadeyle , işleçlerle bağlantılı sabitler ve tanımlayıcılardan oluşur. Özellikle, bir ifade bir atama operatörü içerebilir veya içermeyebilir ve bir işlev çağrısı içerebilir veya içermeyebilir.

Dolayısıyla , noktalı virgül ve ardından gelen herhangi bir ifade, ifade ifadesi olarak nitelendirilir. Aslında, kodunuzdaki bu satırların her biri bir ifade ifadesine örnektir:

i = i + 2;
5;
i;
printf("i: %d\n", i);

Bazı işleçler, atama işleçleri kümesi ve arttırma öncesi / arttırma / azaltma işleçleri gibi yan etkiler içerir ve işlev çağrısı işleci () , söz konusu işlevin ne yaptığına bağlı olarak bir yan etkiye sahip olabilir . Ancak operatörlerden birinin yan etkisi olması gerekmemektedir.

İşte başka bir örnek:

atoi("1");

Bu, bir işlevi çağırmak printfve örneğin örneğindeki çağrı gibi sonucu atmaktır, ancak printfişlev çağrısının aksine bir yan etkisi yoktur.


1

Bazen böyle bir ifade çok kullanışlıdır:

int foo(int x, int y, int z)
{
    (void)y;   //prevents warning
    (void)z;

    return x*x;
}

Veya referans el kitabı bize sadece bir şeyleri arşivlemek için kayıtları okumamızı söylediğinde - örneğin bazı bayrağı temizlemek veya ayarlamak için (UC dünyasında çok yaygın bir durum)

#define SREG   ((volatile uint32_t *)0x4000000)
#define DREG   ((volatile uint32_t *)0x4004000)

void readSREG(void)
{
    *SREG;   //we read it here
    *DREG;   // and here
}

https://godbolt.org/z/6wjh_5


*SREGUçucu olduğu zaman , *SREG;C standardı tarafından belirtilen model üzerinde hiçbir etkisi yoktur. C standardı, gözlenebilir bir yan etkiye sahip olduğunu belirtir.
Eric Postpischil

@EricPostpischil - hayır gözlemlenebilir etkisi yoktur, ancak etkisi vardır. Görünen C nesnelerinin hiçbiri değişmedi.
P__J__

C 2018 5.1.2.3 6 , programın gözlemlenebilir davranışını “Uçucu nesnelere erişim soyut makinenin kurallarına göre kesinlikle değerlendirildiğini” içerecek şekilde tanımlar . Yorum ya da kesinti söz konusu değildir; bu gözlemlenebilir davranışın tanımıdır .
Eric Postpischil
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.