トピック: C 言語の一般的な関数

目次

序文

1.strlen関数 

(1) strlen()の基本的な使い方

(2) strlen()の戻り値の型

(3) my_strlen()をカスタマイズする

2.strcpy関数

(1) strcpy()の基本的な使い方

(2) my_strcpy()をカスタマイズする

3. strcat関数

(1) strcat()の基本的な使い方

(2) my_strcat()をカスタマイズする

4.strcmp関数

(1) strcmp()の基本的な使い方

(2) my_strcmp()をカスタマイズする

5.str-とstrn-

(1) 長さ無制限の文字列関数

(2) 長さ制限のある文字列関数

6.strstr関数

(1) strstr()の基本的な使い方

(2) my_strstr()をカスタマイズする

(3) strchr関数、strrchr関数

7.strtok関数

(1) strtok()の基本的な使い方

8.strerror関数

(1) strerror()の基本的な使い方

9. その他の文字列操作関数

入出力機能

大文字小文字変換機能

文字列検索

10.共通キャラクター操作機能

入出力機能

大文字小文字変換機能

文字分類機能

11.memset関数

(1) memset()の基本的な使い方

12.memcpy関数

(1) memcpy()の基本的な使い方

(2) my_memcpy()をカスタマイズする

13.memmove関数

(1) memmove()の基本的な使い方

(2) my_memcpy()をカスタマイズする

14.memcmp関数

(1) memcmp()の基本的な使い方

要約する



序文

        C 言語の関数をいくつか学んだ後、著者は問題を解決するときにそれらをほとんど使用しません (strlen を除く)。偉い人たちの賢い問題解決のアイデアや方法を観察した後、彼はいつもため息をつきます。「すべてのコードを理解できた。なぜそうしなかったのか」私はこれをします?それで、上司の考えに従ってもう一度書くつもりだったのですが、書けないことに気づきました。このような初心者の方も少なくないと思うので、また改めて勉強して、その学習過程をここで共有していきたいと思います。

注: この記事では主に、いくつかの一般的な文字 (文字列) 操作関数 ( string ) とメモリ関連関数 ( mem ory ) について説明します。最も一般的に使用される関数のみをマスターする必要があり、必要に応じて他の関数を探すことができます。

機能学習の推奨サイト:cplusplus.com


1.strlen関数 

(1) strlen()の基本的な使い方

#include <stdio.h>
#include <string.h>

int main()
{
	char arr1[] = "abcdefg";
	char arr2[] = "abc\0defg";
	char arr3[20] = "abcdef";
	char arr4[] = { 'a','b','c','d','e','f','g' };
	char* arr5 = "abcde";
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	printf("%d\n", strlen(arr3));
	printf("%d\n", strlen(arr4));
	printf("%d\n", strlen(arr5));
	return 0;
}

機能: strlen() 関数は、文字列内の '\0' より前に現れる文字数 ('\0' を除く) を返します。通常の結果を返すには、パラメーターが指す文字列が「\0」で終わる必要があります。


(2) strlen()の戻り値の型

int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abcdefg";
	printf("part1:>");
	if (strlen(arr1) - strlen(arr2) < 0)
	{
		printf("arr1的长度小于arr2\n");
	}
	else
	{
		printf("arr1的长度大于等于arr2\n");
	}

	printf("part2:>");
	if ((int)strlen(arr1) - (int)strlen(arr2) < 0)
	{
		printf("arr1的长度小于arr2\n");
	}
	else
	{
		printf("arr1的长度大于等于arr2\n");
	}
	return 0;
}

注: strlen() 関数を使用して計算された 2 つの文字列の長さの減算は、常に 0 以上です。strlen() 関数の戻り値は、私たちが想像していた int 型ではないことがわかりますが、符号なし型 size_t

 

(3) my_strlen()をカスタマイズする

自定义my_strlen()函数类型为 int 或 size_t 都可以,但它们各有优缺点
例如:如果为 int 类型,就比较方便理解 strlen(arr1) - strlen(arr2) 这样
的形式,但是如果出现意料之外的错误,就有可能导致计算的字符串长度为负数

//创建临时变量-计数器count
int my_strlen1(const char *arr)
{
	assert(arr != NULL);
	int count = 0;
	while (*arr != '\0')
	{
		count++;
		arr++;
	}
	return count;
}

//不创建临时变量-递归
int my_strlen2(const char* arr)
{
	assert(arr != NULL);
	if (*arr != '\0')
		return 1 + my_strlen2(arr + 1);
	else
		return 0;
}

int main()
{
	char arr[] = "abcdefghijk";
	int ret = my_strlen1(arr);
	printf("%d\n", ret);
	printf("%d\n", my_strlen2(arr));
	return 0;
}


