【備忘録】詳説 Cポインタ

はじめに

この記事は「詳説 Cポインタ」を読んで
忘れそうなことや重要だったことのメモです。

自分用のメモなのでかなり読みにくいかもしれませんが
ご了承ください。

詳しく学びたい方はぜひご購入ください。

www.amazon.co.jp




本の全体像

この本ではポインタについて徹底的に掘り下げられています。



第1章 ポインタの復習


  • メモリは3つに分類される
    • 静的(static) / 大域(Global)
      • プログラム開始時に割り当てられる
      • プログラム終了まで維持
    • 自動
      • 関数の中で定義される
      • 関数が実行されている間のみ存在する
    • 動的
      • ヒープから割り当てられる
      • 解放されるまで存在する


  • null
    • NULLマクロの定義 #define NULL ((void *)0)


  • voidへのポインタ
    • charへのポインタと同じ表現、メモリ上の並び
    • 基本的に他のポインタと等しくなることはない
      • 2つのvoidへのポインタがNULLのとき等しいとされる
    • sizeof(void *)は問題ないが、sizeof(void)は不正


  • ポインタの大きさは、コンピュータの種類とコンパイラによって決まる


  • const
    • 定数へのポインタ(const int *)
      • 間接参照(*x)はOK
      • 間接参照で値変更はNG
      • しかしポインタ自身は定数ではないため書き換えることができる


変数名 ポインタ
int O -
int * O O
const int X -
const int * O X
int *const (定数ポインタ) X O
const int *const (定数への定数ポインタ) X X

(変更できる: O , 変更できない : X)

  • const int * = int const *どちらでも可



第2章 C言語の動的メモリ管理


  • malloc
    • 引数が0の時は処理系依存(nullポインタや0バイトのメモリを返すなど)
    • キャストするべきか
      • voidへのポインタはあらゆるポインタ型に代入できるためキャストは必要ない
      • しかし意図を明示することやキャストが必要なコンパイラとの互換性のためにキャストすべきという意見も


  • 解放済みポインタにはNULLを代入し、ぶら下がりポインタをつくらない


  • プログラム終了時のメモリ解放
    • OSによってはメモリをOSの責任で管理するものと、プログラムの責任で管理するものがある
    • すべてのメモリを解放するとプログラムや実行時間が長くなったり、バグが増えるなどのデメリットもある
    • OSの責任でメモリを管理する場合は、メモリを解放するメリットとして再利用することがあげられる
      • プログラム終了後はOSがリソースを回収するため、メモリを解放する理由はあまりない



第3章 ポインタと関数


  • 使う場面
    • 関数へのポインタ渡し
    • 関数のポインタ


参考 : https://www.wake-mob.jp/2020/11/blog-post.html


  • 定数へのポインタを関数の引数に渡すことで、関数の中でデータ変更することを許容しない


  • 関数ポインタ
    • 関数のアドレスを持っているポインタ
    • 関数の名前自身は関数のアドレスとして評価される
    • 関数ポインタは別の型の関数ポインタにキャストできる
      • ただし適切な引数が与えられているか確認しないため慎重に行うこと

名前自身がアドレスとして評価されている


  • 関数ポインタで遊んでみる ( おまけ )
#include <stdio.h>

typedef int (*operation)(int, int);
static operation operations[128] = {NULL};

int    add(int n1, int n2){return (n1 + n2);}
int    sub(int n1, int n2){return (n1 - n2);}

void   initOperations()
{
    operations['+'] = add;
    operations['-'] = sub;
}

int    calc(char opcode, int n1, int n2)
{
    operation f = operations[opcode];
    return f(n1, n2);
}

int    main()
{
    operation operations[128] = {NULL};
    initOperations(operations);

    int n1 = 4;
    int n2 = 2;

    printf("%d\n", calc('+', n1, n2));
    printf("%d\n", calc('-', n1, n2));

    return (0);
}


足し算と引き算を計算できる、シンプルなプログラム

関数ポインタを使うことで計算の切り替えを綺麗に書ける



第4章 ポインタと配列


2次元配列のイメージ

int matrix[2][3] = {{1,2,3},{4,5,6}}; のとき...

index ポインタ
matrix[0][0] 1 0x7fffd297af20
matrix[0][1] 2 0x7fffd297af24
matrix[0][2] 3 0x7fffd297af28
matrix[1][0] 4 0x7fffd297af2c
matrix[1][1] 5 0x7fffd297af30
matrix[1][2] 6 0x7fffd297af34

上記の例は 連続的であるが

メモリが連続することは保証できない


たいていの場合連続になるが、ヒープの状態に依存する


