C++ 基礎編 5日目

この章では、配列と文字列について説明を行っていきます

配列

配列とは「同じ型のデータを複数まとめて扱うことができるデータ構造」のことです

配列の基本

配列を宣⾔するには、データ型と配列名と要素数を指定します
宣言の方法は以下の通りです

データ型 変数名[要素数];

例えば、’array’ という名前の要素数5のint型配列を宣言する場合は以下のようになります

int array[5];

配列の各要素には、下記のように、配列名と添字を[](角括弧)で囲んで指定します

array[0] = 0;
array[1] = 1;
array[2] = 2;
array[3] = 3;
array[4] = 4;

また、配列の宣言時に、初期化も行う方法としては初期化子リストを使用して以下のように記述します

int array[5] = {0,1,2,3,4};
// または
int array[] = {0,1,2,3,4};

上記の初期化は同じ結果になります
初期化子リストを使用すると配列の要素数を省略することができます

一方、以下のように配列の要素数が初期化子リストよりも長い場合、初期化がない要素は0で初期化されます

#include <iostream>

int main(){
    int array[5] = {0,1,2};

    for(int i=0;i<5;i++){
        std::cout << "[" << i << "]=" << array[i] << std::endl;
    }

    return 0;
}
実行結果
[0]=0
[1]=1
[2]=2
[3]=0
[4]=0

もし、配列の要素すべてを0にしたい場合は以下のように記述します

int array[5]={};

注意点
配列を使う上での注意点は以下の通りです

  • 配列の要素数は宣⾔時に決まり、その後変更することはできません
  • 配列の添字は0~要素数ー1です。範囲外の添え字を使い読み書きしてはいけません

多次元配列

2次元またはそれ以上の次元の配列を多次元配列と呼びます
ここでは、2次元配列の説明を行います

2次元配列の宣言は次のように記述します

データ型 配列変数名[行数][列数];

宣言と同時に初期化する場合は以下のように記述します

データ型 配列変数名[行数][列数] = {
    {値1,値2,}, // 1行目の列の値を定義
    {値3,値4,}, // 2行目の列の値を定義

};

また、配列を参照するには次のように記述します

配列変数名[行の添字][列の添字]

std::arrayについて

C++ではstd::arrayという、固定サイズの配列を扱う、安全で便利な標準ライブラリコンテナがあります
std::arrayには、サイズ取得や先頭の値の取得、要素へのアクセス、空判定などのメンバ関数が提供されています
これらメンバ関数を使用することで、境界外の領域にアクセスすることを防ぎ、安全に使用することができます

以下に、std::arrayを使用したサンプルプログラムを示します

#include <iostream>
#include <array> // std::arrayを使うために必要

int main() {
  // サイズが5のint型 std::array を宣言し、初期化
  std::array<int, 5> numbers = {10, 20, 30, 40, 50};

  std::cout << "std::array の要素 (添字アクセス):" << std::endl;
  for (size_t i = 0; i < numbers.size(); ++i) {
    std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
  }
  std::cout << std::endl;

 // at() メソッドを使った要素へのアクセス (範囲外アクセスは例外を投げる)
  try {
    std::cout << "numbers.at(2) = " << numbers.at(2) << std::endl;
    std::cout << "numbers.at(5) = " << numbers.at(5) << std::endl; // 範囲外アクセス
  } catch (const std::out_of_range& e) {
    std::cerr << "例外が発生しました: " << e.what() << std::endl;
  }
  std::cout << std::endl;

  // front() と back() で最初の要素と最後の要素にアクセス
  std::cout << "numbers.front() = " << numbers.front() << std::endl;
  std::cout << "numbers.back() = " << numbers.back() << std::endl;
  std::cout << std::endl;

  // empty() で配列が空かどうかをチェック (std::array は常に固定サイズなので基本的には false)
  std::cout << "numbers.empty() = " << std::boolalpha << numbers.empty() << std::endl;
  std::cout << std::endl;

  return 0;
}
std::array<int, 5> numbers = {10, 20, 30, 40, 50}; 

上記のように、要素の型とサイズをテンプレート引数として指定し、初期化リストを使って初期化しています

  std::cout << "std::array の要素 (添字アクセス):" << std::endl;
  for (size_t i = 0; i < numbers.size(); ++i) {
    std::cout << "numbers[" << i << "] = " << numbers[i] << std::endl;
  }

numbers[i] のように、通常の配列と同じように添字を使って要素にアクセスできます
numbers.size()は 配列の要素数を返します