2.strcpy関数

(1) strcpy()の基本的な使い方

パラメータ 1: ターゲット文字列 (変更可能でなければなりません); パラメータ 2: ソース文字列 (変更を防ぐために const 型として定義されます)。

下面哪些代码可以正常运行?

	char arr1_src[] = "abcdef";
	char arr1_dest[] = { 0 };
	strcpy(arr1_dest, arr1_src);
	printf("%s\n", arr1_dest);
//arr1_dest 所占空间太小
	char arr2_src[] = { 'a','b','c','d','e','f' };
	char arr2_dest[20] = { 0 };
	strcpy(arr2_dest, arr2_src);
	printf("%s\n", arr2_dest);
//arr2_src 中没有 '\0' 
	char arr3_src[] = "abc\0def";
	char arr3_dest[20] = { 0 };
	strcpy(arr3_dest, arr3_src);
	printf("%s\n", arr3_dest);
//仅成功拷贝 \0 之前的 abc 三个字符
	char arr4_src[] = "abcdef";
	char* arr4_dest = "qwerty";
	strcpy(arr4_dest, arr4_src);
	printf("%s\n", arr4_dest);
//目标空间为 const char 类型,不可被修改
	char arr5_src[] = "abcdef";
	char arr5_dest[20] = { 0 };
	strcpy(arr5_dest, arr5_src);
	printf("%s\n", arr5_dest);
//拷贝成功

機能: ソース文字列をコピーし、ターゲット文字列に保存します。ソース文字列は '\0' で終わる必要があります。ソース文字列の '\0' はターゲット スペースにコピーされます。ターゲット スペースは、ターゲット スペースに十分な大きさが必要です。ソース文字列が保存できることを確認してください。宛先スペースは可変である必要があります。

(2) my_strcpy()をカスタマイズする

char* my_strcpy(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}

int main()
{
	char arr1[] = "Practise makes perfect!";
	char arr2[30] = { 0 };
	my_strcpy(arr2, arr1);
	printf("%s", arr2);
	return 0;
}


3. strcat関数

(1) strcat()の基本的な使い方

パラメータ 1: メイン文字列、パラメータ 2: 追加文字列。

int main()
{
	char arr1_dest[20] = "hello ";
	char arr1_src[] = "world!";
	strcat(arr1_dest, arr1_src);
	printf("%s\n", arr1_dest);

	char arr2_dest[20] = "hello \0xxxxxx";
	char arr2_src[] = "world!";
	strcat(arr2_dest, arr2_src);
	printf("%s\n", arr2_dest);

	char arr3_dest[20] = { 'h','e','l','l','o',' '};
	char arr3_src[] = "world!";
	strcat(arr3_dest, arr3_src);
	printf("%s\n", arr3_dest);

	char arr4_dest[20] = "hello ";
	char arr4_src[] = { 'w','o','r','l','d', '!'};
	strcat(arr4_dest, arr4_src);
	printf("%s\n", arr4_dest);
	return 0;
}

機能: ソース文字列の内容 ('\0' を含む) をターゲット文字列の '\0' の末尾に接続して (ターゲット文字列の '\0' を覆う)、ターゲット領域が十分に大きいことを確認します。そして変更することができます。

(2) my_strcat()をカスタマイズする

char* my_strcat(char* dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest != '\0')
	{
		dest++;
	}
	while (*dest++ = *src++)
	{
        ;
	}
	return ret;
}

int main()
{
	char arr1[30] = "hello ";
	char arr2[30] = "world!";
	my_strcat(arr1, arr2);
	printf("%s", arr1);
	return 0;
}

思考:
my_strcat(arr1, arr1);
可以达到如下效果吗?
arr1 = "hello hello ";


4.strcmp関数

(1) strcmp()の基本的な使い方

char arr1[] = "compare";
char arr2[] = "compbre";
//可以这样比较字符串的大小吗?
if("compare" == "compbre")
    printf("相等\n");
//这里实际上比较的是它们的地址
//因此这种写法是不能满足我们的需要的



int main()
{
	char arr1[] = "abcde";
	char arr2[] = "abcde";
	char arr3[] = "abcdabcd";
	char arr4[] = "z";

	if (strcmp(arr1, arr2) > 0)
		printf("arr1 > arr2\n");
	else if (strcmp(arr1, arr2) == 0)
		printf("arr1 = arr2\n");
	else
		printf("arr1 < arr2\n");

	if (strcmp(arr1, arr3) > 0)
		printf("arr1 > arr3\n");
	else if (strcmp(arr1, arr3) == 0)
		printf("arr1 = arr3\n");
	else
		printf("arr1 < arr3\n");

	if (strcmp(arr1, arr4) > 0)
		printf("arr1 > arr4\n");
	else if (strcmp(arr1, arr4) == 0)
		printf("arr1 = arr4\n");
	else
		printf("arr1 < arr4\n");
	return 0;
}

