MW211 EXIT

devlog
C/ヘッダファイルのインクルード
2014年07月24日
ヘッダファイルのインクルードをソースファイル側でするべきか
インクルードしているヘッダファイル側でするべきか迷うことがある。

ということでまとめた。
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【1】公開関数や公開グローバル変数を使用する場合
┌──┬───────────────────────────────────┐
│①.h│extern void 公開関数①();                                             │
├──┼───────────────────────────────────┤
│①.c│#include "①.h"                                                       │
│    │void 公開関数①()                                                     │
│    │{                                                                     │
│    │    処理;                                                             │
│    │}                                                                     │
└──┴───────────────────────────────────┘
┌──┬───────────────────────────────────┐
│②.c│#include "①.h"                                                       │
│    │公開関数①();                                                         │
└──┴───────────────────────────────────┘
  ②ではソースファイル側でインクルードする。
  →②のヘッダファイルでインクルードし、
    そのヘッダファイルをソースファイルでインクルードするといった
    まわりくどいことはしなくてもよい
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
【2】define定義はtypdef定義をヘッダ側でも使用する場合
┌──┬───────────────────────────────────┐
│①.h│typedef int 新型;                                                     │
└──┴───────────────────────────────────┘
┌──┬───────────────────────────────────┐
│②.h│#include "①.h"                                                       │
│    │extern void 公開関数②(新型 arg);                                     │
├──┼───────────────────────────────────┤
│②.c│#include "②.h"                                                       │
│    │void 公開関数②(新型 arg)                                             │
│    │{                                                                     │
│    │    処理;                                                             │
│    │}                                                                     │
└──┴───────────────────────────────────┘
┌──┬───────────────────────────────────┐
│③.c│#include "②.h"                                                       │
│    │新型  arg = 0;                                                        │
│    │公開関数②(arg);                                                      │
└──┴───────────────────────────────────┘
  ②のヘッダファイルにとって、①のヘッダファイルは必須なので
  ②ではヘッダファイル側でインクルードする。
  →別の③が、②のヘッダファイルのみをインクルードすれば
    完結する(①のヘッダファイルのインクルードがなくてもよい状態)ように
    なっていればよい
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
分類:C/C++
C/空文字の設定方法
2014年07月08日
文字列に「」(空文字)を設定する方法はいくつかあるのでまとめてみた。
#PHPとかでいう「文字列変数 = ""」の処理

┌──────────────────────────────────────┐
│char  c[4];                                                                 │
└──────────────────────────────────────┘
サンプルは上記として、以下のような方法がある。

┌─┬────────────────────────────────────┐
│ 1│c[0] = 0;                                                               │
├─┼────────────────────────────────────┤
│ 2│memset(c, 0, sizeof(c));                                                │
├─┼────────────────────────────────────┤
│ 3│snprintf(c, sizeof(c), "");                                             │
└─┴────────────────────────────────────┘
1は最速だが、ぱっとみわかりづらい。いかにもC言語って書き方。
2と3は若干遅いがすっきりしている。
3はなんでもかんでも「snprintf()」派って感じのイレギュラーなやつだ。

但し「c = ""」はダメなので注意のこと。
分類:C/C++
C/snprintfは便利な関数
2014年07月07日
文字列を代入するにはこの関数は便利。
┌──────────────────────────────────────┐
│snprintf(出力先, sizeof(出力先), 入力元);                                   │
├──────────────────────────────────────┤
│snprintf(buf, sizeof(buf), "123");                                          │
└──────────────────────────────────────┘
┌──────────────────────────────────────┐
│snprintf(出力先, sizeof(出力先), 書式, パラメータ, …);                     │
├──────────────────────────────────────┤
│snprintf(buf, sizeof(buf), "%s-%d", s, d);                                  │
└──────────────────────────────────────┘
こんな感じで使う。

何よりも便利なのは、出力先の末尾に必ずNULLが保証されること。

以下のような場合でも
┌──────────────────────────────────────┐
│char  buf[4];                                                               │
│snprintf(buf, sizeof(buf), "1234");                                         │
└──────────────────────────────────────┘
「1234」ではなく「123+NULL」となってくれる。
最後の一文字は紛失してしまうが、文字の終わりがなくなってしまうので
その恐怖(「%s」とかで出力した場合に例外など)に比べれば全然問題ない。

これが「memcpy()」だと「-1」とかややこしい数式が必要になってくる。
┌──────────────────────────────────────┐
│char  buf[4];                                                               │
│memset(buf, 0, sizeof(buf));                                                │
│memcpy(buf, "1234", sizeof(buf) - 1);                                       │
└──────────────────────────────────────┘

すっごい便利。

