Sauveur du langage C (implémentation simulée des fonctions de chaîne et des fonctions de mémoire--11)

teneur

Le langage C lui-même n'a pas de type chaîne et les chaînes sont généralement placées dans des chaînes constantes ou des tableaux de caractères.

1. Fonctions de chaîne de longueur illimitée

1.1 strlen

1.2 Trois façons de simuler la fonction de bibliothèque strlen

1.3 copie de chaîne strcpy

1.4 Simuler strcpy

La fonction strcpy renvoie l'adresse de départ de l'espace cible

Le type de retour de la fonction strcpy est défini pour obtenir un accès chaîné

1.5 strcat Chaîne Ajoute

1.6 Simuler strcat

1.7 strcmp compare les chaînes

1.8 Simuler l'implémentation de strcmp

2 fonctions de chaîne avec une longueur limitée

2.1 strncpy

2.2 strncat

2.3 strncmp

2.4 strstr trouve une chaîne dans une autre

2.5 Simuler la réalisation de strstr

2.6 strtok

2.7 strerror perror renvoie le code d'erreur et le message d'erreur correspondant

2.8 Fonction de classification des caractères

3. Fonctions d'opération de mémoire

3.1 copie de données d'espace mémoire memcpy

3.2 Simuler l'implémentation de memcpy

 Dans la même copie mémoire, les espaces de données cible et source sont intersectés, nous devrions utiliser memmove

3.3 memmove peut réaliser une copie de mémoire qui se chevauche

3.4 Simulation pour réaliser memmove

Comparaison de correspondance d'octets de mémoire 3,5 memcpy

3.6 paramètre de mémoire memset en octets


Le langage C lui-même n'a pas de type chaîne et les chaînes sont généralement placées dans des chaînes constantes ou des tableaux de caractères.

1. Fonctions de chaîne de longueur illimitée

1.1 strlen

size_t strlen ( const char * str );//函数原型,注意函数的返回值为size_t

