MW211 EXIT

devlog
C言語/qsort()
2013年03月29日
qsort関数は、第四引数に比較関数名を指定して、
その比較関数で昇順降順の実装を行う。
なお、昇順と降順は、比較する項目の順番を入れ替えるだけ切り替えることができる。

数値配列を並び替える場合。
┌──────────────────────────────────────┐
│int  配列[] = {3,1,5,2,4};  // サンプルデータ                               │
├──────────────────────────────────────┤
│int  配列数 = sizeof(配列) / sizeof(配列[0]);                               │
│qsort(配列, 配列数, sizeof(配列[0]), 比較関数);  // ソート実行              │
├──────────────────────────────────────┤
│// 昇順にする場合  → 1,2,3,4,5                                             │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return *(int*)a - *(int*)b;                                             │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にする場合  → 5,4,3,2,1                                             │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return *(int*)b - *(int*)a;                                             │
│}                                                                           │
└──────────────────────────────────────┘

文字列配列を並び替える場合。
┌──────────────────────────────────────┐
│char*  配列[] = {"ccc","aaa","eee","bbb","ddd"};  // サンプルデータ         │
├──────────────────────────────────────┤
│int  配列数 = sizeof(配列) / sizeof(配列[0]);                               │
│qsort(配列, 配列数, sizeof(配列[0]), 比較関数);  // ソート実行              │
├──────────────────────────────────────┤
│// 昇順にする場合  → "aaa","bbb","ccc","ddd","eee"                         │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return strcmp(*(const char**)a, *(const char**)b);                      │
│}                                                                           │
├──────────────────────────────────────┤
│// 降順にする場合  → "eee","ddd","ccc","bbb","aaa"                         │
│int  比較関数(const void* a, const void* b)                                 │
│{                                                                           │
│    return strcmp(*(const char**)b, *(const char**)a);                      │
│}                                                                           │
└──────────────────────────────────────┘
strcmp()を使うところがミソ。

※NULLの対策が抜けていた(後述)
分類:C/C++
C言語/strlen()とNULL
2013年03月22日
「strlen()」は指定されたアドレスから最初に出現する「NULL(\0)」の手前までの
長さを計測してくれる関数。

ということで、つい、「NULL(\0)」を渡したら「0」になるじゃないかと錯覚しがち。
┌──────────────────────────────────────┐
│strlen("")                                                            →0   │
│strlen(NULL)                                                          →例外│
└──────────────────────────────────────┘
最初の位置がわからないんで例外になってしまうんですね。
分類:C/C++
C言語/文字型←→数値型(2)
2013年03月06日
数値型から文字型に変換するなら、書式付き変換できるsprint系がいいかも。
┌──────────────────────────────────────┐
│sprintf(文字列, "%d", 数値);                                                │
│snprintf(文字列, sizeof(文字列), "%d", 数値);                               │
└──────────────────────────────────────┘
ファイルとかに出力するなら、「fprintf()」とかもあるけど。

これの対応馬(っていうと類似っぽな、真逆な存在)は、sscanf系。
文字型(文字列)から数値に変換してくれる。
┌──────────────────────────────────────┐
│int  sscanf(文字列, "%d", 数値);                                            │
└──────────────────────────────────────┘

sprintf系もsscanf系も複数の変換も一気にできるところが魅力か。
┌──────────────────────────────────────┐
│sprintf(文字列, "%d,%d", 数値, 数値);                                       │
│snprintf(文字列, sizeof(文字列), "%d,%d", 数値, 数値);                      │
├──────────────────────────────────────┤
│int  sscanf(文字列, "%d,%d", 数値, 数値);                                   │
└──────────────────────────────────────┘
分類:C/C++
C言語/文字型←→数値型(1)
2013年03月05日
文字型を数値型に変換するには以下の関数を使う。
┌──────────────────────────────────────┐
│int                 atoi(const char* 文字);                                 │
│long                atol(const char* 文字);                                 │
│long long int       atoll(const char* 文字);                                │
│double              atof(const char* 文字);                                 │
├──────────────────────────────────────┤
│long                strtol(const char* 文字列, char** 不正文字, int 基数);  │
│float               strtof(const char* 文字列, char** 不正文字);            │
│double              strtod(const char* 文字列, char** 不正文字);            │
│unsigned long       strtoul(const char* 文字列, char** 不正文字, int 基数); │
│long long           strtoll(const char* 文字列, char** 不正文字, int 基数); │
│unsigned long long  strtoull(const char* 文字列, char** 不正文字, int 基数);│
│long double         strtold(const char* 文字列, char** 不正文字);           │
└──────────────────────────────────────┘
a系は一文字が対象で、str系は文字列の一文字が対象。