但し、高機能ゆえの弱点で処理速度が若干遅いらしい。そこだけは気を付けて。
分類:C/C++
C/配列の初期化と全コピー
2014年06月26日
配列の実体の場合
┌──────────────────────────────────────┐
│型   配列[添字];                                                            │
├──────────────────────────────────────┤
│memset(配列, 0, sizeof(配列));                                              │
│memset(配列, 0, sizeof(配列[0]) * 添字);                                    │
│memset(配列, 0, sizeof(型) * 添字);                                         │
│memset(配列, 0, 配列のサイズ);                                              │
├──────────────────────────────────────┤
│memcpy(コピー先の配列, 配列, sizeof(コピー先の配列));                       │
│memcpy(コピー先の配列, 配列, sizeof(配列[0]) * 添字);                       │
│memcpy(コピー先の配列, 配列, sizeof(型) * 添字);                            │
│memcpy(コピー先の配列, 配列, コピー先の配列のサイズ);                       │
│──────────────────────────────────────│
│memcpy(コピー先の配列のポインタ, 配列, sizeof(型) * 添字);                  │
│memcpy(コピー先の配列のポインタ, 配列, コピー先の配列のサイズ);             │
└──────────────────────────────────────┘

配列のポインタの場合
┌──────────────────────────────────────┐
│型*  配列のポインタ = 配列;                                                 │
├──────────────────────────────────────┤
│memset(配列のポインタ, 0, sizeof(型) * 添字);                               │
│memset(配列, 0, 配列のサイズ);                                              │
├──────────────────────────────────────┤
│memcpy(コピー先の配列のポインタ, 配列, sizeof(型) * 添字);                  │
│memcpy(コピー先の配列のポインタ, 配列, コピー先の配列のサイズ);             │
│──────────────────────────────────────│
│memcpy(コピー先の配列のポインタ, 配列のポインタ, sizeof(型) * 添字);        │
│memcpy(コピー先の配列のポインタ, 配列のポインタ, コピー先の配列のサイズ);   │
└──────────────────────────────────────┘
分類:C/C++
C/signのキャスト
2014年06月23日
「int」型と「unsigned int」型を比較する場合に、
キャストする必要があった場合、どちらをキャストすべきか?

  (1) 「(unsigned int)int_value ==      unsigned_int_value」
  (2) 「              int_value == (int)unsigned_int_value」

  (1) 「-1」が「65535(4294967295)」とみなされる
  (2) 「65535(4294967295)」が「-1」とみなされる

どちらかというと(1)の方が(2)よりも頻度が高いだろう。
ということで、30000にも満たないループインデックスとかを
「unsigned int」型(「size_t」など)と比較する場面があった場合には
(1)より(2)の方、つまり「int」型で比較した方がよいということになる。

但し、上記は如何なる場合にも通用する訳ではないので(30000を超える場合など)
注意のこと。
分類:C/C++
C/typedefによる配列数縛り
2014年06月22日
構造体(普通の型でもよいが)の配列数を縛りたい場合がある。

構造体の一部としての構造体であれば、こんな感じでできる。
┌──────────────────────────────────────┐
│typedef struct {                                                            │
│    構造体  変数[配列数];                                                   │
│} 配列型;                                                                   │
└──────────────────────────────────────┘

では、上記のように項目が一つしかない場合はもっとシンプルに書けないか?
このように書くらしい。
┌──────────────────────────────────────┐
│typedef 構造体 配列型[配列数];                                              │
└──────────────────────────────────────┘

ポインタ関数配列の定義といい、なんだかわかりません。
(たぶん理にはかなってるんだろうなぁ)
分類:C/C++
C言語/定義と宣言の違い
2014年06月21日
C言語において、変数定義と変数宣言は違う。
  「定義」…「領域を確保する」
  「宣言」…「(存在を知らせるだけで)領域を確保しない」

externするグローバル変数は、実体がなければならない。
┌──────────────────────────────────────┐
│int グローバル変数 = 0;                                                     │
└──────────────────────────────────────┘
これが変数定義。

一方、ヘッダファイル等を通じてこれをexternすることができる。
┌──────────────────────────────────────┐
│extern int グローバル変数;                                                  │
└──────────────────────────────────────┘
これが変数宣言。
この場合、初期値を代入できないのは、定義じゃないからだ。

ちなみに、関数の処理がない方は、関数宣言となる。
┌──────────────────────────────────────┐
│static void  func(void);                                                    │
└──────────────────────────────────────┘
処理を書いている方は、関数定義となる。
┌──────────────────────────────────────┐
│static void  func(void) {                                                   │
│    // 処理;                                                                │
│}                                                                           │
└──────────────────────────────────────┘
分類:C/C++
C/do-while中のcontinue文
2014年06月18日
continue文は先頭に戻ると考えがちだが…。
┌──────────┐
│do {               ←─┐
│    continue;      ──┘
│} while (条件式);   │
└──────────┘