La chaîne a '\0' comme marqueur de fin et la fonction strlen renvoie le nombre de caractères qui apparaissent avant '\0' dans la chaîne (à l'exception de '\0').

char arr[] = { 'a', 'b', 'c', 'd', 'e', 'f' };//无法用strlen求字符串长度

char arr[10] = { 'a', 'b', 'c', 'd', 'e', 'f' };//限定长度,可以求

 Quel est le résultat ci-dessous ?

 if ((int)strlen("abc") - (int)strlen("qwerty") > 0)
	{
		printf(">\n");
	}
	else
	{
		printf("<=\n");
	}

Réponse : >, le résultat de la soustraction de deux nombres non signés est un nombre positif


1.2 Trois façons de simuler la fonction de bibliothèque strlen

généralement écrit

 #include <assert.h>
 #include <stdio.h>

size_t my_strlen(const char* str)
{
	int count = 0;//统计字符的个数
    assert(str);
	while (*str != '\0')
	{
		count++;
		str++;
	}
	return count;
}
 
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

récursivité

 #include <assert.h>
 #include <stdio.h>

//my_strlen("abcdef")
//1+my_strlen("bcdef")
//1+1+my_strlen("cdef")
//1+1+1+ my_strlen("def")
//1+1+1+1+ my_strlen("ef")
//1 + 1 + 1 + 1 +1+my_strlen("f")
//1 + 1 + 1 + 1 + 1 + 1+ my_strlen("")
//1 + 1 + 1 + 1 + 1 + 1 + 0 = 6
 
size_t my_strlen(const char* str)
{
    assert(str);
	if (*str != '\0')
		return 1 + my_strlen(str+1);
	else
		return 0;
}
 
int main()
{
	char arr[] = "abcd";
	//char* str = arr;
	int len = my_strlen(arr);
	printf("%d\n", len);
	return 0;
}

méthode pointeur-pointeur

 #include <assert.h>
 #include <stdio.h>


size_t my_strlen(const char* arr,int sz)//指针-指针
{
    assert(arr);
    char* right = arr + sz - 1;
    return right - arr;
}
 
int main()
{
    char arr[] = "hello";
    int sz = sizeof(arr) / sizeof(arr[0]);
    int len=my_strlen(arr,sz);
    printf("%d", len);
    return 0;
}

1.3 copie de chaîne strcpy

Copie de chaîne, copiez la chaîne source dans la chaîne d'espace cible, les questions nécessitant une attention

1. La chaîne source doit se terminer par '\0'.

char arr1[20] = {0};
char arr2[] = {'a','b','c'};//程序崩溃,没有\0

2. Le '\0' dans la chaîne source sera copié dans l'espace cible.

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

int main()
{
	
	char arr1[] = "XXXXXXXXXXXX";//arr1指向的是常量字符串,常量是不可修改的
	char arr2[] = "abcdef";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3. L'espace de destination doit être suffisamment grand pour contenir la chaîne source.

strcpy n'a pas d'importance s'il y a assez d'espace, tant que vous osez l'écrire, vous pouvez le mettre, même si le programme plante

char arr1[4] = "x";
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

4. L'espace cible doit être variable.

char* arr1 = "qwertyuiop";//arr1指向的是常量字符串,常量是不可修改的
char arr2[] = "abcdef";
strcpy(arr1, arr2);//程序崩溃

1.4 Simuler strcpy

La fonction strcpy renvoie l'adresse de départ de l'espace cible

Le type de retour de la fonction strcpy est défini pour obtenir un accès chaîné

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

int main()
{
	char arr1[20] = { 0 };
	char* arr2 = "hello bit";

	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}


1.5 strcat Chaîne Ajoute

int main()
{
	char arr1[10] =  "hello " ;
	char* arr2 = "bit";

	printf("%s\n", strcat(arr1, arr2));
	return 0;
}

Précautions:

1. La chaîne source doit se terminer par '\0'. Ajouter à partir de \0

int main()
{
	char arr1[20] =  "hello\0XXXXX" ;
	char arr2[] = "bit";

	printf("%s\n", strcat(arr1, arr2));
	return 0;
}

2. L'espace cible doit être suffisamment grand pour accueillir le contenu de la chaîne source.

3. L'espace cible doit être modifiable.

1.6 Simuler strcat

char* my_strcat(char* dest, char* src)
{
	assert(dest && src);
	char* ret = dest;
	//找目标空间中的\0
	while (*dest)
	{
		dest++;
	}
	//拷贝
	while (*dest++ = *src++)
	{
		;
	}

	return ret;
}
int main()
{
	char arr1[20] = "hello";
	char arr2[] = " bit";

	printf("%s\n", my_strcat(arr1, arr2));
	
	return 0;
}

Que diriez-vous d'ajouter la chaîne à elle-même?

La fonction de simulation que nous avons écrite ne peut pas terminer son ajout, et il n'est pas recommandé de l'utiliser comme ceci


1.7 strcmp compare les chaînes

La fonction strcmp ne compare pas la longueur de la chaîne 

mais la taille (valeur du code ASCII) du caractère à la position correspondante dans la chaîne. S'ils sont identiques, comparez la paire suivante jusqu'à ce qu'ils soient différents ou que les deux rencontrent l'ASCII de \ 0 \0 la valeur du code est 0

 règlement standard:

Si la première chaîne est supérieure à la deuxième chaîne, renvoie un nombre supérieur à 0

Si la première chaîne est égale à la deuxième chaîne, renvoie 0

Si la première chaîne est inférieure à la deuxième chaîne, renvoie un nombre inférieur à 0


1.8 Simuler l'implémentation de strcmp

int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;//相等
		}
		s1++;
		s2++;
	}
	//不相等
	return *s1 - *s2;
}

int main()
{
	char arr1[] = "abcd";
	char arr2[] = "abdc";
	int ret = my_strcmp(arr1, arr2);
	if (ret >0)
	{
		printf(">\n");
	}
	else if (ret == 0)
	{
		printf("== \n");
	}
	else
	{
		printf("<\n");
	}
	printf("%d\n", ret);
	return 0;
}

2 fonctions de chaîne avec une longueur limitée

2.1 strncpy

 Copie num caractères de la chaîne source vers l'espace de destination.

Si la longueur de la chaîne source est inférieure à num, après avoir copié la chaîne source, ajoutez 0 à la fin de la cible jusqu'à num.

