Misalnya, saya baru-baru ini menemukan ini di kernel linux:
/ * Paksa kesalahan kompilasi jika kondisinya benar * / #menentukan BUILD_BUG_ON (kondisi) ((kosong) ukuran (char [1 - 2 * !! (kondisi)]))
Jadi, dalam kode Anda, jika Anda memiliki beberapa struktur yang harus, katakanlah kelipatan ukuran 8 byte, mungkin karena beberapa kendala perangkat keras, Anda dapat melakukan:
BUILD_BUG_ON ((sizeof (struct mystruct)% 8)! = 0);
dan itu tidak akan dikompilasi kecuali ukuran struct mystruct adalah kelipatan 8, dan jika kelipatan 8, tidak ada kode runtime yang dihasilkan sama sekali.
Trik lain yang saya tahu adalah dari buku "Graphics Gems" yang memungkinkan satu file header untuk mendeklarasikan dan menginisialisasi variabel dalam satu modul sementara di modul lain menggunakan modul itu, cukup mendeklarasikannya sebagai eksternal.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL # Tentukan INIT (x, y) (x) = (y) #lain #define GLOBAL extern # Tentukan INIT (x, y) #berakhir jika INIT int GLOBAL (x, 0); GLOBAL int somefunc (int a, int b);
Dengan itu, kode yang mendefinisikan x dan somefunc:
#tentukan DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
sedangkan kode yang hanya menggunakan x dan somefunc () melakukan:
#include "the_above_header_file.h"
Jadi, Anda mendapatkan satu file header yang mendeklarasikan instance global dan prototipe fungsi jika diperlukan, dan deklarasi eksternal terkait.
Jadi, apa trik pemrograman C favorit Anda di sepanjang garis itu?
BUILD_BUG_ON
makro, apa salahnya menggunakan#error
inside dan#if
?Jawaban:
C99 menawarkan beberapa hal yang sangat keren menggunakan array anonim:
Menghapus variabel yang tidak berguna
{ int yes=1; setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int)); }
menjadi
setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));
Meneruskan Jumlah Argumen Variabel
void func(type* values) { while(*values) { x = *values++; /* do whatever with x */ } } func((type[]){val1,val2,val3,val4,0});
Daftar tertaut statis
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; } }
Saya yakin banyak teknik keren lainnya yang belum saya pikirkan.
sumber
&(int){1}
, jika Anda ingin memperjelas maksud Anda di sini.Saat membaca kode sumber Quake 2, saya menemukan sesuatu seperti ini:
double normals[][] = { #include "normals.txt" };
(kurang lebih, saya tidak memiliki kode yang berguna untuk memeriksanya sekarang).
Sejak itu, dunia baru penggunaan kreatif preprocessor terbuka di depan mata saya. Saya tidak lagi hanya menyertakan header, tetapi seluruh potongan kode sekarang dan nanti (ini meningkatkan banyak kegunaan kembali) :-p
Terima kasih John Carmack! xD
sumber
Saya suka menggunakan
= {0};
untuk menginisialisasi struktur tanpa perlu memanggil memset.struct something X = {0};
Ini akan menginisialisasi semua anggota struct (atau array) ke nol (tapi bukan byte padding - gunakan memset jika Anda juga perlu nol).
Namun Anda harus menyadari bahwa ada beberapa masalah dengan ini untuk struktur besar yang dialokasikan secara dinamis .
sumber
const struct something zero_something = { 0 };
dan kemudian saya dapat mengatur ulang variabel dengan cepatstruct something X = zero_something;
atau sebagian melalui rutinitas. Saya dapat menggunakan 'X = zero_something;'. Keberatan satu-satunya yang mungkin adalah bahwa ini melibatkan membaca data dari suatu tempat; akhir-akhir ini, 'memset ()' mungkin lebih cepat - tetapi saya suka kejelasan tugas, dan juga memungkinkan untuk menggunakan nilai selain nol di penginisialisasi juga (dan memset () diikuti dengan penyesuaian ke anggota individu mungkin lebih lambat dari salinan biasa).Jika kita berbicara tentang trik c favorit saya harus Perangkat Duff untuk membuka gulungan! Saya hanya menunggu kesempatan yang tepat untuk datang bersama saya untuk benar-benar menggunakannya dalam kemarahan ...
sumber
menggunakan
__FILE__
dan__LINE__
untuk debugging#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
sumber
__FUNCTION__
hanya alias untuk__func__
, dan__func__
di c99. Cukup berguna.__PRETTY_FUNCTION__
di C (GCC) hanyalah alias lain untuk__func__
, tetapi di C ++ ini akan memberi Anda tanda tangan fungsi lengkap.Di C99
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};
sumber
Setelah teman saya dan saya mendefinisikan ulang kembali untuk menemukan bug korupsi tumpukan yang rumit.
Sesuatu seperti:
#define return DoSomeStackCheckStuff, return
sumber
DoSomeStackCheckStuff
fungsi yang ingin Anda lacak.#define return if((DoSomeStackCheckStuff) && 0) ; else return
... sama gilanya saya kira!Saya suka "struct hack" karena memiliki objek berukuran dinamis. Situs ini menjelaskannya dengan cukup baik juga (meskipun mereka mengacu pada versi C99 di mana Anda dapat menulis "str []" sebagai anggota terakhir dari sebuah struct). Anda bisa membuat string "objek" seperti ini:
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;
di sini, kita telah mengalokasikan struktur tipe X pada heap yang berukuran int (untuk len), ditambah panjang "hello world", ditambah 1 (karena str 1 termasuk dalam sizeof (X).
Biasanya berguna jika Anda ingin memiliki "header" tepat sebelum beberapa data panjang variabel di blok yang sama.
sumber
str[1]
(tidakstr[]
) 1 byte str disertakan dalam filesizeof(struct X)
. Ini termasuk bantalan apa pun antaralen
danstr
.str
. OK, Ketika saya mengalokasikansizeof(struct X) + 10
Kemudian ini membuatstr
efektif10 - sizeof(int)
(atau lebih, karena kami mengatakan ada padding) besar. Ini overlaystr
dan padding apapun setelahnya. Satu-satunya cara agar ada perbedaan, adalah jika ada anggota yang kemudianstr
merusak semuanya, anggota yang fleksibel harus menjadi yang terakhir. Padding apa pun di bagian akhir hanya akan menyebabkan terlalu banyak yang dialokasikan. Berikan contoh spesifik tentang bagaimana hal itu sebenarnya bisa salah.Kode berorientasi objek dengan C, dengan meniru kelas.
Cukup buat struct dan satu set fungsi yang mengarahkan pointer ke struct itu sebagai parameter pertama.
sumber
Dari pada
printf("counter=%d\n",counter);
Menggunakan
#define print_dec(var) printf("%s=%d\n",#var,var); print_dec(counter);
sumber
Menggunakan trik makro yang bodoh untuk membuat definisi rekaman lebih mudah dipertahankan.
#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;
sumber
Untuk membuat variabel yang hanya-baca di semua modul kecuali yang dideklarasikan di:
// 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
sumber
Source2.c
, kompilator mungkin menganggap ituMyVar
tidak berubah, bahkan di seluruh panggilan fungsi keSource1.c
. (Perhatikan bahwa ini, sebagai variabel const yang sebenarnya, berbeda dari satu pointer ke const. Dalam kasus terakhir, objek yang diarahkan ke mungkin masih dapat dimodifikasi melalui pointer yang berbeda.)Pergeseran bit hanya ditentukan hingga jumlah pergeseran 31 (pada integer 32 bit) ..
Apa yang Anda lakukan jika Anda ingin memiliki shift yang dihitung yang perlu bekerja dengan nilai shift yang lebih tinggi juga? Berikut adalah bagaimana Theora vide-codec melakukannya:
unsigned int shiftmystuff (unsigned int a, unsigned int v) { return (a>>(v>>1))>>((v+1)>>1); }
Atau lebih mudah dibaca:
unsigned int shiftmystuff (unsigned int a, unsigned int v) { unsigned int halfshift = v>>1; unsigned int otherhalf = (v+1)>>1; return (a >> halfshift) >> otherhalf; }
Melakukan tugas dengan cara yang ditunjukkan di atas jauh lebih cepat daripada menggunakan cabang seperti ini:
unsigned int shiftmystuff (unsigned int a, unsigned int v) { if (v<=31) return a>>v; else return 0; }
sumber
v
, sedangkanhalfshift
triknya hanya menggandakan rentang yang diizinkan menjadi 63 pada arsitektur 32-bit, dan 127 pada arsitektur 64-bit.Mendeklarasikan array penunjuk ke fungsi untuk mengimplementasikan mesin status hingga.
int (* fsm[])(void) = { ... }
Keuntungan yang paling menyenangkan adalah sederhana untuk memaksa setiap stimulus / status untuk memeriksa semua jalur kode.
Dalam sistem tertanam, saya akan sering memetakan ISR untuk menunjuk ke tabel seperti itu dan merombaknya sesuai kebutuhan (di luar ISR).
sumber
"Trik" pra-prosesor bagus lainnya adalah menggunakan karakter "#" untuk mencetak ekspresi debugging. Sebagai contoh:
#define MY_ASSERT(cond) \ do { \ if( !(cond) ) { \ printf("MY_ASSERT(%s) failed\n", #cond); \ exit(-1); \ } \ } while( 0 )
edit: kode di bawah ini hanya berfungsi pada C ++. Terima kasih untuk smcameron dan Evan Teran.
Ya, waktu kompilasi menegaskan selalu bagus. Itu juga bisa ditulis sebagai:
#define COMPILE_ASSERT(cond)\ typedef char __compile_time_assert[ (cond) ? 0 : -1]
sumber
Saya tidak akan benar-benar menyebutnya sebagai trik favorit, karena saya tidak pernah menggunakannya, tetapi penyebutan Perangkat Duff mengingatkan saya pada artikel tentang penerapan Coroutine di C. Itu selalu membuat saya tertawa, tetapi saya yakin itu bisa berguna suatu saat.
sumber
static
variabel tetapi mengalokasikan struct secara dinamis dan meneruskan pointer ke fungsi coroutine. Banyak makro membuat ini lebih enak. Ini tidak bagus tapi lebih baik daripada versi async / callback yang melompat ke mana-mana. Saya akan menggunakan utas hijau (melaluiswapcontext()
on * nixes) jika saya bisa.#if TESTMODE == 1 debug=1; while(0); // Get attention #endif
Sementara (0); tidak berpengaruh pada program, tetapi kompilator akan mengeluarkan peringatan tentang "ini tidak melakukan apa-apa", yang cukup untuk membuat saya melihat baris yang menyinggung dan kemudian melihat alasan sebenarnya saya ingin menarik perhatiannya.
sumber
Saya penggemar xor hacks:
Tukar 2 pointer tanpa pointer suhu ketiga:
int * a; int * b; a ^= b; b ^= a; a ^= b;
Atau saya sangat suka daftar tertaut xor dengan hanya satu penunjuk. (http://en.wikipedia.org/wiki/XOR_linked_list)
Setiap node dalam daftar tertaut adalah Xor dari node sebelumnya dan node berikutnya. Untuk melintasi ke depan, alamat node ditemukan dengan cara berikut:
dll.
atau untuk melintasi mundur:
dll.
Meskipun tidak terlalu berguna (Anda tidak dapat mulai melakukan traverse dari node arbitrer) menurut saya ini sangat keren.
sumber
Yang ini berasal dari buku 'Cukup tali untuk menembak diri sendiri di kaki':
Di header menyatakan
#ifndef RELEASE # define D(x) do { x; } while (0) #else # define D(x) #endif
Dalam pernyataan pengujian tempat kode Anda misalnya:
D(printf("Test statement\n"));
Do / while membantu jika konten makro diperluas ke beberapa pernyataan.
Pernyataan hanya akan dicetak jika tanda '-D RELEASE' untuk kompilator tidak digunakan.
Anda kemudian bisa misalnya. berikan bendera ke makefile Anda, dll.
Tidak yakin bagaimana ini bekerja di windows tetapi di * nix bekerja dengan baik
sumber
#define D(x) do { } while(0)
sebagai gantinya menangani kasus itu (dan dapat diterapkan ke cabang yang menyisipkanx
juga untuk konsistensi)Rusty sebenarnya menghasilkan seluruh rangkaian kondisional build di ccan , lihat modul build assert:
#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; }
Ada banyak makro bermanfaat lainnya di tajuk sebenarnya, yang mudah ditempatkan.
Saya mencoba, dengan segenap kekuatan saya untuk menahan tarikan sisi gelap (dan penyalahgunaan preprocessor) dengan menggunakan sebagian besar fungsi inline, tetapi saya menikmati makro yang pintar dan berguna seperti yang Anda jelaskan.
sumber
Dua buku sumber yang bagus untuk hal-hal semacam ini adalah The Practice of Programming dan Penulisan Kode Padat . Salah satunya (saya tidak ingat yang mana) mengatakan: Lebih suka enum untuk #define di mana Anda bisa, karena enum diperiksa oleh kompilator.
sumber
Tidak spesifik untuk C, tapi saya selalu menyukai operator XOR. Satu hal keren yang dapat dilakukan adalah "swap tanpa nilai temp":
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);
Hasil:
sumber
Lihat pertanyaan "Fitur tersembunyi dari C" .
sumber
Saya suka konsep yang
container_of
digunakan misalnya dalam daftar. Pada dasarnya, Anda tidak perlu menentukannext
danlast
kolom untuk setiap struktur yang akan ada di daftar. Sebagai gantinya, Anda menambahkan header struktur daftar ke item tertaut yang sebenarnya.Lihat
include/linux/list.h
contoh kehidupan nyata.sumber
Saya pikir penggunaan pointer userdata cukup rapi. Mode kehilangan pijakan sekarang. Ini bukan fitur C tetapi cukup mudah digunakan di C.
sumber
Saya menggunakan X-Macros untuk membiarkan pra-kompilator menghasilkan kode. Mereka sangat berguna untuk mendefinisikan nilai kesalahan dan string kesalahan terkait di satu tempat, tetapi mereka bisa lebih dari itu.
sumber
Basis kode kami memiliki trik yang mirip dengan
#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 }
yang memungkinkan untuk melacak kebocoran memori dalam mode debug. Saya selalu berpikir ini keren.
sumber
Bersenang-senang dengan makro:
#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 } }
sumber
Berikut adalah contoh bagaimana membuat kode C sama sekali tidak menyadari tentang apa yang sebenarnya digunakan HW untuk menjalankan aplikasi. Main.c melakukan pengaturan dan kemudian lapisan gratis dapat diimplementasikan pada kompiler / arch manapun. Menurut saya cukup rapi untuk mengabstraksi sedikit kode C, jadi tidak terlalu spesifik.
Menambahkan contoh kompilasi lengkap di sini.
/* 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; }
sumber
if(---------) printf("hello"); else printf("hi");
Isi bagian yang kosong sehingga hello maupun hi tidak akan muncul di keluaran.
ans:
fclose(stdout)
sumber
{}
tombol toolbar (Saya telah melakukannya untuk Anda). Tombol "Kutip" tidak mempertahankan spasi atau menerapkan penyorotan sintaks.