(例)

(const int) { 100 }
(int[3]) { 1,2,3 }


  • ジャグ配列
    • 各行が異なる列数で構成される2次元配列の一種

(例)

int (*(arr2[])) = {
    (int[]) {0,1,2,3},
    (int[]) {4,5},
    (int[]) {6,7,8},
};



第5章 ポインタと文字列


  • C言語の文字列は2種類ある
    • バイト文字列 (char)
    • ワイド文字列 (wchar_t)
      • 16または32ビットの文字であるワイド文字のための型


  • 文字列定数 (文字列リテラル)
    • 基本的にリテラルプールに割り当てられる (プログラムが必要とするメモリを減らす)
    • リテラルプールは文字列を構成する文字を格納しておく場所


コンパイラによって、文字列定数が変更される場合がある

#include <stdio.h>

int main(void){
    char *x = "AAA";
    *x = 'B';
    printf("%s\n",x); // コンパイラによってはBAAとでる
    return (0);
}


const を付けて定数にすると安全

const char *x = "AAA";


文字列の領域について

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void){
    char *a1 = "42";
    char *a2 = "42";

    char *b1 = (char *)malloc(sizeof(char) * strlen("42"));
    char *b2 = (char *)malloc(sizeof(char) * strlen("42"));

    char c1[] = "42";
    char c2[] = "42";

    printf("a1 = %p, a2 = %p\n",a1,a2); // リテラルプール
    printf("b1 = %p, b2 = %p\n",b1,b2); // ヒープ
    printf("c1 = %p, c2 = %p\n",c1,c2); // スタック
    return (0);
}


実行結果

a1 = 0x55dfb685d004, a2 = 0x55dfb685d004
b1 = 0x55dfb81b62a0, b2 = 0x55dfb81b62c0
c1 = 0x7ffedc9952e2, c2 = 0x7ffedc9952e5


上から順に

リテラルプール, ヒープ , スタック

の位置で確保される


領域のイメージ



第6章 構造体とポインタ


  • 構造体用メモリの割り当てと解法を繰り返すと、オーバーヘッドが生じパフォーマンスが悪くなる場合がある
    • 保存場所を用意することでオーバーヘッドを小さくできる


本記事には載せませんが, リンクリストの図が非常にわかりやすかったです



第7章 セキュリティの問題と不適切なポインタの使用


  • アドレス空間配置のランダム化 (ASLR)
    • メモリのデータをランダムに配置 (コード、スタック、ヒープ)



  • マクロ宣言よりtypedefのほうが良い


  • ポインタの初期化忘れの確認
    • assert(p!=NULL)がおすすめ
    • 条件式が偽だった場合プログラムが終了する



  • DoS攻撃 (2種類ある)
    • フラッド攻撃 (大量のリクエスト)
    • サービスの脆弱性を利用して例外処理を引き起こす ( segmentation faultなど )
      • これにより権限を奪取できるわけではないが、プログラムやサーバーの速度が低下する可能性がある


  • 境界付きポインタ
    • 有効な範囲でだけ使用できるポインタ
    • 配列の要素以外をアクセスできないように条件分岐で指定


  • 配列はメモリ上連続した領域に割り当てられることが保証されている
  • しかし、構造体は上記が保証されていないためポインタの算術演算は行うべきではない


  • シグネチャ (引数の数や型の順序などの組み合わせ)が異なる関数のアドレスを関数ポインタに入れたらだめ


  • たいていのOSはプログラム終了時にメモリを0で上書きなどの処理はしない
  • したがって解放する前にmemsetなどでデータをクリアにするべき



第8章 残りの話題



(例)

0x12345678 の場合

78から格納するのがリトルエンディアン

12から格納するのがビックエンディアン


  • 型パンニング
    • ある方から別の型に変換する際に、unionなどを使って型システムを壊す手法

参考 : http://www.itsenka.com/contents/development/c/union.html


  • restrict
    • コンパイラにその変数の別名が存在しないことを伝える
    • ポインタの参照先をキャッシュし、より効率的なコードになる (コンパイラによる)
    • 別名を持つポインタを使った場合の実行結果は未定義


  • スレッド間でポインタを共有する場合はミューテックスを用いてデータの保護をする


以下の記事が参考になりました

minus9d.hatenablog.com


参考 : https://medium-company.com/%E3%83%9D%E3%83%AA%E3%83%A2%E3%83%BC%E3%83%95%E3%82%A3%E3%82%BA%E3%83%A0/


以下の記事が参考になりました

admarimoin.hatenablog.com



感想


