本記事について
はじめまして
今回、初めて記事を書かせていただきます。
競技プログラミングサイト AtCoder には、
AtCoder Programming Guide for beginners というプログラミング入門教材があります。とても完成度が高く、私も利用させていただきました。
APG4bで扱われている言語はC++ですが、別のやりたいことや授業などの関係で
「プログラミングはC言語から始めるから使えない!」
という方もいるのではないかと思います。
世の中にはC言語の勉強ができるサイトはいくらでも存在しているとはおもいますが、自分としてはやはり競プロの入門にはAPG4bをオススメしたいところです。1
大部分が APG4b をパクって元にしているため、本記事が問題あるようでしたらすぐに削除します。
とりあえず第一章だけ書いてみます。
※自分は普段C++を使って競技プログラミングをしているため、C言語に対する知識は浅いです。2
目次
- 1.00.はじめに
- 1.01.出力とコメント | EX1
- 1.02.プログラムの書き方とエラー | EX2
- 1.03.四則演算と優先順位 | EX3
- 1.04.変数と型 | EX4
- 1.05.プログラムの実行順序と入力 | EX5
↑ここまで
- 1.06.if文・比較演算子・論理演算子 | EX6
- 1.07.条件式の結果とbool型 | EX7
- 1.08.変数のスコープ | EX8
- 1.09.複合代入演算子 | EX9
- 1.10.while文 | EX10
- 1.11.for文・break・continue | EX11
- 1.12.文字列と文字 | EX12
- 1.13.配列 | EX13
- 1.14.STLの関数 | EX14
- 1.15.関数 | EX15
A - 1.00.はじめに
第1章について
第1章では、プログラムの基本的な文法について説明します。
第1章をマスターすれば、理論上は全ての「計算処理」を行うプログラムを書ける程の知識が身につくらしいです。頑張りましょう。
プログラミング言語
プログラミングはプログラミング言語を使って行います。プログラミング言語にも様々なものがありますが、本家 APG4b ではその中でも C++ という名前のプログラミング言語を使います。
本記事は C言語 というプログラミング言語を使っています。
プログラムの実行方法
環境構築(パソコンの設定)は初心者にとってかなりつらいところだと思います。AtCoderにはコードテストというものがありそこで十分プログラムを書くことができるので、無理に環境を作ってから取り組まなくても大丈夫です。
問題
提出方法
本記事にC言語のコードは載せますが、もちろんここでは実行できません。
本家 APG4b のページに提出場所があるので、そちらに提出してください。
各章タイトルを本家へのリンクにしてあります。
まずは提出欄の言語を C(GCC 9.2.1) に設定して、以下のコードをコピー&ペーストして提出してみましょう。
(C言語はコンパイラが二つあると思います。どちらでもいいですが、本記事ではGCCで動作を確認したものを載せているので、Clangでの動作は確認しておりません。Clangでやりたい!という方がいて、もし何か不具合があった場合は教えていただけると幸いです)
#include <stdio.h>
int main(void){
printf("Hello, world!\n");
}
ページが切り替わり、結果欄が緑色の「AC」になっていれば提出成功です。
1.01.出力とコメント
キーポイント
-
#include <stdio.h>
は毎回最初に書く - C言語のプログラムは__main関数__から始まる
-
printf("文字列\n");
で文字列を出力できる -
//
や/* */
でコメントを書ける
書き方 | コメントになる場所 |
---|---|
// |
同じ行の// を書いた場所より後 |
/* */ |
/* と*/ の間 |
プログラムの基本形
まずはプログラムの基本形について学びましょう。次のプログラムは何もしないプログラムです。
この形式は今後解説するプログラムのほぼ全てに登場しますが、暗記する必要はありません。
#include <stdio.h>
int main(void){
}
このプログラムについて知っておくべきことは、次で説明する「main関数」についてのみです。
main関数
C言語のプログラムは__main関数__から始まります。
int main(void){
}
main関数の{
の次の行から実行され、それに対応する}
に辿り着くとプログラムを終了します。
このプログラムではmain関数の{ }
の中に何も書いていないので、何もせずにプログラムは終了します。
出力
最初に提出したプログラムの詳細を見てみましょう。
このプログラムは「Hello, world!」という文字列を出力する(画面に表示する)プログラムです。
#include <stdio.h>
int main(void){
printf("Hello, world!\n");
}
Hello, world!
このプログラムのmain関数の中には次の1行のプログラムが書かれています。
printf("Hello, world!\n");
この行が出力を行うプログラムです。この行について以下で詳しく説明します。
printf()
C言語で文字列を出力するには printf() を使います。
出力する文字列を指定している部分は"Hello, world!"
の部分です。
C言語プログラムの中で文字列を扱う場合、" "
で囲う必要があります。\n
は改行を表しています。
セミコロン
行の一番最後には;
(セミコロン)が必要です。C言語では、大抵の行の一番最後にはセミコロンが必要になります。どの行にセミコロンが必要でどの行に不要なのかは新しい文法を学んだ際に適宜見ていって下さい。
セミコロンが必要なところで書き忘れるとエラーになるので、注意しましょう。
別の文字列の出力
別の文字列を出力したい場合、次のように" "
の中身を変えます。
#include <stdio.h>
int main(void){
printf("こんにちは世界\n");
}
こんにちは世界
また、次のように書くこともできます。
#include <stdio.h>
int main(void){
printf("%s\n","こんにちは世界");
}
こんにちは世界
今後、こちらの書き方も重要になってきます。
くわしくは「E - 1.04.変数と型」で扱います。
複数の出力
出力を複数回行うこともできます。
#include <stdio.h>
int main(void){
printf("a");
printf("b\n");
printf("cd\n");
}
ab
cd
このプログラムのポイントは2点です。
まず、5行目で改行を表す\n
を出力していないため、aの後に改行がされずにabと出力されます。
次に、7行目でprintf("cd\n")
と書くことで、cdと出力することができます。この書き方を知っているとプログラムを短く書けることがあるので覚えておきましょう。
数値の出力
数値を出力するときも、" "
の中に書くことで(文字として)出力できます。
#include <stdio.h>
int main(void){
printf("2525\n");
}
2525
また、文字のときと同じように、次のようにも書けます。
#include <stdio.h>
int main(void){
printf("%d\n",2525);
}
2525
数値を表現するときは、" "
を使わずに直接書きます。
コメント
__コメント__は人間が「プログラムがどういう動作をしているか」等のメモ書きをプログラム中に残しておくための機能です。
プログラムとしての意味はないので、書いても動作に影響はありません。
#include <stdio.h>
int main(void){
printf("Hello, world!\n"); //Hello, worldと表示
/*ここも
コメント*/
}
Hello, world!
コメントには二種類の書き方があります。
書き方 | コメントになる場所 |
---|---|
// |
同じ行の// を書いた場所より後 |
/* */ |
/* と*/ の間 |
コメントを使わなくてもプログラムは書けますが、コメントを使うことでプログラムがどのようなものなのかわかりやすくなり、便利です。今後プログラム例の中で使うことがあるため、覚えておいて下さい。
コメントアウト
一時的に実行させたくない部分をコメントにしてしまうことで、その部分のプログラムを無効化するというテクニックがあります。このテクニックは コメントアウト と呼ばれ、よく使われています。
#include <stdio.h>
int main(void){
printf("Hello\n");
// printf("world\n");
}
Hello
5行目の処理はコメント扱いされるので、"World"
は出力されません。
"World"
と出力させたくなった場合もコメントを解除する(//
を消す)だけなので、書き直す手間が省けます。
注意点
半角文字と全角文字
基本的にC言語プログラムの中に「全角文字」(あいう12 等)が入り込むとエラー(AtCoderではCE)になってしまいます。
プログラムを書くときは必ず「半角文字」(abc12 等)で書くようにしましょう。
特に全角スペース「 」が入り込んでしまった場合、発見が難しくなってしまうので気をつけましょう。
ただし、" "
の中とコメント内では全角文字を使うことができます。
コメントで全角文字を使った後、半角入力に戻し忘れて全角文字を紛れ込ませてしまう、というミスに気をつけましょう。
細かい話
#include <stdio.h>
#include <stdio.h>
は stdio.h
というヘッダファイルを読み込むための命令です。stdio.h
には入出力に関する関数が定義されています。例えばすでに紹介したprintf()
は#include <stdio.h>
によって読み込まれた機能です。#include <stdio.h>
と書かずに読み込みが必要な機能を使った場合、エラーが発生します。
stdio.h
以外にも、様々なヘッダファイルがあります。必要な機能があったら、その時々に調べてみましょう。
問題
以下のリンク先に問題文が載っています。
#include <stdio.h>
int main(void) {
printf("Hello, world!\n");
printf("Hello, AtCoder!\n");
printf("Hello, C!\n");
}
Hello, world!
Hello, AtCoder!
Hello, C!
解答例
必ず自分で問題に挑戦してみてから見てください。
解答例
#include <stdio.h>
int main(void) {
printf("こんにちは\n");
printf("AtCoder\n");
}
C - 1.02.プログラムの書き方とエラー
キーポイント
- スペース・改行・インデントを使ってプログラムを見やすくする
- プログラムは「書く→実行→正しく動作することを確認」で初めて完成と言える
- コンパイルエラーは文法のエラーで、プログラムは実行されない
- 実行時エラーは内容のエラーで、プログラムは強制終了される
- 論理エラーは内容のエラーで、プログラムは正しく動いているように見えてしまう
- エラーは「実行して動作を確認する」「Webで検索する」等して修正する
- エラーの大まかな発生箇所は
./Main.c:行:文字目: error
からわかる - エラーメッセージが複数表示された場合は最初のエラーから直す
プログラムの書き方
プログラム中のスペースと改行、およびインデントについて説明します。
これらを使ってプログラムを読みやすくしておくと、エラーが発生した時に修正しやすくなります。
スペースと改行
C言語では、基本的にスペースと改行は同じ意味になります。
また、どちらも省略できることが多いです。
以下の2つプログラムは全く同じ意味になります。
#include <stdio.h>
int main(void){
printf("a");
printf("b\n");
printf("cd\n");
}
#include <stdio.h>
int main(void){printf("a");printf("b\n");printf("cd\n");}
ab
cd
詰め込んであるプログラムは読みづらいので、スペースと改行を多く使って読みやすく書くのが一般的です。
スペースと改行の使い方に決まったルールはありません。複数人での開発などでは何かに統一するのが一般的ですが、競技プログラミングでは自分の好みでいいのではないかと思います。一例としてサンプルプログラムを参考にしてください。
インデント
行のはじめにある連続したスペースのことを__インデント__と言います。
スペースとインデントでプログラムの動作が変わることはありません。プログラムを見やすくするために行います。
インデントはキーボードのTabキーを押して行います。TabキーはQキーの隣にあります。
#include <stdio.h>
int main(void){
//←インデント
printf("こんにちは世界\n");
}
基本的に{
が出てきたら一段インデントし、}
が出てきたら一段戻します。
また、元々一行に書いていたプログラムが長くなった場合は、改行してインデントすることがあります。
#include <stdio.h>
int main(void){
printf("%s%s\n","ABCDEFGHIJKLMNOPQRSTUVWXYZ",
"abcdefghijklmnopqrstuvwxyz" // 長いので改行してインデント
);
}
インデントの幅はプログラムを書いている環境により異なり、スペース2個から8個分であることが多いです。
どの程度の幅を使うかは基本的に自由ですが、プログラムを読みやすくするにはインデントの幅が統一されていることが重要です。
今後複雑なプログラムを書くようになった場合、インデントをしているかどうかで大きく読みやすさが変わってきます。
しっかりインデントをするようにしましょう。
プログラムのエラー
プログラムは書き終わったら完成ではありません。動かしてみてその動作が正しいことを確認して、はじめて完成と言えます。
しかし書き終えたばかりのプログラムを実行しようとすると、たいてい何らかのエラーが発生します。そのときにエラーの原因を理解して修正できることもプログラマーの重要なスキルです。
プログラムには主に3つのエラーがあります。
- コンパイルエラー
- 実行時エラー
- 論理エラー
それぞれについて説明します。
コンパイルエラー
コンパイルエラーとは、書いたプログラムの文法にミスがあるときに発生するエラーです。
全角文字がプログラム中に入り込んだり、文末の;を忘れたときなどにはコンパイルエラーになります。
コンパイルエラーが起きた場合、プログラムは一切動作しません。
実行時エラー
「プログラムを動かす」ことを「プログラムを実行する」といいます。
実行時エラーとは、プログラムの文法に間違いはなかったが、内容に致命的な間違いがあったときに発生するエラーです。
具体的には3÷0のように、0で割り算を行ってしまった場合などに発生します。
スマホアプリやゲーム等が強制終了してしまったとき、多くの場合実行時エラーが発生しています。
実行時エラーが起きた場合、実行時エラーが起きる直前までプログラムは動作しますが、エラー以降は動かなくなってしまいます。
論理エラー
__論理エラー__とは、プログラムは一見正しく動作しているが、その動作が実は正しくないときに発生するエラーです。
例えば、「300円のクッキーと100円のアメを買ったときに払うお金」を計算するプログラムでは300 + 100
と計算するべきですが、間違って300 - 100
としてしまった場合などは論理エラーに当たります。
論理エラーは勘違いで生まれたり、タイピングのミスで発生したりと様々です。
論理エラーは一見問題なくプログラムが動作してしまうため、発見することが難しくなりやすいです。
基本的に、AtCoderではコンパイルエラーが発生したときはCE、実行時エラーが発生したときはRE、論理エラーが発生したときはWAとジャッジされます。CEが出たときは書き直せばいいだけですが、REやWAが出るとペナルティが付きます。一度のペナルティで順位が大きく変わってしまうことも多いので、気をつけましょう。
エラーの直し方
実行してエラーや出力を確認→プログラムを修正→実行してエラーや出力を確認→... を繰り返すことが基本的な流れです。
エラーメッセージが表示される場合は、そのメッセージをコピー&ペーストしてWeb検索してみるのも手です。
特にコンパイルエラーに関しては、エラーメッセージで検索すると分かることが多々あります。
調べてみてもよくわからない場合は、わかりそうな人に質問するのも良いでしょう。
コンパイルエラーの例
コンパイルエラーのうち、よくある3つの例を紹介します。
コンパイルエラーの内容は英語で表示されますが、英語が読める必要はありません。
エラーメッセージのパターンからなんとなく原因が推測できれば十分です。
全角スペース
全角スペースを紛れ込ませてしまうケースです。
次のプログラムでは、printf()
の前に全角スペースが紛れ込んでいます。
#include <stdio.h>
int main(void){
//↓に全角スペース
printf("Hello, world!\n");
}
./Main.c: In function ‘main’:
./Main.c:5:1: error: stray ‘\343’ in program
5 | printf("Hello, world!\n");
| ^
./Main.c:5:2: error: stray ‘\200’ in program
5 | printf("Hello, world!\n");
| ^
./Main.c:5:3: error: stray ‘\200’ in program
5 | printf("Hello, world!\n");
| ^
error: stray ‘\343’ in program
(エラー: ‘\343’がプログラム中に紛れ込んでいます)というエラーメッセージが表示されています。
全角スペースのコンパイルエラーでは、stray ‘\343’ in program
やstray ‘\200’ in program
と表示されるのが特徴です。
多くの場合、エラーメッセージがどこに全角スペースが入っているのかも教えてくれます。
上記のケースでは./Main.c:5:1:
と書いてあり、6行目の1文字目にエラーがあることがわかります。
このように、エラーの発生箇所は./Main.c:行:文字目: error
を読めば特定できます。
また、エラーメッセージの3行目に
5 | printf("Hello, world!\n");
| ^
と書いてあり、printf()
の前に全角スペースがあることが視覚的にもわかります。
ただし、エラーメッセージが示す位置は少しずれることもあるので注意してください。
セミコロン忘れ
セミコロン;
が必要な行の末尾にセミコロンを付け忘れるケースです。
次のプログラムでは、printf()
の後にセミコロンを付けるのを忘れてしまっています。
#include <stdio.h>
int main(void){
printf("Hello, world!\n")
}
./Main.c: In function ‘main’:
./Main.c:4:28: error: expected ‘;’ before ‘}’ token
4 | printf("Hello, world!\n")
| ^
| ;
5 | }
| ~
error: expected ‘;’ before ‘}’ token
(エラー: ‘}’トークンの前に‘;’が予期されます)というエラーメッセージが表示されています。
セミコロン忘れのコンパイルエラーでは、expected ‘;’ before ...
というメッセージが表示されるのが特徴です。
大量のエラー・謎のエラー
一つのミスで大量のエラーメッセージが出てくることもあります。また、エラーメッセージが直接的な原因を示していないことがあります。
その場合は一番最初のエラーが指し示している場所を見て、原因を推測しましょう。
エラーメッセージが直接的な原因を示していないこともあります。
エラーメッセージの内容がよくわからない場合でも、エラーの発生箇所は参考になることが多いです。
コンパイルエラーを直す際は、エラーの発生箇所から原因を推測することも大切です。
問題
以下のリンク先に問題文が載っています。
#include <stdio.h>
int main(void) {
printf("%s%d\n","いつも,252);
printf("AtCoderくん\n")
}
./Main.c: In function ‘main’:
./Main.c:4:19: warning: missing terminating " character
4 | printf("%s%d\n","いつも,252);
| ^
./Main.c:4:19: error: missing terminating " character
4 | printf("%s%d\n","いつも,252);
| ^~~~~~~~~~~~~~~~
./Main.c:5:28: error: expected ‘)’ before ‘}’ token
5 | printf("AtCoderくん\n")
| ^
| )
6 | }
| ~
./Main.c:4:12: warning: format ‘%s’ expects argument of type ‘char *’, but argument 2 has type ‘int’ [-Wformat=]
4 | printf("%s%d\n","いつも,252);
| ~^
| |
| char *
| %d
5 | printf("AtCoderくん\n")
| ~~~~~~~~~~~~~~~~~~~~~~~~~
| |
| int
./Main.c:4:14: warning: format ‘%d’ expects a matching ‘int’ argument [-Wformat=]
4 | printf("%s%d\n","いつも,252);
| ~^
| |
| int
./Main.c:5:28: error: expected ‘;’ before ‘}’ token
5 | printf("AtCoderくん\n")
| ^
| ;
6 | }
| ~
解答例
必ず自分で問題に挑戦してみてから見てください。
解答例
#include <stdio.h>
int main(void) {
printf("%s%d\n","いつも",2525);
printf("AtCoderくん\n");
}
D - 1.03.四則演算と優先順位
キーポイント
演算子 | 計算内容 |
---|---|
+ |
足し算 |
- |
引き算 |
* |
掛け算 |
/ |
割り算 |
% |
割った余り |
- 整数同士の割り算は小数点以下切り捨て
四則演算
C言語プログラムで簡単な計算をする方法を見ていきましょう。
次のプログラムは上から順に、「足し算」「引き算」「掛け算」「割り算」を行っています。
#include <stdio.h>
int main(void){
printf("%d\n", 1 + 1); // 2
printf("%d\n", 3 - 4); // -1
printf("%d\n", 2 * 3); // 6
printf("%d\n", 7 / 3); // 2
}
2
-1
6
2
これらの記号+
, -
, *
, /
のことを算術演算子といいます。
3 - 4
が-1
になっている通り、負の値も計算できます。
*
が__「掛け算」で、/
が「割り算」__です。
7 / 3
が2
になっていることに注意して下さい。C言語では、整数同士で割り算した場合、結果は小数点以下を切り捨てした値になります。
($7 \div3 =2.33...→2, −7 \div 3=−2.33...→−2$)
小数点以下を切り捨てないで計算してほしい場合、7.0 / 3.0
のように.0
をつけます。同様に7.0 / 3.5
のような計算も行えます。
剰余演算
もう一つ重要な算術演算子として %
があります。この演算子は「割ったときの余り」を計算します。
#include <stdio.h>
int main(void){
printf("%d\n", 7 % 3); // 1
printf("%d\n", 4 % 5); // 4
}
1
4
$7 \div 3 = 2$ あまり $1$
$4 \div 5 = 0$ あまり $4$
となるため、このプログラムの出力は1
と4
になります。
演算子の優先順位
演算子には優先順位があります。
例えば1 + 2 * 3
のような式を計算する場合、先に2 * 3
が計算された後、1
が足され、計算結果は7
になります。
1 + 2 * 3 → 1 + 6 → 7
*
は+
よりも優先順位が高いというわけです。
C言語の算術演算子の優先順位は、一般的な数学の優先順位と同じです。%
は*
や/
と同じ優先順位になります。
優先順位 | 高い | 低い |
---|---|---|
演算子 | *``/``% |
+``- |
( )
を使って優先順位を変えることもできます。
#include <stdio.h>
int main(void){
printf("%d\n", (1 + 2) * 3); // 9
}
9
( )
の中が先に計算され、(1 + 2) * 3
→ 3 * 3
となり結果は9
になります。
なお、5 / 2 * 3
のように同じ優先順位の演算子が並んでいる式は、基本的に左の演算子から順に計算されます。
注意点
除算の順序
割り算/
のある整数同士の計算では、切り捨てが行われるタイミングの違いで結果が異なることがあります。
多くの場合、割り算はできるだけ後の方で行うようにしたほうが正しい結果になります。
ゼロ除算
0で割ると実行時エラーが発生します。
#include <stdio.h>
int main(void){
printf("%d\n", 3 / 0);
}
132
3 % 0
のように0で剰余演算%
をした場合も同様に実行時エラーになります。
本家 APG4b では
コードテスト上で表示される終了コードは136になります。
とのことでした。
どちらにせよ、実行時エラーが発生します。
細かい話
細かい話なので、飛ばして問題を解いても良いです。
負の数の剰余
剰余演算A % B
を行う時にA
またはB
が負の数の場合、計算結果は以下のようになります。$|A|,|B|$はそれぞれの絶対値を表すとします。
- $A$が正のとき:$|A|%|B|$
- $A$が負のとき:$-(|A|%|B|)$
つまりB
の正負に関わらず、計算結果はA
の正負と一致します。
より正確には、(A / B) * B + (A % B)
とA
が等しくなるように定義されています。
整数同士で割り算した場合、結果は小数点以下を切り捨てした値になりますが、切り捨て、というのは0方向に丸められます。
例:$1.5 \rightarrow 1 \qquad-2.7 \rightarrow -2$
よって、結果的にA
の正負と一致します。
問題
以下のリンク先に問題文が載っています。
#include <stdio.h>
int main(void){
printf("%d\n", /* ここに式を書く */);
}
解答例
必ず自分で問題に挑戦してみてから見てください。
解答例
#include <stdio.h>
int main(void){
// / 2を最後に計算することで正しい計算結果になる
printf("%d\n", 100 * (100 + 1) / 2);
}
E - 1.04.変数と型
キーポイント
- 変数は「メモ」
-
=
は代入 - 「データの種類」のことを型という
型 | 書き込むデータの種類 |
---|---|
int | 整数 |
double | 小数 |
char | 文字 |
変数
__変数__という機能について見ていきましょう。変数は「メモ」だと考えて下さい。
一度使ったデータをまた後で使いたいときのために、名前を付けてメモをして覚えておくというものです。
例を見てみましょう。
#include <stdio.h>
int main(void){
int name;
name = 10;
printf("%d\n",name); // 10
printf("%d\n",name+2); // 10 + 2 → 12
printf("%d\n",name*3); // 10 * 3 → 30
}
10
12
30
宣言
変数を使うには、最初に__宣言__を行う必要があります。
変数を宣言するときは、「データの種類」と「変数の名前」を指定します。
int name;
「整数」のデータを書き込む変数を「name」という名前で宣言しています。
int
の部分がデータの種類が整数だと指定している部分です。
「データの種類」のことを__型(かた)__と言い、「変数の名前」のことを__変数名__と言います。型についての詳しい説明はこの節の後半で行います。
また、「データ」のことを__値(あたい)__と言うこともあります。今後この表現を使うこともあるので覚えておきましょう。
代入
宣言した変数にデータをメモするには、以下のように=を使って__代入__をします。
name = 10;
これでname
という名前の変数に10
が書き込まれます。
C言語の =
は代入 であって、__「等しい」という意味ではない__ということに注意して下さい。
読み込み
main関数の最後の3行で変数にメモした値を読み込んで出力しています。
printf("%d\n",name); // 10
printf("%d\n",name+2); // 10 + 2 → 12
printf("%d\n",name*3); // 10 * 3 → 30
変数の値を読み書きすることを「変数にアクセスする」ということもあります。
変数の初期化
変数の宣言と代入は同時に行うこともできます。
#include <stdio.h>
int main(void){
int name = 10;
printf("%d\n",name);
}
10
変数を宣言した後の最初の代入を__初期化__といいます。上のプログラムで変数name
は10
に初期化されています。
変数はコピーされる
変数1 = 変数2
と書いた場合、変数の値そのものがコピーされます。
その後にどちらかの変数の値が変更されても、もう片方の変数は影響を受けません。
#include <stdio.h>
int main(void){
int a = 10;
int b;
b = a; // aの値がコピーされ、bに10が代入される
a = 5; // aの値は5に書き換わるが、bは10のまま
printf("%d\n",a);
printf("%d\n",b);
}
5
10
変数b
が10
のままであることに注意してください。
変数を同時に宣言
変数の宣言時に,
を間に入れることで複数の変数を同時に宣言することもできます。
#include <stdio.h>
int main(void){
int a = 10, b = 5;
printf("%d\n",a);
printf("%d\n",b);
}
10
5
上のプログラムは次のように書いた場合と同じ意味になります。
int a = 10;
int b = 5;
変数名のルール
変数名は基本的に自由につけることができますが、一部の名前を変数名にしようとするとコンパイルエラーになります。
利用できない変数名
以下の条件に該当する名前は変数名にできません。
- 数字で始まる名前
- _以外の記号が使われている名前
- キーワード(C言語が使っている一部の単語)
以下は変数名にできない名前の例です。
int 100hello; // 数字で始まる名前にはできない
int na+me; // 変数名に+を使うことはできない
int int; // intはキーワードなので変数名にできない
以下のような名前は変数名にできます。
int hello10; // 2文字目以降は数字にできる
int hello_world; // _ は変数名に使える
無意識にキーワードを変数名にしてしまうことはあまり無いので、キーワードに関してはあまり気にしなくても良いです。気になる人は「C言語 キーワード」等でWebで検索してみてください。
同じ名前の変数は宣言できない
同じ名前の変数を複数宣言することはできません。
#include <stdio.h>
int main(void){
int a = 0;
int a = 5; // この行でコンパイルエラーになる
printf("%d\n",a);
}
./Main.c: In function ‘main’:
./Main.c:7:7: error: redefinition of ‘a’
7 | int a = 5; // この行でコンパイルエラーになる
| ^
./Main.c:5:7: note: previous definition of ‘a’ was here
5 | int a = 0;
| ^
error: redefinition of ‘a’
(エラー: a
の再宣言)というエラーメッセージが表示されています。
例外的に同じ名前の変数を宣言する方法もありますが、それについては「1.08.変数のスコープ」で説明します。
型
int
以外にもC言語には様々な型があります。ここでは重要な3つの型だけを紹介します。
型 | 書き込むデータの種類 |
---|---|
int | 整数 |
double | 小数(実数) |
char | 文字 |
C言語には、C++でいうstring
のような文字列を扱う型は存在しません。そのため、文字型の配列として文字列を扱うことになります。
詳しくは「M - 1.12.文字列と文字」で扱います。
#include <stdio.h>
int main(void){
int i = 10;
double d = 0.5;
char c = 'A';
printf("%d\n",i);
printf("%lf\n",d);
printf("%c\n",c);
}
10
0.500000
A
A のように一つの文字をchar型として扱う場合、''
で囲みます。
変数の出力
さて、printf
関数を初めて紹介したときに、なぜわざわざ
#include <stdio.h>
int main(void){
printf("%d\n",2525);
}
といったような書き方をしていたのか、疑問に思った方も多いかと思います。
今回のように変数を出力したいときに例えば
#include <stdio.h>
int main(void){
int i = 10;
printf("i\n");
}
のように書いた場合、出力されるのは10
ではなくi
になってしまいます。これは、printf()
の中のi
が変数として宣言したi
ではなくアルファベットの i として認識されているためです。
そのため、変数を出力したい場合は
printf("%d\n",i);
というような書き方が必要になるわけです。
変換指定子
" "
で囲まれた「出力したい文字列」のうち、変数の値を出力したい場所には__変換指定子__を使い、,
で区切って後ろに変数を記述します。
変換指定子は、変数の型によって異なります。以下の表をご覧ください。
型 | データの種類 | 変換指定子 |
---|---|---|
int | 整数 | %d |
double | 小数 | %lf |
char | 文字 | %c |
変換指定子にはほかにも便利な機能があります。(小数点以下何桁まで表示するのか指定 など)
また、変数の型はほかにもあり、それぞれで変換指定子も違います。必要になったら、その時々に調べてみましょう。
異なる型同士の計算
異なる型同士の計算では__型変換__が行われます。
例えば、int型とdouble型の計算結果はdouble型になります。
ただし、__変換できない型同士は計算はコンパイルエラー__になります。
double型とint型は変換できる型同士なので互いに代入できます。double型をint型に変換したときは小数点以下切り捨てになります。
細かい話
細かい話なので、飛ばして問題を解いても良いです。
初期化する前の変数の値
初期化する前のint型やdouble型の変数の値を読み込んだ場合、どのような値になっているか分かりません。
次のプログラムは初期化する前の変数の値を読み込む例です。
#include <stdio.h>
int main(void){
int name;
printf("%d\n",name); //なにが出力されるかわからない
}
初期化しなくても変数の値が0になっていることは多いですが、確実にそうなるような仕様ではありません。
変数の初期化をし忘れてしまい、このプログラムのような状況になることがしばしばあります。
変数の初期化し忘れによるバグ(プログラムのミス)を防ぎたい場合、すべての変数を宣言時に適当な値で初期化しておくのも一つの手です。
問題
以下のリンク先に問題文が載っています。
#include <stdio.h>
int main(void){
// 一年の秒数
int seconds = 365 * 24 * 60 * 60;
// 以下のコメント/* */を消して追記する
printf("%d\n", /* 1年は何秒か */ );
printf("%d\n", /* 2年は何秒か */ );
printf("%d\n", /* 5年は何秒か */ );
printf("%d\n", /* 10年は何秒か */ );
}
解答例
必ず自分で問題に挑戦してみてから見てください。
解答例
#include <stdio.h>
int main(void){
// 一年の秒数
int seconds = 365 * 24 * 60 * 60;
// 以下のコメント/* */を消して追記する
printf("%d\n", seconds );
printf("%d\n", seconds * 2 );
printf("%d\n", seconds * 5 );
printf("%d\n", seconds * 10 );
}
F - 1.05.実行順序と入力
キーポイント
- プログラムは上から下へ順番に実行される
-
scanf("変換指定子",&変数)
で入力を受け取ることができる - スペースと改行で区切られて入力される
プログラムの実行順序
基本的に__プログラムは上から下へ順番に実行されます。__
次のプログラムを見てみて下さい。
#include <stdio.h>
int main(void){
int name;
// nameに10を代入
name = 10;
printf("%d\n", name); // 10
// nameに5を代入
name = 5;
printf("%d\n", name); // 5
}
10
5
1回目の出力の時点ではname
に10
が書かれていますが、その後name = 5;
でname
のデータを5
に上書きしているため、2回目の出力では5
が出力されます。
「プログラムが実行される順序」を意識してプログラムを読み書きしましょう。
入力
プログラムの実行時にデータの入力を受け取る方法を見ていきましょう。
「入力機能」を使うことにより、プログラムを書き換えなくても様々な計算を行えるようになります。
入力を受け取るには scanf()
を使います。
次のプログラムは、入力として受け取った数値を10倍にして出力するプログラムです
#include <stdio.h>
int main(void){
int a;
// 変数aで入力を受け取る
scanf("%d", &a);
printf("%d\n", a * 10);
}
5
50
ここではint型の変数a
で入力を受け取っています。
実際に入力を受け取っているのは次の部分です。
scanf("%d", &a);
受け取る変数の型は、printf()
のときに使ったものと同じ変換指定子です。今回はint型なので%d
になっています。
ただし、scanf("%d", a)
ではなく scanf("%d", &a);
と、&
をつける必要があることに気を付けてください。3
整数以外のデータの入力
整数以外のデータを受け取りたいときは、そのデータの種類に合わせた型の変数と変換指定子を使います。
#include <stdio.h>
int main(void){
char c;
double d;
scanf("%c", &c);
scanf("%lf", &d);
printf("%c, %lf\n", c, d);
}
x
1.5
x, 1.500000
繋げて入力
printf()
と同じように、scanf()
もカンマ区切りで変数を並べて入力を受け取ることができます。
入力が複数ある場合は、スペースか改行で区切られていれば分解して入力してくれます。
#include <stdio.h>
int main(void){
int a, b, c;
scanf("%d%d%d", &a, &b, &c);
printf("%d\n", a * b * c);
}
2 3
4
24
ここで、
scanf("%d %d %d", &a, &b, &c);
と書きたくなる人もいるかもしれませんが、離して書いてはいけません。
printf("%d%d%d", a, b, c);
と
printf("%d %d %d", a, b, c);
で出力されるものが変わるように、scanf()
も空白を入れるなどすると動作が変わってしまいます。
逆にそれを利用してうまく処理をすることができることもあるので、興味がある人は調べてみましょう。
問題
以下のリンク先に問題文が載っています。
#include <stdio.h>
int main(void){
// ここにプログラムを追記
}
解答例
必ず自分で問題に挑戦してみてから見てください。
解答例
#include <stdio.h>
int main(void){
// ここにプログラムを追記
int a, b;
scanf("%d%d", &a, &b);
printf("%d\n", a + b );
}
おわりに
今回は、1.05まで書いてみました(最初は第一章全部書く予定でしたが、量が多すぎました)
ちまちまと続きを書いているので、そのうち続編が出るかもしれません。気長にお待ちください。
はじめに書いたように、問題があれば本記事は削除します。その点はよろしくお願いいたします。
-
競プロをするならC言語ではなくC++をしろと言うのはまた別の話になります
そこで、全編C言語で説明したものを書いてみようかと思いました。 ↩ -
C++の知識が深いとは言っていません
そのため、ミスやまずい表現などがあるかもしれません。見つけた場合、優しく教えていただけると嬉しいです。 ↩ -
どうして
&
が必要なのかということの説明には、ポインタ渡しについての知識が必要になります。ポインタ渡しは本家APG4bでは説明されていませんが、参照渡しはは第2章で説明されており、そこで説明するのが自然かと思います。なお、現在そこまで書く予定はありません。
実行はされるのに コンパイルエラー という表示が出ているかもしれませんが、特に問題はありません。 ↩