C言語におけるstring.hを自作で実装してみた(実装途中)
目次
| 番号 | 内容 |
|---|---|
| 1 | 前置き |
| 2 | 探索関数 |
| 2-1 | strchr.c |
| 2-2 | strrchr.c |
| 2-3 | strcspn.c |
| 2-4 | strspn.c |
| 2-5 | strpbrk.c |
| 2-6 | strstr.c |
| 3 | 比較関数 |
| 3-1 | strcmp.c |
| 3-2 | strncmp.c |
| 4 | コピー関数 |
| 4-1 | strcpy.c |
| 4-2 | strncpy.c |
| 5 | 連結関数 |
| 5-1 | strcat.c |
| 5-2 | strncat.c |
| 6 | その他 |
| 6-1 | strlen.c |
前置き
- 一旦ここまで(1/15)で関数作りは休止します。また気が向いたら作ります。(すぐ作るかも?作らないかも?)
- 自分のC言語の練習のためにstring.hにある関数を自作してみました。
- 独学&趣味&勉強で行っています。
- そのため、実装がコロコロ変わります。
- 解説は書きません(間違っていたら大変なので)。
- 実装している順番に意味はありません。
- 学習のついでにコードの置き場として使っています。
- 順番として
Cファイル -> ヘッダファイル -> 使い方
の順で並べてます。 - 追記: string.hの中で実装しないもの(確定してるもの)
- strcoll
- strxfrm
- strerror
他のものはできるだけ実装を試みてみますが、自作で無理そうならここに追記します(例えばmem系の関数とか)
環境
- OS: Windows 11
- エディタ(IDE): Microsoft Visual Studio Community 2026
追記
1/3 strcspn.cを追加
1/3 軽妙な修正
1/4 strcspn.cを書き直し
1/7 strchr.cを書き直し
1/7 strrchr.cを書き直し
1/7 strcspn.cを書き直し
1/8 軽妙な修正
1/8 strspn.cを追加
1/10 strpbrk.cを追加
1/10 strcmp.cを追加
1/10 軽妙な修正
1/11 軽妙な修正 (VScode + gcc -> MSVC)
1/12 strcmp.cを書き直し
1/12 strncmp.cを追加
1/12 strncmp.cを書き直し
1/13 軽妙な修正
1/13 strcpy.cを追加
1/14 strncpy.cを追加
1/14 strcat.cを追加
1/14 strncat.cを追加
1/15 strstr.cを追加
1/16 軽妙な修正
探索関数
strchr.c
- 文字列内を先頭から走査し、指定した文字が最初に出現した位置へのポインタを返す関数
- 無い場合はNULLを返す
- 指定した文字がヌル文字の場合はヌル文字のアドレスを返す
mystrchr.c
#include <stddef.h>
char *mystrchr(const char *s, int c) {
// 終端文字までsの各文字とcを比較
while ( *s ) {
// sの文字とcの文字が一致したとき
if ( *s == (char)c ) {
return (char*)s;
}
// sの文字を右に一つずらす (s++ = s + 1)
s++;
}
// cが終端文字のとき
if ( (char)c == '\0' ) {
return (char*)s;
}
// cが見つからなかったとき
return NULL;
}
mystrchr.h
#ifndef MYSTRCHR_H
#define MYSTRCHR_H
char *mystrchr(const char *s, int c);
#endif /* MYSTRCHR_H */
使い方
main.c
#include <stdio.h>
#include "mystrchr.h"
int main(void) {
// s
char s[] = "ABCDEF";
// c
int c = 'D';
// mystrchr.c
char *p = mystrchr(s, c);
if ( p != NULL ) {
printf("%s: %c is line %d.\n", s, c, p - s);
}
else {
printf("c is no exist.\n");
}
return 0;
}
bash
$ gcc main.c mystrchr.c -o main
$ ./main
ABCDEF: D is line 3.
strrchr.c
- 文字列内を末尾から逆方向に走査し、指定した文字が最後に出現した位置へのポインタを返す関数
mystrrchr.c
#include <stddef.h>
char *mystrrchr(const char *s, int c) {
// 戻り値 (cと一致した場所)
char *position = NULL;
// 終端文字までsの各文字とcを比較
while ( *s ) {
// sの文字とcの文字が一致したとき
if ( *s == (char)c ) {
position = (char*)s;
}
// sの文字を右に一つずらす (s++ = s + 1)
s++;
}
// c = 終端文字のとき
if ( (char)c == '\0' ) {
return (char*)s;
}
// cと一致したアドレス or NULLを返す
return position;
}
mystrrchr.h
#ifndef MYSTRRCHR_H
#define MYSTRRCHR_H
char *mystrrchr(const char *s, int c);
#endif /* MYSTRRCHR_H */
使い方
main.c
#include <stdio.h>
#include "mystrrchr.h"
int main(void) {
// s
char s[] = "ABCDCBA";
// c
int c = 'A';
// mystrrchr.c
char *p = mystrrchr(s, c);
if ( p != NULL ) {
printf("%s: %c is line %d.\n", s, c, p - s);
}
else {
printf("%c is no exist.\n", c);
}
return 0;
}
bash
$ gcc main.c mystrrchr.c -o main
$ ./main
ABCDCBA: A is line 6.
strcspn.c
- 文字列の先頭から、指定した『拒否文字リスト』に含まれるいずれかの文字が最初に出現するまでの文字数を返す関数
mystrcspn.c
#include <stddef.h>
size_t mystrcspn(const char *s1, const char *s2) {
// 戻り値 (s1の文字列の中の先頭からs2の文字が含まれていない長さ)
size_t len = 0;
// チェック表
unsigned char check[256] = { 0 };
// チェック表のs2の文字の場所を1に書き換える
while ( *s2 ) {
check[(unsigned char)*s2] = 1;
// s2の文字を右に一つずらす
s2++;
}
// チェック表のs1の項目が1かどうか比較
while ( *s1 ) {
// チェック表とs1を比較して、チェック表が1ならその時点での長さを返す
if ( check[(unsigned char)*s1] ) {
return len;
}
// s1の文字を右に一つずらす
s1++;
// 戻り値の位置を右にずらす
len++;
}
// s2の文字がs1に無かったとき
return len;
}
mystrcspn.h
#ifndef MYSTRCSPN_H
#define MYSTRCSPN_H
size_t mystrcspn(const char *s1, const char *s2);
#endif /* MYSTRCSPN_H */
使い方
main.c
#include <stdio.h>
#include "mystrcspn.h"
int main(void) {
// s1
char s1[] = "abcde";
// s2
char s2[] = "ed";
// mystrcspn.c
size_t len = mystrcspn(s1, s2);
printf("len: %zu.\n", len);
return 0;
}
bash
$ gcc main.c mystrcspn.c -o main
$ ./main
len: 3.
strspn.c
- 文字列の先頭から、指定した『許可文字リスト』に含まれる文字だけで構成されている区間の長さを返す関数
mystrspn.c
#include <stddef.h>
size_t mystrspn(const char *s1, const char *s2) {
// 戻り値 (s1の文字列の先頭からs2の文字列の文字のいづれかが含まれている長さ)
size_t len = 0;
// チェック表
unsigned char check[256] = { 0 };
// チェック表のs2の文字の場所を1に書き換える
while ( *s2 ) {
check[(unsigned char)*s2] = 1;
// s2の文字を右に一つずらす
s2++;
}
// チェック表のs1の項目が1かどうか比較
while ( *s1 ) {
// チェック表とs1を比較して、チェック表が0ならその時点での長さを返す
if ( !(check[(unsigned char)*s1]) ) {
return len;
}
// s1の文字を右に一つずらす
s1++;
// 戻り値の位置を右にずらす
len++;
}
// s2の文字がs1に無かったとき
return len;
}
mystrspn.h
#ifndef MYSTRSPN_H
#define MYSTRSPN_H
size_t mystrspn(const char *s1, const char *s2);
#endif /* MYSTRSPN_H */
使い方
main.c
#include <stdio.h>
#include "mystrspn.h"
int main(void) {
// s1
char s1[] = "abcdef";
// s2
char s2[] = "abc";
// mystrspn.c
size_t len = mystrspn(s1, s2);
printf("len: %zu.\n", len);
return 0;
}
bash
$ gcc main.c mystrspn.c -o main
$ ./main
len: 3.
strpbrk.c
- 文字列内を走査し、指定した文字セットに含まれるいずれかの文字が最初に出現した位置へのポインタを返す関数
mystrpbrk.c
#include <stddef.h>
char *mystrpbrk(const char *s1, const char *s2) {
// チェック表
unsigned char check[256] = { 0 };
// チェック表のs2の位置を1に置き換える (*s2 != '\0')
while ( *s2 ) {
check[(unsigned char)*s2] = 1;
// s2の文字を右に一つずらす
s2++;
}
// チェック表のs1の項目が1かどうか比較 (*s1 != '\0')
while ( *s1 ) {
// チェック表とs1を比較して、チェック表が1ならその時点でのアドレスを返す
if ( check[(unsigned char)*s1] ) {
return (char*)s1;
}
// s1の文字を右に一つずらす
s1++;
}
// s1の中にs2の文字が無かったとき
return NULL;
}
mystrpbrk.h
#ifndef MYSTRPBRK_H
#define MYSTRPBRK_H
char *mystrpbrk(const char *s1, const char *s2);
#endif /* MYSTRPBRK_H */
使い方
main.c
#include <stdio.h>
#include "mystrpbrk.h"
int main(void) {
// s1
char s1[] = "abcdef";
// s2
char s2[] = "ef";
// mystrpbrk.c
char *p = mystrpbrk(s1, s2);
if ( p != NULL ) {
printf("%s -- %s: position is %d.\n", s1, s2, p - s1);
} else {
printf("%s is not exist in %s.\n", s1, s2);
}
return 0;
}
bash
$ gcc main.c mystrpbrk.c -o main
$ ./main
abcdef -- ef: position is 4.
strstr.c
- 文字列内を走査し、指定した部分文字列が最初に出現した位置へのポインタを返す関数
- もっと速く探索できるらしい(私には厳しそう...)
mystrstr.c
#include <stddef.h>
char *mystrstr(const char *s1, const char *s2) {
// 戻り値
char *position = NULL;
// s1のアドレスを保存
char *s1_ptr = (char *)s1;
// s2のアドレスを保存
char *s2_ptr = (char *)s2;
// s1のアドレスを保存
size_t s1_len = 0;
// s2のアドレスを保存
size_t s2_len = 0;
// s1とs2の一致度
size_t count = 0;
// s1の文字の位置を右に一つずらす
size_t next = 0;
// s1の文字数をカウント
while( *s1_ptr ) {
s1_len++;
s1_ptr++;
}
// s2の文字数をカウント
while( *s2_ptr ) {
s2_len++;
s2_ptr++;
}
// s1のアドレスを初期化
s1_ptr = (char *)s1;
// s1のアドレスを初期化
s2_ptr = (char *)s2;
// s2が空文字のとき
if( *s2 == '\0' ) {
// s1のアドレスを返す
return (char *)s1;
}
// s1の文字列の長さがs2の文字列の長さ以下になるまで
while( s1_len >= s2_len ) {
// s1の文字とs2の最初の文字が一致したとき
while( *s1_ptr == *s2_ptr ) {
// positionがNULLのとき
if( position == NULL ) {
// 現在のs1のアドレスを代入
position = s1_ptr;
}
// s1の文字を右に一つずらす
s1_ptr++;
// s2の文字を右に一つずらす
s2_ptr++;
// 一致度をインクリメント
count++;
// s2の文字列と一致度が等しいとき
if( s2_len == count ) {
return position;
}
}
// 初期化
position = NULL;
next++;
s1_ptr = (char *)s1 + next;
s2_ptr = (char *)s2;
s1_len--;
count = 0;
}
// s1の文字列とs2の文字列が一致しなかったとき
return NULL;
}
mystrstr.h
#ifndef MYSTRSTR_H
#define MYSTRSTR_H
char *mystrstr(const char *s1, const char *s2);
#endif /* MYSTRSTR_H */
使い方
main.c
#include <stdio.h>
#include "mystrstr.h"
int main(void) {
// s1
char s1[] = "ababc";
// s2
char s2[] = "abc";
// mystrstr.c
char *p1 = mystrstr(s1, s2);
printf("%s position is %d in %s.\n", s2, p1 - s1, s1);
return 0;
}
bash
$ gcc main.c mystrstr.c -o main
$ ./main
abc position is 2 in ababc.
比較関数
strcmp.c
- 2つの文字列を先頭から順に比較し、最初に異なる文字の大小関係(または一致)を整数値で返す関数
mystrcmp.c
#define POSITIVE_NUM 1
#define NEGATIVE_NUM -1
#define EQUAL_NUM 0
int mystrcmp(const char *s1, const char *s2) {
// 差分
int diff = 0;
// s1とs2のどちらかが終端文字に到達するまでループ
while ( *s1 || *s2 ) {
// s1の文字とs2の文字を比較
diff = (unsigned char)*s1 - (unsigned char)*s2;
// s1の方が大きいとき
if ( diff > 0 ) {
return POSITIVE_NUM;
}
// s1の方が小さいとき
else if ( diff < 0 ) {
return NEGATIVE_NUM;
}
// s1の文字を右に一つずらす
s1++;
// s2の文字を右に一つずらす
s2++;
}
// s1とs2の文字が最後まで等しかったとき
return EQUAL_NUM;
}
mystrcmp.h
#ifndef MYSTRCMP_H
#define MYSTRCMP_H
int mystrcmp(const char *s1, const char *s2);
#endif /* MYSTRCMP_H */
使い方
main.c
#include <stdio.h>
#include "mystrcmp.h"
int main(void) {
// s1
char s1[] = "abe";
// s2
char s2[] = "abc";
// mystrcmp.c
int result = mystrcmp(s1, s2);
if (result == 0) {
printf("s1 and s2 is equal - %d.\n", result);
}
else if (result > 0) {
printf("s1 is bigger than s2 - %d.\n", result);
}
else if (result < 0) {
printf("s1 is smaller than s2 - %d.\n", result);
}
}
bash
$ gcc main.c mystrcmp.c -o main
$ ./main
s1 is bigger than s2 - 1.
strncmp.c
- 2つの文字列を先頭から最大 n 文字まで比較し、その範囲内での文字の大小関係を整数値で返す関数
mystrncmp.c
#include <stddef.h>
#define POSITIVE_NUM 1
#define NEGATIVE_NUM -1
#define EQUAL_NUM 0
int mystrncmp(const char *s1, const char *s2, size_t n) {
// 差分
int diff = 0;
while ( n && *s1 && *s2 ) {
// s1からs2を引くことで大小を判定する
diff = (unsigned char)*s1 - (unsigned char)*s2;
// s1の方が大きいとき
if ( diff > 0 ) {
return POSITIVE_NUM;
}// s1の方が小さいとき
else if ( diff < 0 ) {
return NEGATIVE_NUM;
}
// 比較回数をデクリメント
n--;
// s1の文字を右に一つずらす
s1++;
// s2の文字を右に一つずらす
s2++;
}
// nが0のとき
if ( n == 0 ) {
return EQUAL_NUM;
}
diff = (unsigned char)*s1 - (unsigned char)*s2;
// s1の方が大きいとき
if ( diff > 0 ) {
return POSITIVE_NUM;
}
// s1の方が小さいとき
else if ( diff < 0 ) {
return NEGATIVE_NUM;
}
// s1とs2の文字が最後まで等しかったとき
return EQUAL_NUM;
}
mystrncmp.h
#ifndef MYSTRNCMP_H
#define MYSTRNCMP_H
int mystrncmp(const char *s1, const char *s2, size_t n);
#endif /* MYSTRNCMP_H */
使い方
main.c
#include <stdio.h>
#include "mystrncmp.h"
int main(void) {
// s1
char s1[] = "abcedf";
// s2
char s2[] = "abcdef";
// n
size_t n = 4;
// mystrncmp.c
int result = mystrncmp(s1, s2, n);
if (result == 0) {
printf("s1 and s2 is equal - %d.\n", result);
}
else if (result > 0) {
printf("s1 is bigger than s2 - %d.\n", result);
}
else if (result < 0) {
printf("s1 is smaller than s2 - %d.\n", result);
}
}
bash
$ gcc main.c mystrncmp.c -o main
$ ./main
s1 is bigger than s2 - 1.
コピー関数
strcpy.c
- コピー元文字列を、終端文字 \0 を含めてコピー先メモリ領域へそのまま複写する関数
- s1の大きさは十分確保されていることに注意してください
mystrcpy.c
char *mystrcpy(char * restrict s1, const char * restrict s2) {
// 戻り値 (s1の最初のアドレスを保存)
char *s1_ptr = s1;
// s2が終端文字までループ
while( *s2 ) {
// s1にs2の文字を代入
*s1 = *s2;
// s1の文字を右へ1つずらす
s1++;
// s2の文字を右へ1つずらす
s2++;
}
// s2の終端文字を代入
*s1 = *s2;
// s1の最初のアドレスを返す
return s1_ptr;
}
mystrcpy.h
#ifndef MYSTRCPY_H
#define MYSTRCPY_H
char *mystrcpy(char * restrict s1, const char * restrict s2);
#endif /* MYSTRCPY_H */
使い方
main.c
#include <stdio.h>
#include "mystrcpy.h"
int main(void) {
// s1
char s1[256] = "Hello";
// s2
char s2[256] = "World";
// mystrcpy.c
mystrcpy(s1, s2);
printf( "s1 value: %s\n", s1 );
printf( "s2 value: %s\n", s2 );
return 0;
}
bash
$ gcc main.c mystrcpy.c -o main
$ ./main
s1 value: World
s2 value: World
strncpy.c
- コピー元から最大 n 文字を複写し、足りない場合は \0 で埋め、溢れる場合は終端させないまま終了する関数
mystrncpy.c
#include <stddef.h>
char *mystrncpy(char * restrict s1, const char * restrict s2, size_t n) {
// 戻り値 (s1の最初のアドレスを保存)
char *s1_ptr = s1;
// nが0またはs2が終端文字になるまでループ
while ( n && *s2 ) {
*s1 = *s2;
// nをデクリメント
n--;
// s1の文字を右へ1つずらす
s1++;
// s2の文字を右へ1つずらす
s2++;
}
// s2の文字列の長さよりnの大きさの方が大きいのとき
if ( n ) {
while ( n ) {
// nの分だけ終端文字を挿入
*s1 = '\0';
// nをデクリメント
n--;
// s1の文字を右へ1つずらす
s1++;
}
}
// s1の最初のアドレスを返す
return s1_ptr;
}
mystrncpy.h
#ifndef MYSTRNCPY_H
#define MYSTRNCPY_H
char *mystrncpy(char * restrict s1, const char * restrict s2, size_t n);
#endif /* MYSTRNCPY_H */
使い方
main.c
#include <stdio.h>
#include "mystrncpy.h"
int main(void) {
// s1
char s1[256] = "Hello";
// s2
char s2[256] = "World";
// n
size_t n = 4;
// mystrncpy.c
mystrncpy(s1, s2, n);
printf( "s1 value: %s\n", s1 );
printf( "s2 value: %s\n", s2 );
return 0;
}
bash
$ gcc main.c mystrncpy.c -o main
$ ./main
s1 value: Worlo
s2 value: World
連結関数
strcat.c
- コピー先文字列の末尾(\0 の位置)から、コピー元文字列を終端文字を含めて順に複写・連結する関数
mystrcat.c
char *mystrcat(char * restrict s1, const char * restrict s2) {
// 戻り値 (s1の最初のアドレスを保存)
char *s1_ptr = s1;
// s1のアドレスを終端文字まで移動
while( *s1 ) {
// s1の文字を1つ右にずらす
s1++;
}
while( *s2 ) {
// s1の終端文字からs2の文字を代入
*s1 = *s2;
// s1の文字を1つ右にずらす
s1++;
// s2の文字を1つ右にずらす
s2++;
}
// s2の終端文字を代入
*s1 = *s2;
// s1の最初のアドレスを返す
return s1_ptr;
}
mystrcat.h
#ifndef MYSTRCAT_H
#define MYSTRCAT_H
char *mystrcat(char * restrict s1, const char * restrict s2);
#endif /* MYSTRCAT_H */
使い方
main.c
#include <stdio.h>
#include "mystrcat.h"
int main(void) {
// s1
char s1[256] = "Hello";
// s2
const char s2[256] = "World";
// mystrcat.c
mystrcat(s1, s2);
printf("s1 value: %s\n", s1);
printf("s2 value: %s\n", s2);
return 0;
}
bash
$ gcc main.c mystrcat.c -o main
$ ./main
s1 value: HelloWorld
s2 value: World
strncat.c
- コピー先文字列の末尾に、コピー元から最大 n 文字を連結し、最後に必ず終端文字 \0 を付加する関数
mystrncat.c
#include <stddef.h>
char *mystrncat(char * restrict s1, const char * restrict s2, size_t n) {
// 戻り値 (s1の最初のアドレスを保存)
char *s1_ptr = s1;
// s1の文字を終端文字まで移動
while( *s1 ) {
// s1の文字を右に一つずらす
s1++;
}
while( n && *s2 ) {
// s1にs2の文字を代入
*s1 = *s2;
// nをデクリメント
n--;
// s1の文字を右に一つずらす
s1++;
// s2の文字を右に一つずらす
s2++;
}
// 最後に終端文字を代入
*s1 = '\0';
// s1の最初のアドレスを返す
return s1_ptr;
}
mystrncat.h
#ifndef MYSTRNCAT_H
#define MYSTRNCAT_H
char *mystrncat(char * restrict s1, const char * restrict s2, size_t n);
#endif /* MYSTRNCAT_H */
使い方
main.c
#include <stdio.h>
#include "mystrncat.h"
int main(void) {
// s1
char s1[256] = "Test ";
// s2
char s2[256] = "Programming";
// n
size_t n = 7;
// mystrncat.c
mystrncat(s1, s2, n);
printf("s1 value: %s\n", s1);
printf("s2 value: %s\n", s2);
return 0;
}
bash
$ gcc main.c mystrncat.c -o main
$ ./main
s1 value: Test Program
s2 value: Programming
その他
strlen.c
- 文字列の先頭から、終端文字 \0(ヌル文字)に到達する直前までの文字数をカウントして返す関数
mystrlen.c
#include <stddef.h>
// whileループによるstrlen関数の実装
size_t mystrlen_1(const char *s) {
// 戻り値 (引数の文字列の文字数を格納) (size_t = 8byte)
size_t len = 0;
// 終端文字までlenをインクリメント (*(s + len) == s[len]) (s[len] != '\0')
while ( s[len] ) {
len++;
}
// 文字列の長さを返す
return len;
}
// forループによるstrlen関数の実装
size_t mystrlen_2(const char *s) {
// 戻り値 (引数の文字列の文字数を格納) (size_t = 8byte)
size_t len = 0;
// 終端文字までlenをインクリメント
for (; s[len]; len++) {
}
// 文字列の長さを返す
return len;
}
mystrlen.h
#ifndef MYSTRLEN_H
#define MYSTRLEN_H
// whileループで実装
size_t mystrlen_1(const char *s);
// forループで実装
size_t mystrlen_2(const char *s);
#endif /* MYSTRLEN_H*/
使い方
main.c
#include <stdio.h>
#include "mystrlen.h"
int main(void) {
// s
char s[] = "Hello";
// mystrlen.c
printf("mystrlen_1: %zu\n", mystrlen_1(s));
printf("mystrlen_2: %zu\n", mystrlen_2(s));
return 0;
}
bash
$ gcc main.c mystrlen.c -o main
$ ./main
mystrlen_1: 5
mystrlen_2: 5