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.
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.
Yanıtlar:
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.
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();
...
}
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!
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.
yapıyı sıfıra başlatma
struct mystruct a = {0};
bu tüm yapı elemanlarını sıfırlayacaktır.
memset
/ calloc
do "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
memset
(ile yaptığı 0
ikinci 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 }
memset
0x0000
0
0xBAADF00D
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 d
sıfır olmayacağı olabilir.
Çok karakterli sabitler:
int x = 'ABCD';
Bu x
, 0x41424344
(veya 0x44434241
mimariye 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.
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.
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 .
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);
}
}
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.
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 ...
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)));
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.
Ş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.
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);
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:
Hiç kullanmadım ve hiç kimseye tavsiye edip etmeyeceğimden emin değilim, ama bu sorunun Simon Tatham'ın ortak rutin numarasından bahsetmeden eksik olacağını hissediyorum .
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ı.
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.
Garip vektör indeksleme:
int v[100]; int index = 10;
/* v[index] it's the same thing as index[v] */
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:
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.
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
kodda kırılma noktası ayarlamak için INT (3) kullanmak benim en sevdiğim
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.
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)
};
#define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1]
)
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.
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
)
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.
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.