C'de Arabirim Ayrımı ilkesi nasıl uygulanır?


15

Birkaç istemciye sahip olan 'M', 'C1', 'C2', 'C3' gibi bir modülüm var. Modül M'nin ad alanını, yani ortaya koyduğu API'lerin ve verilerin bildirimlerini başlık dosyalarına öyle bir şekilde atamak istiyorum -

  1. herhangi bir istemci için yalnızca gereksinim duyduğu veriler ve API'ler görünür; modülün ad alanının geri kalanı istemciden gizlenir, yani Arabirim Ayrımı ilkesine uyun .
  2. Bir deklarasyon yani değil ihlal birden başlık dosyalarında tekrar edilmez KURU .
  3. M modülünün müşterilerine bağımlılığı yoktur.
  4. bir müşteri M modülünün kullanmadığı kısımlarında yapılan değişikliklerden etkilenmez.
  5. mevcut istemciler daha fazla istemcinin eklenmesinden (veya silinmesinden) etkilenmez.

Şu anda bunun için modülün ad alanını müşterilerinin gereksinimlerine göre bölerek başa çıkıyorum. Örneğin, aşağıdaki resimde 3 istemcinin gerektirdiği modül ad alanının farklı bölümleri gösterilmektedir. İstemci gereksinimleri çakışıyor. Modülün ad alanı 4 ayrı başlık dosyasına bölünmüştür - 'a', '1', '2' ve '3' .

Modül ad alanı bölümleme

Bununla birlikte, bu, R3 ve R5 gibi yukarıda bahsedilen bazı gereksinimleri ihlal etmektedir. Gereksinim 3 ihlal edilmiştir, çünkü bu bölümleme müşterilerin doğasına bağlıdır; ayrıca yeni bir istemcinin eklenmesiyle bu bölümleme gereksinimi 5. değiştirir ve ihlal eder. Yukarıdaki görüntünün sağ tarafında görüldüğü gibi, yeni bir istemcinin eklenmesiyle modülün ad alanı şimdi 7 başlık dosyasına bölünmüştür - 'a ',' b ',' c ',' 1 ',' 2 * ',' 3 * 've' 4 ' . Üstbilgi dosyaları, daha eski istemcilerin 2'sinin değişmesi anlamına gelir ve böylece yeniden oluşturma işlemlerini tetikler.

C'de Arabirim Ayrımı'nı tutarlı olmayan bir şekilde elde etmenin bir yolu var mı?
Evetse, yukarıdaki örnekle nasıl başa çıkardınız?

Tahmin edebileceğim gerçek dışı bir varsayımsal çözüm -
Modül, tüm ad alanını kapsayan 1 yağ başlık dosyasına sahiptir. Bu başlık dosyası, Wikipedia sayfası gibi adreslenebilir bölümlere ve alt bölümlere ayrılmıştır. Daha sonra her istemcinin kendisi için uyarlanmış belirli bir başlık dosyası vardır. İstemciye özgü başlık dosyaları yalnızca yağ başlık dosyasının bölümlerine / alt bölümlerine giden köprülerin bir listesidir. Modül üstbilgisinde işaret ettiği bölümlerden herhangi biri değiştirilirse, derleme sisteminin istemciye özgü bir üstbilgi dosyasını 'değiştirilmiş' olarak tanıması gerekir.


1
Bu sorun neden C'ye özgüdür? C'nin mirası olmadığı için mi?
Robert Harvey

Ayrıca, ISS'yi ihlal etmek tasarımınızı daha iyi hale getirir mi?
Robert Harvey

2
C gerçekten OOP kavramlarını (arayüzler veya kalıtım gibi) desteklemez. Ham (ama yaratıcı) hack'lerle yapıyoruz. Arabirimleri simüle etmek için bir kesmek arıyorum. Genellikle, başlık dosyasının tamamı bir modülün arabirimidir.
work.bin

1
structbir arayüz istediğinizde C'de kullandığınız şeydir. Verilen yöntemler biraz zor. Bunu ilginç bulabilirsiniz: cs.rit.edu/~ats/books/ooc.pdf
Robert Harvey

Bir arabirim eşdeğer kullanarak gelmedi structve function pointers.
work.bin

Yanıtlar:


5

