teneur
1. Fonctions de chaîne de longueur illimitée
1.2 Trois façons de simuler la fonction de bibliothèque strlen
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.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.4 strstr trouve une chaîne dans une autre
2.5 Simuler la réalisation de strstr
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
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;
}