Saya menulis aplikasi dalam c untuk STM32F105, menggunakan gcc.
Di masa lalu (dengan proyek-proyek sederhana), saya selalu didefinisikan sebagai variabel char
, int
, unsigned int
, dan sebagainya.
Saya melihat bahwa itu adalah umum untuk menggunakan jenis didefinisikan dalam stdint.h, seperti int8_t
, uint8_t
, uint32_t
, dll Hal ini benar dalam beberapa API yang saya gunakan, dan juga di perpustakaan ARM CMSIS dari ST.
Saya percaya bahwa saya mengerti mengapa kita harus melakukannya; untuk memungkinkan kompiler mengoptimalkan ruang memori dengan lebih baik. Saya berharap mungkin ada alasan tambahan.
Namun, karena aturan promosi bilangan bulat c, saya terus berjalan melawan peringatan konversi setiap kali saya mencoba menambahkan dua nilai, melakukan operasi bitwise, dll. Peringatan itu berbunyi seperti conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]
. Masalahnya dibahas di sini dan di sini .
Itu tidak terjadi ketika menggunakan variabel dinyatakan sebagai int
atau unsigned int
.
Untuk memberikan beberapa contoh, berikan ini:
uint16_t value16;
uint8_t value8;
Saya harus mengubah ini:
value16 <<= 8;
value8 += 2;
untuk ini:
value16 = (uint16_t)(value16 << 8);
value8 = (uint8_t)(value8 + 2);
Itu jelek, tapi aku bisa melakukannya jika perlu. Ini pertanyaan saya:
Apakah ada kasus di mana konversi dari tidak ditandatangani menjadi ditandatangani dan kembali ke tidak ditandatangani akan membuat hasilnya salah?
Apakah ada alasan besar lainnya untuk / menentang penggunaan tipe integer stdint.h?
Berdasarkan jawaban yang saya terima, sepertinya jenis stdint.h umumnya lebih disukai, meskipun c mengkonversi uint
ke int
dan kembali. Ini mengarah ke pertanyaan yang lebih besar:
- Saya bisa mencegah peringatan kompiler dengan menggunakan typecasting (mis
value16 = (uint16_t)(value16 << 8);
.). Apakah saya hanya menyembunyikan masalahnya? Apakah ada cara yang lebih baik untuk melakukannya?
8u
dan2u
.value8 += 2u;
danvalue8 = value8 + 2u;
, tetapi saya mendapatkan peringatan yang sama.Jawaban:
Kompiler yang memenuhi standar di mana
int
saja dari 17 hingga 32 bit dapat melakukan apa pun yang diinginkan dengan kode berikut:Sebuah implementasi yang ingin melakukannya dapat secara sah menghasilkan program yang tidak akan melakukan apa pun kecuali mengeluarkan string "Fred" berulang kali pada setiap port pin menggunakan setiap protokol yang bisa dibayangkan. Peluang suatu program untuk porting ke implementasi yang akan melakukan hal seperti itu sangat rendah, tetapi secara teori dimungkinkan. Jika ingin menulis kode di atas sehingga dijamin tidak terlibat dalam Perilaku Tidak Terdefinisi, perlu dituliskan ekspresi yang terakhir sebagai
(uint32_t)x*x
atau1u*x*x
. Pada kompiler di manaint
antara 17 dan 31 bit, ekspresi terakhir akan memotong bit atas, tetapi tidak akan terlibat dalam Perilaku Tidak Terdefinisi.Saya pikir peringatan gcc mungkin mencoba menyarankan bahwa kode yang ditulis tidak sepenuhnya portabel 100%. Ada saat-saat ketika kode benar-benar harus ditulis untuk menghindari perilaku yang tidak terdefinisi pada beberapa implementasi, tetapi dalam banyak kasus yang lain orang hanya perlu mengetahui bahwa kode tersebut tidak mungkin digunakan pada implementasi yang akan melakukan hal-hal yang terlalu mengganggu.
Perhatikan bahwa menggunakan jenis suka
int
danshort
mungkin menghilangkan beberapa peringatan dan memperbaiki beberapa masalah, tetapi kemungkinan akan membuat yang lain. Interaksi antara tipe likeuint16_t
dan aturan integer-promotion C memang menjengkelkan, tetapi tipe seperti itu mungkin masih lebih baik daripada alternatif apa pun.sumber
1) Jika Anda baru saja beralih dari bilangan bulat ditandatangani ke bilangan bulat dengan panjang yang sama bolak-balik, tanpa operasi di antaranya, Anda akan mendapatkan hasil yang sama setiap kali, jadi tidak ada masalah di sini. Tetapi berbagai operasi logis dan aritmetika bertindak berbeda pada operan yang ditandatangani dan tidak ditandatangani.
2) Alasan utama untuk menggunakan
stdint.h
jenis adalah bahwa ukuran bit dari jenis tersebut didefinisikan dan sama di semua platform, yang tidak benar untukint
,long
dll, sertachar
tidak memiliki tanda standar, dapat ditandatangani atau tidak ditandatangani oleh standar. Itu membuat lebih mudah untuk memanipulasi data mengetahui ukuran yang tepat tanpa menggunakan pemeriksaan dan asumsi tambahan.sumber
int32_t
danuint32_t
sama di semua platform di mana mereka didefinisikan . Jika prosesor tidak memiliki jenis perangkat keras yang sama persis , jenis ini tidak ditentukan. Karenanya keuntungan dariint
dll. Dan, mungkin,int_least32_t
dll.Karena Eugene's # 2 mungkin adalah poin yang paling penting, saya hanya ingin menambahkan bahwa ini adalah sebuah nasihat
Jack Ganssle juga tampaknya menjadi pendukung aturan itu: http://www.ganssle.com/tem/tem265.html
sumber
uint32_t
.Cara mudah untuk menghilangkan peringatan adalah dengan menghindari penggunaan -Wversi di GCC. Saya pikir Anda harus mengaktifkan opsi ini secara manual, tetapi jika tidak, Anda dapat menggunakan -Wno-konversi untuk menonaktifkannya. Anda dapat mengaktifkan peringatan untuk konversi presisi tanda dan FP melalui opsi lain , jika Anda menginginkannya.
Peringatan-konversi hampir selalu positif palsu, yang mungkin mengapa tidak -Wextra mengaktifkannya secara default. Pertanyaan Stack Overflow memiliki banyak saran untuk set opsi yang baik. Berdasarkan pengalaman saya sendiri, ini adalah tempat yang baik untuk memulai:
Tambahkan lebih banyak jika Anda membutuhkannya, tetapi kemungkinan besar tidak.
Jika Anda harus menjaga -Konversi, Anda dapat mempersingkat kode sedikit dengan hanya mengetikkan operan angka:
Itu tidak mudah dibaca tanpa penyorotan sintaks.
sumber
dalam proyek perangkat lunak apa pun, sangat penting untuk menggunakan definisi tipe portabel. (bahkan versi berikutnya dari kompiler yang sama perlu pertimbangan ini.) Contoh yang baik, beberapa tahun yang lalu saya mengerjakan proyek di mana kompiler saat ini mendefinisikan 'int' sebagai 8bits. Versi selanjutnya dari kompiler mendefinisikan 'int' sebagai 16bits. Karena kami tidak menggunakan definisi portabel untuk 'int', ram (secara efektif) menggandakan ukuran dan banyak urutan kode yang bergantung pada int 8bit gagal. Penggunaan definisi tipe portabel akan menghindari masalah (ratusan jam kerja untuk memperbaiki).
sumber
int
untuk merujuk pada tipe 8 bit. Bahkan jika kompiler non-C seperti CCS melakukannya, kode yang masuk akal harus menggunakan salah satuchar
atau tipe yang diketikkan untuk 8 bit, dan tipe yang diketikkan (tidak "panjang") untuk 16 bit. Di sisi lain, kode porting dari sesuatu seperti CCS ke kompiler nyata cenderung bermasalah bahkan jika menggunakan typedef yang tepat, karena kompiler seperti itu sering "tidak biasa" dengan cara lain.Iya nih. Integer bertanda n-bit dapat mewakili kira-kira setengah dari angka non-negatif sebagai integer n-bit yang tidak ditandatangani, dan bergantung pada karakteristik overflow adalah perilaku yang tidak terdefinisi sehingga segala sesuatu dapat terjadi. Sebagian besar prosesor saat ini dan yang lalu menggunakan dua komplemen sehingga banyak operasi kebetulan melakukan hal yang sama pada tipe integral yang ditandatangani dan tidak ditandatangani, tetapi meskipun demikian tidak semua operasi akan menghasilkan hasil yang identik sedikit-bijaksana. Anda benar-benar meminta masalah tambahan nanti ketika Anda tidak tahu mengapa kode Anda tidak berfungsi sebagaimana dimaksud.
Walaupun int dan unsigned memiliki ukuran implementasi yang ditentukan, ini sering dipilih "secara cerdas" oleh implementasi baik untuk ukuran atau alasan kecepatan. Saya biasanya tetap pada ini kecuali saya memiliki alasan yang baik untuk melakukan yang sebaliknya. Demikian juga, ketika mempertimbangkan apakah akan menggunakan int atau tidak, saya biasanya lebih suka int kecuali saya memiliki alasan yang baik untuk melakukannya.
Dalam kasus di mana saya benar-benar membutuhkan kontrol yang lebih baik atas ukuran atau keabsahan suatu jenis, saya biasanya akan lebih suka menggunakan typedef yang ditentukan sistem (size_t, intmax_t, dll.) Atau membuat typedef saya sendiri yang menunjukkan fungsi yang diberikan ketik (prng_int, adc_int, dll.)
sumber
Seringkali kode ini digunakan pada ARM jempol dan AVR (dan x86, powerPC dan arsitektur lainnya), dan 16 atau 32 bit dapat lebih efisien (kedua cara: flash dan siklus) pada STM32 ARM bahkan untuk variabel yang sesuai dalam 8 bit ( 8 bit lebih efisien pada AVR) . Namun jika SRAM hampir penuh, kembali ke 8 bit untuk vars global bisa masuk akal (tetapi tidak untuk vars lokal). Untuk portabilitas dan perawatan (terutama untuk vars 8 bit), ia memiliki kelebihan (tanpa kerugian) untuk menentukan ukuran MINIMUM yang sesuai , daripada ukuran yang tepat dan mengetikkan di satu tempat. uint_least8_t) selama porting / waktu pembuatan, misalnya:
Perpustakaan GNU sedikit membantu, tetapi biasanya typedef masuk akal:
// tapi uint_fast8_t untuk KEDUA ketika SRAM bukan masalah.
sumber