C'nin gizli özellikleri


141

Tüm C derleyici uygulamalarının arkasında bir standart olduğunu biliyorum, bu yüzden gizli özellikler olmamalıdır. Buna rağmen, tüm C geliştiricilerinin her zaman kullandıkları gizli / gizli numaralara sahip olduklarından eminim.


Siz / birileri bu sorunun C # ve Perl sürümlerinde olduğu gibi en iyi gizli özelliklerin seçimini belirtmek için “soruyu” düzenleyecek olsaydınız harika olurdu.
Donal Fellows

Yanıtlar:


62

Fonksiyon göstergeleri. Hızlı dolaylı iş parçacıklı kod yorumlayıcıları (FORTH) veya bayt kodu dağıtıcıları uygulamak veya OO benzeri sanal yöntemleri simüle etmek için işlev işaretçileri tablosunu kullanabilirsiniz.

Sonra standart kütüphanede qsort (), bsearch (), strpbrk (), strcspn () gibi gizli mücevherler bulunur.

C'nin bir yanlış özelliği, imzalı aritmetik taşmanın tanımsız davranış (UB) olmasıdır. Dolayısıyla, her ikisi de oturum açmış olan x + y gibi bir ifade gördüğünüzde, bu durum taşabilir ve UB'ye neden olabilir.


29
Ancak taşma konusunda davranış belirtmiş olsalardı, bu normal davranış olmayan mimarilerde çok yavaş olurdu. Çok düşük çalışma zamanı yükü her zaman C'nin bir tasarım hedefi olmuştur ve bu, bunun gibi birçok şeyin tanımsız olduğu anlamına gelir.
Mark Baker

9
Taşmanın neden UB olduğunu çok iyi biliyorum . Bu hala bir yanlışlıktır, çünkü standart, UB'ye neden olmadan aritmetik taşmayı (tüm temel işlemlerin) test edebilen en azından kütüphane rutinlerini sağlamalıdır.
zvrba

2
@zvrba, "tüm temel işlemlerin aritmetik taşmasını test edebilen kütüphane rutinleri" bunu eklerseniz, tamsayı aritmetik işlemleri için önemli bir performans artışı elde etmiş olursunuz. ===== Örnek olay Matlab özellikle sarma veya doygunluk için tamsayı taşma davranışını kontrol etme özelliğini EKLEMEKTEDİR. Ayrıca taşma gerçekleştiğinde de bir istisna atar ==> Matlab tamsayı işlemlerinin performansı: ÇOK YAVAŞ. Benim kendi sonucum: Matlab'ın neden tamsayı taşma kontrolü istemediğinizi gösteren zorlayıcı bir vaka çalışması olduğunu düşünüyorum.
Trevor Boyd Smith

15
Standardın aritmetik taşma kontrolü için kütüphane desteği sağlaması gerektiğini söyledim . Şimdi, bir kütüphane rutini hiç kullanmadıysanız nasıl bir performansa neden olabilir?
zvrba

5
Büyük bir olumsuzluk, GCC'nin imzalı tamsayı taşmalarını yakalamak ve bir çalışma zamanı istisnası atmak için bir bayrağı olmamasıdır. Bu gibi durumları tespit etmek için x86 bayrakları olsa da, GCC bunları kullanmaz. Böyle bir bayrağa sahip olmak, performans açısından kritik olmayan (özellikle eski) uygulamalara en az kod gözden geçirme veya yeniden düzenleme olmadan güvenlik avantajı sağlar.
Andrew Keeton

116

GCC derleyicisinin bir hile daha, ancak derleyiciye şube gösterge ipuçları verebilir (Linux çekirdeğinde yaygındır)

#define likely(x)       __builtin_expect((x),1)
#define unlikely(x)     __builtin_expect((x),0)

bkz. http://kerneltrap.org/node/4705

Bu konuda sevdiğim şey, aynı zamanda bazı fonksiyonlara biraz ifade katması.

void foo(int arg)
{
     if (unlikely(arg == 0)) {
           do_this();
           return;
     }
     do_that();
     ...
}

2
Bu hile harika ... :) Özellikle tanımladığınız makrolarla. :)
sundar - Monica'yı eski durumuna getir

77
int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

Bunlar standartta isteğe bağlı bir öğedir, ancak insanlar sürekli olarak yeniden tanımladıkları için gizli bir özellik olmalıdır. Üzerinde çalıştığım bir kod tabanı (ve şimdilik yapıyorum), hepsi farklı tanımlayıcılara sahip birden fazla yeniden tanımlamaya sahip. Çoğu zaman önişlemci makrolarıyla:

#define INT16 short
#define INT32  long

Ve bunun gibi. Saçımı çekmemi istiyor. Sadece ucube standart tamsayı typedefs kullanın!


3
Bence onlar C99 ya da öylesine. Bunların ortalıkta olmasını sağlamak için taşınabilir bir yol bulamadım.
akauppi

3
C99'un isteğe bağlı bir parçasıdır, ancak bunu uygulamayan derleyici satıcıları bilmiyorum.
Ben Collins

10
stdint.h, C99'da isteğe bağlı değildir, ancak görünüşe göre C99 standardını izlemek bazı satıcılar içindir ( öksürük Microsoft).
Ben Combee

5
@Pete, anal olmak istiyorsanız: (1) Bu ileti dizisinin herhangi bir Microsoft ürünü ile ilgisi yoktur. (2) Bu dizinin C ++ ile hiçbir ilgisi yoktu. (3) C ++ 97 diye bir şey yoktur.
Ben Collins

5
Azillionmonkeys.com/qed/pstdint.h - taşınabilir yakın stdint.h
gnud'a

73

Virgül operatörü yaygın olarak kullanılmaz. Kesinlikle istismar edilebilir, ancak aynı zamanda çok yararlı olabilir. Bu kullanım en yaygın olanıdır:

for (int i=0; i<10; i++, doSomethingElse())
{
  /* whatever */
}

Ancak bu operatörü her yerde kullanabilirsiniz. Gözlemek:

int j = (printf("Assigning variable j\n"), getValueFromSomewhere());

Her ifade değerlendirilir, ancak ifadenin değeri değerlendirilen son ifadenin değeri olacaktır.


7
20 yıldır CI bunu ASLA görmedim!
Martin Beckett

11
C ++ 'da aşırı yükleyebilirsiniz.
Wouter Lievens

6
can! = elbette olmalı. Aşırı yüklenmenin tehlikesi, yerleşik olanın boşluk dahil zaten her şey için geçerli olmasıdır, bu nedenle mevcut aşırı yüklenme eksikliği için derleme asla başarısız olmaz. Yani, programcıya çok fazla ip verir.
Aaron

Döngü içindeki int C ile çalışmaz: C ++ geliştirmesi. "," (İ = 0, j = 10; i <j; j--, i ++) ile aynı işlem mi?
Aif

63

yapıyı sıfıra başlatma

struct mystruct a = {0};

bu tüm yapı elemanlarını sıfırlayacaktır.


2
Bununla birlikte, eğer doldurmayı sıfırlamaz.
Mikeage

2
@simonn, hayır yapı tümleşik olmayan türler içeriyorsa tanımsız davranış yapmaz. Bir şamandıra / çiftin hafızasında 0 ile memset, şamandıra / çiftliği yorumladığınızda hala sıfır olacaktır (şamandıra / çift bilerek tasarlanmıştır).
Trevor Boyd Smith

6
@Andrew: memset/ callocdo "all bytes zero" (yani fiziksel sıfırlar); bu, tüm türler için tanımlanmamıştır. her şeyi uygun mantıksal sıfır değerleri ile { 0 } intihar etmeyi garanti eder . Örneğin, işaretçiler, verilen platformdaki null değeri olsa bile, uygun null değerlerini almaları garantilidir . 0xBAADFOOD
AnT

1
@nvl: Nesnenin kapladığı tüm belleği zorla tamamen bit-sıfır durumuna ayarladığınızda fiziksel sıfır elde edersiniz. Bu nedir memset(ile yaptığı 0ikinci argüman olarak). Kaynak koddaki nesneyi başlattığınızda / atadığınızda (veya ) mantıksal sıfır alırsınız . Bu iki sıfır türü mutlaka aynı sonucu vermez. İşaretçi ile örnekte olduğu gibi. Bir işaretçi üzerinde yaptığınızda , bir işaretçi alırsınız . Ancak bir işaretçiye atadığınızda , fiziksel düzeyde olabilecek başka bir şey olan boş gösterici değeri elde edersiniz . 0{ 0 }memset0x000000xBAADF00D
AnT

3
@nvl: Pratikte, fark genellikle sadece kavramsaldır. Ancak teoride, hemen hemen her tür buna sahip olabilir. Örneğin double,. Genellikle, mantıksal sıfır ve fiziksel sıfırın aynı olduğu IEEE-754 standardına göre uygulanır. Ancak IEEE-754 dil için gerekli değildir. Bu yüzden double d = 0;(mantıksal sıfır) yaptığınızda, fiziksel olarak işgal edilen hafızadaki bazı bitlerin dsıfır olmayacağı olabilir.
AnT