char *strncpy( char *strDest, const char *strSource, size_t count );
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwewwwwww";

	strncpy(arr1, arr2, 5);

	printf("%s\n", arr1);
	return 0;
}

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "qwe";

	strncpy(arr1, arr2, 5);//不够默认补\0

	printf("%s\n", arr1);
	return 0;
}


2.2 strncat

 Ajouter num caractères de la chaîne source à l'espace de destination.

int main()
{
	char arr1[20] = "abcdef\0XXXXX";
	char arr2[] = "qwe";

	strncat(arr1, arr2, 3);//追加三个,还会再把\0放进去

	printf("%s\n", arr1);
	return 0;
}


2.3 strncmp

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "abcdq";
	int ret = strncmp(arr1, arr2, 4);//相等
	
	printf("%d\n", ret);

	return 0;
}


2.4 strstr trouve une chaîne dans une autre

char * strstr ( const char *str1, const char * str2);

 Détermine si str2 est une sous-chaîne de str1, si str2 apparaît dans str1, renvoie l'adresse de la première apparition dans str1

S'il n'est pas présent, renvoie un pointeur nul

int main()
{
	char arr1[] = "abcdef";
	char arr2[] = "de";
	char * p=strstr(arr1, arr2);
	if (p == NULL)//strstr找不到返回NULL指针,我们需要判断
	{
		printf("找不到");
	}
	else
	{
		printf("%s ", p);
	}
	return 0;
}

2.5 Simuler la réalisation de strstr

 Idée : Si la sous-chaîne à trouver est compliquée, nous avons besoin de trois pointeurs pour nous aider

Le pointeur s1 pointe vers str1 et le pointeur s2 pointe vers str2. Le pointeur cur pointe vers str1, qui est utilisé pour enregistrer l'adresse où la correspondance commence

Si les positions correspondantes des deux chaînes ne sont pas égales, str1 recule

Si égal, commencez à faire correspondre, nous devons nous souvenir de la position str1 pour commencer à faire correspondre, car il peut être égal, il peut ne pas être égal

S'il se termine par \0, str2 est une sous-chaîne de str1

S'ils ne sont pas égaux, retrouvez l'adresse de l'emplacement de l'enregistrement, revenez en arrière + 1. Revenez en arrière et recommencez la correspondance, où le pointeur str2 pointe à nouveau vers l'adresse de départ du tableau

char* my_strstr(const char* str1, const char* str2)
{
	assert(str1 && str2);

	const char* s1 = str1;
	const char* s2 = str2;

	const char* cur = str1;
	while (*cur)//cur不等于\0进来 
	{
		s1 = cur;//判断失败返回cur指向的位置
		s2 = str2;//判断失败回到起始位置

		while (*s1 && *s2 && (*s1 == *s2))//两个字符串都被查找完,没有数据了
		{
			s1++;
			s2++;
		}
		if (*s2 == '\0')//字串找到,返回记录地址
		{
			return (char*)cur;
		}
		cur++;//匹配不成功,指向下一步
	}

	return NULL;//找不到
}

int main()
{
	char arr1[] = "abbbcdef";
	char arr2[] = "bbc";
	char* ret = my_strstr(arr1, arr2);
	if (NULL == ret)
	{
		printf("找不到子串\n");
	}
	else
	{
		printf("%s\n", ret);
	}
	return 0;
}

2.6 strtok

Rôle : spécifiez le délimiteur, laissez le tableau être segmenté

char * strtok ( char * str, const char * sep );

 Le paramètre sep est une chaîne qui définit le jeu de caractères à utiliser comme séparateurs

Le premier paramètre spécifie une chaîne contenant zéro ou plusieurs jetons séparés par un ou plusieurs délimiteurs dans la chaîne sep.

La fonction strtok trouve le jeton suivant dans str, le termine par \0 et renvoie un pointeur sur ce jeton. (Remarque : la fonction strtok modifie la chaîne manipulée, de sorte que la chaîne segmentée à l'aide de la fonction strtok est généralement le contenu d'une copie temporaire et peut être modifiée.)

 Le premier paramètre de la fonction strtok n'est pas NULL, la fonction trouvera le premier jeton dans str et la fonction strtok enregistrera sa position dans la chaîne.