// at() メソッドを使った要素へのアクセス (範囲外アクセスは例外を投げる)
  try {
    std::cout << "numbers.at(2) = " << numbers.at(2) << std::endl;
    std::cout << "numbers.at(5) = " << numbers.at(5) << std::endl; // 範囲外アクセス
  } catch (const std::out_of_range& e) {
    std::cerr << "例外が発生しました: " << e.what() << std::endl;
  }

at() を呼び出して、指定したインデックスの要素にアクセスしますが、範囲外のインデックスを指定した場合は std::out_of_range 例外を投げます

  // front() と back() で最初の要素と最後の要素にアクセス 
  std::cout << "numbers.front() = " << numbers.front() << std::endl;
  std::cout << "numbers.back() = " << numbers.back() << std::endl;

front()back()関数は、それぞれ最初の要素と最後の要素への参照を返します

  // empty() で配列が空かどうかをチェック (std::array は常に固定サイズなので基本的には false)
  std::cout << "numbers.empty() = " << std::boolalpha << numbers.empty() << std::endl;

empty()は配列が空かどうか(要素数が0かどうか)を返します
std::array は固定サイズなので、通常は false を返します

文字列

char型の配列をヌル文字(‘\0’)で終端することで、文字列として扱うことができます
std::coutなどの出力関数や、ヘッダで提供される文字列操作関数は、このヌル文字を文字列の終わりとして認識します

文字列リテラルで初期化する場合、以下のように記述します

char greet[] = "Hello!";

コンパイラは自動的に終端のヌル文字を含めたサイズの配列を生成します

一方、配列の要素を個別に初期化する場合は、最初の頃は明示的にヌル文字を末尾に代入することをお勧めします

#include <iostream>

int main(){
    char array[6] = {'a','b','c'}; // 残りの要素は0(ヌル文字)で初期化される
    array[3]='d';
    array[4]='e';
    array[5]='\0';
    std::cout << array << std::endl;

    return 0;
}

注意点
ヌル文字で終端された文字列のことをヌル終端文字列といいます
ヌル終端文字列を扱う際には、バッファオーバーフローに注意が必要です
例えば、配列のサイズよりも長い文字列をコピーしようとすると、プログラムが予期せぬ動作をしたり、セキュリティ上の問題を引き起こしたりする可能性があります
文字列を扱う関数を定義した ヘッダなどには、文字列の長さを安全に扱うための関数も存在しますが、常に配列のサイズを超えないように注意することが必要です

また、文字列リテラルは const char 型の配列として扱われるため、文字列リテラルを指すポインタは const char* 型で宣言することが推奨されます

const char greet[] = "Hello!";

ヌル終端文字列には、ここで説明したヌル終端バイト文字列の他に、ワイド文字 (wchar_t) を用いたヌル終端ワイド文字列や、マルチバイト文字を扱うヌル終端マルチバイト文字列があります
これらの文字列型は、より複雑な文字コードや多言語対応のために使用されます

std::stringについて

C++には、文字列を簡単に扱うことができるstd::stringというクラスがあります
std::stringクラスを使うことで、より簡潔に文字列を実装することができます
std::stringクラスを使用するには、<string>ヘッダをインクルードする必要があります

変数の宣言は以下のように記述します

std::string 変数名;
std::string 変数名 = 文字列リテラル

以下にstd::stringを使用したサンプルプログラムを示します

#include <string>
#include <iostream>

int main(){
    std::string greet="hello!";

    std::cout << greet << std::endl;

    greet = "good morning!";
    std::cout << greet << std::endl;

    greet = greet + " hanako";
    std::cout << greet << std::endl;

    for(int i=0;i<greet.size();i++){
        std::cout << greet[i] << " ";
    }
    return 0;
}
実行結果
hello!
good morning!
good morning! hanako
g o o d   m o r n i n g !   h a n a k o

std::stringは代入する文字列の長さに応じて自動で長さが変わるので、以下のように記述できます

std::string greet="hello!";
greet = "good morning!";

また、演算子を使い以下のように記述することで、末尾に、指定した文字列が追加されます

greet = greet + " hanako";

std::stringは文字列データをchar型の配列のように操作することができます
文字数はreet.size()で取得できます

for(int i=0;i<greet.size();i++){
    std::cout << greet[i] << " ";
}

その他、便利な関数なども用意されています
参考:std::basic_string – cppreference.com

コメント

この記事へのコメントはありません。

関連記事

Python 基本編 7日目

C++ 6日目:ACボタンのイベント処理

C++ 13日目:ルートボタン

PAGE TOP