Di mana MIN
dan MAX
didefinisikan dalam C, jika ada?
Apa cara terbaik untuk menerapkan ini, secara umum dan ketik seaman mungkin? (Ekstensi kompiler / builtin untuk kompiler arus utama lebih disukai.)
sumber
Di mana MIN
dan MAX
didefinisikan dalam C, jika ada?
Apa cara terbaik untuk menerapkan ini, secara umum dan ketik seaman mungkin? (Ekstensi kompiler / builtin untuk kompiler arus utama lebih disukai.)
Di mana
MIN
danMAX
didefinisikan dalam C, jika ada?
Mereka bukan.
Apa cara terbaik untuk mengimplementasikannya, secara umum dan ketik seaman mungkin (ekstensi kompiler / builtin untuk kompiler arus utama lebih disukai).
Sebagai fungsi. Saya tidak akan menggunakan makro seperti #define MIN(X, Y) (((X) < (Y)) ? (X) : (Y))
, terutama jika Anda berencana untuk menyebarkan kode Anda. Entah menulis sendiri, gunakan sesuatu seperti standar fmax
atau fmin
, atau perbaiki makro menggunakan GCC typeof (Anda juga mendapatkan bonus typesafety) dalam ekspresi pernyataan GCC :
#define max(a,b) \
({ __typeof__ (a) _a = (a); \
__typeof__ (b) _b = (b); \
_a > _b ? _a : _b; })
Semua orang mengatakan "oh saya tahu tentang evaluasi ganda, tidak ada masalah" dan beberapa bulan kemudian, Anda akan men-debug masalah paling konyol selama berjam-jam.
Perhatikan penggunaan __typeof__
bukannya typeof
:
Jika Anda menulis file header yang harus berfungsi saat disertakan dalam program ISO C, tulislah
__typeof__
alih-alihtypeof
.
warning: expression with side-effects multiply evaluated by macro
pada titik penggunaan ...decltype
kata kunci baru MSVC ++ 2010 - tetapi meskipun demikian, Visual Studio tidak dapat melakukan pernyataan majemuk dalam makro (dandecltype
C ++ pula), yaitu({ ... })
sintaks GCC jadi saya cukup yakin itu tidak mungkin. Saya belum melihat kompiler lain mengenai masalah ini, maaf Luther: SMAX(someUpperBound, someRandomFunction())
untuk membatasi nilai acak ke batas atas. Itu adalah ide yang mengerikan, tetapi bahkan tidak berhasil, karena yangMAX
dia gunakan memiliki masalah evaluasi ganda, jadi dia berakhir dengan angka acak yang berbeda dari yang awalnya dievaluasi.MIN(x++, y++)
preprocessor akan menghasilkan kode berikut(((x++) < (y++)) ? (x++) : (y++))
. Jadi,x
dany
akan bertambah dua kali.Ini juga disediakan dalam versi GNU libc (Linux) dan FreeBSD dari sys / param.h, dan memiliki definisi yang disediakan oleh dreamlax.
Di Debian:
Di FreeBSD:
Repositori sumber ada di sini:
sumber
openSUSE/Linux 3.1.0-1.2-desktop
/gcc version 4.6.2 (SUSE Linux)
juga. :) Buruk itu tidak portabel.Ada a
std::min
danstd::max
di C ++, tapi AFAIK, tidak ada yang setara di pustaka standar C. Anda dapat mendefinisikannya sendiri dengan makro sepertiTetapi ini menyebabkan masalah jika Anda menulis sesuatu seperti
MAX(++a, ++b)
.sumber
#define MIN(A, B) ((A < B) ? A : B)
itu bukan cara yang fleksibel, mengapa ???#define MULT(x, y) x * y
. KemudianMULT(a + b, a + b)
mengembang kea + b * a + b
, yang mem-parsinga + (b * a) + b
karena didahulukan. Bukan itu yang dimaksudkan oleh programmer.Hindari ekstensi kompiler non-standar dan implementasikan sebagai makro yang benar-benar aman di C standar murni (ISO 9899: 2011).
Larutan
Pemakaian
Penjelasan
Max makro menciptakan makro lain berdasarkan
type
parameter. Makro kontrol ini, jika diimplementasikan untuk jenis yang diberikan, digunakan untuk memeriksa bahwa kedua parameter dari jenis yang benar. Jikatype
tidak didukung, akan ada kesalahan kompiler.Jika salah satu x atau y bukan dari jenis yang benar, akan ada kesalahan kompilator di
ENSURE_
makro. Lebih banyak makro seperti itu dapat ditambahkan jika lebih banyak jenis yang didukung. Saya berasumsi bahwa hanya tipe aritmatika (integer, float, pointer dll) yang akan digunakan dan bukan struct atau array dll.Jika semua jenis sudah benar, makro GENERIC_MAX akan dipanggil. Tanda kurung tambahan diperlukan di sekitar setiap parameter makro, sebagai tindakan pencegahan standar yang biasa saat menulis makro C.
Lalu ada masalah biasa dengan promosi tipe implisit di C.
?:
Operator menyeimbangkan operan ke-2 dan ke-3 terhadap satu sama lain. Misalnya, hasil dariGENERIC_MAX(my_char1, my_char2)
akan menjadiint
. Untuk mencegah makro dari melakukan promosi tipe yang berpotensi berbahaya seperti itu, tipe final cast ke tipe yang dimaksud digunakan.Alasan
Kami ingin kedua parameter makro memiliki tipe yang sama. Jika salah satu dari mereka adalah tipe yang berbeda, makro tidak lagi aman, karena operator seperti
?:
akan menghasilkan promosi tipe implisit. Dan karena itu terjadi, kita juga selalu perlu mengembalikan hasil akhir ke jenis yang dimaksud seperti dijelaskan di atas.Makro dengan hanya satu parameter bisa ditulis dengan cara yang lebih sederhana. Tetapi dengan 2 atau lebih parameter, ada kebutuhan untuk memasukkan parameter tipe tambahan. Karena sesuatu seperti ini sayangnya tidak mungkin:
Masalahnya adalah bahwa jika makro di atas disebut
MAX(1, 2)
dengan duaint
, makro masih akan mencoba untuk memperluas semua skenario yang mungkin dari_Generic
daftar asosiasi. JadiENSURE_float
makro juga akan diperluas, meskipun tidak relevan untuk ituint
. Dan karena makro itu sengaja hanya berisifloat
tipe, kode tidak akan dikompilasi.Untuk mengatasi ini, saya membuat nama makro selama fase pra-prosesor sebagai gantinya, dengan operator ##, sehingga tidak ada makro yang diperluas secara tidak sengaja.
Contohnya
sumber
GENERIC_MAX
makro adalah ide yang buruk dengan cara, Anda hanya harus mencobaGENERIC_MAX(var++, 7)
untuk mencari tahu mengapa :-) Saat ini (terutama dengan berat mengoptimalkan / compiler inlining), makro harus cukup banyak diturunkan ke bentuk yang sederhana saja. Fungsi seperti lebih baik sebagai fungsi dan yang nilai-kelompok lebih baik sebagai enumerasi.Saya tidak berpikir bahwa itu adalah makro standar. Sudah ada fungsi standar untuk floating point,
fmax
danfmin
(danfmaxf
untuk float, danfmaxl
untuk double panjang).Anda dapat menerapkannya sebagai makro selama Anda mengetahui masalah efek samping / evaluasi ganda.
Dalam kebanyakan kasus, Anda dapat menyerahkannya ke kompiler untuk menentukan apa yang Anda coba lakukan dan mengoptimalkannya sebaik mungkin. Sementara ini menyebabkan masalah ketika digunakan seperti
MAX(i++, j++)
, saya ragu ada banyak kebutuhan dalam memeriksa nilai maksimum yang meningkat dalam sekali jalan. Selisih lebih dulu, lalu periksa.sumber
Ini adalah jawaban yang terlambat, karena perkembangan yang cukup baru. Karena OP menerima jawaban yang bergantung pada ekstensi GCC (dan dentang) yang tidak portabel
typeof
- atau__typeof__
untuk 'bersih' ISO C - ada solusi yang lebih baik tersedia pada gcc-4.9 .Manfaat nyata dari ekstensi ini adalah bahwa setiap argumen makro hanya diperluas satu kali, tidak seperti
__typeof__
solusinya.__auto_type
adalah bentuk terbatas C ++ 11'sauto
. Tidak dapat (atau tidak?) Digunakan dalam kode C ++, meskipun tidak ada alasan yang baik untuk tidak menggunakan kemampuan inferensi tipe superiorauto
saat menggunakan C ++ 11.Yang mengatakan, saya berasumsi tidak ada masalah menggunakan sintaks ini ketika makro termasuk dalam ruang
extern "C" { ... }
lingkup; misalnya, dari header C. AFAIK, ekstensi ini belum menemukan info caranyasumber
clang
mulai mendukung__auto_type
sekitar 2016 (lihat tambalan ).c-preprocessor
tag. Suatu fungsi tidak dijamin akan digarisbawahi bahkan dengan kata kunci tersebut, kecuali menggunakan sesuatu seperti__always_inline__
atribut gcc .Saya menulis versi ini yang berfungsi untuk MSVC, GCC, C, dan C ++.
sumber
Jika Anda membutuhkan min / maks untuk menghindari cabang yang mahal, Anda sebaiknya tidak menggunakan operator ternary, karena akan dikompilasi menjadi lompatan. Tautan di bawah ini menjelaskan metode yang berguna untuk menerapkan fungsi min / max tanpa bercabang.
http://graphics.stanford.edu/~seander/bithacks.html#IntegerMinOrMax
sumber
@ David Titarenco memakukannya di sini , tapi biarkan saya setidaknya membersihkannya sedikit agar terlihat bagus, dan tunjukkan keduanya
min()
danmax()
bersama - sama untuk membuat menyalin dan menempel dari sini lebih mudah. :)Pembaruan 25 April 2020: Saya juga menambahkan Bagian 3 untuk menunjukkan bagaimana hal ini akan dilakukan dengan template C ++, sebagai perbandingan yang berharga bagi mereka yang belajar C dan C ++, atau beralih dari satu ke yang lain. Saya telah melakukan yang terbaik untuk menjadi teliti dan faktual dan benar untuk menjadikan jawaban ini sebagai referensi kanonis yang dapat saya kembalikan lagi dan lagi, dan saya harap Anda merasa ini sama bermanfaatnya dengan saya.
1. Cara makro C lama:
Teknik ini umumnya digunakan, dihormati oleh mereka yang tahu cara menggunakannya dengan benar, cara "de facto" dalam melakukan sesuatu, dan baik untuk digunakan jika digunakan dengan benar, tetapi buggy (pikirkan: efek samping evaluasi ganda ) jika Anda pernah lulus ekspresi termasuk penugasan variabel untuk membandingkan:
2. Cara " ekspresi pernyataan " gcc yang baru dan lebih baik:
Teknik ini menghindari efek samping dan bug "evaluasi-ganda" di atas, dan karenanya dianggap sebagai cara GCC C yang superior, lebih aman, dan "lebih modern" untuk melakukan ini. Harapkan itu bekerja dengan kompiler gcc dan dentang, karena dentang, secara desain, kompatibel dengan gcc (lihat catatan dentang di bagian bawah jawaban ini).
TETAPI: Tetap berhati-hati terhadap efek " variabel shadowing ", karena ekspresi pernyataan tampaknya digarisbawahi dan karena itu TIDAK memiliki cakupan variabel lokal mereka sendiri!
Perhatikan bahwa dalam ekspresi pernyataan gcc, ekspresi terakhir dalam blok kode adalah apa yang "dikembalikan" dari ekspresi, seolah-olah itu dikembalikan dari fungsi. Dokumentasi GCC mengatakannya seperti ini:
3. Cara templat C ++:
C ++ Catatan: jika menggunakan C ++, templat mungkin direkomendasikan untuk jenis konstruk ini sebagai gantinya, tetapi saya pribadi tidak menyukai templat dan mungkin akan menggunakan salah satu konstruk di atas dalam C ++, karena saya sering menggunakan dan lebih suka gaya C pada embedded C ++ juga.
Bagian ini ditambahkan 25 April 2020:
Saya telah melakukan satu ton C ++ beberapa bulan terakhir, dan tekanan untuk lebih memilih template daripada makro, jika mampu, di komunitas C ++ cukup kuat. Sebagai hasilnya, saya menjadi lebih baik dalam menggunakan templat, dan ingin memasukkan versi templat C ++ di sini untuk kelengkapan dan menjadikannya jawaban yang lebih kanonik dan menyeluruh.
Inilah versi templat fungsi dasar
max()
danmin()
mungkin terlihat seperti di C ++:Lakukan bacaan tambahan tentang templat C ++ di sini: Wikipedia: Templat (C ++) .
Namun, keduanya
max()
danmin()
sudah menjadi bagian dari pustaka standar C ++, di<algorithm>
header (#include <algorithm>
). Di pustaka standar C ++ mereka didefinisikan sedikit berbeda dari yang saya miliki di atas. Prototipe default untukstd::max<>()
danstd::min<>()
, misalnya, dalam C ++ 14, melihat prototipe mereka di tautan cplusplus.com tepat di atas, adalah:Perhatikan bahwa kata kunci
typename
adalah alias untukclass
(sehingga penggunaannya identik apakah Anda katakan<typename T>
atau<class T>
), karena itu kemudian mengakui setelah penemuan C ++ template, bahwa jenis template mungkin tipe biasa (int
,float
, dll) bukan hanya tipe kelas.Di sini Anda dapat melihat bahwa kedua jenis input, serta jenis kembali, adalah
const T&
, yang berarti "referensi konstan untuk mengetikT
". Ini berarti parameter input dan nilai balik diteruskan dengan referensi alih-alih lewat nilai . Ini seperti melewati pointer, dan lebih efisien untuk tipe besar, seperti objek kelas. Bagianconstexpr
dari fungsi memodifikasi fungsi itu sendiri dan menunjukkan bahwa fungsi tersebut harus mampu dievaluasi pada waktu kompilasi (setidaknya jika diberikanconstexpr
parameter input), tetapi jika tidak dapat dievaluasi pada waktu kompilasi, maka defaultnya kembali ke evaluasi run-time, seperti fungsi normal lainnya.Aspek waktu kompilasi dari
constexpr
fungsi C ++ membuatnya menjadi semacam makro-C, di mana jika evaluasi waktu kompilasi dimungkinkan untuk suatuconstexpr
fungsi, itu akan dilakukan pada waktu kompilasi, sama seperti substitusi makroMIN()
atauMAX()
mungkin sepenuhnya dievaluasi pada waktu kompilasi dalam C atau C ++ juga. Untuk referensi tambahan untuk info templat C ++ ini, lihat di bawah.Referensi:
Dentang catatan dari Wikipedia :
sumber
Ada baiknya menunjukkan saya pikir jika Anda mendefinisikan
min
danmax
dengan tersier sepertikemudian untuk mendapatkan hasil yang sama untuk kasus khusus
fmin(-0.0,0.0)
danfmax(-0.0,0.0)
Anda perlu menukar argumensumber
fmin(3.0,NaN)==fmin(NaN,3.0)==fmax(3.0,NaN)==fmax(NaN,3.0)==3.0
Sepertinya makro
Windef.h
(a la#include <windows.h>
) memilikimax
danmin
(huruf kecil), yang juga mengalami kesulitan "evaluasi ganda", tetapi mereka ada di sana untuk mereka yang tidak ingin memutar ulang sendiri :)sumber
Saya tahu orang itu berkata "C" ... Tetapi jika Anda memiliki kesempatan, gunakan templat C ++:
Ketik aman, dan tidak ada masalah dengan ++ yang disebutkan dalam komentar lain.
sumber
Maksimal dua bilangan bulat
a
danb
adalah(int)(0.5((a+b)+abs(a-b)))
. Ini juga dapat bekerja dengan(double)
danfabs(a-b)
untuk ganda (serupa untuk pelampung)sumber
Cara paling sederhana adalah mendefinisikannya sebagai fungsi global dalam suatu
.h
file, dan menyebutnya kapan saja Anda inginkan, jika program Anda bersifat modular dengan banyak file. Jika tidak,double MIN(a,b){return (a<b?a:b)}
adalah cara paling sederhana.sumber