0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語学習記録

Posted at

Cポインタとアライメント

個人学習記録です。
これまではjavaをメインに勤怠アプリなど(github に公開しているので興味あれば)を開発してきましたが、自分でOSを作成したいと思い立ち学習を始めました。

使用環境

作業開始

今回は教本2章7項「アライメント」の学習をします
まずはコードから(あ、あくまで自己学習なので間違いだらけです。なので、もしわかる方がいれば教えていただけると幸いです)
まずは前提知識の整理から

  • cにおける構造体とは
    • 複数の値をまとめて格納できるデータ型
    • 定義する際は「struct」とつけて宣言する
      #include <stdio.h>
      /*構造体定義
      定義されている内容を「メンバ」と呼ぶ*/
      typedef struct Hoge
      {
          char    char1; //char型のデータ
          int     int1;  //int型のデータ
          char    char2;
          double  double1; //double型のデータ
          char    char3;
      }Hoge; //タグ名
      
    • 配列と異なり違う型であっても同じ構造体に入れられる
    • 使う際はインスタンスの宣言を行い変数に代入する
      int main(void) {
      //インスタンスの宣言
      Hoge    hoge;
      //インスタンスの変数int1に10を代入
      hoge.int1 = 10;
      //int1を出力
      printf("%i\n",hoge.int1);
      return 0;
      }
      
      結果
      10
      

本題

さて、ここからが本題。ここまでのコードの解説をしていきます。

#include <stdio.h>
/*構造体定義*/
typedef struct Hoge
{
    char    char1;
    int     int1;
    char    char2;
    double  double1;
    char    char3;
}Hoge;
/*構造体ごとのポインタを表示*/
int main(void) {
    Hoge    hoge;
    //構造体(Hoge)の大きさ
    printf("hoge size.. %d\n",(int)sizeof(Hoge));

    //構造体とその中身のポインタ
    printf("hoge.. %p\n",(void*)&hoge);
    printf("char1.. %p\n",(void*)&hoge.char1);
    printf("int1.. %p\n",(void*)&hoge.int1);
    printf("char2.. %p\n",(void*)&hoge.char2);
    printf("double1.. %p\n",(void*)&hoge.double1);
    printf("char3.. %p\n",(void*)&hoge.char3);
    return 0;
}

私の環境では以下の結果を得ました

hoge size.. 32
hoge.. 0x7fffffffd990
char1.. 0x7fffffffd990
int1.. 0x7fffffffd994
char2.. 0x7fffffffd998
double1.. 0x7fffffffd9a0
char3.. 0x7fffffffd9a8

ここまでの内容から、構造体のデータごとにアドレスが並び替えられているとわかります。
これは CPUの都合によって決まる ようで、環境ごとにどんな法則(CPUごとのアドレス番地とデータサイズの組み合わせの得意・不得意)で配置されるかは実験しないとわからないようです。
(更に詳しい内容はこちらの記事に詳しく載っています)

アライメントとは結局何だ?

アライメントとは「境界調整」とも呼ばる。
この調整はコンパイラが行い、データのアドレスの間に詰物(パディング)を入れることでCPUがデータにアクセスする際の高速化を担う、と調べた結果出てきた。

つまりアライメントとは
ハードウェアの環境に合わせてコンパイラがアドレスに対しての適切な調整をしてくれること
と考えています。

注意点

ハードウェアの環境に合わせてコンパイラがアドレスに対しての適切な調整をしてくれること

と言っても注意点もあるようで、例えば以下の構造体の場合

typedef struct Hoge2{
    char    char1;   // 1バイト
    int     int1;    // 4バイト
    double  double1; // 8バイト
    int     int2;    // 4バイト
    double  double2; // 8バイト
}Hoge2;

ここで問題なのは、 同じ型同士が離れている ことが問題となる模様。
アライメントを行う際CPUは上から順に調整を行うため、上記の例ではこのようになります。

Hoge2 構造体のメモリレイアウト

オフセット サイズ メンバ パディング 備考
0 1バイト char1 なし 1バイトアライメント
1 3バイト なし パディング int1のためのパディング
4 4バイト int1 なし 4バイトアライメント
8 8バイト double1 なし 8バイトアライメント
16 4バイト int2 なし 4バイトアライメント
20 4バイト なし パディング double2のためのパディング
24 8バイト double2 なし 8バイトアライメント

問題となるのは、各メンバの合計サイズを25バイトを想定していたとしても
実際の結果は32となります。

これはアライメント規則に以下の制限があるためです。
「造体全体のサイズは、そのメンバの中で最も大きなデータ型のアライメントサイズの倍数になる」

なので今回の場合、最も大きなバイト数のdouble型に合わせて8の倍数でアライメントを行ったため、実際の結果が8の倍数である32となってしまう。

具体的な問題点

特に構造体をバイト配列として扱う際、パディング部分も含んでしまい、実行時に予期せぬエラーへと繋がることがある。

0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?