52

Çok karakterli sabitler:

int x = 'ABCD';

Bu x, 0x41424344(veya 0x44434241mimariye bağlı olarak) olarak ayarlanır .

EDIT: Bu teknik, özellikle int seri hale getirirseniz taşınabilir değildir. Ancak, kendi kendini belgeleyen numaralandırmalar oluşturmak son derece yararlı olabilir. Örneğin

enum state {
    stopped = 'STOP',
    running = 'RUN!',
    waiting = 'WAIT',
};

Bu, ham bir bellek dökümü arıyorsanız ve bir numaralandırmanın değerini aramak zorunda kalmadan belirlemeniz gerektiğinde çok daha basit hale getirir.


Eminim bu taşınabilir bir yapı değildir. Çok karakterli bir sabit oluşturmanın sonucu, uygulama tanımlıdır.
Mark Bessey

8
"Taşınabilir değil" yorumları tamamen noktayı kaçırıyor. INT_MAX'ın "taşınabilir" olmadığı için bir programı eleştirmek gibidir :) Bu özellik olması gerektiği kadar portatiftir. Çoklu karakter sabiti, benzersiz tamsayı kimlikleri oluşturmak için okunabilir bir yol sağlayan son derece kullanışlı bir özelliktir.
AnT

1
@Chris Lutz - Sondaki virgülün K&R'ye geri döndüğünden eminim. İkinci baskıda (1988) açıklanmaktadır.
Ferruccio

1
@Ferruccio: Toplam initailizer listelerindeki sondaki virgül hakkında düşünüyor olmalısınız. Numaralandırma bildirimlerinde sondaki virgül gelince - bu yeni bir ek, C99.
AnT

3
'HANG' veya 'BSOD' :-)
unuttunuz

44

Bit alanlarını hiç kullanmadım ama ultra düşük seviye şeyler için kulağa hoş geliyorlar.

struct cat {
    unsigned int legs:3;  // 3 bits for legs (0-4 fit in 3 bits)
    unsigned int lives:4; // 4 bits for lives (0-9 fit in 4 bits)
    // ...
};

cat make_cat()
{
    cat kitty;
    kitty.legs = 4;
    kitty.lives = 9;
    return kitty;
}

Bu sizeof(cat), bunun kadar küçük olabileceği anlamına gelir sizeof(char).


Tarafından Incorporated yorumlar Aaron ve leppie , teşekkürler çocuklar.


Gömülü sistemlerde veya düşük seviyeli sürücü kodunda yapıların ve birliklerin kombinasyonu daha da ilginçtir. Bir SD kartın kayıtlarını ayrıştırmak istediğinizde, birliği (1) kullanarak okuyabilir ve bit alanlarının bir yapısı olan birliği (2) kullanarak okuyabilirsiniz.
ComSubVie

5
Bitfieldler taşınabilir değildir - derleyici, örneğin, bacaklara en önemli 3 biti veya en az 3 biti tahsis edip etmeyeceğini özgürce seçebilir.
zvrba

3
Bitfield'ler, standardın uygulamalara nasıl eklendikleri konusunda çok fazla özgürlük verdiği, uygulamada neredeyse işe yaramaz oldukları bir örnektir. Bir değerin kaç biti aldığını ve nasıl depolandığını önemsiyorsanız, bit maskeleri kullanmanız daha iyi olur.
Mark Bessey

26
Bitfield'ler, "tamsayı parçaları" olarak değil, onlara yapı elemanları olarak davrandığınız sürece gerçekten taşınabilirdir. Boyut, konum değil, gömülü bir sistemde sınırlı hafızaya sahip, her bit değerli olduğu için önemli ... ancak günümüzdeki kodlayıcıların çoğu bunu hatırlamak için çok genç. :-)
Adam Liss

5
@Adam: bit alanının bayt içindeki konumuna bağlıysanız, yerleşik bir sistemde (veya başka bir yerde) konum önemli olabilir. Maskeleri kullanmak belirsizlikleri ortadan kaldırır. Sendikalar için de benzer şekilde.
Steve Melnikoff

37

C'nin bir standardı var, ancak tüm C derleyicileri tam uyumlu değil (Henüz tam uyumlu bir C99 derleyicisi görmedim!).

Bununla birlikte, tercih ettiğim hileler, C semantiğine bağlı oldukları için platformlar arasında açık ve taşınabilir olmayanlar. Genellikle makrolar veya bit aritmetiği ile ilgilidir.

