C言語のキャスト演算子を教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Verse(UEFN), Assembler (x64,aarch64).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
目次
キャスト演算子【明示的な型変換】
キャスト演算子とは,データ型を別のデータ型に明示的に変換する演算子のことです.
キャスト演算子の書式は,以下のようになります.
1 |
(型名) 変数または定数 |
例えば,int型からlong型にキャスト演算子で型変換する(キャストする)コードは以下になります.
1 2 |
int x = 123; long a = (long) x; |
また,ポインタ型も変換することができます.
int型の変数xのアドレスを指すint型のポインタ変数yを,long型のポインタにキャストして,long型のポインタ変数aに代入するコードは以下になります.
1 2 3 |
int x = 123; int *y = &x; long *a = (long *) y; |
ポインタを学びたいあなたはこちらからどうぞ.
キャスト演算子でオーバーフローの回避
各々のデータ型には扱える値の範囲があり,この範囲を越える値の代入があった場合,データはオーバーフロー(算術オーバーフロー)します.
しかし,C言語では以下のコードのように,オーバーフローしてもエラーメッセージは出力されず,演算は何事もなかったように実行します.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { char c; short data; c = 400; printf("c = %d\n", c); data = 65536; printf("data = %d\n", data); return 0; } |
実行結果は以下になります.
GCCでは以下の警告が発生して,オーバーフローを検知できます(2~8行目).
c = -112,data = 0と間違った計算結果になっていますね(10~11行目).
1 2 3 4 5 6 7 8 9 10 11 |
$ gcc overflow.c overflow.c: In function 'main': overflow.c:12:7: warning: overflow in conversion from 'int' to 'char' changes value from '400' to '-112' [-Woverflow] 12 | c = 400; | ^~~ overflow.c:15:10: warning: overflow in conversion from 'int' to 'short int' changes value from '65536' to '0' [-Woverflow] 15 | data = 65536; | ^~~~~ $ a.out c = -112 data = 0 |
さらに,オーバーフローでは演算の途中でも生じます.
例えば,2147483647+1の計算の答えは2147483648です.
2147483647はint型で,2147483648はlong型で入ります.
※ちなみに2147483647(=2^31-1)はint型の最大値INT_MAXです.
しかし,以下のコードの「a = x + y;」の部分は正常に動作しません.
1 2 3 4 5 6 |
int x, y; long a; x = 2147483647; y = 1; a = x + y; |
つまり,「int型 + int型 = long型」とはなりません.
int型 + int型の計算はint型で行われます.
2147483647+1はこの時点でオーバーフローしています.
int型では2147483648という整数は扱えません.
既にオーバーフローした値をlong型変数に代入した結果,間違った値(実行結果は-2147483648と負の値)になります.
演算して式の値を求めることと,代入することはC言語では別の処理になっていることを理解しておかないと,このようなミスをしてしまいます.
この問題はキャスト演算子を利用することで解決できます.
オーバーフローを発生するコードと,キャスト演算子を利用してオーバーフローを回避するコードは以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <limits.h> int main(void) { int x, y; long a; x = INT_MAX; y = 1; a = x + y; printf("%d + %d = %ld\n", x, y, a); a = (long) x + (long) y; printf("%d + %d = %ld\n", x, y, a); return 0; } |
実行結果は以下になります.
3行目ではオーバーフローして「2147483647 + 1 = -2147483648」と間違った計算結果になっていますが,4行目ではオーバーフローを回避することで「2147483647 + 1 = 2147483648」と正しい計算結果になっています.
1 2 3 4 |
$ gcc overflow_cast.c $ a.out 2147483647 + 1 = -2147483648 2147483647 + 1 = 2147483648 |
処理系依存の整数と浮動小数点数の最小値と最大値,オーバーフローについて詳しく知りたいあなたはこちらからどうぞ.
キャスト演算子で汎用ポインタ型(void *)からデータ型ポインタに変換
キャスト演算子で汎用ポインタ型(void *)からデータ型ポインタに変換します.
よくmalloc関数等で動的にメモリ確保する場合,その返り値の型は汎用ポインタ(void *)なので,データ型ポインタへ明示的にキャストします.
以下に汎用ポインタ型からint型のポインタに変換するコード例を示します.
※「(int *)」と明示的に型変換しなくても動作しますが,以下のようにコードを書くことは定石ですので,覚えておきましょう.
1 2 3 4 5 6 |
int *p; if ((p = (int *) malloc(sizeof(int) * 10)) == NULL) { fprintf(stderr, "Error: cannot allocate memory %zu bytes.\n", sizeof(int) * 10); exit(1); } |
キャスト演算子を利用して動的にメモリ確保する方法を知りたいあなたは,malloc/calloc/realloc/alloca関数と可変長配列で動的にメモリ確保を読みましょう.
暗黙的な型変換
暗黙的な型変換は,異なるデータ型同士で演算する際に発生します.
例えば,int型とlong型で演算する時,int型はより大きな型であるlong型に暗黙的な型変換が発生します.
データ型の暗黙的な型変換は以下の関係になります.
signed char<=short int<=int<=long int<=long long int <= float <= double
int型とlong型の演算で暗黙的な型変換
int型とlong型の演算で暗黙的な型変換が発生するコードは以下になります.
このコードは,overflow_cast.cをベースにint型の変数yをlong型に定義を変更したものです.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { int x; long a, y; x = 2147483647; y = 1; a = x + y; printf("%d + %ld = %ld\n", x, y, a); return 0; } |
実行結果は以下になります.
implicit.cの14行目の「a = x + y;」の演算で,xはint型ですがyはlong型なので,暗黙的な型変換でxはlong型に変換されます.
これにより,オーバーフローが発生せずに,x + yの正しい計算結果がaに格納されます.
1 2 3 |
$ gcc implicit.c $ a.out 2147483647 + 1 = 2147483648 |
符号ありのsignedは符号なしのunsignedに暗黙的な型変換
また,符号ありのsignedは符号なしのunsignedに暗黙的な型変換が発生しますので注意して下さい.
以下のコードでよくある間違いを見てましょう.
int型の変数iの値は12行目のfor文でn(値は10)に初期化され,i * n >= 0が成立する場合,for文の中身を実行し,最後にiをデクリメントします.
一見すると,正しく動作するように見えますよね.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> int main(void) { unsigned int n = 10; int i; for (i = n; i * n >= 0; i--) { printf("i = %d, n = %u, i * n = %u\n", i, n, i * n); } return 0; } |
それでは,実際に実行してみましょう.
iの値が負になっても実行が終了しません.これはi * nの演算でiは符号ありのint型ですがnは符号なしのint型なので,暗黙的な型変換で符号なしのint型になってしまったからです.
この問題を回避する方法は主に以下の2つです.
- nを符号ありのint型で定義:9行目を「int n = 10;に変換」
- キャストで明示的に符号なしのint型に変換:12行目の判定を「(int) (i * n) >= 0」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
$ gcc implicit_sign.c $ a.out i = 10, n = 10, i * n = 100 i = 9, n = 10, i * n = 90 i = 8, n = 10, i * n = 80 i = 7, n = 10, i * n = 70 i = 6, n = 10, i * n = 60 i = 5, n = 10, i * n = 50 i = 4, n = 10, i * n = 40 i = 3, n = 10, i * n = 30 i = 2, n = 10, i * n = 20 i = 1, n = 10, i * n = 10 i = 0, n = 10, i * n = 0 i = -1, n = 10, i * n = 4294967286 i = -2, n = 10, i * n = 4294967276 i = -3, n = 10, i * n = 4294967266 ... |
まとめ
C言語のキャスト演算子による明示的な型変換を紹介しました.
キャスト演算子を利用することで,オーバーフローを回避できることや,汎用ポインタ型(void *)からデータ型ポインタに変換できることがわかりました.
また,キャスト演算子を利用しない暗黙的な型変換も紹介しました.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!