機能: 文字列のサイズを比較します。まず、2 つの文字列の最初の要素の ASCII コードのサイズを比較します。それらが等しい場合は、次の要素を逆方向に比較します。2 つの文字列のすべての要素が等しい場合は、次のことができます。サイズを判断します。2 つの文字列が等しいです。

(2) my_strcmp()をカスタマイズする

int my_strcmp(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);
	while (*arr1 == *arr2)
	{
		if (*arr1 == '\0')
			return 0;
		arr1++;
		arr2++;
	}
	return *arr1 - *arr2;
}

int main()
{
	char arr1[] = "compare";
	char arr2[] = "compbre";
	int ret = my_strcmp(arr1, arr2);

	if (my_strcmp(arr1, arr2) > 0)
		printf("arr1 > arr2\n");
	else if(my_strcmp(arr1, arr2) == 0)
		printf("arr1 = arr2\n");
	else
		printf("arr1 < arr2\n");
	return 0;
}


5.str-とstrn-

(1) 長さ無制限の文字列関数

前に紹介した strlen、strcpy、strcmp、および strcat 関数のパラメータとして使用される文字列の長さは制限されていないため、次のようなセキュリティ リスクが残る可能性があります。

(2) 長さ制限のある文字列関数

長さが制限された文字列関数は比較的安全なだけであり、バグを書きたければ誰もそれを止めることはできません。

strnlen、strncpy、strncmp、strncat

例えば:


6.strstr関数

(1) strstr()の基本的な使い方

パラメータ 1: 親文字列、パラメータ 2: 部分文字列。

機能: str2 が str1 の部分文字列であるかどうかを判断し、str1 内で str2 が最初に出現するアドレスを返します。

(2) my_strstr()をカスタマイズする

char* my_strstr(const char* arr1, const char* arr2)
{
	assert(arr1 && arr2);
	char* str1 = NULL;
	char* str2 = NULL;
	char* p = (char*)arr1;
	while (*p)
	{
		str1 = p;
		str2 = (char*)arr2;
		while (*str1 && *str2 && *str1++ == *str2++)
		{
			;
		}
		if (*str2 == '\0')
			return p;
		p++;
	}
	return NULL;
}

int main()
{
	char arr1[] = "qwereabcd";
	char arr2[] = "were";
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了:%s中存在字符串%s\n", ret, arr2);
	}
	return 0;
}

(3) strchr関数、strrchr関数

int main()
{
	char arr1[] = "abcdefgbcde";
	char arr2[] = "bcde";
	char* ret1 = strchr(arr1, 'b');
	if (ret1 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret1);
	}
	char* ret2 = strrchr(arr1, 'b');
	if (ret2 == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("%s\n", ret2);
	}
	return 0;
}

概要: strchr はターゲット文字が最初に検出されたときのアドレスを返し、strrchr はターゲット文字が最後に検出されたときのアドレスを返します。


7.strtok関数

(1) strtok()の基本的な使い方

パラメータ 1: 分割する文字列、パラメータ 2: 定数文字列 (分割文字のセット)。

0

機能: 「マーク」: 分割する文字列のセグメントへのポインタを返します (元の文字列を分割するには、元の文字列の分割文字を '\0' に変更します)。

strtok の最初のパラメータが NULL でない場合、関数は最初のトークンを返し、文字列内のその位置を保存します。

strtok の最初のパラメータが NULL の場合、関数は保存された位置から開始して次のマークを検索します。

文字列内にそれ以上のトークンがない場合は、NULL を返します。

とりあえず strtok 関数の使い方を覚えれば十分だと思います

興味のある学生は自分で試してみることができます。また、コメント エリアで自分の my_strtok を共有することも歓迎します。


8.strerror関数

(1) strerror()の基本的な使い方

パラメータ: エラーコード

機能: エラー コードに対応するエラー メッセージの最初の文字のアドレスを返します (エラー コードをエラー メッセージに変換します)。

説明: C 言語ライブラリ関数の呼び出しが失敗すると、エラー コードが errno という変数に格納されます。どのようなエラーが発生したかを知りたい場合は、strerror を使用してエラー コード errno をエラー メッセージに変換できます。

次に例を示します。


9. その他の文字列操作関数

入出力機能

文字列 (スペースを含む) を取得します。

put は文字列を出力します

大文字小文字変換機能

strlwr 小文字に変換

strpr 大文字に変換

文字列検索

strpbrk...

