MochiuWiki : SUSE, EC, PCB
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
検索
個人用ツール
ログイン
Toggle dark mode
名前空間
ページ
議論
表示
閲覧
ソースを閲覧
履歴を表示
C++の基礎 - 構造体のソースを表示
提供: MochiuWiki : SUSE, EC, PCB
←
C++の基礎 - 構造体
あなたには「このページの編集」を行う権限がありません。理由は以下の通りです:
この操作は、次のグループのいずれかに属する利用者のみが実行できます:
管理者
、new-group。
このページのソースの閲覧やコピーができます。
== 概要 == 構造体は、異なるデータ型の要素をグループ化するためのユーザ定義データ型である。<br> これにより、関連するデータを1つの単位として扱うことができる。<br> <br> C++における構造体の特徴として、以下に示すものが挙げられる。<br> C言語とは異なることに注意する。<br> * メンバアクセス *: メンバには、<code>.</code>演算子でアクセスする。 *: 構造体へのポインタを使用する場合、<code>-></code>演算子でメンバにアクセスする。 * デフォルトはpublic *: クラスと異なり、構造体のメンバはデフォルトで<code>public</code>である。 * 継承可能 *: 構造体は他の構造体やクラスを継承することができる。 <br><br> == 構文 == <syntaxhighlight lang="c++"> struct <構造体名> { <データ型1> <メンバ1>; <データ型2> <メンバ2>; // ... }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> // 例: struct Person { std::string name; int age; double height; }; int main() { Person p1; p1.name = "田中太郎"; p1.age = 30; p1.height = 175.5; } </syntaxhighlight> <br><br> == 構造体の初期化 == C++11以降では、一様初期化構文を使用することができる。<br> <syntaxhighlight lang="c++"> struct Point { int x, y; }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> Point p1 = {10, 20}; // 従来の初期化 Point p2 {30, 40}; // 一様初期化 Point p3 = Point{50, 60}; // コンストラクタ形式の一様初期化 </syntaxhighlight> <br><br> == ビットフィールド == メモリの使用を最適化するために、構造体内でビットフィールドが使用できる。<br> これは、個々のビットレベルでデータを管理することができる。<br> <br> このようなビットフィールドの使用は、メモリが制限されている環境やハードウェアレジスタと直接やり取りする必要がある場合等、<br> 効率的なメモリ使用が重要な場面で特に有用である。<br> <syntaxhighlight lang="c++"> struct Flags { unsigned int flag1 : 1; // 1[bit]のフィールドとして定義されており、本質的にブール値 (0 または 1) が格納できる unsigned int flag2 : 1; // 1[bit]のフィールドとして定義されており、本質的にブール値 (0 または 1) が格納できる unsigned int data : 6; // 6[bit]のフィールドとして定義されており、0 から 63 (2^6 - 1) の範囲の値が格納できる }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> Flags f; f.flag1 = 1; // フラグをオンに設定 f.flag2 = 0; // フラグをオフに設定 f.data = 42; // 6ビットの範囲内の値を設定 if (f.flag1) { // flag1 がオンの場合の処理 } // data の値を出力 (0 - 63の範囲) std::cout << "Data value: " << f.data << std::endl; </syntaxhighlight> <br> この構造体の特徴とメリットを以下に示す。<br> * メモリ効率 *: 一般的に、unsigned int型は32[bit] (4[byte]) を使用する。 *: この構造体は合計で8[ビット] (1[byte]) しか使用しない。 *: 結果として、メモリ使用量が75[%]削減される。 *: <br> * パッキング *: コンパイラは、これらのビットフィールドを1つのunsigned int型にパックする。 *: <br> * アクセス *: これらのフィールドは、通常の構造体のメンバのようにアクセスできるが、値は自動的に適切なビット数に制限される。 <br><br> == 構造体ポインタ == 構造体へのポインタを使用する場合、<code>-></code>演算子でメンバにアクセスする。<br> <syntaxhighlight lang="c++"> Person* pPtr = &p1; pPtr->name = "鈴木花子"; </syntaxhighlight> <br><br> == 構造体の配列 == 構造体の配列を作成して、複数のデータセットを管理することができる。<br> <syntaxhighlight lang="c++"> Person people[3] = { {"山田一郎", 25, 170.0}, {"佐藤二郎", 35, 180.0}, {"高橋三郎", 45, 165.5} }; </syntaxhighlight> <br><br> == メソッド == C++では、構造体内にメソッドを定義することもできる。<br> <syntaxhighlight lang="c++"> struct Rectangle { double width; double height; double area() { return width * height; } }; </syntaxhighlight> <br><br> == 匿名構造体 == C++11以降では、構造体に名前のない入れ子構造体を定義することができる。<br> <syntaxhighlight lang="c++"> struct OuterStruct { int x; struct { int y; int z; }; }; </syntaxhighlight> <br> <syntaxhighlight lang="c++"> OuterStruct os; os.x = 1; os.y = 2; // 直接アクセス可能 </syntaxhighlight> <br><br> == 構造体のムーブセマンティクス == C++11以降では、構造体にムーブコンストラクタとムーブ代入演算子を定義することができる。<br> <syntaxhighlight lang="c++"> struct Resource { std::unique_ptr<int> data; Resource(Resource&& other) noexcept : data(std::move(other.data)) {} Resource& operator=(Resource&& other) noexcept { if (this != &other) { data = std::move(other.data); } return *this; } }; </syntaxhighlight> <br><br> == メモリアライメント == ==== メモリアライメントとは ==== 構造体のメモリレイアウトは、コンパイラによりパディングが挿入される場合がある。<br> これは、メモリアクセスの効率化のためである。<br> <br> 以下の例では、パディングのため、実際の構造体のサイズは12[バイト]になる可能性がある。<br> ただし、<code>#pragma pack</code>ディレクティブを使用して、アラインメントを制御することができる。<br> <br> <syntaxhighlight lang="c++"> struct AlignmentExample { char c; // 1 バイト int i; // 4 バイト short s; // 2 バイト }; </syntaxhighlight> <br> ==== メモリアライメントの制御 ==== 以下に示す方法により、構造体のアライメントを制御することができる。<br> <br> <u>※注意 1</u><br> <u>Clangは、以下に示す全ての方法をサポートしている。</u><br> <u>ただし、特定のターゲットプラットフォームやClangのバージョンにより、サポートされる機能や動作が若干異なる可能性があることに注意する。</u><br> <br> <u>※注意 2</u><br> * アライメントの変更は、プラットフォーム依存の問題を引き起こす可能性がある。 * 一部のCPUアーキテクチャでは、アライメントされていないメモリアクセスがパフォーマンスの低下やクラッシュを引き起こす可能性がある。 * クロスプラットフォーム開発を行う場合は、異なるコンパイラやプラットフォームでの動作を確認する必要がある。 <br> アライメントの変更が必要かどうかを検討して、可能な限り標準的なアライメントを使用することを推奨する。<br> <br> ===== GNU C/C++の属性を使用する方法 ===== GCC / Clangで動作する。<br> <syntaxhighlight lang="c++"> struct __attribute__((packed)) MyStruct { // 構造体のメンバ }; </syntaxhighlight> <br> ===== プラグマを使用する方法 ===== GCC / Clangで動作する。<br> <syntaxhighlight lang="c++"> #pragma pack(push, 1) struct MyStruct { // 構造体のメンバ }; #pragma pack(pop) </syntaxhighlight> <br> ===== Clang特有の属性を使用する方法 ===== Clangのみ動作する。<br> <syntaxhighlight lang="c++"> struct [[gnu::packed]] MyStruct { // 構造体のメンバ }; </syntaxhighlight> <br> ===== C++11以降の標準属性を使用する方法 ===== GCC / Clangで動作する。<br> <syntaxhighlight lang="c++"> struct alignas(1) MyStruct { // 構造体のメンバ }; </syntaxhighlight> <br> <u>※注意</u><br> * <code>__attribute__((packed))</code> や <code>#pragma pack(1)</code> を使用する場合、構造体全体が1バイトアライメントになる。 * <code>alignas(1)</code> を使用する場合、構造体全体のアライメントは1バイトになるが、個々のメンバは自然なアライメントを保持する。<br>完全に詰めたパッキングが必要な場合は、<code>__attribute__((packed))</code> や <code>#pragma pack(1)</code> の方が適している。 * クロスプラットフォーム開発を行う場合は、異なるコンパイラやプラットフォームでの互換性を確認することが重要である。 * アライメントの変更は、特に必要な場合にのみ行うべきである。<br>不適切なアライメントは、パフォーマンスの低下や一部のアーキテクチャでは問題を引き起こす可能性がある。 <br><br> == Linux固有の情報 == Linuxシステムにおいて、多くのシステムコールやライブラリ関数は構造体を使用してデータを受け渡しする。<br> 例えば、struct statはファイル情報を格納するために使用される。<br> <syntaxhighlight lang="c++"> #include <sys/stat.h> struct stat file_info; if (stat("example.txt", &file_info) == 0) { // file_infoを使ってファイル情報にアクセス } </syntaxhighlight> <br><br> == nullptrの代替 == <code>std::optional</code>を使用することにより、<code>nullptr</code>のような振る舞いを実現することができる。<br> <u>ただし、C++17以降で利用可能である。</u><br> <br> この記述は、以下に示すようなメリットがある。<br> * 関数が値を持っているかどうかを明示的に示すことができる。 * 呼び出し側において、値の存在を確認した後に使用することを強制することができる。 <br> <syntaxhighlight lang="c++"> #include <string> #include <vector> #include <optional> struct HOGE { int i; double f; bool b; std::string str; }; class SomeClass { private: std::vector<HOGE> m_hoges; public: std::optional<HOGE> getHoge() const { if (m_hoges.empty()) { return std::nullopt; } return m_hoges[0]; } }; // 使用例 void someFunction() { SomeClass obj; auto result = obj.getHoge(); if (result.has_value()) { HOGE hoge = result.value(); // ...略 } else { // データが存在しない場合の処理 // ...略 } } </syntaxhighlight> <br> また、C++17のif文と構造化束縛を使用して、より簡潔に記述することもできる。<br> <syntaxhighlight lang="c++"> if (auto hoge = obj.getHoge(); hoge) { // *hogeを使用する処理 } else { // データが存在しない場合の処理 } </syntaxhighlight> <br><br> == 応用例 == これらの高度な概念は、C++での構造体の強力さと柔軟性を示している。<br> 構造体は、単純なデータグループ化から複雑なシステムプログラミングまで、幅広い用途に活用できる。<br> <br> ==== データシリアライゼーション ==== 構造体を使用して、データをバイナリ形式でシリアライズすることができる。<br> <syntaxhighlight lang="c++"> #include <fstream> #include <cstring> struct Record { int id; char name[50]; double salary; }; void saveRecord(const Record& r, const std::string& filename) { std::ofstream file(filename, std::ios::binary); file.write(reinterpret_cast<const char*>(&r), sizeof(Record)); } Record loadRecord(const std::string& filename) { Record r; std::ifstream file(filename, std::ios::binary); file.read(reinterpret_cast<char*>(&r), sizeof(Record)); return r; } </syntaxhighlight> <br> ==== メモリマッピングファイル (Linux) ==== 構造体を使用して、メモリマッピングファイルの内容を直接操作することができる。<br> <syntaxhighlight lang="c++"> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> struct SharedData { int value; char message[256]; }; int main() { int fd = open("shared_memory.bin", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR); ftruncate(fd, sizeof(SharedData)); SharedData* data = static_cast<SharedData*>(mmap(NULL, sizeof(SharedData), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); data->value = 42; strcpy(data->message, "Hello, shared memory!"); munmap(data, sizeof(SharedData)); close(fd); return 0; } </syntaxhighlight> <br> ==== ネットワークプロトコル ==== 構造体を使用して、カスタムネットワークプロトコルを定義することができる。<br> <syntaxhighlight lang="c++"> #include <arpa/inet.h> struct PacketHeader { uint32_t magic; uint16_t version; uint16_t type; uint32_t length; void toNetworkOrder() { magic = htonl(magic); version = htons(version); type = htons(type); length = htonl(length); } void toHostOrder() { magic = ntohl(magic); version = ntohs(version); type = ntohs(type); length = ntohl(length); } }; </syntaxhighlight> <br><br> == IDEでの設定 == Qt CreatorおよびCLionでは、直接アライメントを制御する設定は提供していない。<br> アライメントの調整は主にコンパイラレベルでの操作となる。<br> <br> しかし、これらのIDEを使用してプロジェクトの設定を調整することにより、間接的にアライメントを制御することは可能である。<br> <br> Qt CreatorおよびCLionにおいて、アライメントに関連する設定を行う方法を、以下に示す。<br> <br> <u>※注意</u><br> <u>これらの設定はプロジェクト全体に適用される。</u><br> <u>特定の構造体だけにアライメントを適用する場合は、ソースコード内で直接属性やプラグマを使用する方がよい。</u><br> <u>これにより、より細かい制御が可能になり、意図しない副作用を避けることができる。</u><br> <br> <u><code>-fpack-struct</code>フラグは全ての構造体を1バイトアライメントにする。</u><br> <u>これは広範囲に影響を与える可能性があるため、慎重に使用する必要がある。</u><br> <br> <u>クロスプラットフォーム開発を行う場合、これらの設定が全てのターゲットプラットフォームで同じように動作するとは限らない。</u><br> <br> <u>アライメントの調整について、IDEの設定を通じて行うよりも、ソースコード内で直接制御する方が一般的で安全である。</u><br> <br> ==== Qt Creator ==== ===== 方法 1 ===== Qt Creatorでは、プロジェクトファイル (.pro) にコンパイラフラグを追加することができる。<br> <br> <code>-fpack-struct</code>フラグは、構造体のパッキングを有効にする。<br> <syntaxhighlight lang="make"> QMAKE_CXXFLAGS += -fpack-struct </syntaxhighlight> <br> ===== 方法 2 ===== プロジェクトの設定において、追加のコンパイラオプションを指定できる。<br> # プロジェクトを右クリックして、[プロジェクトを編集]を選択する。 # [ビルド設定] - [追加の引数]にコンパイラフラグを追加する。 <br> ==== CLion ==== ===== 方法 1 ===== CLionでは、CMakeをビルドシステムとして使用しているため、CMakeLists.txtファイルに変数<code>CMAKE_CXX_FLAGS</code>に<code>-fpack-struct</code>フラグを設定する。<br> <syntaxhighlight lang="cmake"> set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpack-struct") </syntaxhighlight> <br> ===== 方法 2 ===== [ファイル]メニューバー - [設定]を選択する。<br> [ビルド、実行、デプロイ] - [CMake] - [CMake options]項目に、<code>-DCMAKE_CXX_FLAGS="-fpack-struct"</code>を追加する。<br> <br><br> __FORCETOC__ [[カテゴリ:C++]]
C++の基礎 - 構造体
に戻る。
案内
メインページ
最近の更新
おまかせ表示
MediaWiki についてのヘルプ
ツール
リンク元
関連ページの更新状況
特別ページ
ページ情報
We ask for
Donations
Collapse