Soru C varsayılan argümanlar


C'deki bir işleve varsayılan argümanlar belirtmenin bir yolu var mı?


228
2017-09-24 14:38


Menşei


Sadece biraz daha iyi C, C ++ değil. düşün C +. C ++ 'dan kaldırılan küçük iyileştirmeler ile büyük karışıklık değil. Ve lütfen, farklı link-loader yok. bir başka önişlemci benzeri adım olmalıdır. standardize. her yerde... - ivo Welch
@ivoWelch Ben "C +" hayranıyım: sadece C sözleşmelerini takip edin, C ++ derleyicisini kullanın ve bazen C yolu gerçekten topları bir ağrı olduğunda bir C ++ özelliği ödünç - William Entriken
İlgili soru Ben yan çubuğunda listelenen görmedim. - Shelby Moore III
Barbar olmayı bırak ve C ++ (11, ...) iyi - jk kullanmayı öğrenirim diyorum. / ben alevler koyar ... ama ... onu sevmeye geleceksin ... hahaha kendime yardım edemem, üzgünüm. - moodboom
Olası kopya C fonksiyonlarındaki argümanlarda ve C'deki fonksiyon aşırı yüklerinde varsayılan değerler - Jonathan Mee


Cevaplar:


Pek sayılmaz. Tek yolu olurdu varargs işlevi yaz ve arayanın iletmediği argümanlar için varsayılan değerleri manuel olarak doldurun.


114
2017-09-24 14:40



Varargs'ı kullanırken kontrol eksikliğinden nefret ediyorum. - dmckee
Sen de yapmalısın; Aslında bunu önermiyorum; Sadece bunun mümkün olduğunu iletmek istedim. - Eli Courtwright
Ancak, arayanın argümanı geçip geçmediğini nasıl kontrol etmek istersiniz? Bunun işe yaradığını düşünüyorum, onu geçmediğini söyleyen arayan sizde yok mu? Bence bu yaklaşım tümüyle daha az kullanılabilir hale geliyor - arayan kişi başka bir isimle bir işlevi çağırıyor olabilir. - Johannes Schaub - litb
open(2) sistem çağrısı, gerekli argümanlara bağlı olarak mevcut olabilecek isteğe bağlı bir argüman için bunu kullanır ve printf(3) Kaç argüman bulunacağını belirten bir biçim dizesi okur. Her ikisi de vararg'ları oldukça güvenli ve etkili bir şekilde kullanırlar, ve kesinlikle onları kandırırsınız. printf() Özellikle oldukça popüler gibi görünüyor. - Chris Lutz
@Eli: Tüm C derleyiciler gcc değil. Printf () kodlarınız biçim dizginizle eşleşmediğinde uyarmak için bazı gelişmiş derleyici sihirbazı var. Ve ben kendi variadic fonksiyonlarınız için benzer uyarılar almanın mümkün olmadığını düşünüyorum (aynı format dizisini kullanmıyorlarsa). - tomlogic


Vay, herkes buralarda çok kötümser. Cevap Evet.

Bu önemsiz değil: Sonuç olarak, çekirdek işlevi, destekleyici bir yapı, bir sarmalayıcı işlevi ve bir makroya sahip olacağız. sarıcı işlevi etrafında. Çalışmamda bütün bunları otomatikleştirmek için bir dizi makro var; bir Zamanlar akışını anlıyorsun, aynı şeyi yapman kolay olacak.

Bunu başka bir yerde yazdım, işte burada özetini tamamlamak için ayrıntılı bir dış link var: http://modelingwithdata.org/arch/00000022.htm

Açmak isteriz

double f(int i, double x)

varsayılanı alan bir işleve (i = 8, x = 3.14). Tamamlayıcı bir yapı tanımlayın:

typedef struct {
    int i;
    double x;
} f_args;

Fonksiyonunuzu yeniden adlandırın f_baseve varsayılanları ve çağrıları belirleyen bir sarmalayıcı işlevi tanımlayın. baz:

double var_f(f_args in){
    int i_out = in.i ? in.i : 8;
    double x_out = in.x ? in.x : 3.14;
    return f_base(i_out, x_out);
}

Şimdi C'nin variadic makrolarını kullanarak bir makro ekleyin. Bu sayede kullanıcılar aslında bir f_args Yapılandırın ve her zamanki işi yaptığını düşünün:

#define f(...) var_f((f_args){__VA_ARGS__});

Tamam, şimdi aşağıdakilerin hepsi işe yarayacaktı:

f(3, 8);      //i=3, x=8
f(.i=1, 2.3); //i=1, x=2.3
f(2);         //i=2, x=3.14
f(.x=9.2);    //i=8, x=9.2

Bileşik başlatıcıların kesin kurallar için varsayılanları nasıl belirlediğiyle ilgili kuralları kontrol edin.

Çalışmayacak bir şey: f(0)çünkü eksik bir değeri ayırt edemiyoruz sıfır. Benim deneyimime göre, bu dikkat etmeniz gereken bir şeydir, ancak İhtiyaç duyulur --- varsayılan sürenin yarısı sıfırdır.

Bunu yazmakta zorlandım çünkü argüman ve varsayılanları isimlendirdim.  C'deki kodlamayı gerçekten daha kolay ve daha da eğlenceli hale getirir. Ve C bu kadar basit ve hala tüm bunları mümkün kılmak için yeterince sahip olmak için harika.


232
2018-05-28 01:39