Arayüz Ayrımı, genel olarak, müşteri gereksinimlerine dayanmamalıdır. Bunu başarmak için tüm yaklaşımı değiştirmelisiniz. Ben özellikleri, uyumlu gruplar halinde gruplandırarak arayüzü modülerize söyleyebilirim . Yani gruplama, müşteri gereksinimlerine değil, özelliklerin kendi tutarlılığına dayanır. Bu durumda, bir dizi arabiriminiz olacak, I1, I2, ... vs. İstemci C1, I2'yi tek başına kullanabilir. İstemci C2'de I1 ve I5 vb. Kullanılabilir. İstemci birden fazla Ii kullanıyorsa sorun olmadığını unutmayın. Arayüzü tutarlı modüllere ayırdıysanız, maddenin kalbi burasıdır.

Yine, ISS istemci tabanlı değildir. Arayüzü daha küçük modüllere ayırmakla ilgilidir. Bu düzgün bir şekilde yapılırsa, istemcilerin ihtiyaç duydukları kadar az özelliğe maruz kalmasını da sağlayacaktır.

Bu yaklaşımla müşterileriniz herhangi bir sayıya yükselebilir ancak siz M etkilenmez. Her istemci, gereksinimlerine göre bir veya birkaç arabirim kombinasyonu kullanacaktır. Bir istemcinin (C), I1 ve I3 diyelim, ancak bu arabirimlerin tüm özelliklerini kullanmaması gereken durumlar olacak mı? Evet, bu bir sorun değil. Sadece en az sayıda arabirim kullanır.


Kesinlikle ayrık veya örtüşmeyen gruplar demek istediniz ?
Doc Brown

Evet, ayrık ve örtüşmeyen.
Nazar Merza

3

Arayüz Ayrışma İlkesi diyor ki:

Hiçbir müşteri, kullanmadığı yöntemlere bağımlı olmaya zorlanmamalıdır. İSS, çok büyük olan arayüzleri daha küçük ve daha spesifik arayüzlere böler, böylece müşteriler sadece ilgilendikleri yöntemleri bilmek zorunda kalırlar.

Burada cevaplanmamış birkaç soru var. Biri:

Ne kadar küçük?

Diyorsun:

Şu anda bunun için modülün ad alanını müşterilerinin gereksinimlerine göre bölerek başa çıkıyorum.

Bu manuel ördekle yazma diyorum . Yalnızca istemcinin neye ihtiyacı olduğunu ortaya koyan arabirimler oluşturursunuz. Arabirim ayırma prensibi basitçe manuel ördek yazma değildir.

Ancak ISS, yalnızca yeniden kullanılabilen "tutarlı" rol arabirimleri için bir çağrı değildir. Hiçbir "tutarlı" rol arayüz tasarımı, kendi rol ihtiyaçları ile yeni bir müşterinin eklenmesine karşı mükemmel bir koruma sağlayamaz.

İSS , müşterileri hizmette yapılan değişikliklerin etkisinden soyutlamanın bir yoludur. Değişiklik yaptıkça yapının daha hızlı gitmesi amaçlanmıştır. Elbette, müşterileri kırmamak gibi başka faydaları da var, ancak ana nokta buydu. Hizmetler count()işlev imzasını değiştirirsem, kullanmayan istemcilerin count()düzenlenmesi ve yeniden derlenmesi gerekmez.

Arayüz Ayrışma İlkesini NEDEN önemsiyorum. İnanç konusunda benim kadar önemli bir şey değil. Gerçek bir problemi çözer.

Dolayısıyla, uygulanması gereken yöntem sizin için bir sorunu çözmelidir. İSS'yi uygulamak için sadece gerekli bir değişikliğin doğru örneğiyle yenilemeyen beyin ölü bir yolu yoktur. Sistemin nasıl değiştiğine bakmanız ve işleri sessizleştirecek seçimler yapmanız gerekiyor. Seçenekleri inceleyelim.

