En sevdiğiniz C programlama hilesi nedir? [kapalı]


134

Örneğin, son zamanlarda linux çekirdeğinde bununla karşılaştım:

/ * Koşul doğruysa bir derleme hatasını zorla * /
#define BUILD_BUG_ON (koşul) ((geçersiz) sizeof (karakter [1 - 2 * !! (koşul)]))

Bu nedenle, kodunuzda, belki de bazı donanım kısıtlamaları nedeniyle, 8 baytın katlarından biri olması gereken bir yapıya sahipseniz, şunları yapabilirsiniz:

BUILD_BUG_ON ((sizeof (yapı gizemi)% 8)! = 0);

ve struct mystruct boyutu 8'in katı değilse ve 8'in katı ise hiçbir çalışma zamanı kodu üretilmezse derlenmez.

Bildiğim başka bir numara, tek bir başlık dosyasının bir modüldeki değişkenleri hem modül olarak tanımlamasına hem de başlatmasına izin veren "Grafik Taşları" kitabından kaynaklanıyor.

#ifdef DEFINE_MYHEADER_GLOBALS
#define KÜRESEL
#define INIT (x, y) (x) = (y)
#Başka
#define GLOBAL extern
#define INIT (x, y)
#endif

GLOBAL int INIT (x, 0);
GLOBAL int somefunc (int a, int b);

Bununla birlikte, x ve somefunc'u tanımlayan kod şunları yapar:

#define DEFINE_MYHEADER_GLOBALS
#include "the_above_header_file.h"

yalnızca x ve somefunc () kullanan kodlar:

#include "the_above_header_file.h"

Böylece, hem globallerin hem de ihtiyaç duydukları yerde işlev prototiplerinin ve karşılık gelen extern bildirimlerinin bildirildiği bir başlık dosyası alırsınız.

Peki, bu hatlar boyunca en sevdiğiniz C programlama hileleri nelerdir?


9
Bu daha çok C önişlemci hilelerine benziyor.
jmucchiello

Hakkında BUILD_BUG_ONkullanarak makro, bilmem ne yanlış #erroriçini ve #if?
Ricardo

Yanıtlar:


80

C99, anonim diziler kullanarak gerçekten harika şeyler sunar:

Anlamsız değişkenleri kaldırma

{
    int yes=1;
    setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}

olur

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

Değişken Bir Değişken Miktarını Geçme

void func(type* values) {
    while(*values) {
        x = *values++;
        /* do whatever with x */
    }
}

func((type[]){val1,val2,val3,val4,0});

Statik bağlantılı listeler

int main() {
    struct llist { int a; struct llist* next;};
    #define cons(x,y) (struct llist[]){{x,y}}
    struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL))));
    struct llist *p = list;
    while(p != 0) {
        printf("%d\n", p->a);
        p = p->next;
    }
}

Herhangi bir eminim aklıma hiç gelmemiş birçok teknik.


2
&(int){1}Buradaki amacınızın ne olduğunu biraz daha netleştirmek istiyorsanız, ilk örneğinizin de yazılabileceğine inanıyorum .
Lily Ballard

67

Quake 2 kaynak kodunu okurken şöyle bir şey buldum:

double normals[][] = {
  #include "normals.txt"
};

(az ya da çok, şimdi kontrol etmek için kullanışlı kod yok).

O zamandan beri, önişlemcinin yaratıcı kullanımının yeni bir dünyası gözlerimin önünde açıldı. Artık sadece üstbilgileri içermez, ancak şimdi ve sonra tüm kod parçaları (yeniden kullanılabilirliği çok geliştirir) :-p

Teşekkürler John Carmack! xD


13
Deprem kaynağındaki hızlı ters sqrt bahsetmeden bir optimizasyon iş parçacığında carmack diyemezsiniz. en.wikipedia.org/wiki/Fast_inverse_square_root
pg1989

İlk etapta nereden 0x5f3759df aldı?
RSH1

