Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

This article is a Private article. Only a writer and users who know the URL can access it.
Please change open range to public in publish setting if you want to share this article with other users.

1週間で打つC++7本ノック1日目 足し算と引き算の評価

Posted at

今日やること

  • 足し算引き算を計算する
  • 標準入出力(IOストリーム)の使い方

完成イメージ
image.png

main関数を作る

まずはmain関数を作りましょう。今回は先にお手本コードをお見せします。main.cppというファイルに以下の内容を書いてください。

main.cpp
 int main() {
   std::string input;
 
   std::cout << "式を入力: ";
   std::cin >> input;
   std::cout << "実行結果: " << eval(input) << std::endl;
   return 0;
 }

1行ずつ解説していきますね。

std::string input;

この行はinputという変数を宣言しています。変数の宣言は型名 変数名;の形で書きます。この行だとstd::stringが型で、inputが変数名です。std::stringは文字列を表すC++のクラスです。

std::cout << "式を入力: ";

この行はコマンドラインに「式を入力: 」という文字列を表示(標準出力)しています。
std::coutは標準出力ストリームと呼び、標準出力に出す文字列を貯めて、ストリームに改行文字が入ってきたら1行出力する機能を持ちます。<<は本来なら左シフト演算子と呼ばれるものなのですが、ストリームに対して使うと「右辺の文字列をストリームに入れる」という意味になります。std::coutに文字列を押し込むイメージです。
他の言語と同様に、C++では文字列を"で囲みます。

std::cin >> input;

この行は標準入力から文字列を取得し、inputに格納しています。std::coutと同様にシフト演算子>>を用いて取り出すことができます。

std::cout << "実行結果: " << eval(input) << std::endl;]

この行は「実行結果: (入力された式の結果)」を標準出力しています。std::coutは出力したいものが複数ある時でも<<を用いてつなげて書くことができます。

std::cout << "a=" << 1; // a=1と出力される

真ん中のeval(input)はこれから実装するeval関数を呼び出しています。eval関数は入力された文字列を評価し、結果をint型の数値で返す関数です。
std::endlは改行をストリームに送って、ストリームの中身を出力するためのものです。これは関数などではなく、ストリームに入れる値です。

return 0;

この行はmain関数の処理をこの行で終了させるコードです。return自体はC言語と同様に関数から戻り値を返して、処理を終了するキーワードです。特に、main関数から0を返すと、プログラム自体が正常終了します。本当は色々深掘りできるのですが、この記事ではこれくらいにしておきます。詳しいことは「終了コード」でググってください。

初めてのコンパイル

ではmain.cppをコンパイルしてみましょう!コンパイルとは、コード全体を機械語(CPUが解釈する命令)に変換して、実行できるようにすることです。
コマンドラインにg++ main.cppと入力してみましょう。すると画像のようなエラーが出るはずです。

image.png

エラーメッセージは基本的に「ファイル名:行数:列数: error: エラーメッセージ」の書式で書かれています。今回だと5つエラーが出ていますが、エラーメッセージは全て「use of undeclared identifier 'std'」ですね。
直訳すると「'std'という宣言されていない識別子の使用」という意味になります。これは、stdがファイル内で宣言されていないので、コンパイラが宣言を見つけられずに困っている、ということです。

エラーの解消方法

これを解決するためには、std::coutstd::cinが宣言されているファイルをincludeする必要があります。main.cppの先頭行に#include <iostream>という行を追加しましょう。#include <iostream>は、iostreamという標準入出力関係のライブラリの宣言をmain.cppに挿入します。#includeは、ファイルの中身をコードに挿入するC++の機能(ディレクティブ)で、特にライブラリの関数や自作の別ファイルの関数などを使用するときに使います。詳しいことは別の記事にいつかします。

リコンパイル

修正できたら再度g++ main.cppを実行してコンパイルしましょう。するとまたエラーが出るはずです。

image.png

今度はevalが見つからずにエラーが出たようです。evalは後で実装する関数でまだ定義してません。なので、プリミティブ宣言をmain関数の前に追加しておきましょう。以下の行をmain関数の前に入れてください。

int eval(std::string input);

プリミティブ宣言は、関数の戻り値の型、名前、引数を先に宣言する方法です。コンパイラは、関数呼び出しをコンパイルする際に関数名から宣言を探します。なので、関数呼び出しより前に宣言しておく必要があるのです。

では再度コンパイルしてみましょう!
すると、またまたエラーが出ます...

image.png

エラー文の最後に「error: linker command failed with...」とか書いてますね。これはリンクエラーと呼ばれ、コードの構文が正しいかどうか解析した後に、コードを機械語に変換して実行ファイルを生成するタイミングで起こります。
今回の場合、エラーの1行目に「Undefined symbols」(未定義のシンボル)と書いてあります。これはエラーの種類で、今回だとシンボル(関数名などの識別子のこと)の定義が見つからないということです。定義とは、変数なら変数の値、関数なら関数の処理が記載されたコードのことです。このエラーの場合、下のエラー文に未定義の関数と呼び出し箇所が書いてあります。
この例だと未定義の関数は「eval(std::__1::basic...)」で、「_main in main-6ccf6f.o」が呼び出し箇所になります。

以上を要約すると「evalという関数の定義がmainの中で見つからない」というエラーです。eval関数は後ほど実装する関数なので定義がなくて当然です。よって、このエラーは一旦無視しても大丈夫ですね!

eval関数の実装

int eval(std::string input);

ここからはeval関数を実装していきます。先に実装方針を示すのでぜひ自分で手を動かして進めてみてください。

実装方針

eval関数は、inputに渡される式を計算して、計算結果を返す関数です。
まず、inputに渡される文字列に着目します。inputに入ってきそうな文字列は1+1 1-1 1+2-3などですね。

この文字列を一般的に表すと次のようになります。

input := 数字 +か- 数字 +か- ... 数字

この規則をもとにループでinputを処理して、計算を実行してください。
もしinputに数字や+-以外の文字が含まれていた場合、eval関数は0を返すものとします。

以上をもとに挑戦してみてください!

解答例

解答例
main.cpp
#include <iostream>
#include <string>

int eval(std::string input) {
  int result = 0;
  int flag = 1;
  int i = 0;

  while (i < input.size()) {
    int num = 0;

    if (input[i] == '+') {
      flag = 1;
      i++;
    } else if (input[i] == '-') {
      flag = -1;
      i++;
    } else if (isdigit(input[i])) {
      while (isdigit(input[i])) {
        num = num * 10 + (input[i] - '0');
        i++;
      }

      result += num * flag;
    } else {
      return 0; // 不正な文字が入っている時は即座に0を返すものとした
    }

  }

  return result;
}

int main() {
  std::string input;

  std::cout << "式を入力: ";
  std::cin >> input;
  std::cout << "実行結果: " << eval(input) << std::endl;
  return 0;
}

0

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?