Saya memiliki program sederhana di bawah ini:
#include <stdio.h>
#define INT32_MIN (-0x80000000)
int main(void)
{
long long bal = 0;
if(bal < INT32_MIN )
{
printf("Failed!!!");
}
else
{
printf("Success!!!");
}
return 0;
}
Kondisinya if(bal < INT32_MIN )
selalu benar. Bagaimana itu mungkin?
Ini berfungsi dengan baik jika saya mengubah makro ke:
#define INT32_MIN (-2147483648L)
Adakah yang bisa menunjukkan masalah ini?
c
signed
numeric-limits
numeric-conversion
Jayesh Bhoi
sumber
sumber
CHAR_BIT * sizeof(int)
?-0x80000000
, tetapi salah untuk-0x80000000L
,-2147483648
dan-2147483648L
(gcc 4.1.2), jadi pertanyaannya adalah: mengapa int literal-0x80000000
berbeda dari int literal-2147483648
?<limits.h>
didefinisikanINT_MIN
sebagai(-2147483647 - 1)
, sekarang Anda tahu mengapa.Jawaban:
Ini cukup halus.
Setiap literal integer dalam program Anda memiliki tipe. Jenis yang dimilikinya diatur oleh tabel pada 6.4.4.1:
Jika angka literal tidak dapat masuk ke dalam
int
tipe default , itu akan mencoba tipe yang lebih besar berikutnya seperti yang ditunjukkan pada tabel di atas. Jadi untuk literal bilangan bulat desimal biasa seperti:int
long
long long
.Hex literal berperilaku berbeda! Jika literal tidak dapat masuk ke dalam tipe bertanda suka
int
, ia akan terlebih dahulu mencobaunsigned int
sebelum beralih ke mencoba tipe yang lebih besar. Lihat perbedaan pada tabel di atas.Jadi pada sistem 32 bit,
0x80000000
tipe literal Andaunsigned int
.Ini berarti bahwa Anda dapat menerapkan
-
operator unary pada literal tanpa menerapkan perilaku yang ditentukan implementasi, seperti yang Anda lakukan ketika meluap bilangan bulat yang ditandatangani. Sebaliknya, Anda akan mendapatkan nilai0x80000000
, nilai positif.bal < INT32_MIN
mengaktifkan konversi aritmatika yang biasa dan hasil ekspresi0x80000000
dipromosikan dariunsigned int
kelong long
. Nilai0x80000000
dipertahankan dan 0 kurang dari 0x80000000, karenanya hasilnya.Ketika Anda mengganti literal dengan
2147483648L
Anda menggunakan notasi desimal dan oleh karena itu kompiler tidak memilihunsigned int
, tetapi mencoba untuk memasangnya di dalam along
. Sufiks L juga mengatakan bahwa Anda menginginkanlong
jika mungkin . Akhiran L sebenarnya memiliki aturan yang sama jika Anda terus membaca tabel yang disebutkan di 6.4.4.1: jika nomor tidak sesuai dengan yang dimintalong
, yang tidak dalam kasus 32 bit, kompiler akan memberi Anda tempat dilong long
mana akan cocok dengan baik.sumber
long
sistem2147483648L
, tidak akan muat dalamlong
, sehingga menjadilong long
, maka yang-
diterapkan - atau jadi saya pikir.0x7FFFFFFF
. Coba sendiri:#include <limits.h> printf("%X\n", INT_MAX);
0x7FFFFFFF
ketika ditulis dalam kode sumber selalu angka positif, tetapiint
variabel Anda tentu saja dapat berisi angka biner mentah hingga nilai 0xFFFFFFFF.ìnt n = 0x80000000
memaksa konversi dari literal tanpa tanda tangan ke tipe yang ditandatangani. Apa yang akan terjadi tergantung pada kompiler Anda - itu adalah perilaku yang ditentukan implementasi. Dalam hal ini ia memilih untuk menunjukkan keseluruhan literal ke dalamint
, menimpa bit tanda. Pada sistem lain mungkin tidak mungkin untuk mewakili tipe dan Anda menjalankan perilaku yang tidak terdefinisi - program mungkin macet. Anda akan mendapatkan perilaku yang sama jika Anda melakukannyaint n=2147483648;
tidak terkait dengan notasi heksa sama sekali.-
diterapkan pada bilangan bulat yang tidak ditandatangani dapat diperluas sedikit. Saya selalu berasumsi (walaupun untungnya tidak pernah bergantung pada asumsi) bahwa nilai yang tidak ditandatangani akan "dipromosikan" ke nilai yang ditandatangani, atau mungkin hasilnya tidak akan ditentukan. (Jujur, itu harus menjadi kesalahan kompilasi; apa- 3u
artinya?)0x80000000
adalahunsigned
literal dengan nilai 2147483648.Menerapkan minus unary pada ini masih memberi Anda jenis unsigned dengan nilai bukan nol. (Faktanya, untuk nilai yang bukan nol
x
, nilai yang Anda dapatkan adalahUINT_MAX - x + 1
.)sumber
Literal integer ini
0x80000000
memiliki tipeunsigned int
.Menurut Standar C (6.4.4.1 Konstanta bilangan bulat)
Dan konstanta integer ini dapat diwakili oleh tipe
unsigned int
.Jadi ungkapan ini
-0x80000000
memilikiunsigned int
tipe yang sama . Selain itu memiliki nilai yang sama0x80000000
dalam representasi komplemen keduanya yang menghitung dengan cara berikutIni memiliki efek samping jika menulis misalnya
Hasilnya akan kembali
INT_MIN
.Demikian di dalam kondisi ini
ada dibandingkan
0
dengan nilai unsigned yang0x80000000
dikonversi ke tipe long int menurut aturan konversi aritmatika yang biasa.Jelaslah bahwa 0 lebih kecil dari
0x80000000
.sumber
Konstanta numerik
0x80000000
adalah tipeunsigned int
. Jika kita mengambil-0x80000000
dan melakukan 2s matematika pujian, kita dapatkan ini:Jadi
-0x80000000 == 0x80000000
. Dan membandingkan(0 < 0x80000000)
(karena0x80000000
tidak ditandatangani) adalah benar.sumber
int
s. Meskipun itu pilihan yang sangat umum, dalam implementasi yang diberikanint
mungkin lebih sempit atau lebih luas. Namun, ini adalah analisis yang tepat untuk kasus itu.-0x80000000
adalah aritmatika yang tidak ditandatangani.~0x800000000
adalah kode yang berbeda.-0x80000000
! Bahkan komplemen 2 tidak relevan dengan pertanyaan ini sepenuhnya.Suatu titik kebingungan muncul ketika berpikir bahwa itu
-
adalah bagian dari konstanta numerik.Dalam kode di bawah
0x80000000
ini adalah konstanta numerik. Jenisnya hanya menentukan itu. Ini-
diterapkan sesudahnya dan tidak mengubah jenisnya .Konstanta numerik mentah tanpa hiasan adalah positif.
Jika desimal, maka jenis ditugaskan adalah jenis pertama yang akan terus:
int
,long
,long long
.Jika konstan adalah oktal atau heksadesimal, hal itu akan jenis pertama yang memegang itu:
int
,unsigned
,long
,unsigned long
,long long
,unsigned long long
.0x80000000
, pada sistem OP mendapatkan jenisunsigned
atauunsigned long
. Apa pun itu, ini adalah tipe yang tidak ditandatangani.-0x80000000
juga beberapa nilai non-nol dan menjadi beberapa tipe yang tidak ditandatangani, ini lebih besar dari 0. Ketika kode membandingkannya dengan along long
, nilainya tidak berubah pada 2 sisi perbandingan, begitu0 < INT32_MIN
juga benar.Definisi alternatif menghindari perilaku aneh ini
Mari kita berjalan di dunia fantasi untuk sementara di mana
int
danunsigned
48-bit.Kemudian
0x80000000
cocokint
dan begitu juga tipenyaint
.-0x80000000
kemudian merupakan angka negatif dan hasil cetaknya berbeda.[Kembali ke kata sebenarnya]
Karena
0x80000000
cocok dengan beberapa jenis yang tidak ditandatangani sebelum jenis yang ditandatangani karena hanya lebih besar dari yangsome_signed_MAX
ada disome_unsigned_MAX
dalamnya, itu adalah beberapa jenis yang tidak ditandatangani.sumber
C memiliki aturan bahwa literer integer mungkin
signed
atauunsigned
tergantung pada apakah itu cocoksigned
atau tidakunsigned
(promosi integer). Pada32
mesin-bit, literalnya0x80000000
adalahunsigned
. Komplemen 2-0x80000000
ada0x80000000
pada mesin 32-bit. Oleh karena itu, perbandingannyabal < INT32_MIN
adalah antarasigned
danunsigned
sebelum perbandingan sesuai aturan Cunsigned int
akan dikonversi menjadilong long
.C11: 6.3.1.8/1:
Karena itu,
bal < INT32_MIN
selalutrue
.sumber