配列のループが1つずれて範囲外を読む・最後の要素が抜ける
添字は0から要素数-1までという境界を1つ間違えると、最後の要素が抜けたり、配列の外を読んでしまったりします。
なぜエラーが出ないのか
出力: 170
(エラーなし)- Cは何も報告しません。コンパイルも実行も正常に完了するためです
出力: 170- 実際の挙動 — 期待した結果と食い違っている箇所
見つけ方- エラーが出ないので、出力を目で確かめるか、コードを目で追うしかありません。この種の誤りが最も発見が遅れます
このエラーが出る典型パターン
パターン1
1 #include <stdio.h> 2 3 int main(void) { 4 int scores[3] = {70, 80, 90}; 5 int sum = 0; 6 for (int i = 1; i < 3; i++) { ^ 7 sum += scores[i]; 8 } 9 printf("%d\n", sum); 10 return 0; 11 }
ループをi=1から始めると、添字0の要素(70)が合計に含まれません。配列の最初の要素は添字0であることを忘れると起きるミスです。
直し方: 1 を 0 にします。
パターン2
1 #include <stdio.h> 2 3 int main(void) { 4 int prices[4] = {10, 20, 30, 40}; 5 int total = 0; 6 for (int i = 0; i < 3; i++) { ^ 7 total += prices[i]; 8 } 9 printf("%d\n", total); 10 return 0; 11 }
要素数4の配列なのに境界をi<3にすると、最後の要素(40)が合計に含まれません。有効な添字は0〜3までなので、境界には要素数そのものを使います。
直し方: 3 を 4 にします。
パターン3
1 #include <stdio.h> 2 3 int main(void) { 4 int temps[5] = {18, 20, 19, 21, 17}; 5 int count = 4; ^ 6 int sum = 0; 7 for (int i = 0; i < count; i++) { 8 sum += temps[i]; 9 } 10 printf("%d\n", sum); 11 return 0; 12 }
countを要素数5より1つ少ない4にすると、最後の要素(17)が合計から漏れます。要素数を数える変数は実際の要素数と一致させる必要があります。
直し方: 4 を 5 にします。
パターン4
1 #include <stdio.h> 2 3 int main(void) { 4 int nums[4] = {5, 10, 15, 20}; 5 int sum = 0; 6 for (int i = 1; i < 4; i++) { ^ 7 sum += nums[i]; 8 } 9 printf("%d\n", sum); 10 return 0; 11 }
i=1から始めると添字0の要素(5)が合計から漏れます。
直し方: 1 を 0 にします。
パターン5
1 #include <stdio.h> 2 3 int main(void) { 4 int vals[3] = {100, 200, 300}; 5 int total = 0; 6 for (int i = 0; i < 2; i++) { ^ 7 total += vals[i]; 8 } 9 printf("%d\n", total); 10 return 0; 11 }
境界をi<2にすると最後の要素(300)が合計に含まれません。
直し方: 2 を 3 にします。
よくある誤解
「要素数と同じ数まで回せば全部読める」という直感はCでは通用しません。要素数5の配列で有効な添字は0〜4で、<=を使うと存在しない5番目にアクセスしてしまいます。範囲外アクセスはエラーにならず、たまたま近くにあった値を読んでしまうため発見が遅れます。
実務での勘所
Cの配列には範囲チェックの仕組みが一切無く、範囲外アクセスは未定義動作(undefined behavior)として規格上扱われます。これは「実行時に何か良くないことが起きる」という意味だけでなく、コンパイラが「そんなコードは書かれていないはず」という前提で最適化を行ってよいことも意味します。実際に、最適化の設定によっては範囲外アクセスを含むコードのチェック処理そのものがコンパイラに削除されてしまうことがあり、単に「たまたま近くの値を読む」以上に予測不能な結果を招くことがあります。
関連するエラー
- Segmentation fault(NULLポインタの参照)NULL(何も指していないポインタ)の指す先を読み書きしようとしたときに発生するクラッシュです。
- int同士の割り算で小数点以下が切り捨てられるint型同士を/で割ると、結果もint型になり小数点以下が切り捨てられます。