2
@RoryHarvey: Aradığımda bulabildiğim kadarıyla tamamen ampirik görünüyor. Bazı çalışmalar (onları nerede gördüğümü hatırlamıyorum), bunun optimal seviyeye yakın olduğunu, ancak tamamen optimal olmadığını gösterdi. Benzer şekilde, 64 bit için değerin hesaplanmak yerine keşfedildiği görülmektedir.
Matthieu M.

50

= {0};Memset çağırmaya gerek kalmadan yapıları başlatmak için kullanmaktan hoşlanıyorum .

struct something X = {0};

Bu, yapı (veya dizinin) tüm üyelerini sıfıra başlatır (ancak herhangi bir dolgu baytı değil - bunları sıfırlamanız gerekiyorsa memset kullanın).

Ancak , büyük ve dinamik olarak ayrılmış yapılar için bununla ilgili bazı sorunlar olduğunu bilmelisiniz .


Bu arada küresel değişkenler için gerekli değildir.
Strager

5
Statik değişkenler için gerekli değildir . Global değişkenler sıfırlanabilir, ancak bu bir zorunluluk değildir.
Jamie

4
Bazen bunu genişletmek için: const struct something zero_something = { 0 };ve sonra bir değişken struct something X = zero_something;rutin ile veya kısmen bir yol boyunca 'X = sıfır_something;' kullanabilirsiniz sıfırlayabilirsiniz. Olası tek itiraz, bir yerden veri okumayı içermesidir; bu günlerde, bir 'memset ()' daha hızlı olabilir - ancak ödevin netliğini seviyorum ve başlatıcıda sıfırdan farklı değerler kullanmak da mümkündür (ve memset () ve ardından bireysel üyeye ince ayar yapmak mümkündür basit bir kopyadan daha yavaş olabilir).
Jonathan Leffler

45

Eğer c hilelerden bahsediyorsak, favorim loop unrolling için Duff'ın Cihazı olmalı ! Sadece onu öfkeyle kullanmam için doğru fırsatı bekliyorum ...


4
Bir kez ölçülebilir bir performans kazancı elde etmek için kullandım, ancak bu günlerde çok fazla donanımda kullanışlı değil. Her zaman profil yapın!
Dan Olson

6
Evet, Duff'ın cihazının bağlamını anlamayan bir çeşit insan şu şekilde yaratıldı: "kod okunabilirliği" eğer kod çalışmak için yeterince hızlı değilse işe yaramaz. Muhtemelen sizi küçümseyen insanların hiçbiri zor gerçek zamanlı kodlamak zorunda kalmadı.
Rob K

1
+1, aslında Duff'ın cihazını birkaç kez kullanmam gerekiyordu. İlk kez, temelde sadece kopyalanan ve yolda küçük bir dönüşüm gerçekleştiren bir döngü oldu. Bu mimarideki basit bir memcpy'den () çok, çok daha hızlıydı.
Makis

3
Öfke, sizden sonra kodunuzu korumak zorunda olan meslektaşlarınız ve haleflerinizden olacaktır.
Jonathan Leffler

1
Dediğim gibi, hala doğru fırsatı bekliyorum - ama henüz kimse beni yeterince rahatsız etmedi. Yaklaşık 25 yıldır C yazıyorum, sanırım ilk kez 90'ların başında Duff'ın cihazına rastladım ve henüz kullanmak zorunda kalmadım. Diğerlerinin yorumladığı gibi, derleyiciler bu tür bir optimizasyonda daha iyi hale geldiğinden, bu tür hile daha az yararlıdır.
Jackson

42

hata ayıklama için __FILE__ve kullanma __LINE__

#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);

6
Bazı derleyicilerde FUNCTION da bulunur.
JBRWilkinson

11
__FUNCTION__yalnızca bir takma addır __func__ve __func__c99'dadır. Oldukça kullanışlı. __PRETTY_FUNCTION__C (GCC) için başka bir takma addır __func__, ancak C ++ 'da size tam işlev imzasını alır.
sklnd

DOSYA dosya
adının

31

C99'da

