こんにちは、もがちゃんです。
今回は、C言語で文字列をコピーする際、一部分だけコピーしたい場合に良く使用するstrncpyの使い方を簡単なサンプルソースとともに説明します。
目次
strncpyの構文
#include <string.h>
char *strncpy(char* restrict s1, const char* restrict s2, size_t n);
strncpyの説明
strncpyは、s2で指定されたポインタが指す文字列をnで指定されたバイト数分s1で指定されたポインタが指すメモリ領域へコピーします。
strncpy関数は、s2が指す配列から、s1が指す配列に、n個以下の文字(ナル文字に続く文字はコピーしない。)をコピーする。領域の重なり合うオブジェクト間でコピーが行われるとき、その動作は未定義とする。
JISX3010 プログラム言語C
s2が指す配列がn文字より短い文字列である場合、s1が指す配列中のコピーの後ろに、全体でn文字書き込むまでナル文字を付加する。
s1
コピー先メモリ領域の先頭を指すポインタを指定します。
コピー先のメモリ領域として最低でもコピーするバイトサイズの領域が必要です。
s2
コピー元文字列の先頭を指すポインタを指定します。
n
コピーするサイズをバイトサイズで指定します。
返却値
s1を返します。
strncpyの使い方サンプル
strncpyを使った簡単なサンプルプログラムとその実行結果を紹介します。
サンプルプログラムとその実行結果
#include <stdio.h>
#include <string.h>
int main(void) {
char buffer1[20];
char buffer2[20];
char buffer3[40]; /* 重複する領域用 */
size_t n;
memcpy(buffer1, "s1 strncpy", strlen("s1 strncpy")+1);
memcpy(buffer2, "s2 strncpy", strlen("s2 strncpy")+1);
/* 重複しない領域へのcopy s2の文字列がnより長い */
char* restrict s1 = buffer1;
char* restrict s2 = buffer2;
n = 2;
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複しない領域へのcopy s2の文字列がnより短い */
s1 = buffer1;
s2 = buffer2;
n = 20;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その1 */
memset(buffer3, '#include <stdio.h>
#include <string.h>
int main(void) {
char buffer1[20];
char buffer2[20];
char buffer3[40]; /* 重複する領域用 */
size_t n;
memcpy(buffer1, "s1 strncpy", strlen("s1 strncpy")+1);
memcpy(buffer2, "s2 strncpy", strlen("s2 strncpy")+1);
/* 重複しない領域へのcopy s2の文字列がnより長い */
char* restrict s1 = buffer1;
char* restrict s2 = buffer2;
n = 2;
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複しない領域へのcopy s2の文字列がnより短い */
s1 = buffer1;
s2 = buffer2;
n = 20;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その1 */
memset(buffer3, '\0', sizeof(buffer3));
s1 = &buffer3[0];
s2 = &buffer3[1];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その2 */
memset(buffer3, '\0', sizeof(buffer3));
s1 = &buffer3[1];
s2 = &buffer3[0];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
}
', sizeof(buffer3));
s1 = &buffer3[0];
s2 = &buffer3[1];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その2 */
memset(buffer3, '#include <stdio.h>
#include <string.h>
int main(void) {
char buffer1[20];
char buffer2[20];
char buffer3[40]; /* 重複する領域用 */
size_t n;
memcpy(buffer1, "s1 strncpy", strlen("s1 strncpy")+1);
memcpy(buffer2, "s2 strncpy", strlen("s2 strncpy")+1);
/* 重複しない領域へのcopy s2の文字列がnより長い */
char* restrict s1 = buffer1;
char* restrict s2 = buffer2;
n = 2;
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複しない領域へのcopy s2の文字列がnより短い */
s1 = buffer1;
s2 = buffer2;
n = 20;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その1 */
memset(buffer3, '\0', sizeof(buffer3));
s1 = &buffer3[0];
s2 = &buffer3[1];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
/* 重複する領域へのcopy その2 */
memset(buffer3, '\0', sizeof(buffer3));
s1 = &buffer3[1];
s2 = &buffer3[0];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
}
', sizeof(buffer3));
s1 = &buffer3[1];
s2 = &buffer3[0];
n = 11;
fprintf(stdout, "\n\n");
/* s2 -> s1へコピー */
strcpy(s1, "");
strcpy(s2, "s2 strncpy");
fprintf(stdout, "before strncpy s1[%s]\n", s1);
fprintf(stdout, "before strncpy s2[%s]\n", s2);
fprintf(stdout, "n[%d]\n", (int)n);
fprintf(stdout, ">after strncpy s1[%s]\n", strncpy(s1, s2, n));
}
s2で指定したポインタが指す文字列の長さが、nで指定したサイズより長い場合、自動的に終端文字(’\0’)は付加されません。
重複する領域へのcopy その2の実行結果は、想像とだいぶ違う結果になりました。(重複する領域の場合の動作は未定義なのでこれはこれで正しい動作)
strncpyの使い方まとめ
- s1のポインタが指すメモリ領域は、最低でもnで指定したバイトサイズ以上の領域が必要。
- nのサイズがs2のポインタが指す文字列の長さよりも小さい場合、文字列の終端文字(’\0’)は付加されない。
- s1、s2のポインタが指すメモリ領域は、重複しない領域を指定しなければならない。