C言語の標準関数を自作する:文字列処理 strchr()編
目次
はじめに
C言語には、文字列の中から指定した文字を検索するための標準関数として strchr() が用意されています。
今回は、この strchr() を標準Cライブラリの strchr() を利用せずに自作します。
このプロジェクトでは、標準関数と同じインターフェースを提供するラッパー関数と、実際の処理を行うコア関数を分離する構成を採用しています。
今回作成する strchr() は、以下のような構成とします。
利用側
↓
my_strchr()
↓
core_strchr()
my_strchr() が利用側から呼び出され、実際の文字検索は core_strchr() が担当します。
なお、本プロジェクトでは、自作した標準関数模擬関数であるwrapper関数を、他の自作関数の実装から利用することを許容しています。
つまり、標準Cライブラリの関数を直接利用することは禁止しますが、自作した標準関数模擬関数については、必要に応じて他の自作関数から利用することができます。
これは、自作した標準関数群を組み合わせて利用できるようにするためのプロジェクト上の設計方針です。
1. strchr() の仕様を確認
まず、標準関数としての strchr() の仕様を確認します。
Linux環境では、以下のコマンドでmanページを確認できます。
man strchr
オンラインでは、Linux man-pagesの strchr(3) — Linux manual page を参照できます。
POSIXの仕様については、strchr(3p) — Linux manual page も参照できます。
strchr(3p) — Linux manual page
1.1 関数プロトタイプ
strchr() のプロトタイプは以下です。
char *strchr(const char *s, int c);
strchr() は、第1引数で指定された文字列の中から、第2引数で指定された文字を検索し、最初に見つかった位置へのポインタを返します。
第2引数は int ですが、検索時には unsigned char に変換された値として扱われます。
また、第1引数には const char * が指定されています。
これは、検索対象となる文字列の内容を strchr() が変更しないためです。
一方、戻り値は char * となっています。
検索に成功した場合、文字列中の該当文字へのポインタが返され、そのポインタを利用して検索結果の文字を変更することもできます。
今回の自作関数でも、標準 strchr() と同じインターフェースを提供します。
1.2 文字の検索
strchr() は、文字列の先頭から順番に文字を調べ、第2引数で指定された文字と一致する最初の位置を検索します。
例えば、
文字列
+---+---+---+---+---+----+
| H | e | l | l | o | \0 |
+---+---+---+---+---+----+
↑
l
に対して、
strchr("Hello", 'l');
を実行すると、最初の 'l' へのポインタが返されます。
つまり、"Hello" の後半の 'l' ではなく、最初の 'l' が検索結果になります。
検索結果のポインタから文字列として利用すると、
llo
を指していることになります。
重要なのは、strchr() は文字列全体を返すのではなく、検索対象の文字が存在する位置へのポインタを返すという点です。
1.3 戻り値
strchr() は、検索対象の文字が見つかった場合、その文字へのポインタを返します。
例えば、
char lv_Buffer[] = "Hello";
char *lp_Result = strchr(lv_Buffer, 'l');
とした場合、lp_Result は最初の 'l' を指します。
そのため、
printf("%s\n", lp_Result);
とすると、
llo
と表示されます。
検索対象の文字が文字列中に存在しない場合は NULL を返します。
例えば、
char *lp_Result = strchr(lv_Buffer, 'x');
では、'x' が存在しないため、
lp_Result == NULL
となります。
1.4 '\0' の検索
strchr() では、文字列の終端を表す '\0' も検索対象に含まれます。
例えば、
char lv_Buffer[] = "Hello";
char *lp_Result = strchr(lv_Buffer, '\0');
とした場合、lp_Result は文字列終端の '\0' を指します。
つまり、
H e l l o \0
↑
戻り値
となります。
これは strchr() を実装する際に重要な点です。
検索処理は、通常の文字を検索している間だけ続けるのではなく、'\0' そのものが検索対象になる可能性も考慮する必要があります。
そのため、
while ('\0' != ap_String[lv_Index])
のような条件だけでループを終了すると、strchr(s, '\0') を正しく処理できません。
現在の文字を確認してから、検索対象との一致を判定し、その後に '\0' であるかを確認する必要があります。
1.5 検索対象の文字について
strchr() の第2引数は int ですが、検索対象として扱われる値は unsigned char に変換されます。
例えば、
strchr(lv_Buffer, 'A');
のように通常の文字を指定できます。
また、
strchr(lv_Buffer, '\0');
のように終端文字を指定することもできます。
第2引数は int ですが、実際の検索対象となる文字との比較では、unsigned char として扱う必要があります。
これは、char が符号付きとなる環境でも文字値を正しく扱うためです。
今回の実装でも、この仕様を考慮します。
1.6 NULL ポインタについて
strchr() に NULL ポインタを渡した場合の動作は未定義です。
そのため、
strchr(NULL, 'A');
のような呼び出しを標準 strchr() の正常な仕様として扱うことはできません。
今回の自作関数では、この点についてプロジェクト独自の仕様を設定しています。
独自仕様の詳細については、標準関数の仕様とは分けて扱います。
2. 実装
ここまでで、標準 strchr() の基本的な仕様を確認しました。
では、先程確認した仕様を参考に、実際に strchr() を自作してみます。
今回のプロジェクトでは、利用側から呼び出されるラッパー関数と、実際の処理を行うコア関数を分離しています。
実装はこちらです。
2.1 コア関数のヘッダーファイル
まず、コア関数のインターフェースをヘッダーファイルに定義します。
#ifndef CORE_STRING_CORE_STRCHR_H
#define CORE_STRING_CORE_STRCHR_H
#include "myc/myc_define.h"
EXTERN char *core_strchr(const char *ap_String, int av_Character);
#endif
第1引数は検索対象となる文字列であり、処理中に内容を変更しないため const char * とします。
第2引数は標準 strchr() と同じく int 型とします。
戻り値は標準 strchr() と同じ char * とします。
ヘッダーファイルには、プロジェクトのコーディングルールに従い、インクルードガードを付けています。
2.2 コア関数の実装
コア関数では、実際の文字検索を行います。
#include <stddef.h>
#include "core/string/core_strchr.h"
char *core_strchr(const char *ap_String, int av_Character)
{
size_t lv_Index = 0;
if (NULL == ap_String) {
return NULL;
}
while (1) {
if ((unsigned char)av_Character ==
(unsigned char)ap_String[lv_Index]) {
return (char *)&ap_String[lv_Index];
}
if ('\0' == ap_String[lv_Index]) {
return NULL;
}
lv_Index++;
}
}
この実装では、文字列の現在位置を確認し、検索対象の文字と一致した場合には、その位置へのポインタを返します。
一致しなかった場合に '\0' であれば、文字列の終端まで検索しても対象文字が見つからなかったことになるため NULL を返します。
この順番にすることで、
core_strchr("ABC", '\0');
の場合には、'\0' へのポインタを正しく返すことができます。
また、検索対象の文字を unsigned char に変換して比較することで、標準 strchr() の仕様に合わせています。
ここでは検索対象文字列が const char * であるため、戻り値として返す際には char * へキャストしています。
2.3 ラッパー関数のヘッダーファイル
次に、利用側から呼び出されるラッパー関数のインターフェースをヘッダーファイルに定義します。
#ifndef WRAPPER_STRING_MY_STRCHR_H
#define WRAPPER_STRING_MY_STRCHR_H
#include "myc/myc_define.h"
/*
* 標準関数: strchr.
*
* 文字列から指定した文字を検索し、最初に一致した文字へのポインタを返します.
*
* 引数:
* ap_String : 検索対象の文字列.
* av_Character : 検索する文字.
*
* 戻り値:
* 文字発見 : 発見した文字へのポインタ.
* 未発見 : NULL.
* NULL入力 : NULL.
*
* 標準strchrとの相違点:
* 標準strchrではNULLポインタを渡した場合の動作は未定義です.
* 本関数ではNULLを独自に処理し、NULLを返します.
*/
EXTERN char *my_strchr(const char *ap_String, int av_Character);
#endif
こちらのヘッダーファイルにも、インクルードガードを付けています。
なお、wrapper側については、利用側に対して標準C関数と同じインターフェースを提供することを優先します。
2.4 ラッパー関数の実装
ラッパー関数では、利用側から呼び出される my_strchr() を実装します。
#include "core/string/core_strchr.h"
#include "wrapper/string/my_strchr.h"
char *my_strchr(const char *ap_String, int av_Character)
{
return core_strchr(ap_String, av_Character);
}
ラッパー自身は文字検索を行わず、コア関数へ処理を委譲しています。
このように、今回の実装では、
my_strchr()
↓
core_strchr()
↓
文字を検索
という役割分担になっています。
ここまでで、実際に動作する my_strchr() の実装を確認しました。
ただし、このコードには、本プロジェクト独自の仕様やコーディング規約が反映されています。
ここからは、なぜこのような実装になっているのかを、要件定義や設計の観点から説明します。
3. 自作 strchr() 関数の要件定義
標準 strchr() の仕様を確認し、実装も確認したところで、今回作成する my_strchr() にどのような要件を設定したのかを整理します。
3.1 正常系
以下を正常系の要件とします。
- 第1引数が指す文字列から、第2引数で指定された文字を検索する。
- 文字列の先頭から検索する。
- 最初に一致した文字へのポインタを返す。
-
'\0'も検索対象に含める。 - 指定した文字が見つからない場合は
NULLを返す。 - 第2引数は
int型とする。 - 通常の文字について、標準
strchr()と同じ結果になる。 - 文字列が有効なC文字列であることを前提とする。
- 戻り値の型は
char *とする。
3.2 安全性
標準 strchr() の仕様とは別に、自作関数独自の安全性要件を追加します。
- 第1引数が
NULLの場合はNULLを返す。 - 第1引数が
NULLの場合は、文字列へのアクセスを行わない。 - 第2引数については
int型の値そのものを受け取るため、NULLチェックの対象とはしない。
したがって、今回の自作関数では、
NULL / 'A' → NULL
"Hello" / 'l' → 最初の 'l' へのポインタ
"Hello" / 'x' → NULL
"Hello" / '\0' → 終端文字へのポインタ
となります。
この NULL に対する動作は標準 strchr() の仕様ではなく、今回の自作関数独自の安全性要件です。
4. 基本設計
strchr() の基本的な処理は、文字列の先頭から1文字ずつ確認し、指定された文字と一致する位置を探すことです。
処理の流れは以下のようになります。
- 第1引数が
NULLか確認する。 -
NULLの場合はNULLを返す。 - 文字列の現在位置の文字を確認する。
- 現在位置の文字と検索対象の文字が一致するか確認する。
- 一致した場合は、その位置へのポインタを返す。
- 一致しなかった場合、現在位置が
'\0'か確認する。 -
'\0'の場合は検索対象が存在しないためNULLを返す。 -
'\0'でなければ、次の文字へ進む。 - 一致する文字が見つかるまで繰り返す。
特に重要なのは、'\0' も検索対象になることです。
例えば、
strchr("ABC", '\0');
では、文字列終端へのポインタを返す必要があります。
そのため、単純に、
while ('\0' != ap_String[lv_Index])
としてしまうと、'\0' を検索できません。
現在位置の文字と検索対象を先に比較し、一致しなかった場合に '\0' であるかを確認する設計とします。
5. 詳細設計
5.1 NULL チェック
最初に検索対象となる文字列が NULL か確認します。
if (NULL == ap_String) {
return NULL;
}
これにより、今回の独自安全性仕様では、NULL ポインタを参照せずに処理を終了できます。
5.2 文字の比較
文字列を先頭から順番に確認します。
検索対象となる文字は int 型で渡されますが、strchr() の仕様に合わせ、比較時には unsigned char に変換して扱います。
if ((unsigned char)av_Character ==
(unsigned char)ap_String[lv_Index]) {
return (char *)&ap_String[lv_Index];
}
この比較によって、指定した文字と一致した場合には、その文字へのポインタを返します。
同じ文字が複数存在する場合でも、先頭から順番に検索しているため、最初に一致した位置で処理を終了します。
5.3 '\0' の処理
指定した文字が見つからなかった場合、現在位置が '\0' かを確認します。
if ('\0' == ap_String[lv_Index]) {
return NULL;
}
ただし、'\0' 自体が検索対象の場合には、先に文字の一致判定が行われるため、
strchr(ap_String, '\0');
では '\0' へのポインタが返されます。
例えば、
A B C \0
↑
戻り値
となります。
この順序が strchr() の実装では重要です。
5.4 検索結果
検索対象の文字が見つかった場合、その位置へのポインタを返します。
例えば、
H e l l o \0
↑
'l'
の場合、
char *lp_Result = my_strchr(lv_Buffer, 'l');
とすると、lp_Result は最初の 'l' を指します。
そのため、
printf("%s\n", lp_Result);
とすると、
llo
と表示されます。
5.5 見つからない場合
文字列の終端まで検索しても指定した文字が見つからなかった場合は NULL を返します。
例えば、
char *lp_Result = my_strchr(lv_Buffer, 'x');
で、lv_Buffer が "Hello" の場合、
lp_Result == NULL
となります。
6. コーディング規約
本プロジェクトでは、独自のコーディング規約を定めています。
今回のコア関数では、特に以下の規約を適用します。
なお、本プロジェクトでは、core側にはコーディング規約を厳格に適用し、wrapper側については標準Cと同名・同引数・同型のインターフェースを提供することを優先します。
また、自作した標準関数模擬関数であるwrapper関数は、他の自作関数から利用することを許容します。
一方、標準Cライブラリの関数をcore関数から直接利用することは禁止します。
6.1 引数とローカル変数の命名
core側では、引数には a、ローカル変数には l のプレフィックスを付けます。
また、ポインタ系と値系を区別します。
例えば、
const char *ap_String;
int av_Character;
size_t lv_Index;
のようにします。
6.2 ポインタのNULL判定
ポインタの NULL 判定は、暗黙的な真偽値判定を行わず、明示的に NULL と比較します。
if (NULL == ap_String) {
return NULL;
}
6.3 比較演算子の記述
== および != による比較では、右辺に変数を記述するというルールを採用しています。
例えば、
if (NULL == ap_String)
や、
if ((unsigned char)av_Character ==
(unsigned char)ap_String[lv_Index])
のように記述します。
6.4 コメント
// によるコメントを記述する場合は、必ず半角のピリオド . で終了します。
6.5 インクルードガード
ヘッダーファイルには、必ずインクルードガードを付けます。
インクルードガードには、ヘッダーファイルのパスをもとにした一意なマクロ名を使用します。
#ifndef CORE_STRING_CORE_STRCHR_H
#define CORE_STRING_CORE_STRCHR_H
/* ヘッダーの内容. */
#endif
7. テスト
今回は、主に以下の2種類の試験を実施します。
7.1 要件達成試験
標準 strchr() と自作 my_strchr() の検索結果を比較します。
strchr() の戻り値は検索結果となる文字へのポインタであるため、以下を確認します。
- 標準
strchr()の戻り値が、標準strchr()に渡した文字列中の期待する位置を指している。 - 自作
my_strchr()の戻り値が、自作my_strchr()に渡した文字列中の期待する位置を指している。 - 検索結果が標準
strchr()と自作my_strchr()で一致する。 - 指定した文字が存在しない場合、両方とも
NULLを返す。
標準関数との比較には、標準 strchr() が仕様範囲内となる入力だけを使用します。
以下のような入力を使用します。
- 通常の文字を検索するケース。
- 先頭の文字を検索するケース。
- 中間の文字を検索するケース。
- 末尾の文字を検索するケース。
- 同じ文字が複数存在するケース。
- 存在しない文字を検索するケース。
- 空文字列から検索するケース。
-
'\0'を検索するケース。
特に、同じ文字が複数存在する場合には、最初に出現した文字へのポインタが返されることを確認します。
7.2 安全性試験
NULL 入力については、標準 strchr() と比較せず、自作関数が独自に設定した安全性要件を満たしているかを確認します。
今回の要件では、以下を確認します。
my_strchr(NULL, 'A') → NULL
また、通常の入力について、指定した文字が存在しない場合に NULL を返すことも要件達成試験で確認します。
8. テストコード
テストコードでは、要件達成試験として標準 strchr() と自作 my_strchr() の検索結果を比較します。
また、戻り値については、検索対象文字列の先頭から同じ位置を指していることを確認します。
検索結果が NULL の場合についても、標準関数と自作関数の両方が NULL を返すことを確認します。
'\0' を検索するケースでは、文字列終端へのポインタが返されることを確認します。
安全性試験では NULL を入力し、自作関数独自の安全性要件を確認します。
#include <stdio.h>
#include <string.h>
#include "wrapper/string/my_strchr.h"
static int test_character(
const char *ap_Source,
int av_Character)
{
char *lp_Expected;
char *lp_Actual;
lp_Expected = strchr(ap_Source, av_Character);
lp_Actual = my_strchr(ap_Source, av_Character);
printf("[要件達成試験] \"%s\", '%c'\n",
ap_Source,
(char)av_Character);
printf(" 標準 strchr : %p\n", (void *)lp_Expected);
printf(" 自作 my_strchr : %p\n", (void *)lp_Actual);
if ((NULL == lp_Expected) || (NULL == lp_Actual)) {
if (lp_Expected != lp_Actual) {
printf(" 結果 : FAIL\n\n");
return 1;
}
} else {
if ((lp_Expected - ap_Source) !=
(lp_Actual - ap_Source)) {
printf(" 結果 : FAIL\n\n");
return 1;
}
printf(" 検索結果 : \"%s\"\n", lp_Actual);
}
printf(" 結果 : PASS\n\n");
return 0;
}
static int test_null(void)
{
int lv_Result = 0;
char *lp_Actual;
lp_Actual = my_strchr(NULL, 'A');
printf("[安全性試験] NULL / 'A'\n");
printf(" 自作 my_strchr : %p\n", (void *)lp_Actual);
if (NULL != lp_Actual) {
printf(" 結果 : FAIL\n\n");
lv_Result = 1;
} else {
printf(" 結果 : PASS\n\n");
}
return lv_Result;
}
static int test_null_character(void)
{
char lv_Expected[] = "Hello";
char lv_Actual[] = "Hello";
char *lp_Expected;
char *lp_Actual;
lp_Expected = strchr(lv_Expected, '\0');
lp_Actual = my_strchr(lv_Actual, '\0');
printf("[要件達成試験] '\\0' 検索\n");
printf(" 標準 strchr : %p\n", (void *)lp_Expected);
printf(" 自作 my_strchr : %p\n", (void *)lp_Actual);
if ((NULL == lp_Expected) ||
(NULL == lp_Actual) ||
((lp_Expected - lv_Expected) !=
(lp_Actual - lv_Actual))) {
printf(" 結果 : FAIL\n\n");
return 1;
}
printf(" 終端位置 : %td\n",
lp_Actual - lv_Actual);
printf(" 結果 : PASS\n\n");
return 0;
}
int main(void)
{
int lv_Result = 0;
lv_Result |= test_character("Hello", 'H');
lv_Result |= test_character("Hello", 'e');
lv_Result |= test_character("Hello", 'l');
lv_Result |= test_character("Hello", 'o');
lv_Result |= test_character("Hello", 'x');
lv_Result |= test_character("", 'A');
lv_Result |= test_character("Apple", 'p');
lv_Result |= test_character("APPLE123", 'P');
lv_Result |= test_null_character();
lv_Result |= test_null();
if (0 == lv_Result) {
printf("=== 全試験 PASS ===\n");
} else {
printf("=== 試験 FAIL ===\n");
}
return lv_Result;
}
このテストコードでは、標準 strchr() に対して NULL を渡していません。
NULL 入力については、自作関数独自の安全性試験として分離しています。
また、テストコードはプロジェクトのコーディング規約の適用対象外としているため、標準ライブラリの strchr() を試験目的で利用しています。
なお、strchr() の戻り値は文字列中の位置を指すポインタであるため、文字列内容そのものを比較するのではなく、検索対象文字列の先頭から何文字目を指しているかを比較しています。
9. テスト結果
実装したコードをコンパイルおよび実行し、以下の結果となりました。
[要件達成試験] "Hello", 'H'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "Hello"
結果 : PASS
[要件達成試験] "Hello", 'e'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "ello"
結果 : PASS
[要件達成試験] "Hello", 'l'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "llo"
結果 : PASS
[要件達成試験] "Hello", 'o'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "o"
結果 : PASS
[要件達成試験] "Hello", 'x'
標準 strchr : 0
自作 my_strchr : 0
結果 : PASS
[要件達成試験] "", 'A'
標準 strchr : 0
自作 my_strchr : 0
結果 : PASS
[要件達成試験] "Apple", 'p'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "pple"
結果 : PASS
[要件達成試験] "APPLE123", 'P'
標準 strchr : ...
自作 my_strchr : ...
検索結果 : "PPLE123"
結果 : PASS
[要件達成試験] '\0' 検索
標準 strchr : ...
自作 my_strchr : ...
終端位置 : 5
結果 : PASS
[安全性試験] NULL / 'A'
自作 my_strchr : 0
結果 : PASS
=== 全試験 PASS ===
通常の文字について、標準 strchr() と自作 my_strchr() が同じ位置を指すことを確認しました。
また、指定した文字が存在しない場合に NULL を返すことも確認しました。
さらに、'\0' を検索した場合に文字列終端を指すポインタが返されることを確認しました。
安全性試験では、NULL が渡された場合に文字列へのアクセスを行わず、NULL を返すことを確認しました。
今回の実行では、すべての試験がPASSとなりました。
なお、実行結果に表示されるポインタ値は環境によって異なります。
%p による NULL の表示については、今回の環境では 0 と表示されています。
10. 単体試験について
単体試験では、関数内部の処理を意識してテストケースを設計します。
今回の core_strchr() は処理が比較的単純であるため、以下のようなケースを用意することで、主要な処理を確認できます。
| 入力 | 確認内容 |
|---|---|
NULL / 'A' |
第1引数NULLの場合の早期終了。 |
"" / 'A' |
空文字列から検索するケース。 |
"A" / 'A' |
先頭の文字が一致するケース。 |
"ABC" / 'B' |
中間の文字が一致するケース。 |
"ABC" / 'C' |
末尾の文字が一致するケース。 |
"ABC" / 'X' |
文字が存在しないケース。 |
"ABC" / 'A' |
先頭で一致して処理を終了するケース。 |
"ABCABC" / 'B' |
複数存在する文字について最初の位置を返すケース。 |
"ABC" / '\0' |
終端文字を検索するケース。 |
今回の実際のテストコードでは、my_strchr() を介した要件達成試験および安全性試験を実施しています。
一方、core_strchr() の内部処理を直接対象とした独立した単体試験については、今後必要に応じて追加します。
今回はカバレッジ計測自体は実施していません。
単純な関数についてカバレッジ計測を必須とすると、関数の実装やテストそのものよりも、計測環境の構築にかかる負担が大きくなるためです。
ただし、単体試験ではC1程度の分岐を意識してテストケースを設計しています。
今後、実装が複雑になり、テストケースだけでは処理の網羅性を確認しにくい関数については、必要に応じてカバレッジ計測を実施します。
11. 安全性について
今回の my_strchr() では、標準 strchr() にはない NULL チェックを追加しています。
if (NULL == ap_String) {
return NULL;
}
これにより、NULL が渡された場合には文字列へのアクセスを行わずに処理を終了できます。
ただし、これは strchr() の標準仕様そのものではありません。
そのため、
- 標準関数としての仕様。
- 自作関数に追加した安全性要件。
を分けて考える必要があります。
また、NULL チェックを追加したとしても、文字列が有効な領域を指していることや、終端 '\0' が存在することまで検証できるわけではありません。
例えば、文字列として使用できない不正なポインタを渡した場合や、終端 '\0' が存在しない領域を渡した場合については、今回の安全性要件の対象外です。
今回の安全性向上は、あくまで NULL ポインタへの対応に限定されます。
また、strchr() は検索対象の文字列を変更しませんが、戻り値は char * であるため、検索に成功した場合には戻り値を通じて該当文字を変更することもできます。
例えば、
char lv_Buffer[] = "Hello";
char *lp_Result = my_strchr(lv_Buffer, 'l');
if (NULL != lp_Result) {
*lp_Result = 'L';
}
とすると、
Hello
↓
HeLlo
のように、最初に見つかった文字を変更できます。
ただし、このような操作を行う場合は、検索対象が変更可能な文字列領域であることが利用側の責任となります。
12. まとめ
今回は、標準Cライブラリの strchr() を参考に、自作の my_strchr() を実装しました。
要件定義から設計、実装、試験まで一通り行い、通常の文字検索に加えて、'\0' の検索や NULL に対する独自仕様についても想定した動作になることを確認しました。
今回の実装を通して、C言語における文字列検索や '\0'、ポインタ、const、char と unsigned char の基本についても確認できました。
🔗 関連記事・関連リンク
この記事の最新アップデートや、このシリーズの関連記事は以下のリンクからご覧いただけます。
内容は基本的に同じですので、お好みのサイトでお読みください。
🟢 Zenn
-
この記事の最新版はこちら。
-
「標準関数自作」Topicの関連記事はこちら。
🔵 Qiita
-
「標準関数自作」タグの関連記事はこちら。
🧡 note
-
このシリーズの記事はこちら。