typedef struct{
    int value;
    int otherValue;
} s;

s test = {.value = 15, .otherValue = 16};

/* or */
int a[100] = {1,2,[50]=3,4,5,[23]=6,7};

28

Bir zamanlar bir eşim ve ben zor bir yığın yolsuzluk hata bulmak için geri tanımladı.

Gibi bir şey:

#define return DoSomeStackCheckStuff, return

4
Umarım bu işlev gövdesinde # tanımlı ve sonunda # tanımsızdı!
Strager

Çok düşkün değil - aklıma gelen ilk şey, bazı hatalar nedeniyle DoSomeStackCheckStuff hafızayı vidalıyor ve kodu kim okuyorsa, dönüşün yeniden tanımlanmasının farkında değil ve / cehennem / neler olduğunu merak ediyor.
gilligan

8
@strager Ama bu aslında işe yaramaz. Bütün mesele, her fonksiyon çağrısına bir miktar izleme eklemektir . Aksi takdirde DoSomeStackCheckStuff, izlemek istediğiniz işlevlere bir çağrı ekleyebilirsiniz .
Clueless

1
@gilligan Bunun her zaman etkin bıraktığınız bir şey olduğunu sanmıyorum; tek seferlik hata ayıklama işleri için oldukça kullanışlı görünüyor.
sunetos

bu gerçekten işe yarıyor mu? :) #define return if((DoSomeStackCheckStuff) && 0) ; else returnYazabilirdim ... sanırım deli gibi!
Paolo Bonzini

22

Ben dinamik boyutlu bir nesneye sahip olduğu için "struct hack" seviyorum. Bu site de oldukça iyi açıklıyor (bir yapının son üyesi olarak "str []" yazabileceğiniz C99 sürümüne atıfta bulunsalar da). şöyle bir "nesne" dizesi yapabilirsiniz:

struct X {
    int len;
    char str[1];
};

int n = strlen("hello world");
struct X *string = malloc(sizeof(struct X) + n);
strcpy(string->str, "hello world");
string->len = n;

burada, öbek üzerinde bir int (len için), artı "merhaba dünya", artı 1 (X 1 boyutuna dahil edildiği için) olan X tipi bir yapı tahsis ettik .

Aynı blokta bazı değişken uzunluklu verilerden hemen önce bir "üstbilgi" olmasını istediğinizde genellikle kullanışlıdır.


Şahsen ben sadece malloc () ve realloc () kendimi bulmak ve uzunluğu bulmak gerektiğinde strlen () kullanmak daha kolay, ama dize uzunluğunu asla bilmeyen ve muhtemelen çok bulmak gerekir bir program gerekiyorsa Bu muhtemelen daha iyi bir yoldur.
Chris Lutz

4
"..." str [] "yazabileceğiniz C99 sürümü" str [0] gibi bir bağlamda sıfır boyutlu diziler gördüm; oldukça sık. Bence bu C99. Eski derleyiciler sıfır boyutlu diziler hakkında şikayet biliyorum.
smcameron

3
Bunu da beğendim, ancak malloc (offsetof (X, str) + numbytes) gibi bir şey kullanmalısınız , aksi takdirde dolgu ve hizalama sorunları nedeniyle işler ters gidecektir. Örneğin sizeof (yapı X) 5 değil, 8 olabilir.
Fozi

3
@Fozi: Aslında bunun bir sorun olacağını düşünmüyorum. Bu sürümde str[1](değil str[]) 1 bayt str bulunur sizeof(struct X). Buna ve arasındaki herhangi bir dolgu dahildir . lenstr
Evan Teran

2
@Rusky: Bu herhangi bir şeyi nasıl olumsuz etkiler? Sonra "dolgu" olduğunu varsayalım str. Tamam, tahsis ettiğimde sizeof(struct X) + 10Bu stretkili 10 - sizeof(int)(veya daha fazla, dolgu var dediğimizden beri) büyük yapar. Bu bindirmeler str ve daha sonra herhangi bir dolgu. Herhangi bir farkın olmasının tek yolu str, her şeyi yine de bozan bir üye varsa , esnek üyelerin son olması gerektiğidir. Sondaki herhangi bir dolgu sadece muhtemelen çok fazla tahsis edilmesine neden olacaktır. Lütfen gerçekte nasıl yanlış gidebileceğine dair özel bir örnek verin.
Evan Teran