+1 yaratıcı! Onun sınırlamaları var ama aynı zamanda tabloya adlandırılmış parametreleri getiriyor. Bunu not et, {} (boş başlatıcı) bir hata C99. - u0b34a0f6ae
Ancak, burada sizin için harika bir şey var: Standart, adlandırılmış üyelerin birden çok kez belirtilmesine izin veriyor, daha sonra geçersiz kılma. Bu yüzden sadece adlandırılmış parametreler için varsayılan sorunu çözebilir ve boş bir çağrıya izin verebilirsiniz. #define vrange(...) CALL(range,(param){.from=1, .to=100, .step=1, __VA_ARGS__}) - u0b34a0f6ae
Derleyici hatalarının okunabilir olmasını umuyorum ama bu harika bir teknik! Neredeyse python kwargs gibi görünüyor. - totowtwo
@RunHolt Kesinlikle daha basit olmasına rağmen, nesnel olarak daha iyi değildir; Adlandırılmış parametreler, aramaların okunabilirliği (kaynak kodunun okunabilirliği pahasına) gibi yararlarla birlikte gelir. Biri, kaynağın geliştiricileri için daha iyidir, diğeri ise işlevin kullanıcıları için daha iyidir. "Bu daha iyi!" Diye atmak biraz aceleci. - Alice
@DawidPi: C11 6.7.9 (19), başlatıcı kümeler üzerinde: "açıkça başlatılmamış tüm alt nesneler, statik depolama süresine sahip nesnelerle örtük olarak başlatılır" Ve bildiğiniz gibi, statik süre öğeleri sıfırlanır | NULL | \ 0. [Bu da c99'daydı.] - bk.


Evet. :-) Ama bir şekilde beklemeyeceksin.

int f1(int arg1, double arg2, char* name, char *opt);

int f2(int arg1, double arg2, char* name)
{
  return f1(arg1, arg2, name, "Some option");
}

Ne yazık ki, C size aşırı yükleme yöntemlerine izin vermez, böylece iki farklı işlevle sonuçlanırsınız. Yine de, f2'yi arayarak, aslında f1'i varsayılan bir değerle çağırıyor olursunuz. Bu, mevcut kodu kopyalamak / yapıştırmaktan kaçınmanıza yardımcı olan "Kendinizi Tekrarlama" çözümdür.


145
2017-09-24 15:06



FWIW, fonksiyonun sonunda numaranın aldığı yayın sayısını belirtmek için kullanmayı tercih ederim. Herhangi bir keyfi sayı kullanmaktan daha kolay olur. :) - Macke
Bu, en iyi cevaptır çünkü aynı amacı gerçekleştirmek için basit bir yol gösterir. Değiştirmek istemediğim sabit bir API'nin parçası olan bir işlev var, ancak yeni bir param almak için buna ihtiyacım var. Tabii ki, onu çok özledim ki onu özledim (varsayılan paramın düşüncesinde sıkışıp kaldım!) - RunHolt
f2 ayrıca bir ön işlemci makrosu olabilir - osvein


Varsayılan değerler için adlandırılmış parametreleri (sadece) kullanan işlevler oluşturabiliriz. Bu, bk'nin cevabının devamıdır.

#include <stdio.h>                                                               

struct range { int from; int to; int step; };
#define range(...) range((struct range){.from=1,.to=10,.step=1, __VA_ARGS__})   

/* use parentheses to avoid macro subst */             
void (range)(struct range r) {                                                     
    for (int i = r.from; i <= r.to; i += r.step)                                 
        printf("%d ", i);                                                        
    puts("");                                                                    
}                                                                                

int main() {                                                                     
    range();                                                                    
    range(.from=2, .to=4);                                                      
    range(.step=2);                                                             
}    

C99 standardı, başlangıçtaki sonraki adların önceki öğeleri geçersiz kıldığını tanımlar. Ayrıca bazı standart konumsal parametrelere de sahip olabiliriz, sadece makro ve fonksiyon imzasını buna göre değiştiriniz. Varsayılan değer parametreleri sadece adlandırılmış parametre stilinde kullanılabilir.

Program çıkışı:

1 2 3 4 5 6 7 8 9 10 
2 3 4 
1 3 5 7 9

36
2017-10-29 05:14



Bu Wim ten Brink veya BK çözümünden daha kolay ve daha düz bir uygulama gibi görünüyor. Bu uygulamanın diğerlerinin de sahip olmadığı herhangi bir olumsuzluk var mı? - stephenmm


OpenCV gibi bir şey kullanır:

/* in the header file */

#ifdef __cplusplus
    /* in case the compiler is a C++ compiler */
    #define DEFAULT_VALUE(value) = value
#else
    /* otherwise, C compiler, do nothing */
    #define DEFAULT_VALUE(value)
#endif

void window_set_size(unsigned int width  DEFAULT_VALUE(640),
                     unsigned int height DEFAULT_VALUE(400));

Kullanıcı ne yazması gerektiğini bilmiyorsa, bu hile yararlı olabilir:

usage example


19
2017-12-18 19:38





Yok hayır.

En son C99 standardı bile bunu desteklemez.


18
2017-09-24 14:39



basit bir hayır daha iyi olurdu;) - KevinDTimm
Kevindtimm: Bu mümkün değil, SO cevapları asgari uzunlukta gerektirir. Denedim. :) - unwind
Lütfen cevabıma bakın. :) - chaos


Hayır, bu bir C ++ dil özelliği.


17
2017-09-24 14:39