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

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

ただいまの
回答率

90.52%

  • C

    3670questions

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

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

解決済

回答 8

投稿

  • 評価
  • クリップ 0
  • VIEW 418

ishiwatari

score 1

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

#include <stdio.h>

int main( void )
{
    printf( "%d\n", 0xffffffff == -1 );

    printf( "%ld\n", ( long ) -1 );
    printf( "%ld\n", ( long ) 0xffffffff );

    return 0;
}


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

1
-1
4294967295


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

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

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

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • archiver

    2018/06/16 10:56

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

    キャンセル

  • ishiwatari

    2018/06/16 11:25

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

    キャンセル

回答 8

+4

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 12:16

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

    キャンセル

  • 2018/06/16 18:16

    日本語のCの規格書は、JIS規格
    http://www.jisc.go.jp/app/jis/general/GnrJISSearch.html
    から、規格番号 JISX3010 を検索してください。
    読みにくいPDFで読めます。

    キャンセル

checkベストアンサー

+2

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

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

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

JIS X 3010

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/16 12:13

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

    キャンセル

+2

推測の通り、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 12:20

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

    キャンセル

+1

こんにちは。

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/16 11:32

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

    キャンセル

  • 2018/06/16 12:10

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

    キャンセル

  • 2018/06/16 12:17

    分かりました。

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/16 11:31

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

    キャンセル

  • 2018/06/16 11:47

    ここの回答には登場していませんが、raccyさんがまとめた記事によると、16進リテラルの場合は符号なしを経て昇格(型の拡張)が行われるようです。
    https://qiita.com/raccy/items/e793f67f7e0ab98ef0d9

    キャンセル

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 11: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だと思ったのですが。

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

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](www.open-std.org/JTC1/SC22/WG14/www/docs/n1124.pdf)

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/06/16 19:23

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

    キャンセル

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

  • ただいまの回答率 90.52%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る

  • C

    3670questions

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