17

Sınıfları öykünerek C ile nesne yönelimli kod.

Basitçe, bir yapı ve bu yapıya bir işaretçi ilk parametre olarak götüren bir dizi işlev oluşturun.


2
C ++ 'ı Cfront'a çeviren bir şey var mı?
MarkJ

11
Bu neredeyse nesne yönelimi değildir. Kalıtımla OO için, nesne yapınıza "alt sınıflar" tarafından aşırı yüklenebilen bir çeşit sanal işlev tablosu eklemeniz gerekir. Orada yarı pişmiş "sınıfları ile c" tarzı çerçeveler bu amaçla orada ama bunun dışında kalıyorum öneririz.
exDM69

Söylenmesi gerekiyordu. Bunun için +1.
Amit S

3
@ exDM69, nesne yönelimi bir problemi kodlama paradigması kadar düşünmenin bir yoludur; kalıtım olmadan başarılı bir şekilde yapabilirsiniz. Bunu birkaç projede C ++ içine tam geçiş yapmadan önce yaptım.
Mark Ransom

16

Onun yerine

printf("counter=%d\n",counter);

kullanım

#define print_dec(var)  printf("%s=%d\n",#var,var);
print_dec(counter);

14

Kayıt tanımlarının bakımını kolaylaştırmak için aptal bir makro hile kullanmak.

#define COLUMNS(S,E) [(E) - (S) + 1]

typedef struct
{
    char studentNumber COLUMNS( 1,  9);
    char firstName     COLUMNS(10, 30);
    char lastName      COLUMNS(31, 51);

} StudentRecord;

11

Belirtildiği dışında tüm modüllerde salt okunur olan bir değişken oluşturmak için:

// Header1.h:

#ifndef SOURCE1_C
   extern const int MyVar;
#endif

// Source1.c:

#define SOURCE1_C
#include Header1.h // MyVar isn't seen in the header

int MyVar; // Declared in this file, and is writeable

// Source2.c

#include Header1.h // MyVar is seen as a constant, declared elsewhere

Bu tehlikeli. Bunlar uyuşmayan beyan ve tanımlardır. Derleme yapılırken Source2.c, derleyici MyVarbir işlev çağrısında bile değişmediğini varsayabilir Source1.c. (Bunun, gerçek bir const değişkeni olarak bir göstergeden
sabite

1
Bu, yalnızca bazı derleme birimlerinde salt okunur olan değişken üretmez. Bu tanımlanmamış davranış üretir (bkz. ISO 9899, ​​s. 6.2.7.2 ve ayrıca s. 6.7.3.5).
Ales Hakl

8

Bit kaydırmaları yalnızca 31 (32 bit tamsayıda) kaydırma miktarına kadar tanımlanır.

Daha yüksek vardiya değerleriyle de çalışması gereken hesaplanmış bir vardiya almak istiyorsanız ne yaparsınız? Theora vide-codec bileşeni bunu şöyle yapar:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  return (a>>(v>>1))>>((v+1)>>1);
}

Veya çok daha okunabilir:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  unsigned int halfshift = v>>1;
  unsigned int otherhalf = (v+1)>>1;

  return (a >> halfshift) >> otherhalf; 
}

Görevi yukarıda gösterilen şekilde gerçekleştirmek, böyle bir dalı kullanmaktan daha hızlıdır:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  if (v<=31)
    return a>>v;
  else
    return 0;
}

... ve gcc aslında satır içi :) +1
Tim Post

2
Makinemde, gcc-4.3.2, cmov talimatı kullanarak ikinci koşuldaki daldan kurtulur (koşullu hareket)
Adam Rosenfield

