Apa beberapa kelemahan yang membuat Anda gila di API C (termasuk perpustakaan standar, perpustakaan pihak ketiga, dan header di dalam proyek)? Tujuannya adalah untuk mengidentifikasi perangkap desain API di C, sehingga orang yang menulis perpustakaan C baru dapat belajar dari kesalahan di masa lalu.
Jelaskan mengapa cacatnya buruk (sebaiknya dengan contoh), dan cobalah untuk menyarankan perbaikan. Meskipun solusi Anda mungkin tidak praktis dalam kehidupan nyata (ini sudah terlambat untuk diperbaiki strncpy
), solusi ini harus memberi kesempatan bagi penulis perpustakaan di masa depan.
Meskipun fokus dari pertanyaan ini adalah API C, masalah yang memengaruhi kemampuan Anda untuk menggunakannya dalam bahasa lain dipersilahkan.
Tolong beri satu cacat per jawaban, sehingga demokrasi dapat mengurutkan jawaban.
sumber
malloc
string akan memperbaikinya. Saya pikir memberikan contoh yang baik dengan jawaban pertama bisa sangat membantu pertanyaan ini berkembang. Terima kasih!Jawaban:
Fungsi dengan nilai pengembalian yang tidak konsisten atau tidak logis. Dua contoh bagus:
1) Beberapa fungsi windows yang mengembalikan HANDLE menggunakan NULL / 0 untuk kesalahan (CreateThread), beberapa menggunakan INVALID_HANDLE_VALUE / -1 untuk kesalahan (CreateFile).
2) Fungsi 'waktu' POSIX mengembalikan '(time_t) -1' pada kesalahan, yang benar-benar tidak masuk akal karena 'time_t' dapat berupa tipe yang ditandatangani atau tidak.
sumber
int time(time_t *out);
danBOOL CreateFile(LPCTSTR lpFileName, ..., HANDLE *out);
.Fungsi atau parameter dengan nama yang tidak deskriptif atau membingungkan. Sebagai contoh:
1) CreateFile, di Windows API, tidak benar-benar membuat file, itu membuat file handle. Itu bisa membuat file, sama seperti 'buka' bisa, jika diminta melalui parameter. Parameter ini memiliki nilai yang disebut 'CREATE_ALWAYS' dan 'CREATE_NEW' yang namanya bahkan tidak mengisyaratkan semantik mereka. (Apakah 'CREATE_ALWAYS' berarti gagal jika file tersebut ada? Atau apakah itu membuat file baru di atasnya? Apakah 'CREATE_NEW' berarti selalu membuat file baru dan gagal jika file sudah ada? Atau apakah itu membuat yang baru? file di atasnya?)
2) pthread_cond_wait di API POST pthreads POSIX, yang meskipun namanya, adalah menunggu tanpa syarat .
sumber
pthread_cond_wait
tidak berarti "kondisional menunggu". Ini merujuk pada fakta bahwa Anda sedang menunggu variabel kondisi .Jenis buram yang dilewatkan melalui antarmuka sebagai jenis yang dihapus pegangan. Masalahnya adalah, tentu saja, bahwa kompiler tidak dapat memeriksa kode pengguna untuk jenis argumen yang benar.
Ini datang dalam berbagai bentuk dan rasa, termasuk, tetapi tidak terbatas pada:
void*
penyalahgunaanmenggunakan
int
sebagai pegangan sumber daya (contoh: perpustakaan CDI)argumen yang diketik secara ketat
Jenis yang lebih berbeda (= tidak dapat digunakan sepenuhnya secara bergantian) dipetakan ke jenis yang sama dengan jenis yang dihapus, semakin buruk. Tentu saja, obatnya hanya untuk menyediakan pointer opaque yang aman di sepanjang baris (contoh C):
sumber
Fungsi dengan konvensi pengembalian string yang tidak konsisten dan seringkali rumit.
Misalnya, getcwd meminta buffer yang disediakan pengguna dan ukurannya. Ini berarti aplikasi harus menetapkan batas arbitrer pada panjang direktori saat ini, atau melakukan sesuatu seperti ini ( dari CCAN ):
Solusi saya: kembalikan
malloc
string ed. Sederhana, kuat, dan tidak kalah efisien. Kecuali platform tertanam dan sistem lama,malloc
sebenarnya cukup cepat.sumber
snprintf(buf, 32, "%d", n)
, di mana panjang output diprediksi (tentu saja tidak lebih dari 30, kecualiint
adalah benar-benar besar pada sistem Anda). Memang, malloc tidak tersedia di banyak sistem, tetapi untuk lingkungan desktop dan server, itu, dan berfungsi dengan sangat baik.Fungsi yang mengambil / mengembalikan tipe data majemuk berdasarkan nilai, atau yang menggunakan panggilan balik.
Lebih buruk lagi jika jenis kata adalah gabungan atau berisi bidang bit.
Dari perspektif penelepon C, ini sebenarnya OK, tapi saya tidak menulis dalam C atau C ++ kecuali diminta, jadi saya biasanya menelepon melalui FFI. Sebagian besar FFI tidak mendukung serikat atau bit-bidang, dan beberapa (seperti Haskell dan MLton) tidak dapat mendukung struct yang diteruskan oleh nilai. Bagi mereka yang dapat menangani by-value structs, setidaknya Common Lisp dan LuaJIT dipaksa ke jalur lambat - Antarmuka Fungsi Luar Negeri Umum Lisp harus membuat panggilan lambat melalui libffi, dan LuaJIT menolak untuk JIT-mengkompilasi jalur kode yang berisi panggilan. Fungsi yang dapat memanggil kembali ke host juga memicu jalur lambat di LuaJIT, Java, dan Haskell, dengan LuaJIT tidak dapat mengkompilasi panggilan seperti itu.
sumber