İlk önce kendinize sorun: Servis arayüzünde değişiklik yapmak şu anda zor mu? Değilse, dışarı çıkın ve sakinleşene kadar oynayın. Bu entelektüel bir egzersiz değil. Lütfen tedavinin hastalıktan daha kötü olmadığından emin olun.

  1. Birçok istemci aynı işlev alt kümesini kullanıyorsa, bu "tutarlı" yeniden kullanılabilir arabirimler anlamına gelir. Altküme muhtemelen hizmetin istemciye sağladığı rol olarak düşünebileceğimiz tek bir fikre odaklanır. Bu işe yaradığında güzel. Bu her zaman işe yaramaz.

  2.  

    1. Birçok istemci farklı işlev alt kümeleri kullanıyorsa, istemcinin hizmeti birden çok rol üzerinden kullanması mümkündür. Sorun değil ama rolleri görmeyi zorlaştırıyor. Onları bulun ve birbirinden ayırmaya çalışın. Bu durum bizi durum 1'e geri getirebilir. Müşteri hizmeti birden fazla arabirim üzerinden kullanır. Lütfen hizmeti yayınlamaya başlamayın. Hizmeti istemciye bir kereden fazla geçirmek anlamına gelen bir şey varsa. Bu işe yarıyor ama hizmetin parçalanması gereken büyük bir çamur topu olup olmadığını sorgulamama neden oluyor.

    2. Birçok istemci farklı altkümeler kullanıyor ancak istemcilerin birden fazla kullanabilmesine izin veren roller görmüyorsanız, arayüzlerinizi tasarlamak için ördek yazmadan daha iyi bir şeyiniz yoktur. Arabirimleri tasarlamanın bu yolu, istemcinin kullanmadığı bir işleve bile maruz kalmamasını sağlar, ancak yeni bir istemci eklemenin, hizmet uygulamasının bilmesi gerekmediği her zaman yeni bir arabirim eklemeyi içereceğini neredeyse garanti eder. bu konuda rol arayüzlerini toplayan arayüz olacaktır. Bir acıyı diğerine takas ettik.

  3. Birçok istemci farklı altkümeler kullanıyorsa, çakışıyorsa, öngörülemeyen alt kümelere ihtiyaç duyacak yeni istemcilerin eklenmesi bekleniyor ve hizmeti bölmek istemiyorsanız daha işlevsel bir çözüm düşünün. İlk iki seçenek işe yaramadığından ve hiçbir şeyin bir deseni takip etmediği ve daha fazla değişikliğin geldiği kötü bir yerdeyseniz, her işleve kendi arayüzü sağlamayı düşünün. Buraya gelmek, İSS'nin başarısız olduğu anlamına gelmez. Bir şey başarısız olursa, nesne yönelimli bir paradigma oldu. Tek yöntem arabirimleri aşırı derecede ISP'yi takip eder. Bu, klavye yazmanın adil bir parçasıdır, ancak bunu aniden arayüzleri yeniden kullanılabilir hale getirebilirsiniz. Yine, olmadığından emin ol

Yani gerçekten çok küçük olabilirler.

Bu soruyu ISS'yi en uç durumlarda uygulamak için bir meydan okuma olarak ele aldım. Ancak, aşırı uçlardan en iyi şekilde kaçınıldığını unutmayın. Diğer SOLID ilkelerini uygulayan iyi düşünülmüş bir tasarımda, bu konular genellikle ortaya çıkmaz veya önemli değildir.


Yanıtlanmamış başka bir soru:

Bu arayüzlerin sahibi kim?

Tekrar tekrar "kütüphane" zihniyeti dediğim şekilde tasarlanmış arayüzler görüyorum. Hepimiz sadece bir şey yaptığınız yerde maymun-gör-maymun-yap kodlamasından suçluyduk çünkü bunu böyle gördünüz. Arayüzlerle aynı şeyden suçluyuz.

Bir kütüphanede bir sınıf için tasarlanmış bir arayüze baktığımda şöyle düşünürdüm: ah, bu adamlar profesyoneller. Bir arayüz yapmanın doğru yolu bu olmalıdır. Anlayamadığım şey, bir kütüphane sınırının kendi ihtiyaçları ve sorunları olduğu. Bir kere, bir kütüphane müşterilerinin tasarımından tamamen habersizdir. Her sınır aynı değildir. Ve bazen aynı sınırın bile onu aşmak için farklı yolları vardır.

Arayüz tasarımına bakmanın iki basit yolu:

  • Hizmete ait arabirim. Bazı insanlar bir servisin yapabileceği her şeyi ortaya çıkarmak için her arayüzü tasarlar. IDE'lerde, beslediğiniz sınıfı kullanarak sizin için bir arabirim yazacak olan yeniden düzenleme seçeneklerini bile bulabilirsiniz.

  • Müşteriye ait arayüz. İSS bunun doğru olduğunu ve sahip olunan hizmetin yanlış olduğunu iddia ediyor gibi görünüyor. Müşterilerin ihtiyaçları göz önünde bulundurularak her arayüzü bozmalısınız. İstemci arabirimin sahibi olduğundan, onu tanımlamalıdır.

