質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

Q&A

解決済

8回答

9023閲覧

C言語 16進数リテラルは符号拡張されない?

ishiwatari

総合スコア9

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

0グッド

0クリップ

投稿2018/06/16 01:46

C言語を勉強中の者です。
次のようなサンプルコードを書いてみたのですが、予想と違う結果になりました。

C

1#include <stdio.h> 2 3int main( void ) 4{ 5 printf( "%d\n", 0xffffffff == -1 ); 6 7 printf( "%ld\n", ( long ) -1 ); 8 printf( "%ld\n", ( long ) 0xffffffff ); 9 10 return 0; 11}

GCCでコンパイルした際の実行結果です。

1 -1 4294967295

( long ) 0xffffffffが-1ではなく、4294967295になる理由が分かりません。
0xffffffffはint型でMSBが1なので、負と解釈され、符号拡張されると思ったのですが、ゼロ拡張されています。

考えたのは、16進数リテラルはint型ではなく、unsigned int型なのではないかということです。もしくは、基本的にはint型だが、何らかの理由でunsigned int型に型変換されたのではないかということです。unsigned int型ならば、ゼロ拡張されるのも納得がいきます。

この辺のことをご存じの方がいましたら、仕様ではこうなっている等の理由を教えていただければ助かります。よろしくお願いします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

archiver

2018/06/16 01:56

プログラムの実行環境は何になりますか?
ishiwatari

2018/06/16 02:25

64bitマシン、64bitOSでsizeof(int)=4, sizeof(long)=8です。
guest

回答8

0

16進数リテラルは、ビットパターンじゃなくて整数です。2進数も8進数も。
接尾文字が付いていない場合、intで表現できない場合はunsiged intに、それでも表現できないときはlong intに、それでも表現できないときはunsigned long intに。以下同様にlong long int unsigned long long intになります。

接尾文字が付いていない10進リテラルは、intlong intlong long intのどれかになります。

投稿2018/06/16 02:56

otn

総合スコア84505

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 03:16

回答ありがとうございます。 16進数リテラルをビットパターンとして見てましたが、正の整数と見ることにします。
guest

0

推測の通り、int型が32bitである環境での0xffffffffと言う16進数表記はunsigened int型になります。

Cの整数リテラルは接尾辞の有無にかかわらず、指定の型に収まらない範囲であれば自動的により大きな範囲の型とみなされます(一番大きい型でも入らなければコンパイルエラーになります)。例えば、int型が32bitでINT_MAXが2147483647、long型が64bitである環境の場合、2147483648というリテラルはlong型の2147483648とみなされ、別のリテラルで書けば2147483648Lと全く同じになると言うことです。

この「より大きな範囲の型」についてどのような型が選ばれるかも規則があります。それは、接尾辞と表現方法の組合せによって、なり得る型のリストが存在し、そのリストから表現可能なうち一番最初の物が選ばれます。そして、そのリストはCの仕様書に書いてあります。

6.4.4.1 5 - N1750
※ N1570はC11仕様書の最終ドラフトで、_Alignof周りの一部を除き、C11仕様書と内容は同じです。

接頭辞と10進数表記または8進数表記/16進数表記の組合せで、可能な型が変わってきます。接頭辞がない16進数の場合は

int unsigned int long int unsigned long int long long int unsigned long long int

が対象となり、この順番に可能な表現が選ばれます。0xffffffffは4294967296ですが、一般的なint型が32bitでINT_MAXが2147483647な環境では、int型にはなりません。その次に大きいのはunsigned int型ですがこちらはUINT_MAXが4294967296ですので、範囲内であるとして、unsigned int型になります。つまり、実質的に0xffffffffUと書いているような物と言うことです。

表を見るとわかりますが、Uが無い10進数表記ではunsignedの型になることはなく、longlong longと大きくなっていきます。このように、進数表記によって変わると言うことに注意してください。

投稿2018/06/16 03:04

raccy

総合スコア21735

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 03:20

回答ありがとうございます。 0xffffffffは正の整数と見ると、INT_MAXを超えているので、unsigned intに格上げされるんですね。
guest

0

ベストアンサー