Le premier paramètre de la fonction strtok est NULL, et la fonction commencera à la position enregistrée dans la même chaîne et cherchera le jeton suivant.

S'il n'y a plus de jetons dans la chaîne, un pointeur NULL est renvoyé.

int main()
{
	char arr[] = "[email protected]";
  //char arr[] = "lanyangyang\0landawang\0cunba";  strtok函数会把数组变成这样
	char buf[50] = { 0 };
    const char* sep = "@. ";
	strcpy(buf, arr);
    
   //printf("%s\n", strtok(buf, sep));//只找第一个标记
   //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找
   //printf("%s\n", strtok(NULL, sep));//是从保存的好的位置开始继续往后找
优化
    char* str = NULL;
	for (str=strtok(buf, sep); str!=NULL; str=strtok(NULL, sep))
	{
		printf("%s\n", str);
	}

    return 0;
}


2.7 strerror perror renvoie le code d'erreur et le message d'erreur correspondant

char * strerror ( int errnum );

 Lorsque la fonction de bibliothèque ne parvient pas à être utilisée, elle laisse un code d'erreur errno (variable globale), similaire au code d'erreur 404 du site Web

strerror est le message d'erreur de traduction

#include <string.h>
#include <limits.h>
#include <errno.h>
#include <stdlib.h>

int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	int* p = (int*)malloc(INT_MAX);//想堆区申请内存的
	if (p == NULL)
	{
		printf("%s\n", strerror(errno));
		perror("Malloc");//perror是打印错误信息,strerror是把错误码转换成错误信息
 //只需要写字符串,然后输出错误码对应得错误信息,更加方便
		return 1;
	}
	return 0;
}

Renvoie l'adresse du premier caractère de la chaîne du message d'erreur correspondant au code d'erreur

 


2.8 Fonction de classification des caractères

une fonction Renvoie vrai si son argument remplit les conditions suivantes
iscntrl n'importe quel caractère de contrôle
espace Caractères blancs : espace ' ', saut de page '\f', saut de ligne '\n', retour chariot '\r', tabulation '\t' ou tabulation verticale '\v'
est un chiffre Chiffres décimaux 0~9
estxchiffre Chiffres hexadécimaux, y compris tous les chiffres décimaux, lettres minuscules a~f, lettres majuscules A~F
est plus bas lettres minuscules a~z
est supérieur Lettres majuscules A~Z
isalpha Lettres a~z ou A~Z
isalnum Lettres ou chiffres, a~z, A~Z, 0~9
est ponctuel Signes de ponctuation, tout caractère graphique autre que des chiffres ou des lettres (imprimable)
isgraphe n'importe quel caractère graphique
sprint Tout caractère imprimable, y compris les caractères graphiques et les espaces

 

 Exemple : isdigit reçoit la valeur du code ASCII du caractère et renvoie le type int (si c'est un caractère numérique, il renvoie non-0, si ce n'est pas un caractère tableau, il renvoie 0)

#include <ctype.h>

int main()
{
int ret = isdigit('5');//5
int ret = isdigit('Q');//0
printf("%d\n", ret);

return 0;
}
char ch = 'A';

	if (ch >= 'a' && ch <= 'z')
	{

	}

这样写很麻烦,我们一个函数搞定

int ret = islower(ch);//判断是否小写,是小写字母返回非0,否则返回0,快速判断

Conversion de caractères :

int tolower ( int c );
int toupper ( int c );
	int main()
{
	char ch = 'A';
	putchar(toupper(ch));
	putchar(tolower(ch));

	return 0;
}


3. Fonctions d'opération de mémoire

3.1 copie de données d'espace mémoire memcpy

void * memcpy ( void * destination, const void * source, size_t count );

Précautions:

1. La fonction memcpy copie un nombre d'octets de données vers l'arrière depuis l'emplacement de la source vers l'emplacement mémoire de la destination.

2. Cette fonction ne s'arrête pas lorsqu'elle rencontre '\0'.

3. S'il y a un chevauchement entre la source et la destination, le résultat de la copie est indéfini.