逆に、数値型を文字型(文字列)に変換するには以下の関数を使う。
┌──────────────────────────────────────┐
│char*  itoa(int 数値, char* 文字列, int 基数);                              │
│char*  ltoa(long 数値, char* 文字列, int 基数);                             │
└──────────────────────────────────────┘
こちらはstr系(「ltostr」とか)はないみたい。
ま、a系で文字列を返してる(str系を兼ねている)しね。
分類:C/C++
C言語/数値型
2013年03月04日
数値型に関連するデータは以下の通り。
┌─────────┬──────────┬────┬─────────┬──┐
│signed char       │unsigned char       │ 1バイト│charは文字型専用  │ 2桁│
├─────────┼──────────┼────┼─────────┼──┤
│       short      │unsigned short      │ 2バイト│正式にはshort int │ 4桁│
├─────────┼──────────┼────┼─────────┼──┤
│       int        │unsigned int        │ 4バイト│ 2バイトの場合あり│ 9桁│
├─────────┼──────────┼────┼─────────┼──┤
│       long       │unsigned long       │ 4バイト│正式にはlong int  │ 9桁│
├─────────┼──────────┼────┼─────────┼──┤
│       long long  │unsigned long long  │ 8バイト│後から追加        │18桁│
├─────────┼──────────┼────┼─────────┼──┤
│       float      │unsigned float      │ 4バイト│                  │38桁│
├─────────┼──────────┼────┼─────────┼──┤
│       double     │unsigned double     │ 8バイト│                   308桁│
├─────────┼──────────┼────┼─────────┼──┤
│       long double│unsigned long double│ 8バイト│10バイトの場合あり 308桁│
└─────────┴──────────┴────┴─────────┴──┘
        ※(char型以外)「signed」は付けても付けなくても同じ
        ※桁数はフルに収まる桁数(もし「~127」ならば「~99」まで収まるから2桁)
分類:C/C++
C言語/isdigit()の引数がint型
2013年03月02日
「isdigit()」は「'0'~'9'」なら真(0以外)を返却し、
それ以外なら偽(0)を返却する便利な関数だ。

なのでその定義が「bool isdigit(char c);」とかと思ったら、
「int isdigit(int c);」なのだ。

ま、関数の型のintは、0からそれ以外で解釈するからいいとして
なんで引数はint型なの?

型に従って変換とかすると(「isdigit(atoi('1'))」みたいに)、偽になってしまう。

これは、数値の「0~9」は「0x00~0x09」で、
文字の「'0'~'9'」は「0x30~0x39」の違いがあって、
「0x30~0x39」なら真って判定しようと待ち構えていたら
「0x00~0x09」が入力されたのでアウト(偽)!って判定したってことらしい。

この場合は直接(「isdigit('1')」みたいに)でいいらしい。

なんだかややこしいC言語の世界。

int型は最速なんです、便利なんです、万能なんですってことなんだろうか。
ま、変換だなんだで時間をくうよりは少々わかりにくくてもダイレクトにってのが
C言語だからね(だからとっつきづらいし、罠が多い(覚えてらんないよ))。

つーから、マジマジと定義を見るまでint型に気づかなかった(反省)。
分類:C/C++
C言語/Windows系の排他制御
2013年02月27日
「LockFile()/UnlockFile()」もしくは「LockFileEx()/UnlockFileEx()」を使う。

ま、順番的には以下のような感じなのだろう。
┌──────────────────────────────────────┐
│CreateFile(~)                                                              │
│LockFile(~)                                                                │
│UnlockFile(~)                                                              │
│CloseHandle(~)                                                             │
├──────────────────────────────────────┤
│CreateFile(~)                                                              │
│LockFileEx(~)                                                              │
│UnlockFileEx(~)                                                            │
│CloseHandle(~)                                                             │
└──────────────────────────────────────┘
違いは「LockFileEx()」は排他ロック(LOCKFILE_EXCLUSIVE_LOCK)の
指定ができるらしい(これを指定しないと共有ロックとのこと)。

詳細についてはおいおい調べていく。
分類:C/C++
C言語/UNIX系の排他制御
2013年02月26日
UNIXのファイル排他制御機構としては、「fcntl()」と「flock()」がある。
「fcntl()」は細かくてややこしいので置いといて「flock()」について。

基本的な仕様としては以下のような感じ。
┌──────────────────────────────────────┐
│flock(*ファイルポインタ, LOCK_SH);  …共有ロック                            │
│flock(*ファイルポインタ, LOCK_EX);  …排他ロック                            │
│flock(*ファイルポインタ, LOCK_UN);  …アンロック(fclose()でもやってくれる)  │
└──────────────────────────────────────┘
「fopen()」で「*ファイルポインタ」を取得するので、
そいつを指定してロックをかけたり、はずしたり(アンロック)する。

流れとしてはこんな感じか。
┌──────────────────────────────────────┐
│FILE *fp = fopen("出力ファイル.txt","a");  // 追加モードでOPEN              │
│flock(*ファイルポインタ, LOCK_EX);         // 排他ロック                    │
│fwrite("追加", sizeof("追加"), 1, *fp);    // 追加書込                      │
│fclose(*fp)                                // CLOSEと共にアンロック         │
└──────────────────────────────────────┘