Örneğin: geçici bir değişken kullanmadan iki işaretsiz tamsayıyı değiştirmek:

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

veya aşağıdaki gibi sonlu durum makinelerini temsil etmek için "genişletme":

FSM {
  STATE(x) {
    ...
    NEXTSTATE(y);
  }

  STATE(y) {
    ...
    if (x == 0) 
      NEXTSTATE(y);
    else 
      NEXTSTATE(x);
  }
}

aşağıdaki makrolarla gerçekleştirilebilir:

#define FSM
#define STATE(x)      s_##x :
#define NEXTSTATE(x)  goto s_##x

Genel olarak olsa da, zeki hileleri sevmiyorum ama kodu (takas örneği olarak) okumak için gereksiz yere karmaşık hale getiriyorum ve kodu daha net ve doğrudan niyetini iletenleri (FSM örneği gibi) seviyorum .


18
C zincirlemeyi destekler, böylece bir ^ = b ^ = a ^ = b yapabilirsiniz;
OJ.

4
Açıkça söylemek gerekirse, durum örneği C işlemcisi değil, önişlemcinin bir işaretidir - ilkini ikincisi olmadan kullanmak mümkündür.
Greg Whitfield

15
OJ: aslında önerdiğiniz şey, sıra noktası kuralları nedeniyle tanımlanmamış bir davranıştır. Çoğu derleyicide çalışabilir, ancak doğru veya taşınabilir değildir.
Evan Teran

5
Xor swap aslında ücretsiz kayıt durumunda daha az etkili olabilir. Herhangi bir iyi optimize edici, temp değişkenini bir kayıt haline getirir. Uygulamaya (ve paralellik desteğine duyulan ihtiyaca) bağlı olarak, takas aslında bir kayıt yerine gerçek bellek kullanabilir (ki bu da aynı olurdu).
Paul de Vrieze

27
lütfen bunu asla yapma: en.wikipedia.org/wiki/…
Christian Oudard

37

Duff's Device gibi titreşimli yapılar :

strncpy(to, from, count)
char *to, *from;
int count;
{
    int n = (count + 7) / 8;
    switch (count % 8) {
    case 0: do { *to = *from++;
    case 7:      *to = *from++;
    case 6:      *to = *from++;
    case 5:      *to = *from++;
    case 4:      *to = *from++;
    case 3:      *to = *from++;
    case 2:      *to = *from++;
    case 1:      *to = *from++;
               } while (--n > 0);
    }
}

29
@ComSubVie, Duff'un Cihazını kullanan herkes Duff'ın Cihazını gören ve Duff'ın Cihazını kullanıyorlarsa kodlarının 1337 olacağını düşünen bir senaryo çocuğudur. (1.) Duff'un Cihazı modern işlemcilerde herhangi bir performans artışı sunmuyor, çünkü modern işlemciler sıfır havai döngüye sahip. Başka bir deyişle, kullanılmayan bir kod parçasıdır. (2.) İşlemciniz sıfır havai döngü sunmasa bile, muhtemelen SSc / altivec / vektör işleme gibi bir şey olacak ve memcpy () kullandığınızda Duff Cihazınızı utandıracaktır. (3.) Memcpy () duff'ları yapmanın diğerlerinden fayda olmadığını söylemiş miydim?
Trevor Boyd Smith

2
@ComSubVie, lütfen Ölüm Yumruğumla tanışın ( en.wikipedia.org/wiki/… )
Trevor Boyd Smith

12
@Trevor: Yani sadece script 8051 programı ve PIC mikrodenetleyicileri var, değil mi?
SF.

6
@Trevor Boyd Smith: Duff'ın Cihazı modası geçmiş gibi görünse de, hala ComSubVie'nin cevabını doğrulayan tarihsel bir merak. Her neyse, Wikipedia'dan alıntı: "4.0 sürümünde Duff cihazının sayısız örneği XFree86 Sunucusundan kaldırıldığında, performansta kayda değer bir gelişme oldu." ...
paercebal

2
Symbian'da bir kez hızlı piksel kodlaması için çeşitli döngüler değerlendirdik; duff'un cihazı, montajcı içinde en hızlısıydı. Bu yüzden bugün akıllı telefonlarınızdaki ana ARM çekirdekleriyle hala ilgiliydi.
Will

33

C99'da eklenen ve uzun süre gcc'de desteklenen atanmış başlatıcılara çok düşkünüm:

#define FOO 16
#define BAR 3

