LoginSignup
8
4

More than 3 years have passed since last update.

C言語版 APG4b 前編

Last updated at Posted at 2020-08-28

本記事について

はじめまして
今回、初めて記事を書かせていただきます。

競技プログラミングサイト AtCoder には、
AtCoder Programming Guide for beginners というプログラミング入門教材があります。とても完成度が高く、私も利用させていただきました。
APG4bで扱われている言語はC++ですが、別のやりたいことや授業などの関係で
「プログラミングはC言語から始めるから使えない!」
という方もいるのではないかと思います。
世の中にはC言語の勉強ができるサイトはいくらでも存在しているとはおもいますが、自分としてはやはり競プロの入門にはAPG4bをオススメしたいところです。1
そこで、全編C言語で説明したものを書いてみようかと思いました。

大部分が 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 以外にも、様々なヘッダファイルがあります。必要な機能があったら、その時々に調べてみましょう。

問題

以下のリンク先に問題文が載っています。

EX1 - コードテストと出力の練習

サンプルプログラム
#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 programstray ‘\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 ...というメッセージが表示されるのが特徴です。

大量のエラー・謎のエラー

一つのミスで大量のエラーメッセージが出てくることもあります。また、エラーメッセージが直接的な原因を示していないことがあります。
その場合は一番最初のエラーが指し示している場所を見て、原因を推測しましょう。

エラーメッセージが直接的な原因を示していないこともあります。
エラーメッセージの内容がよくわからない場合でも、エラーの発生箇所は参考になることが多いです。

コンパイルエラーを直す際は、エラーの発生箇所から原因を推測することも大切です。

問題

以下のリンク先に問題文が載っています。

EX2.エラーの修正

A君が書いたプログラム
#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 / 32になっていることに注意して下さい。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$

となるため、このプログラムの出力は14になります。

演算子の優先順位

演算子には優先順位があります。

例えば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) * 33 * 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の正負と一致します。

問題

以下のリンク先に問題文が載っています。

EX3.計算問題

サンプルプログラム
#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

変数を宣言した後の最初の代入を初期化といいます。上のプログラムで変数name10に初期化されています。

変数はコピーされる

変数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

変数b10のままであることに注意してください。

変数を同時に宣言

変数の宣言時に,を間に入れることで複数の変数を同時に宣言することもできます。

#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になっていることは多いですが、確実にそうなるような仕様ではありません。
変数の初期化をし忘れてしまい、このプログラムのような状況になることがしばしばあります。
変数の初期化し忘れによるバグ(プログラムのミス)を防ぎたい場合、すべての変数を宣言時に適当な値で初期化しておくのも一つの手です。

問題

以下のリンク先に問題文が載っています。

EX4.◯年は何秒?

サンプルプログラム
#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回目の出力の時点ではname10が書かれていますが、その後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()も空白を入れるなどすると動作が変わってしまいます。
逆にそれを利用してうまく処理をすることができることもあるので、興味がある人は調べてみましょう。

問題

以下のリンク先に問題文が載っています。

EX5 - A足すB問題

サンプルプログラム
#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まで書いてみました(最初は第一章全部書く予定でしたが、量が多すぎました)
ちまちまと続きを書いているので、そのうち続編が出るかもしれません。気長にお待ちください。

はじめに書いたように、問題があれば本記事は削除します。その点はよろしくお願いいたします。

next


  1. 競プロをするならC言語ではなくC++をしろと言うのはまた別の話になります 

  2. C++の知識が深いとは言っていません 

  3. どうして&が必要なのかということの説明には、ポインタ渡しについての知識が必要になります。ポインタ渡しは本家APG4bでは説明されていませんが、参照渡しはは第2章で説明されており、そこで説明するのが自然かと思います。なお、現在そこまで書く予定はありません。 

8
4
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
8
4