Kim haklı?

Eklentileri düşünün:

resim açıklamasını buraya girin

Burada arayüzlerin sahibi kim? Müşteriler? Hizmetler?

Her ikisini de ortaya çıkarır.

Buradaki renkler katmanlar. Kırmızı katmanın (sağ) yeşil katman (solda) hakkında hiçbir şey bilmemesi gerekiyor. Yeşil katman, kırmızı katmana dokunmadan değiştirilebilir veya değiştirilebilir. Bu şekilde, herhangi bir yeşil katman kırmızı katmana takılabilir.

Neyin ne olduğunu ve neyin bilinmeyeceğini bilmeyi seviyorum. Bana göre, "neyi bilen nedir?", En önemli mimari sorudur.

Bazı kelimeleri açıklığa kavuşturalım:

[Client] --> [Interface] <|-- [Service]

----- Flow ----- of ----- control ---->

İstemci kullanan bir şeydir.

Hizmet kullanılan bir şeydir.

Interactor her ikisi de olur.

ISP, istemciler için ayrılık arayüzleri diyor. Tamam, şunu uygulayalım:

  • Presenter(bir hizmet) Output Port <I>arayüze dikte etmemelidir . Arayüz Interactor(burada bir müşteri olarak hareket eder) ihtiyacı olan şeyle sınırlandırılmalıdır . Bu, ISS'yi Interactortakip etmek için BILGI arabiriminin ve onunla değişmesi gerektiği anlamına gelir . Ve bu iyi.

  • Interactor(burada bir hizmet olarak işlev görür) Input Port <I>arayüze dikte etmemelidir . Arayüz Controller(bir istemcinin) ihtiyacı olan şeyle daraltılmalıdır . Bu, ISS'yi Controllertakip etmek için BILGI arabiriminin ve onunla değişmesi gerektiği anlamına gelir . Ve bu iyi değil .

İkincisi iyi değil, çünkü kırmızı katmanın yeşil katmanı bilmemesi gerekiyor. ISS yanlış mı? İyi gibi. Hiçbir ilke mutlak değildir. Bu, servisin yapabileceği her şeyi göstermek için arayüzü seven aptalların doğru olduğu bir durumdur.

En azından, Interactorbu kullanım durumundan başka bir şey yapmazsa haklılar . InteractorDiğer kullanım durumları için bir şeyler yaparsa, Input Port <I>bunun onlar hakkında bilmesi için bir neden yoktur . Neden Interactorsadece bir Kullanım Örneği'ne odaklanamayacağından emin değilim, bu yüzden bu bir sorun değil, ancak işler oluyor.

Ancak input port <I>arayüz kendisini Controlleristemciye köle edemez ve bunun gerçek bir eklenti olmasını sağlar. Bu bir 'kütüphane' sınırıdır. Tamamen farklı bir programlama mağazası, kırmızı katman yayınlandıktan yıllar sonra yeşil katmanı yazıyor olabilir.

Bir 'kütüphane' sınırını geçiyorsanız ve diğer tarafta arayüze sahip olmasanız bile ISS'yi uygulama gereğini hissediyorsanız, arayüzü değiştirmeden daraltmanın bir yolunu bulmanız gerekecektir.

Bunu çıkarmanın bir yolu bir adaptördür. Gibi istemciler Controlerve Input Port <I>arayüz arasına koyun . Adaptör kabul Interactorbir şekilde Input Port <I>ve delegelere kendisine 's çalışmalarını. Ancak, Controlleryeşil katmana ait bir rol arabirimi veya arabirimler aracılığıyla yalnızca istemcilerin ihtiyaç duydukları şeyleri ortaya çıkarır . Adaptör ISP'yi takip etmez, ancak daha karmaşık sınıfların ControllerISP'nin keyfini çıkarmasına izin verir . Bu gibi istemcilerden daha az bağdaştırıcı varsa Controllerve kitaplık sınırını aştığınız alışılmadık bir durumdaysanız ve yayımlanmasına rağmen kitaplık değişmeyi bırakmazsa bu yararlıdır . Sana bakıyorum Firefox. Şimdi bu değişiklikler sadece adaptörlerinizi bozuyor.