myStructType_t myStuff[] = {
    [FOO] = { foo1, foo2, foo3 },
    [BAR] = { bar1, bar2, bar3 },
    ...

Dizi başlatma artık konuma bağlı değildir. FOO veya BAR değerlerini değiştirirseniz, dizi başlatma otomatik olarak yeni değerlerine karşılık gelir.


Gcc'nin uzun zamandır desteklediği sözdizimi, standart C99 sözdizimi ile aynı değildir.
Mark Baker

28

C99, müthiş herhangi bir sipariş yapısı başlatma özelliğine sahiptir.

struct foo{
  int x;
  int y;
  char* name;
};

void main(){
  struct foo f = { .y = 23, .name = "awesome", .x = -38 };
}


27

anonim yapılar ve diziler benim favorim. (çapraz başvuru http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )

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

veya

void myFunction(type* values) {
    while(*values) x=*values++;
}
myFunction((type[]){val1,val2,val3,val4,0});

bağlantılı listelerin örneğini oluşturmak için bile kullanılabilir ...


3
Bu özelliğe genellikle "bileşik değişmez değerler" adı verilir. Anonim (veya isimsiz) yapılar, üye adı olmayan iç içe geçmiş yapıları belirtir.
Calandoa

GCC'ye göre, "ISO C90 bileşik değişmezleri yasaklar".
jmtd

"ISO C99 bileşik değişmez değerleri destekler." "Bir uzantı olarak GCC, C89 modunda ve C ++ 'da bileşik değişmezleri destekler" (dixit info gcc). Artı, "Bir GNU uzantısı olarak, GCC, statik depolama süresine sahip nesnelerin bileşik değişmez değerleri ile başlatılmasına izin verir (ISO C99'da mümkün değildir, çünkü başlatıcı sabit değildir)."
PypeBros

24

gcc bulunabilir ben zevk C diline göre uzantıları numarası vardır burada . Favorilerimden bazıları işlev özellikleridir . Son derece kullanışlı bir örnek format niteliğidir. Bu, printf biçiminde bir dize alan özel bir işlev tanımlarsanız kullanılabilir. Bu işlev özelliğini etkinleştirirseniz, gcc, biçim dizenizin ve bağımsız değişkenlerin eşleştiğinden emin olmak için bağımsız değişkenlerinizi kontrol eder ve uygun şekilde uyarılar veya hatalar oluşturur.

int my_printf (void *my_object, const char *my_format, ...)
            __attribute__ ((format (printf, 2, 3)));

24

ilk gördüğümde beni şok eden (gizli) özellik printf ile ilgili. bu özellik, biçim belirleyicilerini biçimlendirmek için değişkenleri kullanmanıza olanak tanır. kodu arayın, daha iyi göreceksiniz:

#include <stdio.h>

int main() {
    int a = 3;
    float b = 6.412355;
    printf("%.*f\n",a,b);
    return 0;
}

* karakteri bu efekti elde eder.


24

Şey ... C dilinin güçlü noktalarından birinin taşınabilirliği ve standartlığı olduğunu düşünüyorum, bu yüzden şu anda kullandığım uygulamada bir "gizli numara" bulduğumda kullanmayacağım, çünkü benim Mümkün olduğunca standart ve taşınabilir C kodu.


Ancak gerçekte, kodunuzu başka bir derleyici ile ne sıklıkta derlemelisiniz?
Joe D

3
@Joe D, Windows / OSX / Linux gibi bir çapraz platform projesi, muhtemelen biraz ve ayrıca x86 vs x86_64 ve benzeri gibi farklı bir kemer var ...
Pharaun

@JoeD Bir derleyici satıcısıyla evlenmekten mutlu olan çok dar bir projede değilseniz, çok. Derleyicileri değiştirmek zorunda kalmaktan kaçınmak isteyebilirsiniz, ancak bu seçeneği açık tutmak istersiniz. Gömülü sistemlerde, her zaman bir seçim yapamazsınız. AHS, ASS.
XTL

19

Burada daha önce tartışıldığı gibi derleme zamanı iddiaları .

//--- size of static_assertion array is negative if condition is not met
#define STATIC_ASSERT(condition) \
    typedef struct { \
        char static_assertion[condition ? 1 : -1]; \
    } static_assertion_t

//--- ensure structure fits in 
STATIC_ASSERT(sizeof(mystruct_t) <= 4096);

16

Sabit dize birleştirme

Bildiğim tüm derleyiciler bunu desteklediğinden, cevaplarda zaten görmediğim için oldukça şaşırdım, ancak birçok programcı bunu görmezden geliyor gibi görünüyor. Bazen çok kullanışlı ve sadece makro yazarken değil.

Geçerli kodumda var durumda kullanın: Ben #define PATH "/some/path/"bir yapılandırma dosyasında (gerçekten makefile tarafından ayarlanır) var. Şimdi kaynakların açılması için dosya adları da dahil olmak üzere tam yol oluşturmak istiyorum. Sadece gider:

fd = open(PATH "/file", flags);

Korkunç ama çok yaygın yerine:

char buffer[256];
snprintf(buffer, 256, "%s/file", PATH);
fd = open(buffer, flags);

Ortak korkunç çözümün olduğuna dikkat edin:

  • üç kat daha uzun
  • okunması çok daha az kolay
  • çok daha yavaş
  • daha az güçlü bir keyfi arabellek boyutu sınırına ayarlanmış (ancak sabit dizeler içermemesi için bundan daha uzun kod kullanmanız gerekir).
  • daha fazla yığın alanı kullan

1
Kirli `\` kullanmadan bir dize sabitini birden çok kaynak satırına bölmek de yararlıdır.
dolmen


12

Dizileri veya sıralamaları başlatırken, başlatıcı listesindeki son öğeden sonra virgül koyabilirsiniz. Örneğin:

int x[] = { 1, 2, 3, };

enum foo { bar, baz, boom, };

Bu, otomatik olarak kod oluşturuyorsanız, son virgülün ortadan kaldırılması konusunda endişelenmenize gerek kalmayacak şekilde yapıldı.


Bu, örneğin Eric'in "baz" ve ardından George'un "boom" eklediği çok geliştirici bir ortamda da önemlidir. Eric bir sonraki proje yapımı için kodunu çıkarmaya karar verirse, hala George'un değişimiyle derleniyor. Çok dallı kaynak kodu kontrolü ve çakışan geliştirme programları için çok önemlidir.
Harold Bamford

Numaralamalar C99 olabilir. Dizi başlatıcıları ve sondaki virgül K&R'dir.
Ferruccio

Sade sıralamalar AFAIK c89'daydı. En azından uzun zamandır etraftalar.
XTL

12

Yapı ataması harika. Birçok kişi, yapıların da değerler olduğunu ve etrafa atanabileceğini fark etmiyor gibi görünüyor memcpy(), basit bir atama hile yaptığında kullanmaya gerek yok.

Örneğin, bazı hayali 2D grafik kitaplığını düşünün, (tamsayı) ekran koordinatını temsil etmek için bir tür tanımlayabilir:

typedef struct {
   int x;
   int y;
} Point;

Şimdi, işlev argümanlarından başlatılmış bir nokta oluşturan ve onu döndüren bir işlev yazmak gibi "yanlış" görünebilecek şeyler yaparsınız:

Point point_new(int x, int y)
{
  Point p;
  p.x = x;
  p.y = y;
  return p;
}

Dönüş değeri, yapı ataması kullanılarak değere kopyalandığı sürece (elbette) güvenlidir:

Point origin;
origin = point_new(0, 0);

Bu şekilde, tümü düz standart C'de oldukça temiz ve nesne yönelimli ish kodu yazabilirsiniz.


4
Elbette, büyük yapıların bu şekilde aktarılmasının performans sonuçları vardır; genellikle yararlıdır (ve gerçekten de birçok insanın yapabileceğinizin farkına varmadığı bir şeydir), ancak işaretçilerin geçilmesinin daha iyi olup olmadığını düşünmeniz gerekir.
Mark Baker

1
Tabii ki olabilir . Derleyicinin kullanımı algılaması ve optimize etmesi de mümkündür.
gevşeyin

Öğelerden herhangi biri işaretçi ise dikkatli olun, çünkü işaretçileri içerikleri değil kopyaları kendileri kopyalayacaksınız. Tabii ki, memcpy () kullanırsanız da aynı şey geçerlidir.
Adam Liss

Derleyici, global optimizasyonlar yapmadıkça, bu dönüştürme by-by geçişini referans-referansla optimize edemez.
Blaisorblade

Muhtemelen C ++ 'da standardın kopyayı optimize etmesine izin verdiğini belirtmek gerekir (standart, derleyicilerin bunu uygulamasına izin vermelidir, çünkü yan etkileri olabilecek kopya yapıcısı çağrılamayabilir anlamına gelir) ve çoğu C ++ derleyicisi Ayrıca C derleyicileri, derleyicinizin bu optimizasyonu yapması için iyi bir şans var.
Joseph Garvin

10

Garip vektör indeksleme:

int v[100]; int index = 10; 
/* v[index] it's the same thing as index[v] */

4
Daha da iyisi ... char c = 2 ["Merhaba"]; (bundan sonra c == 'l').
yrp

5
V [index] == * (v + index) ve index [v] == * (index + v) olduğunu düşündüğünüzde çok garip değil
Ferruccio

17
Lütfen bana soruyu soracağı gibi, bunu her zaman kullanmadığınızı söyleyin!
Tryke

9

C derleyicileri çeşitli standartlardan birini uygular. Ancak bir standarda sahip olmak, dilin tüm yönlerinin tanımlandığı anlamına gelmez. Örneğin Duff'ın cihazı , modern derleyicilerin optimizasyon tekniklerinin bu sık kullanılan modelin istenen etkisini azaltmamasını sağlamak için özel amaçlı tanıma koduna sahip olduğu kadar popüler hale gelen favori bir 'gizli' özelliktir.

Genel olarak, derleyicinizin kullandığı C standart (lar) ının ustura kenarında ilerlerken gizli özellikler veya dil hileleri önerilmez. Bu tür birçok numara bir derleyiciden diğerine çalışmaz ve genellikle bu tür özellikler derleyici paketinin bir sürümünden belirli bir üretici tarafından başka bir sürüme başarısız olur.

C kodunu kırmış çeşitli hileler şunları içerir:

  1. Derleyicinin bellekteki yapıları nasıl düzenlediğine dayanarak.
  2. Tamsayıların / şamandıraların endianitesi ile ilgili varsayımlar .
  3. Fonksiyon ABI'leri üzerindeki varsayımlar.
  4. Yığın çerçevelerinin büyüdüğü yöndeki varsayımlar.
  5. İfadelerdeki icra emri ile ilgili varsayımlar.
  6. İşlev bağımsız değişkenlerinde ifadelerin yürütme sırası hakkındaki varsayımlar.
  7. Kısa, int, uzun, şamandıra ve çift tiplerin bit boyutu veya hassasiyeti hakkındaki varsayımlar.

Programcılar, çoğu C standardında 'derleyiciye bağımlı' davranış olarak belirtilen yürütme modelleri hakkında varsayımlar yaptığında ortaya çıkan diğer sorunlar ve sorunlar.


Bunların çoğunu çözmek için, bu varsayımları platformunuzun özelliklerine bağlı hale getirin ve her platformu kendi başlığında tanımlayın. Sipariş yürütme bir istisnadır - asla buna güvenmeyin; diğer fikirlerde ise her platformun güvenilir bir karara sahip olması gerekir.
Blaisorblade

2
@Blaisorblade, Daha da iyisi, varsayımlarınızı, derlemenin ihlal edildiği bir platformda başarısız olmasını sağlayacak şekilde belgelemek için derleme zamanı iddialarını kullanın.
RBerteig

Bence her ikisi de birleştirmek gerektiğini düşünüyorum, böylece kod birden fazla platformda çalışır (bu orijinal niyetiydi) ve eğer özellik makroları yanlış şekilde ayarlanırsa, derleme zamanı iddiaları onu yakalar. Diyelim ki, işlev ABI'leri üzerindeki varsayım derleme zamanı iddiaları olarak kontrol edilebilir olup olmadığından emin değilim, ancak diğer (geçerli) çoğu için (yürütme sırası ;-) hariç) mümkün olmalıdır.
Blaisorblade

Fonksiyon ABI kontrolleri bir test takımı tarafından yapılmalıdır.
dolmen

9

Sscanf kullanırken, okumaya devam etmeniz gereken yeri bulmak için% n kullanabilirsiniz:

sscanf ( string, "%d%n", &number, &length );
string += length;

Görünüşe göre, başka bir cevap ekleyemezsiniz, bu yüzden buraya ikinci bir cevap ekleyeceğim, "&&" ve "||" şartlı olarak:

#include <stdio.h>
#include <stdlib.h>

int main()
{
   1 || puts("Hello\n");
   0 || puts("Hi\n");
   1 && puts("ROFL\n");
   0 && puts("LOL\n");

   exit( 0 );
}

Bu kodun çıktısı:

Selam
ROFL

8

kodda kırılma noktası ayarlamak için INT (3) kullanmak benim en sevdiğim


3
Taşınabilir olduğunu sanmıyorum. Bu x86 üzerinde çalışacak, ama diğer platformlar ne olacak?
Cristian Ciupitu

1
Hiçbir fikrim yok - Bu konuda bir soru göndermelisin
Dror Helper

2
İyi bir tekniktir ve X86'ya özgüdür (diğer platformlarda muhtemelen benzer teknikler olmasına rağmen). Ancak, bu C'nin bir özelliği değildir. Standart olmayan C uzantılarına veya kütüphane çağrılarına bağlıdır.
Ferruccio

1
GCC'de __builtin_trap ve MSVC __debugbreak için desteklenen herhangi bir mimaride çalışacaktır.
Axel Gneiting

8

C'nin en sevdiğim "gizli" özelliği, yığına geri yazmak için printf içinde% n kullanılmasıdır. Normalde printf, parametre dizesini temel alarak parametre değerlerini yığından açar, ancak% n bunları geri yazabilir.

Bölüm 3.4.2'ye buradan göz atın . Çok kötü güvenlik açıklarına yol açabilir.


bağlantı artık çalışmıyor, aslında sitenin kendisi çalışmıyor gibi görünüyor. Başka bir bağlantı sağlayabilir misiniz?
thequark

@thequark: "Biçim dizesi güvenlik açıkları" ile ilgili herhangi bir makalede bazı bilgiler olacaktır .. (örn. crypto.stanford.edu/cs155/papers/formatstring-1.2.pdf ) .. Ancak alanın doğası gereği web sitelerinin kendileri biraz kesintili ve gerçek akademik makaleler (uygulama ile) gelmek zordur.
Sridhar Iyer

8

Enums kullanarak derleme zamanı varsayım denetimi: Aptal örnek, ancak derleme zamanı yapılandırılabilir sabitleri olan kütüphaneler için gerçekten yararlı olabilir.

#define D 1
#define DD 2

enum CompileTimeCheck
{
    MAKE_SURE_DD_IS_TWICE_D = 1/(2*(D) == (DD)),
    MAKE_SURE_DD_IS_POW2    = 1/((((DD) - 1) & (DD)) == 0)
};

2
+1 Düzgün. Microsoft'un CompilerAssert makrosunu kullanıyordum, ama seninki de kötü değil. ( #define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1])
Patrick Schlüter

1
Numaralandırma yöntemini seviyorum. Daha önce kullandığım yaklaşım ölü kod eliminasyonundan yararlandı: "eğer (bir şey_bad) {void BLORG_IS_WOOZLED (void); BLORG_IS_WOOZLED ();}" bağlantı süresine kadar hata yapmadı, ancak programcı hata mesajı ile blorg woozled biliyorum.
supercat

8

Gcc (c), iç içe geçmiş işlev bildirimleri ve? Yanlış bir ise a döndüren?: Operatörünün a?: B formu gibi etkinleştirebileceğiniz bazı eğlenceli özelliklere sahiptir.


8

Son zamanlarda 0 bit alanı keşfettim.

struct {
  int    a:3;
  int    b:2;
  int     :0;
  int    c:4;
  int    d:3;
};

hangi bir düzen verecek

000aaabb 0ccccddd

yerine: 0;

0000aaab bccccddd

0 genişlik alanı, aşağıdaki bit alanlarının bir sonraki atom varlığı üzerinde ayarlanması gerektiğini söyler ( char)


7

C99 tarzı değişken argüman makroları, aka

#define ERR(name, fmt, ...)   fprintf(stderr, "ERROR " #name ": " fmt "\n", \
                                  __VAR_ARGS__)

hangi gibi kullanılır

ERR(errCantOpen, "File %s cannot be opened", filename);

Burada ayrıca stringize operatörü ve string sabit birleşim, gerçekten sevdiğim diğer özellikleri kullanıyorum.


VA_ARGS içinde fazladan bir 'R' var .
Blaisorblade

6

Değişken boyutlu otomatik değişkenler de bazı durumlarda yararlıdır. Bunlar i nC99 eklenmiştir ve gcc'de uzun süredir desteklenmektedir.

void foo(uint32_t extraPadding) {
    uint8_t commBuffer[sizeof(myProtocol_t) + extraPadding];

Sabit boyutlu protokol üstbilgisi artı değişken boyutlu veriler için alan içeren yığın üzerinde bir arabellek ile sonuçlanır. Aynı etkiyi alloca () ile de alabilirsiniz, ancak bu sözdizimi daha kompakttır.

Bu rutini çağırmadan önce extraPadding öğesinin makul bir değer olduğundan emin olmalısınız veya sonunda yığını havaya uçurursunuz. Malloc veya başka bir bellek ayırma tekniği çağırmadan önce argümanları kontrol etmeniz gerekir, bu yüzden bu gerçekten sıra dışı değildir.


Bir bayt / karakter hedef platformda tam olarak 8 bit genişliğinde değilse bu da düzgün çalışır mı? Biliyorum, bu davalar nadir, ama yine de ... :)
Stephan202
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.