Karakteristik yang menarik dari C dibandingkan dengan beberapa bahasa lain adalah bahwa banyak tipe datanya didasarkan pada ukuran kata dari arsitektur target, daripada ditentukan secara absolut. Walaupun ini memungkinkan bahasa yang digunakan untuk menulis kode pada mesin yang mungkin mengalami kesulitan dengan jenis tertentu, itu membuatnya sangat sulit untuk merancang kode yang akan berjalan secara konsisten pada arsitektur yang berbeda. Pertimbangkan kodenya:
uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;
Pada arsitektur di mana int
16 bit (masih berlaku untuk banyak mikrokontroler kecil) kode ini akan menetapkan nilai 1 menggunakan perilaku yang terdefinisi dengan baik. Pada mesin di mana int
64 bit, itu akan menetapkan nilai 4294836225, lagi menggunakan perilaku yang terdefinisi dengan baik. Pada mesin int
yang 32 bit, kemungkinan akan memberikan nilai -131071 (saya tidak tahu apakah itu akan menjadi Implementasi-Ditentukan atau Perilaku Tidak Terdefinisi). Meskipun kode tidak menggunakan apa pun kecuali apa yang secara nominal dianggap sebagai tipe "ukuran tetap", standar akan mensyaratkan bahwa dua jenis kompiler yang digunakan saat ini akan menghasilkan dua hasil yang berbeda, dan banyak kompiler populer saat ini akan menghasilkan sepertiga.
Contoh khusus ini agak dibuat-buat, karena saya tidak akan berharap dalam kode dunia nyata untuk menetapkan produk dua nilai 16-bit langsung ke nilai 64-bit, tetapi dipilih sebagai contoh singkat untuk menunjukkan bilangan bulat tiga cara promosi dapat berinteraksi dengan tipe unsigned yang seharusnya berukuran tetap. Ada beberapa situasi dunia nyata di mana itu perlu untuk matematika pada jenis unsigned yang akan dilakukan sesuai dengan aturan aritmatika integer matematika, yang lain di mana itu perlu dilakukan sesuai dengan aturan aritmatika modular, dan beberapa di mana itu benar-benar tidak ' itu tidak penting. Banyak kode dunia nyata untuk hal-hal seperti checksum bergantung pada uint32_t
pembungkus aritmatika mod 2³², dan pada kemampuan untuk melakukan arbitreruint16_t
aritmatika dan dapatkan hasil yang, minimal, didefinisikan sebagai mod 65536 yang akurat (sebagai lawan memicu Perilaku Tidak Terdefinisi).
Meskipun situasi ini jelas-jelas tampak tidak diinginkan (dan akan menjadi lebih karena pemrosesan 64-bit menjadi norma untuk banyak tujuan), komite standar C dari apa yang saya amati lebih memilih untuk memperkenalkan fitur bahasa yang sudah digunakan dalam beberapa produksi penting lingkungan, daripada menciptakannya "dari awal". Apakah ada ekstensi penting ke bahasa C yang akan memungkinkan kode untuk menentukan tidak hanya bagaimana suatu tipe akan disimpan tetapi juga bagaimana seharusnya berperilaku dalam skenario yang melibatkan kemungkinan promosi? Saya dapat melihat setidaknya tiga cara ekstensi kompiler dapat menyelesaikan masalah seperti:
Dengan menambahkan arahan yang akan menginstruksikan kompiler untuk memaksa tipe integer "fundamental" tertentu menjadi ukuran tertentu.
Dengan menambahkan arahan yang akan menginstruksikan kompiler untuk mengevaluasi berbagai skenario promosi seolah-olah jenis mesin memiliki ukuran tertentu, terlepas dari ukuran sebenarnya dari jenis pada arsitektur target.
Dengan memungkinkan cara mendeklarasikan tipe dengan karakteristik spesifik (mis. Menyatakan bahwa suatu tipe harus berperilaku sebagai cincin aljabar pembungkus mod-65536, terlepas dari ukuran kata yang mendasarinya, dan tidak boleh secara implisit dapat dikonversi ke tipe lain; menambahkan a
wrap32
keint
harus menghasilkan hasil jeniswrap32
terlepas dari apakahint
lebih besar dari 16 bit, sementara menambahkanwrap32
langsung kewrap16
harus ilegal (karena tidak ada yang dapat dikonversi ke yang lain).
Preferensi saya sendiri akan menjadi alternatif ketiga, karena itu akan memungkinkan bahkan mesin dengan ukuran kata yang tidak biasa untuk bekerja dengan banyak kode yang mengharapkan variabel untuk "membungkus" seperti mereka akan dengan kekuatan dua ukuran; kompiler mungkin harus menambahkan instruksi bit-masking untuk membuat tipe tersebut berperilaku sesuai, tetapi jika kode membutuhkan tipe yang membungkus mod 65536, lebih baik membuat kompiler membuat masking seperti itu pada mesin yang membutuhkannya daripada mengacaukan kode sumber dengannya. atau hanya memiliki kode seperti itu oleh tidak dapat digunakan pada mesin di mana masking seperti itu akan diperlukan. Saya penasaran, apakah ada ekstensi umum yang akan mencapai perilaku portabel melalui salah satu cara di atas, atau melalui beberapa cara yang belum saya pikirkan.
Untuk memperjelas apa yang saya cari, ada beberapa hal; terutama:
Walaupun ada banyak cara dimana kode dapat ditulis untuk memastikan semantik yang diinginkan (misalnya mendefinisikan makro untuk melakukan melakukan matematika pada operan unsigned berukuran tertentu sehingga dapat menghasilkan hasil yang secara eksplisit membungkus atau tidak) atau setidaknya mencegah yang tidak diinginkan semantik (misalnya, tentukan secara kondisional tipe yang
wrap32_t
adauint32_t
di kompiler di mana auint32_t
tidak akan dipromosikan, dan bayangkan bahwa lebih baik untuk kode yang mengharuskanwrap32_t
gagal kompilasi pada mesin di mana tipe itu akan dipromosikan daripada menjalankannya dan menghasilkan perilaku palsu), jika ada cara untuk menulis kode yang akan bermain paling baik dengan ekstensi bahasa di masa depan, menggunakan itu akan lebih baik daripada merancang pendekatan saya sendiri.Saya punya beberapa ide yang cukup solid untuk bagaimana bahasa dapat diperluas untuk menyelesaikan banyak masalah ukuran integer, memungkinkan kode untuk menghasilkan semantik identik pada mesin dengan ukuran kata yang berbeda, tetapi sebelum saya menghabiskan waktu yang signifikan untuk menuliskannya, saya ingin untuk mengetahui upaya apa yang telah dilakukan ke arah itu.
Saya tidak dengan cara apa pun ingin dilihat sebagai meremehkan Komite Standar C atau pekerjaan yang mereka hasilkan; Saya berharap, bagaimanapun, bahwa dalam beberapa tahun akan menjadi perlu untuk membuat kode bekerja dengan benar pada mesin di mana jenis promosi "alami" akan 32 bit, serta orang-orang di mana itu akan menjadi 64 bit. Saya pikir dengan beberapa ekstensi sederhana ke bahasa (lebih sederhana daripada banyak perubahan lain antara C99 dan C14) akan mungkin untuk tidak hanya memberikan cara bersih untuk menggunakan arsitektur 64-bit secara efisien, tetapi dalam tawar-menawar juga memfasilitasi interaksi dengan mesin "ukuran kata yang tidak biasa" yang secara historis standar dibelokkan ke belakang untuk mendukung [misalnya memungkinkan untuk mesin dengan 12-bit char
untuk menjalankan kode yang mengharapkanuint32_t
untuk membungkus mod 2³²]. Bergantung pada arah yang diambil ekstensi di masa mendatang, saya juga berharap bahwa mungkin untuk mendefinisikan makro yang akan memungkinkan kode yang ditulis hari ini dapat digunakan pada kompiler hari ini di mana tipe integer default berperilaku sebagai "diharapkan", tetapi juga dapat digunakan pada kompiler masa depan di mana integer tipe akan menjadi standar berperilaku berbeda, tetapi di mana dapat memberikan perilaku yang diperlukan.
sumber
int
lebih besar dariuint16_t
, operan dari perkalian akan dipromosikan keint
dan perkalian akan dilakukan sebagaiint
perkalian, dan nilai yang dihasilkanint
akan dikonversi keint64_t
untuk inisialisasiwho_knows
.int
, namun masih menyelinap masuk (Sekali lagi mengasumsikan pemahaman saya tentang standar C benar.)Jawaban:
Seperti niat khas kode seperti ini
adalah dengan melakukan perkalian dalam 64 bit (ukuran variabel hasil disimpan), cara yang biasa untuk mendapatkan hasil (platform independent) yang benar adalah dengan melemparkan salah satu operan untuk memaksa perkalian 64-bit:
Saya belum pernah menjumpai ekstensi C yang membuat proses ini otomatis.
sumber
uint32_t
atau menggunakan makro yang didefinisikan sebagai salah satu#define UMUL1616to16(x,y)((uint16_t)((uint16_t)(x)*(uint16_t)(y)))
atau#define UMUL1616to16(x,y)((uint16_t)((uint32_t)(x)*(uint16_t)(y)))
tergantung pada ukuranint
) melainkan apakah ada standar apa pun yang muncul untuk bagaimana menangani hal-hal seperti itu secara bermanfaat daripada mendefinisikan makro saya sendiri.(ushort1*ushort2) & 65535u
adalah untuk melakukan mod-65536 aritmatika untuk semua nilai operan. Membaca alasan C89, saya pikir cukup jelas bahwa sementara penulis mengakui bahwa kode tersebut mungkin gagal pada beberapa implementasi jika hasilnya melebihi 2147483647, mereka berharap implementasi tersebut menjadi semakin langka. Namun, kode tersebut terkadang gagal pada gcc modern.