Peki bu ne anlama geliyor? Dürüst olmak gerekirse, size ne yapmanız gerektiğini söylemem için yeterli bilgi sağlamadığınız anlamına gelir. ISS'yi takip etmemek size bir soruna neden olup olmadığını bilmiyorum. Takip etmenizin size daha fazla soruna neden olup olmayacağını bilmiyorum.

Basit bir yol gösterici ilke aradığınızı biliyorum. İSS bu olmaya çalışır. Ama çok söylenmemiş bırakıyor. Buna inanıyorum. Evet, lütfen müşterileri iyi bir neden olmadan kullanmadığı yöntemlere bağımlı olmaya zorlamayın!

Eklentileri kabul etmek için bir şey tasarlama gibi iyi bir nedeniniz varsa, ISS nedenlerini takip etmeyen sorunların (istemcileri bozmadan değiştirmek zor) ve bunları azaltmanın ( Interactoren azından bir Input Port <I>ahırda kalmanın veya en azından bir kararlılığa odaklanmanın ) farkında olun kullanım örneği).


Giriş için teşekkürler. Birden çok istemciye sahip bir hizmet sağlayan modülüm var. Ad alanının mantıksal olarak tutarlı sınırları vardır, ancak müşterinin bu mantıksal sınırları aşması gerekir. Bu nedenle, ad alanını mantıksal sınırlar temelinde bölmek ISP'ye yardımcı olmaz. Bu nedenle, ad alanını, sorudaki şemada gösterildiği gibi müşteri ihtiyaçları temelinde böldüm. Ancak bu, istemcilere ve istemcileri hizmetle eşleştirmenin zayıf bir yoluna bağımlı hale getirir, çünkü istemciler nispeten sık eklenebilir / kaldırılabilir, ancak hizmetteki değişiklikler minimum olacaktır.
work.bin

Şimdi tam ad alanında olduğu gibi bir yağ arayüzü sağlayan servise yaslanıyorum ve bu servislere müşteriye özel adaptörler ile erişmek müşterinin elinde. C açısından bu, istemciye ait işlev paketlerinin bir dosyası olacaktır. Hizmetteki değişiklikler, bağdaştırıcının yeniden derlenmesini zorlar, ancak istemciyi zorunlu kılmaz. .. <
contd

<contd> .. Bu kesinlikle inşa sürelerini minimumda tutacak ve müşteri ile hizmet arasındaki bağlantıyı çalışma zamanına (ara sarma işlevini çağırarak) maliyette tutacak, kod alanını artıracak, yığın kullanımını ve muhtemelen daha fazla zihin alanını artıracaktır. (programcı) adaptörlerin bakımında.
work.bin

Mevcut çözümüm şimdi ihtiyaçlarımı karşılıyor, yeni yaklaşım daha fazla çaba gösterecek ve YAGNI'yı ihlal ediyor olabilir. Her yöntemin artılarını ve eksilerini tartmak ve buraya hangi yoldan gideceğime karar vermek zorunda kalacağım.
work.bin

1

İşte bu nokta:

existent clients are unaffected by the addition (or deletion) of more clients.

YAGNI olan başka bir önemli ilkeyi ihlal ettiğinizi belirtiyor. Yüzlerce müşterim olduğunda bunu önemsiyorum. Önde bir şey düşünmek ve daha sonra bu kod için herhangi bir ek istemcinizin olmadığı anlaşılır.

İkinci

 partitioning depends on the nature of clients

Kodunuz neden DI kullanmıyor, bağımlılık tersine çevirme, hiçbir şey, kitaplığınızdaki hiçbir şey istemcinizin doğasına bağlı olmamalıdır.

Sonunda, örtüşen şeylere (DI böylece ön taraf kodunuz sadece bu ek katmana ve müşterileriniz sadece ön taraf arayüzüne bağlıdır) bu şekilde DRY'yi yenmek için kodunuz altında ek katmana ihtiyacınız var gibi görünüyor.
Bu gerçek için od. Böylece, başka bir modülün altındaki modül katmanında kullandığınız aynı şeyleri yaparsınız. Bu şekilde aşağıda katman elde edersiniz:

herhangi bir istemci için yalnızca gereksinim duyduğu veriler ve API'ler görünür; modülün ad alanının geri kalanı istemciden gizlenir, yani Arabirim Ayrımı ilkesine uyun.

Evet

bir bildirim birden çok başlık dosyasında tekrarlanmaz, yani DRY'yi ihlal etmez. M modülünün müşterilerine bağımlılığı yoktur.

Evet

bir müşteri M modülünün kullanmadığı kısımlarında yapılan değişikliklerden etkilenmez.

Evet

mevcut istemciler daha fazla istemcinin eklenmesinden (veya silinmesinden) etkilenmez.

Evet


1

Beyanda verilen bilgilerin aynısı tanımda her zaman tekrarlanır. Bu dilin çalışma şekli budur. Ayrıca, birden çok başlık dosyasında bir bildirimin tekrarlanması DRY'yi ihlal etmez . Oldukça yaygın olarak kullanılan bir tekniktir (en azından standart kütüphanede).

Dokümanların veya uygulamanın tekrarlanması DRY'yi ihlal eder .

İstemci kodu tarafımdan yazılmadığı sürece kendimi rahatsız etmem.


0

Karışıklığımı reddediyorum. Ancak pratik örneğiniz kafama bir çözüm getiriyor. Kendi kelimelerle ifade yapabiliyorsanız: modülünde tüm bölümleri Mbir sahip birçok birçok türlü ve tüm istemcilerin ile özel ilişkisi.

Örnek Yapısı

M.h      // fat header
 - P1    // Partition 1
 - P2    // ... 2
   - P21 // ... 2 section 1
 - P3    // ... 3
C1.c     // Client 1 (Needs to include P1, P3)
C2.c     // ... 2 (Needs to include P2)
C3.c     // ... 3 (Needs to include P1, P21, P3)

Mh

#ifdef P1
#define _PREF_ P1_             // Define Prefix ("PREF") = P1_
 void _PREF_init();            // Some partition specific function
#endif /* P1 */

#ifdef P2
#define _PREF_ P2_
 void _PREF_init();
#endif /* P2 */

#if defined(P21) || defined (P2) // Part 2.1
#define _PREF_ P2_1_
 void _PREF_oddone();
#endif /* P21 */

#ifdef P3
#define _PREF_ P3_
 void _PREF_init();
#endif /* P3 */

Mc

Mc dosyasında aslında #ifdefs kullanmak zorunda kalmazsınız çünkü .c dosyasına koyduğunuz şey, istemci dosyalarının kullandığı işlevler tanımlandığı sürece istemci dosyalarını etkilemez.

#include "M.h"
#define _PREF_ P1_        
void _PREF_init() { ... };

#define _PREF_ P2_
void _PREF_init() { ... }

#define _PREF_ P2_1_
void _PREF_oddone() { ... }

#define _PREF_ P3_
void _PREF_init() { ... }

C1.c

#define P1     // "invite" P1
#define P3     // "invite" P3
#include "M.h" // Open the door, but only the invited come in.

void main()
{
    P1_init();
    //P2_init();
    //P2_1_oddone();
    P3_init();
}

C2.c

#define P2
#include "M.h

void main()
{
    //P1_init();
    P2_init();
    P2_1_oddone();
    //P3_init();
}

C3.c

#define P1
#define P21
#define P3  
#include "M.h" 

void main()
{
    P1_init();
    //P2_init();
    P2_1_oddone();
    P3_init();
}

Yine, sorduğunuz şeyin bu olup olmadığından emin değilim. Bu yüzden bir tuz tanesi ile alın.


Mc neye benziyor? Tanımlıyor musunuz P1_init() ve P2_init() ?
work.bin

@ work.bin Mc'nin işlevler arasındaki ad alanını tanımlamak dışında basit bir .c dosyası gibi görüneceğini düşünüyorum.
Sanchke Dellowar

Hem C1 hem de C2'nin var olduğunu varsayarsak - ne P1_init()ile P2_init()bağlantılıdır?
work.bin

Mh / Mc dosyasında, önişlemci _PREF_en son tanımlandığı ile değiştirilecektir. Yani _PREF_init()olacak P1_init()çünkü son #define ifadesinin. Daha sonra bir sonraki tanımlamak ifadesi ayarlayacaktır PREF böylece üreten P2_ e eşit P2_init().
Sanchke Dellowar
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.