4. Renvoyez l'adresse de départ de la destination

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	return 0;
    memcpy(arr2,arr1,20);//拷贝20个字节
}

3.2 Simuler l'implémentation de memcpy

Idées :  

1. Lorsque l'auteur implémente la fonction memcpy, l'auteur ne sait pas quelles données vous souhaitez copier, similaire à qsort 

2. Lors de la copie, la conversion de type doit être convertie en fonction du type de données, en copiant octet par octet

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

//void* my_memcpy(void* dest, const void* src, size_t count)
//{
//	assert(src && dest);
//	while (count--)
//	{
//		*(char*)dest = *(char*)src;
//		dest = (char*)dest + 1;
//		src = (char*)src + 1;
//	}
// }

void* my_memcpy(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return ret;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[5] = { 0 };
	my_memcpy(arr2, arr1, 20);



    int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };   
    // 1 2 1 2 3 4 5 8 9 10 期望的结果
	my_memcpy(arr1+2, arr1, 20);

	return 0;
}

Mais quand on veut copier les données dans le même espace, les données sont fausses

 La raison en est que les données écrasent l'espace que nous voulons copier

 Dans la même copie mémoire, les espaces de données cible et source sont intersectés, nous devrions utiliser memmove


3.3 memmove peut réaliser une copie de mémoire qui se chevauche

void *memmove( void *dest, const void *src, size_t count );

#include <string.h>

int main()
{
		int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
		memmove(arr1 + 2, arr1, 20);
	return 0;
}

1

3.4 Simulation pour réaliser memmove

Idées :

Copier 34567 vers 12345 n'écrasera pas les données (lorsque dest<src)

 Et si on veut copier 34567 vers 45678, ça va écraser les données, on peut d'abord mettre 7 sur 8, 6 sur 7, 5 sur 6... Copier les données de l'arrière vers l'avant, pour que les données ne soient pas être couvert

 Résumé : lorsque l'adresse à copier est dest > adresse src, copiez de l'arrière vers l'avant ; lorsque dest < src, copiez de l'avant vers l'arrière

Lorsqu'il n'y a pas d'intersection entre l'espace dest et src, la relation entre l'avant et l'arrière n'a pas d'importance. Nous copions de l'arrière vers l'avant par défaut ici (pratique)

{
	if (dest > src)
	{
		;  //从后向前拷贝
	}
	else
	{
		;  //从前向后拷贝
	}
}

Il peut aussi s'écrire d'une autre manière

if (dest > src && dest<((char*)src+count))
	{
		;//从后向前拷贝
	}
	else
	{
		;//从前向后拷贝
	}

Idée de code : Le code de l'avant vers l'arrière est une simulation memcpy, de l'arrière vers l'avant, nous avons besoin de +20 octets à la fin de dest et src

void* my_memmove(void* dest, const void* src, size_t count)
{
	assert(dest && src);
	void* ret = dest;
	if (dest > src)
	{
		while (count--)
		{
	*((char*)dest + count) =  *((char*)src + count);  //count=19,正好指向最后一个字节
		}
		//从后向前拷贝
	}
	else
	{
			void* ret = dest;
			while (count--)
			{
				*(char*)dest = *(char*)src;
				dest = (char*)dest + 1;
				src = (char*)src + 1;
			}
		 //从前向后拷贝
	}
	return ret;
}


	int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr1 + 2, arr1, 20);
	//my_memmove(arr1 , arr1+2, 20);
	return 0;
}

Comparaison de correspondance d'octets de mémoire 3,5 memcpy

int memcmp ( const void * ptr1, 
 const void * ptr2, 
 size_t num );//比较的字节个数

 

 int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 1,2,3,4,0x11223305 };
	int ret = memcmp(arr1, arr2, 18);
	printf("%d\n", ret);

	return 0;
}


3.6 paramètre de mémoire memset en octets

void *memset( void *dest, int c, size_t count );//目的空间,设置的字符,字符个数

 int main()
{
	int arr[] = { 0x11111111,0x22222222,3,4,5 };
	memset(arr, 6, 20);//memset是以字节为单位来初始化内存单元的
	return 0;
}

Je suppose que tu aimes

Origine blog.csdn.net/weixin_63543274/article/details/123986777
conseillé
Classement