3
"bir dal kullanmaktan çok daha hızlı": fark, dalın tüm değerleri için doğru olması v, ancak halfshifthile sadece 32 bit mimaride izin verilen aralığı iki katına, 64 bit mimaride 127'ye çıkarıyor.
Pascal Cuoq

8

Sonlu durum makinelerini uygulama işlevlerine yönelik işaretçi dizisinin bildirilmesi.

int (* fsm[])(void) = { ... }

En hoş avantaj, her uyarıcıyı / durumu tüm kod yollarını kontrol etmeye zorlamanın basit olmasıdır.

Gömülü bir sistemde, sık sık böyle bir tabloya işaret etmek ve gerektiğinde (ISR dışında) revize etmek için bir ISR'yi eşleştireceğim.


Bunu sevdiğim bir teknik, başlatma gerektiren bir fonksiyonunuz varsa, işaretçiyi başlatma rutinine bir çağrı ile başlatırsınız. Bu çalıştığında, yaptığı son şey işaretçiyi asıl işleve bir işaretçiyle değiştirip, o işlevi çağırmaktır. Bu şekilde, işlev ilk kez çağrıldığında başlatıcı otomatik olarak çağrılır ve sonraki her seferinde gerçek işlev çağrılır.
TMN

7

Başka bir güzel ön işlemci "hile" hata ayıklama ifadeleri yazdırmak için "#" karakteri kullanmaktır. Örneğin:

#define MY_ASSERT(cond) \
  do { \
    if( !(cond) ) { \
      printf("MY_ASSERT(%s) failed\n", #cond); \
      exit(-1); \
    } \
  } while( 0 )

Düzenle: aşağıdaki kod sadece C ++ üzerinde çalışır. Smcameron ve Evan Teran'a teşekkürler.

Evet, derleme zamanı ifadesi her zaman mükemmeldir. Ayrıca şu şekilde de yazılabilir:

#define COMPILE_ASSERT(cond)\
     typedef char __compile_time_assert[ (cond) ? 0 : -1]

COMPILE_ASSERT makrosu, ad alanını bir typedef ile kirlettiği ve 2. kullanımın aldığı iki kez kullanılamaz: hata: typedef '__compile_time_assert' yeniden tanımlaması
smcameron

Bunu gerçekten denedin mi? "Typedef foo;" istediğiniz kadar. Tahminleri böyle yaparsınız. 2.5 yıl boyunca hem gcc, VC hem de gömülü ortam için bir derleyici üzerinde birkaç derleyicide kullandım ve hiçbir zorlukla karşılaşmadım.
Gilad Naor


1
Evet, denedim. Gcc derleyiciden hata mesajını kesip yapıştırdım.
smcameron

1
@Gilad: c ++ 'da yedekli typedef'lere sahip olmak yasaldır, ancak c'de değil.
Evan Teran

6

Daha önce hiç kullanmadığım için gerçekten favori bir hile demem, ama Duff'ın Cihazından bahsetmek bana Coroutines'i C'de uygulamakla ilgili bu makaleyi hatırlattı. bir süre faydalı olabilir.


Aslında bu tekniği bir dizi bağımlı asenkron I / O bir dizi insan tarafından okunabilir sürüş sürüş yapmak için kullandım. Temel fark, bir staticdeğişken içinde koroutin durumunu depolamam, bunun yerine dinamik olarak bir yapı tahsis etmem ve koroutin fonksiyonuna bir işaretçi geçirmem. Bir grup makro bunu daha lezzetli hale getirir. Güzel değil ama her yerde atlar async / geri arama sürümü daha iyi. swapcontext()Eğer olsa ben yeşil konu (üzerinden * nixes) kullanırdım.
pmdj

6
#if TESTMODE == 1    
    debug=1;
    while(0);     // Get attention
#endif

Süre (0); program üzerinde hiçbir etkisi yoktur, ancak derleyici "bu hiçbir şey yapmaz" hakkında bir uyarı yayınlar, bu da beni rahatsız edici çizgiye bakıp daha sonra dikkat çekmek istediğim gerçek nedeni görmemi sağlamak için yeterlidir.


9
onun yerine # warning kullanamaz mısın?
Stefano Borini

Görünüşe göre, yapabilirim. Tamamen standart değil, ama kullandığım derleyicilerde çalıştı. İlginç bir şekilde, gömülü derleyici # tanımlama tanımladı, ancak gcc yapmadı.
gbarry

6

Ben xor hacks hayranıyım:

Üçüncü işaretçi olmadan 2 işaretçi değiştirin:

int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;

Ya da sadece bir işaretçiyle xor bağlantılı listeyi seviyorum. (Http://en.wikipedia.org/wiki/XOR_linked_list)

Bağlantılı listedeki her düğüm, önceki düğümün ve sonraki düğümün Xor'udur. İleriye doğru gitmek için, düğümlerin adresi aşağıdaki şekilde bulunur:

LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;

vb.

veya geriye doğru gitmek için:

LLNode * last = tail;
LLNode * second_to_last = last.linked_nodes;
LLNode * third_to_last = second_to_last.linked_nodes ^ last;
LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last;

vb.

Çok yararlı olmasa da (rastgele bir düğümden geçmeye başlayamazsınız) çok havalı olduğunu düşünüyorum.


5

Bu, 'kendini ayağa vurmak için yeterli ip' kitabından geliyor:

Üstbilgi beyanında

#ifndef RELEASE
#  define D(x) do { x; } while (0)
#else
#  define D(x)
#endif

Kodunuzda test ifadeleri yerleştirin:

D(printf("Test statement\n"));

Do / while, makronun içeriğinin birden çok ifadeye genişlemesi durumunda yardımcı olur.

Deyim yalnızca derleyici için '-D RELEASE' bayrağı kullanılmazsa yazdırılır.

Sonra örneğin. bayrağı makefile vb. geçirin.

Bunun pencerelerde nasıl çalıştığından emin değilim ama * nix'te iyi çalışıyor


RELEASE tanımlandığında D (x) öğesini {} değerine genişletmek isteyebilirsiniz, böylece if ifadeleriyle iyi oynatılır. Aksi takdirde "eğer (a) D (x);" RELEASE tanımlı olduğunda sadece (a) 'ya genişler. Bu size RELEASE verison bazı güzel hatalar verecektir
MarkJ

3
@MarkJ: HAYIR. Bu şekilde, "eğer (a) D (x);" "if (a);" ki bu gayet iyi. D (x) 'i {}' ye genişletin, sonra "eğer (a) eğer (b) D (x); başka foo ();" , "if (a) if (b) {}; else foo ();" değerine YANLIŞ olarak genişler ve "else foo ()" öğesinin, ilk if yerine ikinci ile eşleşmesine neden olur.
Adam Rosenfield

Dürüst olmak gerekirse ben çoğunlukla baskı ifadelerini test etmek için bu makroyu kullanın, ya da koşullu bir deyim olsaydı hepsini örterdim. D (eğer (a) foo (););
Simon Walker

1
@AdamRosenfield: #define D(x) do { } while(0)Bunun yerine bu durumu ele alır (ve xtutarlılık için ekleyen dala uygulanabilir )
rpetrich

3

Rusty aslında inşa Koşullamalar bir bütün seti üretti ccan inşa assert modülüne göz atın:

#include <stddef.h>
#include <ccan/build_assert/build_assert.h>

struct foo {
        char string[5];
        int x;
};

char *foo_string(struct foo *foo)
{
        // This trick requires that the string be first in the structure
        BUILD_ASSERT(offsetof(struct foo, string) == 0);
        return (char *)foo;
}

Gerçek üstbilgide, yerine kolayca bırakılabilecek diğer birçok yararlı makro vardır.

Tüm gücümle, çoğunlukla satır içi işlevlere bağlı kalarak karanlık tarafın çekilmesine (ve önişlemci kötüye kullanımına) karşı koymaya çalışıyorum, ancak tarif ettiğiniz gibi akıllı, kullanışlı makrolardan hoşlanıyorum.


Evet, son zamanlarda ccan ile karşılaştım ve bazı kodlara katkıda bulunmayı düşünüyordum, ama henüz kafamı "ccan yoluna" sarmadım. Yine de bağlantı için teşekkürler, ccan içine bakmak için daha fazla motivasyon, ki gerçekten biraz çekiş umuyoruz.
smcameron

Peki, daha kurulana kadar 'ccan yolu' konusunda fazla endişelenmeyeceğim ... şu anda ccan-lint bir GSOC projesi olarak öneriliyor. Onun küçük ve oldukça samimi bir grup .. ve snippet'leri dökümü için harika bir yer :)
Tim Post

BTW, Rusty'nin BuILD_ASSERT'in linux çekirdeğindeki (şaşırtıcı olmayan) makro gibi olduğunu fark ettim ama "nots" (veya patlama veya! ') doğru değil. Olmalıydı: "BUILD_BUG_ON ((sizeof (struct mystruct)% 8))"
smcameron

3

Bu tür şeyler için iki iyi kaynak kitap Programlama ve Katı Kod Yazma Uygulamasıdır . Bunlardan biri (hangisini hatırlamıyorum) diyor ki: # enum'u yapabildiğiniz yere tanımlayın, çünkü enum derleyici tarafından kontrol edilir.


1
AFAIK, C89 / 90'da numaralandırmalar için tipek denetleme yapılmaz. numaralandırmalar bir şekilde daha uygundur #defines.
cschol

Sayfa 39'un alt kısmı, 2. ED K&R. En azından kontrol etme fırsatı var.
Jonathan Watmough

3

C'ye özgü değil, ancak XOR operatörünü her zaman sevdim. Yapabileceği harika bir şey, "temp değeri olmadan takas":

int a = 1;
int b = 2;

printf("a = %d, b = %d\n", a, b);

a ^= b;
b ^= a;
a ^= b;

printf("a = %d, b = %d\n", a, b);

Sonuç:

a = 1, b = 2

a = 2, b = 1


a = 1; b = 2; a = a + b; b = ab; a = ab; aynı sonucu verir
Grambot

Bu ayrıca a ve b'yi değiştirir: a ^ = b ^ = a ^ = b;
vikhyat

@Capn: ekleme işlemi taşabilir.
Michael Foukarakis


2

container_ofÖrneğin listelerde kullanılan kavramını seviyorum . Temel olarak, belirtmeniz gerekmez nextvelast listede olacak her bir yapı için alan . Bunun yerine, liste yapısı başlığını gerçek bağlantılı öğelere eklersiniz.

include/linux/list.hGerçek hayattan örneklere bir göz atın .


1

Kullanıcı verileri işaretleyicilerinin kullanımı oldukça düzgün olduğunu düşünüyorum . Bugünlerde moda kaybetti. Çok fazla bir C özelliği değil, ancak C'de kullanımı oldukça kolay.


1
Keşke burada ne demek istediğini anlasaydım. Daha fazla açıklayabilir misiniz? Kullanıcı verileri işaretçisi nedir?
Zan Lynx


öncelikle geri aramalar içindir. Geri arama her başlatıldığında size geri vermek istediğiniz bazı verilerdir. Bir nesneyi bir olaya bağlayabilmeniz için bu C ++ bu işaretçiyi geri aramaya geçirmek için özellikle kullanışlıdır.
Evan Teran

Ah evet. Teşekkürler. Bunu çok kullanıyorum ama hiç böyle demedim.
Zan Lynx

1

Ön derleyicinin kod üretmesine izin vermek için X-Makroları kullanıyorum . Hata değerlerini ve ilişkili hata dizelerini tek bir yerde tanımlamak için özellikle yararlıdırlar, ancak bunun ötesine geçebilirler.


1

Kod tabanımızın benzer bir numarası var

#ifdef DEBUG

#define my_malloc(amt) my_malloc_debug(amt, __FILE__, __LINE__)
void * my_malloc_debug(int amt, char* file, int line)
#else
void * my_malloc(int amt)
#endif
{
    //remember file and line no. for this malloc in debug mode
}

hata ayıklama modunda bellek sızıntılarının izlenmesini sağlar. Her zaman bunun harika olduğunu düşündüm.


1

Makrolarla eğlenceli:

#define SOME_ENUMS(F) \
    F(ZERO, zero) \
    F(ONE, one) \
    F(TWO, two)

/* Now define the constant values.  See how succinct this is. */

enum Constants {
#define DEFINE_ENUM(A, B) A,
    SOME_ENUMS(DEFINE_ENUMS)
#undef DEFINE_ENUM
};

/* Now a function to return the name of an enum: */

const char *ToString(int c) {
    switch (c) {
    default: return NULL; /* Or whatever. */
#define CASE_MACRO(A, B) case A: return #b;
     SOME_ENUMS(CASE_MACRO)
#undef CASE_MACRO
     }
}

0

Uygulamanın çalıştırılmasında aslında HW'nin ne için kullanıldığı hakkında C kodunun nasıl tamamen habersiz hale getirileceğine dair bir örnek. Main.c kurulumu yapar ve daha sonra serbest katman herhangi bir derleyici / kemer üzerine uygulanabilir. Ben biraz C kodu soyutlama için oldukça temiz olduğunu düşünüyorum, bu yüzden özel olmak için alamadım.

Buraya eksiksiz bir derleme örneği ekleme.

/* free.h */
#ifndef _FREE_H_
#define _FREE_H_
#include <stdio.h>
#include <string.h>
typedef unsigned char ubyte;

typedef void (*F_ParameterlessFunction)() ;
typedef void (*F_CommandFunction)(ubyte byte) ;

void F_SetupLowerLayer (
F_ParameterlessFunction initRequest,
F_CommandFunction sending_command,
F_CommandFunction *receiving_command);
#endif

/* free.c */
static F_ParameterlessFunction Init_Lower_Layer = NULL;
static F_CommandFunction Send_Command = NULL;
static ubyte init = 0;
void recieve_value(ubyte my_input)
{
    if(init == 0)
    {
        Init_Lower_Layer();
        init = 1;
    }
    printf("Receiving 0x%02x\n",my_input);
    Send_Command(++my_input);
}

void F_SetupLowerLayer (
    F_ParameterlessFunction initRequest,
    F_CommandFunction sending_command,
    F_CommandFunction *receiving_command)
{
    Init_Lower_Layer = initRequest;
    Send_Command = sending_command;
    *receiving_command = &recieve_value;
}

/* main.c */
int my_hw_do_init()
{
    printf("Doing HW init\n");
    return 0;
}
int my_hw_do_sending(ubyte send_this)
{
    printf("doing HW sending 0x%02x\n",send_this);
    return 0;
}
F_CommandFunction my_hw_send_to_read = NULL;

int main (void)
{
    ubyte rx = 0x40;
    F_SetupLowerLayer(my_hw_do_init,my_hw_do_sending,&my_hw_send_to_read);

    my_hw_send_to_read(rx);
    getchar();
    return 0;
}

4
Ayrıntılı bir pratik yapmak ister misiniz, belki pratik bir kullanımı açıklamak?
Leonardo Herrera

Bir wxample olarak, sonunda kesintiler üreten som HW arayüzü kullanarak bir test programı yazmak zorunda kalırsam. Daha sonra bu modül, normal kapsam dışında bir işlevi bir sinyal / kesme işleyici olarak yürütmek için ayarlanabilir.
eaanon01

0
if(---------)  
printf("hello");  
else   
printf("hi");

Boşlukları doldurun, böylece çıktıda ne merhaba ne de merhaba görünmez.
ans:fclose(stdout)


kodu {}araç çubuğu düğmesiyle biçimlendirebilirsiniz (sizin için yaptım). "Alıntı" düğmesi boşluk bırakmaz veya sözdizimi vurgulama uygulamaz.
Álvaro González
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.