1章から6章はポインタの基礎+αという感じでした。

網羅的に一度学びなおしたい人には良いかと思います。


また6章ではリンクリストやキュー、スタックなどの実装の説明がありました。

上記の実装の勉強をしたい方は是非読んでみましょう


7章のセキュリティ関連の話題も面白かったですが

具体的な攻撃の方法が書かれていなかったのが残念でした...


ほかの本と比べ、より専門的な内容となっていますが

ポインタをより詳しく知りたい方は是非読んでみてください。



【備忘録】ネットワークはなぜつながるのか

はじめに

この記事は「ネットワークはなぜつながるのか」を読んで
忘れそうなことや重要だったことのメモです

自分用のメモなのでかなり読みにくいかもしれませんが
ご了承ください

詳しく学びたい方はぜひご購入ください

Amazon.co.jp - ネットワークはなぜつながるのか 第2版 知っておきたいTCP/IP、LAN、光ファイバの基礎知識 | 戸根 勤, 日経NETWORK |本 | 通販



本の全体像

探検ツアーの流れ

上記の画像のように、ネットワークの流れを順番に学ぶことができます。

IPやイーサネット、ハブやルーターについても詳しく書いてありとても勉強になりました。

またネットだと曖昧な情報も、細かく明確に書いてあり良かったです。

第1章 Webブラウザがメッセージを作る

ブラウザにURLを入力してから通信の流れ

第2章 TCP/IPのデータを電気信号にして送る

データを送受信する際のOS内部にあるプロトコル・スタックの動きについて
LANアダプタの内部実装について

  • プロトコルについて

    • 通常のアプリの送受信の際は「TCP
    • DNSへの問い合わせなどは「UDP
  • netstatコマンドで実際にソケットを確認できる

  • 第2章でもsocketライブラリが大活躍

    • ソケットのメモリを確保し、ディスクリプタ(識別番号のようなもの)をアプリに知らせる
    • SYNで接続開始、FINで切断
    • 通信中はACK(アック)番号やシーケンス番号を用いてデータがちゃんと届いたことを確認
    • ウィンドウズ制御方式はACK番号を待つ前に送信するので無駄がない
      • バッファを超える量を受信した場合データが消える...
      • が、しかしTCPヘッダーのウィンドウ・フィールドで受信可能量を超えないように調整する

https://atmarkit.itmedia.co.jp/ait/articles/0402/13/news096_2.html

  • イーサネット
    • イーサネットのパケットの先頭にはMACアドレスがあり、あて先は次のルーターを指している(到着したら次の目的地に更新)
      • IPヘッダーで目的のサーバーを見分けている
    • 実はIPアドレスはコンピュータではなくLANアダプタに対して割り当てられている
      • LANアダプタを複数持つサーバーには複数のIPアドレスが割り振られる
    • ARPという仕組みで、IPアドレスからMACアドレスを調べる
      • (詳しく) ブロードキャストという仕組みを使って特定のIPアドレスを持っている機器のMACアドレスを取得する
      • 調べた結果は一時的にARPキャッシュに保存される
    • イーサネットは以下の3つの性質を持つ
      • MACヘッダーの宛先の相手にパケットを届ける
      • 送信元MACアドレスで送信元を表す
      • イーサ・タイプでパケットの内容物を表す
    • LANアダプタのROM(Read Only Memory)には全世界で重複しないよう管理されたMACアドレスが書き込まれている
    • 電気信号はデータ信号とクロック信号を合成した信号
      • クロック信号を利用することで「データ信号において1や0が続き信号の変化がなくなり、ビットの区切りを判別できなくなる」という問題を解決
      • クロック信号のタイミングを計るために、プリアンブルと呼ばれる特別な信号をパケットの前に付加する
        • エスカレーターに乗る前にしばらく眺めてタイミングを見計らうのと同じ
        • 最後のビットパターンが少し違うので、それを目印に信号からデータの抽出を開始 (SFD(スタートフレームデリミタ))
      • 末尾に付加するFCSでデータの異常を検知
    • LANアダプタのMAC回路が共通形式の信号を作り、PHY(MAU)回路がケーブルに送り出す時に変換して送信する
      • PHY は100メガビット/秒以上の高速なイーサネット
        • 100メガビット/秒 = 1秒間に100メガビット分のデジタル・データを信号に変換して送る
    • 受信動作では、パケットを受信したときに割り込みという仕組みを使う
      • CPUがOS内部の割り込み処理用のプログラムに切り替える
      • その後LANドライバが動き、LANアダプタのバッファ・メモリーから受信したパケットを取り出す

第3章 ケーブルの先はLAN機器だった

