2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

C言語 string.hの自作実装 (順次実装)

Last updated at Posted at 2026-01-02

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の中で実装しないもの(確定してるもの)
  1. strcoll
  2. strxfrm
  3. 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
2
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?