C言語の文字列は厄介
文字列は文字の配列で、Null終端で最後という仕様なので、
どうやっても番兵スタイルになる。
番兵とはNullのことで、処理の進め方として、Nullになるまで処理するというスタイルのこと。
単方向リストなどで使う。
Nullがないと処理がリークするので、Null保証する仕組みを作ったりする。
strcpy関数は危険
strcpy関数は引数が2個のみでサイズを指定できないため、
Null終端があったとしても危険
#define SRC_LENGTH 10
#define DEST_LENGTH 3
char src[SRC_LENGTH];
char dest[DEST_LENGTH];
strcpy(dest,src);
strncpy関数も危険
strcpy関数は危険なので、strncpy関数を使うとサイズ指定ができるが、これも危険である。
以下のコードは危険コード。
#define SRC_LENGTH 10
#define DEST_LENGTH 3
char src[SRC_LENGTH];
char dest[DEST_LENGTH];
memset(src, "aiueoaiue", SRC_LENGTH);
strncpy(dest,src, DEST_LENGTH);
このコードのstrncpy関数呼び出し後のdest変数の中身は
dest[0] = 'a';
dest[1] = 'i';
dest[2] = 'u';
となるので、Null終端がない。
解決策はあるのか?
本質的には無理だと思う。C言語の特性なので、頑張る。
strncpy(dest,src, DEST_LENGTH);
dest[DEST_LRNGTH-1] = '\0';
でとりあえずリークはしない。リークの是正目的だけは満たせる。
Linuxカーネルに
strscpy関数なるものがある。
ssize_t strscpy(char *dest, const char *src, size_t count)
{
const struct word_at_a_time constants = WORD_AT_A_TIME_CONSTANTS;
size_t max = count;
long res = 0;
if (count == 0)
return -E2BIG;
#ifdef CONFIG_HAVE_EFFICIENT_UNALIGNED_ACCESS
/*
* If src is unaligned, don't cross a page boundary,
* since we don't know if the next page is mapped.
*/
if ((long)src & (sizeof(long) - 1)) {
size_t limit = PAGE_SIZE - ((long)src & (PAGE_SIZE - 1));
if (limit < max)
max = limit;
}
#else
/* If src or dest is unaligned, don't do word-at-a-time. */
if (((long) dest | (long) src) & (sizeof(long) - 1))
max = 0;
#endif
while (max >= sizeof(unsigned long)) {
unsigned long c, data;
c = read_word_at_a_time(src+res);
if (has_zero(c, &data, &constants)) {
data = prep_zero_mask(c, data, &constants);
data = create_zero_mask(data);
*(unsigned long *)(dest+res) = c & zero_bytemask(data);
return res + find_zero(data);
}
*(unsigned long *)(dest+res) = c;
res += sizeof(unsigned long);
count -= sizeof(unsigned long);
max -= sizeof(unsigned long);
}
while (count) {
char c;
c = src[res];
dest[res] = c;
if (!c)
return res;
res++;
count--;
}
/* Hit buffer length without finding a NUL; force NUL-termination. */
if (res)
dest[res-1] = '\0';
return -E2BIG;
}
コピーするだけでこのボリューム。さすがC言語。さすがLinuxカーネル。
これの解説はあとで。
使う側の利点としては
strscpy関数は
- コピー元がNull終端でなくてよい
- コピー先は必ずNull終端
- コピー先のサイズが足りない場合はエラーを返す
なので、うまくこれを使っていきたい。