Apakah ekstensi C yang penting termasuk tipe integer yang perilakunya tidak tergantung pada ukuran kata mesin


12

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 int16 bit (masih berlaku untuk banyak mikrokontroler kecil) kode ini akan menetapkan nilai 1 menggunakan perilaku yang terdefinisi dengan baik. Pada mesin di mana int64 bit, itu akan menetapkan nilai 4294836225, lagi menggunakan perilaku yang terdefinisi dengan baik. Pada mesin intyang 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_tpembungkus 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:

  1. Dengan menambahkan arahan yang akan menginstruksikan kompiler untuk memaksa tipe integer "fundamental" tertentu menjadi ukuran tertentu.

  2. 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.

  3. 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 wrap32ke intharus menghasilkan hasil jenis wrap32terlepas dari apakah intlebih besar dari 16 bit, sementara menambahkan wrap32langsung ke wrap16harus 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:

  1. 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_tada uint32_tdi kompiler di mana a uint32_ttidak akan dipromosikan, dan bayangkan bahwa lebih baik untuk kode yang mengharuskan wrap32_tgagal 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.

  2. 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 charuntuk menjalankan kode yang mengharapkanuint32_tuntuk 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.


4
@RobertHarvey Apakah Anda yakin? Seperti yang saya pahami bilangan bulat promosi , jika intlebih besar dari uint16_t, operan dari perkalian akan dipromosikan ke intdan perkalian akan dilakukan sebagai intperkalian, dan nilai yang dihasilkan intakan dikonversi ke int64_tuntuk inisialisasi who_knows.

3
@RobertHarvey Bagaimana? Dalam kode OP, tidak disebutkan int, namun masih menyelinap masuk (Sekali lagi mengasumsikan pemahaman saya tentang standar C benar.)

2
@RobertHarvey Tentu kedengarannya buruk, tetapi kecuali Anda bisa menunjukkan cara seperti itu, Anda tidak berkontribusi apa pun dengan mengatakan "nah Anda pasti melakukan sesuatu yang salah". Pertanyaannya adalah bagaimana menghindari promosi bilangan bulat, atau menyiasati efeknya!

3
@RobertHarvey: Salah satu tujuan historis Komite Standar C adalah memungkinkan hampir semua mesin memiliki "kompiler C", dan aturannya cukup spesifik sehingga kompiler C yang dikembangkan secara independen untuk setiap mesin target tertentu akan sebagian besar dipertukarkan. Ini diperumit oleh fakta bahwa orang mulai menulis kompiler C untuk banyak mesin sebelum standar disusun, dan Komite Standar tidak ingin melarang kompiler melakukan apa pun yang mungkin mengandalkan kode yang ada . Beberapa aspek yang agak mendasar dari standar ...
supercat

3
... adalah karena mereka bukan karena siapa pun mencoba merumuskan seperangkat aturan yang "masuk akal", tetapi karena Komite berusaha untuk memakukan semua hal yang oleh para penyusun yang ditulis secara independen yang sudah ada memiliki kesamaan. Sayangnya, pendekatan ini telah menyebabkan standar yang secara bersamaan terlalu samar untuk memungkinkan programmer untuk menentukan apa yang perlu dilakukan, tetapi terlalu spesifik untuk memungkinkan kompiler untuk "lakukan saja".
supercat

Jawaban:


4

Seperti niat khas kode seperti ini

uint16_t ffff16 = 0xFFFF;
int64_t who_knows = ffff16 * ffff16;

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:

uint16_t ffff16 = 0xFFFF;
int64_t i_know = (int64_t)ffff16 * ffff16;

Saya belum pernah menjumpai ekstensi C yang membuat proses ini otomatis.


1
Pertanyaan saya bukanlah bagaimana memaksakan evaluasi yang benar dari satu ekspresi aritmatika tertentu (tergantung pada jenis hasil yang diinginkan, baik melemparkan operan ke uint32_tatau 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 ukuran int) melainkan apakah ada standar apa pun yang muncul untuk bagaimana menangani hal-hal seperti itu secara bermanfaat daripada mendefinisikan makro saya sendiri.
supercat

Saya seharusnya juga menyebutkan, bahwa untuk hal-hal seperti perhitungan hashing dan checksum, tujuannya sering kali untuk mengambil hasil dan memotongnya ke ukuran operan. Maksud khas dari ekspresi seperti (ushort1*ushort2) & 65535uadalah 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.
supercat
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.