実は、末尾にジャンプして先頭に戻っているのであった。
┌──────────┐
│do {               ←──┐
│    continue;      ──┐│
│} while (条件式);  ←─┘┘
└──────────┘

なので、以下みたいな読み飛ばし条件でcontineする処理を、
普通のwhile文から流用しても、永久ループになるということではないのであった。
┌──────────────────────────────────────┐
│while (ポインタ = 次読込関数()) {                                           │
│    if (読み飛ばし条件) {                                                   │
│        continue;                                                           │
│    }                                                                       │
│}                                                                           │
├──────────────────────────────────────┤
│do {                                                                        │
│    if (読み飛ばし条件) {                                                   │
│        continue;                                                           │
│    }                                                                       │
│} while (ポインタ = 次読込関数());                                          │
└──────────────────────────────────────┘
分類:C/C++
C/ポインタ定義の「*」の位置
2014年06月17日
ポインタ定義において、「*」の位置が以下の二種類のパターンがある。
┌──────────────────────────────────────┐
│char    *ptr  = NULL;                                                       │
├──────────────────────────────────────┤
│char*   ptr   = NULL;                                                       │
└──────────────────────────────────────┘
果たして、どっちがいいのであろうか?

後者を推奨する。根拠は以下の通り。

(1) 「const」との関係から
┌──────────────────────────────────────┐
│char* const   ptr   = NULL;                                                 │
└──────────────────────────────────────┘
  「ptr」が指す値を変更できないように縛るには「const char* ptr」のような
  書き方となるが、「ptr」のアドレスを変更できないように縛る場合、
  「char* const ptr」のように「const」が「*」の後ろに来る。
  これが「char *ptr」の場合だと、説明できない。

(2) 初期値の代入が意味するところから
  「*ptr = NULL」では「ptr」の指す先が「NULL」になるが
  「ptr = NULL」だと、「ptr」のアドレスが「NULL」になる。
  定義時の意味するところは後者なので「char*」+「ptr = NULL」の方が
  わかりやすい。

(3) キャスト時の記述
  「(char *)ptr」だと「(char)」+「*ptr」的な錯覚を与える。
  #ちと強引か。。。

なお、前者の場合、以下のような書き方ができるという利点があるようだが…。
┌──────────────────────────────────────┐
│char    *ptr1, *ptr2;                                                       │
└──────────────────────────────────────┘
分類:C/C++
C/文字列結合
2014年06月16日
    ┌─┬─┬─┬─┐
①=│A │B │\0│塵│
    └─┴─┴─┴─┘
    ┌─┬─┬─┬─┐
②=│X │Y │\0│芥│
    └─┴─┴─┴─┘
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
strcpy(①, ②); 
    ┌─┬─┬─┬─┐
①=│X │Y │\0│塵│
    └─┴─┴─┴─┘
      ↑  ↑  ↑
    ┌─┬─┬─┬─┐
②=│X │Y │\0│芥│  ※結合ではなく上書
    └─┴─┴─┴─┘
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
strcat(①, ②); 
    ┌───────┐
    ┌─┬─┌─┬─┬─┐
①=│A │B │X │Y │\0│  ※この場合、規定領域をはみ出してしまった
    └─┴─└─┴─┴─┘
              ↑  ↑  ↑
            ┌─┬─┬─┬─┐
②=        │X │Y │\0│芥│
            └─┴─┴─┴─┘
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
strncat(①, ②, 2); 
    ┌───────┐
    ┌─┬─┌─┬─┐
①=│A │B │X │Y │  ※この場合、規定領域をはみ出さないが、終端がない
    └─┴─└─┴─┘
              ↑  ↑
            ┌─┬─┬─┬─┐
②=        │X │Y │\0│芥│
            └─┴─┴─┴─┘
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
自製strncat2(①, ②, 4); 
                ┌─┐
                │\0│  ※最後に強制的に終端を上書する
                └─┘
    ┌──────↓┐
    ┌─┬─┌─┬─┐
①=│A │B │X │\0│
    └─┴─└─┴─┘
              ↑  ↑
            ┌─┬─┬─┬─┐
②=        │X │Y │\0│芥│
            └─┴─┴─┴─┘
┌──────────────────────────────────────┐
│size_t 自製strncat2(char* str1, char* str2, size_t size)                    │
│{                                                                           │
│    if ((strlen(str1) + strlen(str2)) < size) {                             │
│        strcat(str1, str2);                                                 │
│    } else {                                                                │
│        strncat(str1, str2, size - strlen(str1));                           │
│        str1[(size - 1)] = 0;                                               │
│    }                                                                       │
│    return strlen(str1);                                                    │
│}                                                                           │
└──────────────────────────────────────┘
分類:C/C++
前へ 1 2 3 4 5 6 7 8 9 次へ