私たちの送信するパケットが家庭や会社のLANにあるハブやルーターを経由して目的地に進んでいく様子を探検する

  • 信号について

  • ルーターについて

    • ルーターは簡略化すると、中継部分とポート部分の2つで構成される
      • 中継部分はパケットの中継先を判断
      • ポート部分はパケットを送受信する
    • ルーターはホスト番号を無視してネットワーク番号のみ調べる
    • ネットマスク欄が0.0.0.0の場合、どんなアドレスでも一致することになる
  • パケットについて

    • パケットには有効期限TTL(Time to live : 生存期間)というIPヘッダーのフィールドがある
      • ルーターを経由するたび1減らし0になったら期限切れとする
      • これにより同じ場所をループすることを防ぐ
    • パケットのサイズが出力側のパケット最大長を超える場合はフラグメンテーションを使ってパケットを分割する
  • 現在のルーターの付加的な機能

    • アドレス変換
      • パケットの送信元IPアドレスをプライベートアドレスからグローバルアドレスに書き換える
      • ポート番号も同時に書き換える
      • アドレス変換装置の対応表に手動でプライベートアドレスを登録することで、インターネットから社内のサーバーにアクセスできる
    • パケットフィルタリング機能
      • パケットのヘッダーを調べて条件に一致したら中継、または棄てるなどの動作をする

第4章 アクセス回線を通ってインターネットの内部へ

パケットがインターネット接続用ルーターを経由してインターネットの中に入って進んでいく様子を探検する

  • 光ファイバの通信高速度が電気より高速なのはなぜ??

    • 信号の速度に大差はない
    • が、減衰率 (信号が途中で弱くなる率)が光信号は低い
    • さらに光信号は電気的な雑音の影響も受けない
  • インターネットと会社や家庭のLANは以下の2点が違う

    • 距離の違い (ツイストペアケーブルは100メートルが限界だが、光ファイバなら数キロメートル)
    • 経路情報の登録の違い (インターネット内のルーターには10万件以上の経路情報が登録されている)
  • インターネットに接続

    • インターネット接続用ルーターはアクセス回線のルールに従う
    • アクセス回線にはADSL, FTTH, CATV, 電話回線などがある
  • ADSLについて

    • ADSLモデムはパケットを細かく分割しセルに格納する
    • 次に変調方式でセルをなだらかな波形に信号化
    • そしてスプリッタという装置に渡す
      • 電話回線に流れてきた信号をスプリッタで、電話とADSLの信号を判別する
  • その後...

    • 電柱を経由し電話局(DSLAM)にたどり着く
    • 次にBASと呼ぶパケット中継装置に届く
    • そしてトンネリング用のヘッダーを付け、トンネリングの出口に向けて中継する
  • ルーターの進化

    • アクセス回線がADSLFTTH (光ファイバ)に進化したためそれに合わせてルーターも進化した
    • その進化したルーターがBAS
      • BASには本人確認と設定値通知の機能がある
      • さらにトンネリングを使ってパケットを運ぶ機能もある
  • インターネットの実態

    • 多数のプロバイダのネットワークを相互に接続したもの
      • プロバイダにはPOPとNOC (中核、POPの規模の大きいもの)などの設備がある
      • NOCにはデータ転送能力がテラビット/秒の高性能ルーターがある (個人ユーザー向けの1万倍以上)
    • プロバイダも会社にあるマシンルームと似ていて、どこかのビルに用意されている
  • イーサネット内部ではBGPという仕組みを使って、ルーターが自動的に経路情報を交換する

    • 社内で使うシステムでは周囲にあるすべてのルーターと無差別に経路情報を交換する
    • プロバイダ間では特定のルーターと1対1で経路情報を交換する
  • IXの必要性

    • プロバイダの中心となる設備を設けて通信回線の数を抑える
    • 高速で巨大なスイッチング・ハブがIXの中核となっている

https://xtech.nikkei.com/it/article/COLUMN/20071009/284013/

第5章 サーバー側のLANには何がある

サーバー側のPOPにパケットが到着してから、パケットはサーバーを目指して進んでいく

この章ではサーバーの手前にある、ファイアーウォールやキャッシュ、サーバー、負荷分散装置について探検する

  • ファイアーウォールについて

    • 特定のサーバーの特定のアプリケーションのパケットだけを通し、それ以外は遮断する
    • 今はパケットフィルタリング型が最も普及している
    • TCPだとSYN=1, ACK=0を用いて片方の通信を防ぐことができる
    • DNSサーバーに用いられるUDPでは片方の通信を防ぐことができない
    • 遮断されたパケットは、不正アクセスの痕跡を示すものがあるため記録せれる
  • 負荷分散について