まず、C言語では負数の表現方法は「2の補数表現」だけではありません。「1の補数表現」「符号+絶対値表現」をとってもいいこととなっています。

なので、0xffffffffのような整数定数は、正の数として解釈しないと環境によって値がずれてしまうことになります。

なお、C言語の整数定数には負のものがなく-10と書いた場合には、10という定数に単項マイナス演算子をかけたものと解釈します。なので、32ビット符号付き整数の最小値は-2147483648とは書けず(2147483648が32ビット符号付き整数をはみ出して型が変わってしまう)、-2147483647 - 1と書く必要があります(参考)。

JIS X 3010

投稿2018/06/16 02:53

maisumakun

総合スコア145183

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 03:13

回答ありがとうございます。 負数の表現法は複数あることは知っていましたが、2の補数表現ばかりで考えていました。確かに、他の表現法を考慮すると、0xffffffffが-1とは限りませんね。
guest

0

こんにちは。

C言語/記法によると、接尾語のない16進数表記の数値リテラルは、その値を表現できる最もビット数の少ない型になるので、0xffffffffはunsgined型が4バイトの処理系ならunsigned int型になります。存在しないとは思いますが、もしもunsigned型が2バイトでlong型が8バイトの処理系があると0xffffffffはlong型になると思いますが、その場合も正の整数となりますね。

投稿2018/06/16 02:26

編集2018/06/16 02:27
Chironian

総合スコア23272

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 02:32

つまり、16進数リテラルは正の整数と解釈されるということでしょうか?
Chironian

2018/06/16 03:10

事実上その通りと思います。
guest

0

ざっと(斜め読みで)確認してみました。
これはCのarithmetic conversionのややこしい規定という奴です。

0xFFFFFFFFは4バイト整数では表現できない16進リテラルなので unsigned int定数と見なされます。なのでこれをlongにキャストすると正の値が保たれます。(10進定数4294967295は signed longと見なされます。)

一方 0xFFFFFFFF==-1 はunsiged intとsigned intの4バイトどうしの比較なので4バイトのままでの型変換が行われて結果として 同じ値 という判定になる、ということのようです。

詳細はこれの
6.4.4.1 Integer constants
6.3.1.8 Usual arithmetic conversions
あたりを確認ください。
www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf

投稿2018/06/16 03:22

a_saitoh

総合スコア702

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 10:23

回答ありがとうございます。
guest

0

推測ですが、リテラルに対する処理なので実行環境だけでなくコンパイル環境にも依存する可能性があると思います。

投稿2018/06/16 02:33

HogeAnimalLover

総合スコア4830

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

longが何ビットの整数になるかは、環境に依存します。

例えば、
32bit CPUの場合は32ビット(4バイト)、64bit CPUの場合は64ビット(8バイト)
64bit CPUのマシンでも、32bit版のOS(Linux, Windows等)の場合は32ビット(4バイト)
というような感じです(OSの種類、バージョン等で変わりますので、「感じ」と表現しました)。

0xffffffffは、32ビット整数なら最上位桁が1ですが、64ビット整数なら最上位桁は0です。
よって、0xffffffffは64ビット整数なら4294967295となって当然です。

投稿2018/06/16 02:04

coco_bauer

総合スコア6915

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 02:23

私の環境はint型は4バイト、long型は8バイトです。 int型の0xffffffffをlong型に型変換したとき、0x????????ffffffffの?の部分に0を埋めるか、1を埋めるかは変換後の型(この場合はlong型)ではなく、変換前の型(この場合はint型)に依存すると思うのですが、どうですか? MSBはlong型では確かに0ですが、int型では1ですよね?そして、int型はsignedなので、拡張された上位ビット(?の部分)に埋められるのは、MSBが0なら0、1なら1だと思ったのですが。
guest

0

コンパイル環境が64bit環境であれば、longのサイズは8バイト長になります。
この場合、0x00000000ffffffffとなり、MSBに1が立たない状態になるため、負数とはならず、正の数として処理されます。

投稿2018/06/16 02:03

archiver

総合スコア1557

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ishiwatari

2018/06/16 02:31

coco_bauerさんへの回答と同じなのですが、拡張された上位のビットは0ではく、1が埋められると思ったのですが。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問