strspn ...

……


10.共通キャラクター操作機能

入出力機能

getchar は文字を読み取ります

putchar は文字を出力します

大文字小文字変換機能

tower 小文字に変換します

タッパーを大文字に変換する

文字分類機能

条件が満たされる場合は true を返します

isalnum は文字か数字かを決定します
isdigital は数字かどうかを決定します
isxdigital は 16 進数かどう
かを決定します isalpha は文字かどうかを決定します isupper は大文字かどうかを決定します islower
は小文字かどうかを決定しますiscntrl は次のいずれかを決定しますそれは制御文字ですisspace はスペース、改ページ、ラインフィード、キャリッジリターン、タブ文字であるかどうかを判断します ispunct は句読点であるかどうかを判断しますisgraph はグラフィック文字であるかどうかを判断しますisprint は印刷可能文字であるかどうかを判断します





……


11.memset関数

(1) memset()の基本的な使い方

パラメータ1: 変更が必要な位置へのポインタ(ポインタ/配列名)、パラメータ2: 変更されたデータ、パラメータ3: 変更された領域のサイズ(単位はバイト)

val は int 型です

val は char 型です

要約:

(1) ptr が char 型アドレスを指す場合、value は任意の文字値にすることができます;
(2) ptr が int 型アドレスなどの非 char 型を指す場合、割り当てが正しく行われる場合、値はvalue は - 1 または 0 のみです。なぜなら、 - 1 と 0 がバイナリに変換された後は、各ビットが同じになるため、配列をすべて 0 に初期化するために memset を使用することがよくあります。
 


12.memcpy関数

(1) memcpy()の基本的な使い方

パラメータ1:コピー先空間へのポインタ(void型)、パラメータ2:コピー元空間へのポインタ(void型)、パラメータ3:コピーした空間のサイズ(単位はバイト)

注意:当我们需要拷贝字符串的时候,是否可以使用memcpy呢?答案是当然可以,
但是我们为什么不直接使用更加方便的strcpy呢?

void test1()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
}

void test2()
{
	float arr1[] = { 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0 };
	float arr2[48] = { 0.0 };
	memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%.2f ", arr2[i]);
	}
	printf("\n");
}

int main()
{
	test1();
	test2();
	return 0;
}

(2) my_memcpy()をカスタマイズする

void* my_memcpy(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	while (num--)
	{
		//我们不知道使用者需要拷贝的类型,所以采用逐字节拷贝的方法
        *(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}
	return ret;
}

void test3()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 32);
	for (int i = 0; arr2[i] != 0; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
}

int main()
{
	test3();
	return 0;
}


13.memmove関数

(1) memmove()の基本的な使い方

パラメータ1: 移動先空間へのポインタ(void型)、パラメータ2: 移動元空間へのポインタ(void型)、パラメータ3: 移動した空間のサイズ(単位はバイト)

 

次のコードは期待どおりの結果を達成できますか?

なぜ?

注: メモリが重複している場合、すべてのコンパイラが正常にコピーできないわけではありませんが、memcpy にはメモリを上書きするリスクがあります。 

(2) my_memcpy()をカスタマイズする

void* my_memmove(void* dest, const void* src, size_t num)
{
	assert(dest && src);
	void* ret = dest;
	if (dest < src)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)src + num);
		}
	}
	return ret;
}


14.memcmp関数

(1) memcmp()の基本的な使い方

パラメータ1:比較対象空間のポインタ1(void型)、パラメータ2:比較対象空間のポインタ2(void型)、パラメータ3:比較対象空間のサイズ(単位はバイト)

void test1()
{
	int arr1[] = { 1,2,3,4,5,7 };
	int arr2[] = { 1,2,3,4,6,7 };
	int ret = memcmp(arr1, arr2, 16);
	printf("%d\n", ret);//0
}

void test2()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 20);
	printf("%d\n", ret);//-1
}

void test3()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,6 };
	int ret = memcmp(arr1, arr2, 17);
	printf("%d\n", ret);//-1
}

int main()
{
	test1();
	test2();
	test3();
	return 0;
}


要約する

        以上が今日お話したいことです。実際、プログラミングを学ぶには、質問に答えるだけでなく、考えてまとめることも必要です。多くの場合、私たちは偉い上司からのひらめきが得られません(一般的に、これには上司が必要だと思います)。できるようになるまでには多大な努力が必要です))、問題解決のスキルやアイデアを記録しておくと、問題に遭遇したときにより多くの方法を用意できるようになります。この記事が皆様のお役に立てば幸いです。読者の皆様は私に対する批判や修正を歓迎します^W^

おすすめ

転載: blog.csdn.net/weixin_72501602/article/details/129194248