https://itmanabi.com/dns-record/

ただしこの方法だと、サーバーが故障しても検知してくれない

  • 上記の問題を解決するために考案されたのが負荷分散装置あるいはロードバランサー
    • どのサーバーにリクエストを送るか判断する
  • ほかにもキャッシュ・サーバーを利用する方法もある

    • プロキシ (サーバーへのアクセス動作を仲介) という仕組みを使ってデータをキャッシュする
    • プロキシは仲介の際にデータをディスクに保存し、サーバーに代わってデータをクライアントに返す機能を持つ
  • キャッシュ・サーバーの位置について

    • サーバーのLAN
      • webサーバーの負荷を抑えられるが、インターネットのトラフィックは抑えられない
    • クライアントのLAN
  • 最寄りのキャッシュ・サーバーの見つけ方

    • DNSの問い合わせの際に、クライアント側のDNSと最も近いキャッシュ・サーバーを探す
      • ただしクライアントのDNSとクライアントが同じ場所にあるとは限らない
    • リダイレクト用サーバーアクセス先を振り分ける
      • リダイレクト用のHTTPメッセージのやり取りが増えるためその分オーバーヘッドが多い
      • が、送信元IPアドレスをもとに距離を計算するので精度が高い
  • CDS(CDN)について

    • コンテンツ配信サービス(CDS)を提供する事業者がプロバイダと契約し、多数のキャッシュ・サーバーを設置する
  • キャッシュについて

    • 1度アクセスしたデータを保存し、2回目以降のアクセス動作に利用する
      • 上記の方法だと、サーバーに更新があったかを確認する必要があるためそれにより応答時間が悪化する場合も
    • CDSではサーバーでデータが更新されたら、それを直ちにキャッシュ・サーバーに反映する
    • 動的にページを作る場合は、静的な部分を分けてキャッシュに保存する方法もある

第6章 Webサーバーに到着し、応答データがWebブラウザに戻る

Webサーバーの中身の探検

  • ソケットライブラリの呼び出し方

    • サーバー側はソケットを接続待ち状態にし、受付を行う
    • 接続が開始したら接続待ちソケットをコピーして新しいソケットを作成する
  • CGI(Common Gateway Interface)

    • クライアント側の要求に応じてサーバーが 外部プログラムを呼び出し、その実行結果がHTTPを介してクライアントに送信される仕組み

感想

すごく細かいところまで丁寧に解説してあり良かった

なんとなくネットワークの知識はあるけど
詳しく説明できる自信がない人はぜひ読むべき

振り返り ( 2022/4 )

こんにちは、mahiroです

今月の振り返り記事をゆるりと書いていきます



振り返り

42Tokyo入学

のんびりと進めていきます

42tokyo.jp



42HoursTuningTheBackend 参加

バックエンドのチューニングコンテストに参加しました

SQLのパフォーマンス改善について学べて楽しかったです


github.com



Jobhunt Naviの公開

jobhunt-navi.com

デザインがいまいちですが、パフォーマンスはよさげ...?


今後はデザインの修正とパフォーマンスをさらに向上させたいです

(記事は先輩に書いていただきました)



fintech関係のシステム開発

フリーランスの業務を再開しました

今回はfintech周りの開発です

最近興味があった分野ということで、働きながらいろいろ学んでいます



do'er の活動

サークルが本格的に動き出しました

今月だけで人数が倍近くに...


初心者も増えてきたので初心者向けのタスクを用意しました

github.com



読書

今月読んだ本です

(まだ途中のものもありますが...)


1冊ですべて身につくWordPress入門講座

wordpressの勉強用に購入しました

内容はかなり初心者向きで、はじめてPC触る人でもサイトを作れるほど丁寧に書いてありました


はじめてのwordpress、はじめてのデプロイという方におすすめです

www.amazon.co.jp



Webエンジニアが知っておきたいインフラの基本

月末のイベントに向けて購入しました(イベント前日に)

www.amazon.co.jp



詳説 Cポインタ

ポインタについての基礎から解説されています

曖昧な部分を確実に解決していく感じが読んでて気持ちよかったです

www.amazon.co.jp



来月頑張ること

  • 内定者アルバイトがそろそろ始まるので頑張りたいです
  • 42Tokyoの課題を少しずつ進めたいです



おまけ

apple watch買いました

かなり気に入ってます

自分はスマホ依存症(多分)ですが、

通知がapple watchに来ることでスマホを触る時間がかなり減りました

www.apple.com