ちなみに、以下みたいに、排他の種別を変更することができる。
┌──────────────────────────────────────┐
│flock(*fp, LOCK_SH);                                                        │
│flock(*fp, LOCK_EX)                                                         │
└──────────────────────────────────────┘
でも、実体としては一回アンロックを挟んでいるので、
他にロックをとられる場合がありうる。
分類:C/C++
C言語/可変引数関数
2013年01月14日
「f("%d", 1);」でも「f("%d-%d", 1, 2);」でも大丈夫な
可変引数をもつ関数の作り方。
┌──────────────────────────────────────┐
│#include <stdarg.h>                                                         │
│void f(const char *format, ...) {                                           │
│    va_list ap;                                                             │
│    char    buff[1024];                                                     │
│                                                                            │
│    va_start(ap, format);                                                   │
│    if (vsnprintf(buff, sizeof(buff), format, ap) < 0) {                    │
│        buff[0] = '\0';                                                     │
│    }                                                                       │
│    va_end(ap);                                                             │
│                                                                            │
│    printf("%s", buff);                                                     │
│}                                                                           │
└──────────────────────────────────────┘
「va_start()」から「va_end()」の区間において
「vsnprintf()」(他に「vsprintf()、vasprintf()」があるらしい)で、
文字列を統合して取得して、後はそれを使えばいいらしい。

詳しいことは追々。。。
分類:C/C++
C言語/文字列のコピー総集編
2013年01月13日
文字列の場合は、代入式は使えない。ということで以下はダメ。
┌──────────────────────────────────────┐
│出力先 = 入力元;                                                            │
└──────────────────────────────────────┘
「&出力先 = 入力元;」とか「&出力先 = &入力元;」とか足掻いてみてもダメ。
構造体の場合は代入式が使えるので、よく勘違いしがちだ。

では、どうするか?

まず、最凶なのは、「memcpy()」。これならなんでもできる。
┌──────────────────────────────────────┐
│memcpy(出力先, 入力元, sizeof(出力先));                                     │
└──────────────────────────────────────┘
ただし、末尾の「\0」を保証してくれないので、入力元の文字数が多いとアウト。
んな訳で、以下のような小細工を施したりする。
┌──────────────────────────────────────┐
│memset(出力先, 0 sizeof(出力先));                                           │
│memcpy(出力先, 入力元, sizeof(出力先) - 1);                                 │
└──────────────────────────────────────┘
まあ、なんつーか、文字をメモリとして機械的扱う感じだ。

じゃ次は、せっかくだから文字として扱ってやろうという話。
代表的なのはこれ、「strcpy()」。
┌──────────────────────────────────────┐
│strcpy(出力先, 入力元);                                                     │
└──────────────────────────────────────┘
こいつは、入力元の「\0」までを強制的にコピーしてしまうので、
扱いにはかなり慎重にならなければなるまい(メモリ侵犯しやすい)。
あらかじめ、出力先と入力元のサイズの関係が保証される時に使う感じか。
ま、予防策を講じるなら以下のような感じか。オールオアナッシングだけど。
┌──────────────────────────────────────┐
│if (strlen(入力元) < sizeof(出力先)) {                                      │
│    strcpy(出力先, 入力元);                                                 │
│}                                                                           │
└──────────────────────────────────────┘

続いて、進化形(?)の「strncpy()」。
┌──────────────────────────────────────┐
│strncpy(出力先, 入力元, sizeof(出力先));                                    │
└──────────────────────────────────────┘
サイズを指定できて、しかも入力元が短い場合、残りを「\0」で埋めてくれる。
ってことで、「strcpy()」よりも遅いらしい。
そしてなによりもこいつも末尾の「\0」を保証してくれない。
前出の「memcpy()」とほとんど同じやん。
やっぱり以下となってしまうのか?(これじゃあんまり使い道ないね)
┌──────────────────────────────────────┐
│memset(出力先, 0 sizeof(出力先));                                           │
│strncpy(出力先, 入力元, sizeof(出力先) - 1);                                │
└──────────────────────────────────────┘

そして、別分野から参入の「snprintf()」。これは使える。
┌──────────────────────────────────────┐
│snprintf(出力先, sizeof(出力先), "%s", 入力元);                             │
└──────────────────────────────────────┘
これだと「出力先≦入力元」でも、最後の末尾に「\0」を押し込めてくれる。
ま、その分最後の一文字は犠牲になるのだが、
「\0」がなくて例外になるよりはかなりまし。
多少速度が犠牲になるらしいが、「memset()」とか使って実装するなら、
一行で済むこいつにしてしまった方がいいのかも。

ま、これらをコングロマリット(?)してできた案が以下。
┌──────────────────────────────────────┐
│if (strlen(入力元) < sizeof(出力先)) {                                      │
│    strcpy(出力先, 入力元);                                                 │
│} else {                                                                    │
│    snprintf(出力先, sizeof(出力先), "%s", 入力元);                         │
│}                                                                           │
└──────────────────────────────────────┘
ま、速度が気になるなら一考かも。
分類:C/C++
前へ 1 2 3 4 5 6 7 8 9 次へ