字符函数、字符串函数、内存函数

字符函数、字符串函数、内存函数


如果想深度理解,请访问:https://s3.bmp.ovh/imgs/2024/08/10/9094bd873e0661bf.jpg


1、strlen函数

<string.h> 中定义

size_t strlen(const char* str)

  • 参数: str 指向要检查的空终止字符串的指针

  • 返回值: 空终止字符串 str 的长度

1.1 使用

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

int main()
{
	const char* str1 = "abcdef";
	const char* str2 = "bbb";
	if (strlen(str1) > strlen(str2))
	{
		printf(">");
	}
	else
	{
		printf("<");
	}

	return 0;
}

1.2 模拟实现

// strlen的模拟实现
# include<stdio.h>
# include<assert.h>

//int my_strlen(char* str)
//{
//	assert(str);
//	char* ret = str;
//	while (*ret != '\0')
//	{
//		ret++;
//	}
//	return ret - str;
//}
//int my_strlen(char* str)
//{
//	int count = 0;
//	assert(str);
//	while (*str)
//	{
//		count++;
//		str++;
//	}
//	return count;
//}

int my_strlen(char* str)
{
	assert(str != NULL);
 
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
}

int main()
{
	char* str = "abcdef";
	int ret = my_strlen(str);
	printf("%d\n", ret);
	return 0;
}

一共使用了三种方法:1、指针—指针 2、计数器 3、递归函数


2、strcpy函数

string.h 中定义

char* strcpy(char* dest, const char* src)

描述: 复制 src 所指向的空终止字节字符串,包含‘\0’,到首元素为 dest 所指的字符数组。

dest 数组长度不足则行为未定义。若字符串重叠则行为未定义。若 dest 不是指向字符数组的指针或 src 不是指向空终止字节字符串的指针则行为未定义。

参数:

  • dest :指向要写入的字符数组的指针, 英文destination的缩写
  • src :指向要复制的空终止字节字符串的指针,英文srcouse的缩写

返回值:

dest的副本

2.1 使用

int main()
{
	char dest[] = "abcdef";
	char str[] = "ghj";
	size_t len = strlen(strcpy(dest, str));
	printf("%zd\n", len);
	printf("%s\n", dest);
}

2.2 模拟实现

// strcpy的使用和模拟实现
// char* strcpy(char * destination, const * source)
//                           |                  |
//                          目标空间            源字符串

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

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

int main()
{
	char dest[] = "abcdef";
	char str[] = "ghj";
	my_strcpy(dest, str);
	size_t len = strlen(my_strcpy(dest, str));
	printf("%zd\n", len);
	printf("%s\n", dest);
}

3、strcat 函数

string.h 中定义

char* strcat(char* dest, const char* src)

描述:src 所指向的空终止字节字符串的副本追加到 dest 所指向的空终止字节字符串的末尾。字符 src[0] 替换 dest 末尾的空终止符。产生的字节字符串是空终止的。

​ 若目标数组对于 srcdest 的内容以及空终止符不够大,则行为未定义。若字符串重叠,则行为未定义。若 destsrc 不是指向空终止字节字符串的指针,则行为未定义。

参数:

  • dest :指向要后附到的空终止字节字符串的指针
  • src :指向作为复制来源的空终止字节字符串的指针

返回值:

​ 返回 dest 的副本。

3.1 strcat的使用

# include<stdio.h>
# include<assert.h>
int main()
{
	char arr1[20] = "hello ";
	size_t len = strlen(strcat(arr1, "world"));
	printf("%zd\n", len);
	printf("%s\n", arr1);
	return 0;
}

3.2 strcat模拟实现

# include<stdio.h>
# include<assert.h>
char* my_strcat(char* dest, const char* str)
{
	char* ret = dest;
	assert(dest && str != NULL);
	// 第一步:找到\0
	while (*dest != '\0')
	{
		dest++;
	}
	// 第二步:复制到目标字符串尾部
	while (*dest++ = *str++)
	{
		;
	}
	return ret;
	
}

size_t my_strlen(const char* str)
{
	assert(str != NULL);
	if (*str == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen(str + 1);
	}
	
}
int main()
{
	char arr1[20] = "hello ";
	// 找到目标空间的第一个'\0'
	// 然后从这个\0开始追加原字符串
	// 源字符串的内容,包括\0都会追加到目标空间
	size_t len = my_strlen(my_strcat(arr1, "world"));
	printf("%zd\n", len);
	printf("%s\n", arr1);
	return 0;
}

4、strcmp函数

string.h 中定义

int strcmp(const char* lhs, const char* rhs);

描述: 以字典序比较两个空终止字节字符串。结果的符号是被比较的字符串中首对不同字符(都转译成 unsigned char)的值间的差的符号。若 lhs 或 rhs 不是指向空终止字节字符串的指针,则行为未定义。

参数:

rhs lhs -指向要比较的空终止字符串的指针

返回值:

​ 根据地址,lhs和rhs一个一个的比较,lhs 先于 rhs 出现则为负值。若 lhs 与 rhs 比较相等则为零。若字典序中 lhs 后于 rhs 出现则为正值。

4.1 strcmp的使用

# include<stdio.h>
# include<assert.h>
int main()
{
	char dest[] = "abcdef";
	char str[] = "ghj";
	int ret = strcmp(dest, str);
	if (ret > 0)
		printf(">");
	if (ret < 0)
		printf("<");
	else
		printf("=");


	return 0;
}

4.2 strcmp的模拟实现

 模拟实现strcmp 函数
 第一个字符串大于第二个字符串时,返回大于零的数字
 第一个字符串等于第二个字符串时,返回等于零的数字
 第一个字符串小于第二个字符串时,返回小于零的数字
//
# include<stdio.h>
# include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
	assert(str1 && str2 != NULL);
	while (*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
	}
	return *str1 - *str2;
}

int main()
{
	char dest[] = "abcdef";
	char str[] = "ghj";
	int ret = my_strcmp(dest, str);
	if (ret > 0)
		printf(">");
	if (ret < 0)
		printf("<");
	else
		printf("=");


	return 0;
}

​ 接下来,我们将讲解另一个种类的,strncpy,strncmp,strncat。 可以看到和之前将讲解的函数的差别是多了一个n,这两种函数到底有什么区别呢?

5、strncpy函数

string.h 中定义

char* strncpy(char* dest, const char* src, size_t count);

描述:

复制 src 所指向的字符数组的至多 count 个字符(包含空终止字符,但不包含后随空字符的任何字符)到 dest 所指向的字符数组。

若在完全复制整个 src 数组前抵达 count,则结果的字符数组不是空终止的。

若在复制来自 src 的空终止字符后未抵达 count,则写入额外的空字符到 dest,直至写入总共 count 个字符。

若字符数组重叠,若 destsrc 不是指向字符数组的指针(包含若 destsrc 为空指针),若 dest 所指向的数组大小小于 count,或若 src 所指向的数组大小小于 count 且它不含空字符,则行为未定义。

参数:

  • dest : 指向要复制到的字符数组的指针
  • src :指向复制来源的字符数组的指针
  • count :要复制的最大字符数

返回值:

​ 返回 dest 的副本

5.1 strncpy使用

# include<stdio.h>
# include<string.h>
# include<assert.h>
int main()
{
	char arr1[] = "asdfghjk";
	char arr2[] = "++++";
    strncpy(arr1, arr2, 1);
	printf("%s\n", arr1);

	return 0;
}

5.2 strncpy模拟实现


 strncp 的使用和模拟实现
# include<stdio.h>
# include<string.h>
# include<assert.h>

char* my_strncpy(char* destination, const char* source, size_t num)
{
	assert(destination && source != NULL);
	char* ret = destination;
	int count = (int)strlen(source);
	while (num)
	{
		if (count > 0)
			*destination++ = *source++;
		else
			*destination++ = '\0';
		num--;
		count--;
	}
	return ret;
}

int main()
{
	char arr1[] = "asdfghjk";
	char arr2[] = "++++";
    // strncpy(arr1, arr2, 1);
	// 模拟实现strncpy
	my_strncpy(arr1, arr2, 7);
	printf("%s\n", arr1);

	return 0;
}

6、strncat函数

string.h 中模拟实习

char* strncat(char* dest, const char* stc, size_t count);

描述:

​ 将来自 src 所指向的字符数组的至多 count 个字符,追加到 dest 所指向的空终止字节字符串的末尾,若找到空字符则停止。字符 src[0] 替换位于 dest 末尾的空终止符。总会在末尾追加终止空字符(故函数可写入的最大字节数是 count+1)。

若目标数组没有对于 destsrc 的前 count 个字符加上终止空字符的足够空间,则行为未定义。若源与目标对象重叠,则行为未定义。若 dest 不是指向空终止字节字符串的指针,或 src 不是指向字符数组的指针,则行为未定义。

参数:

  • dest :指向要后附到的空终止字节字符串的指针
  • src:指向作为复制来源的字符数组的指针
  • count : 要复制的最大字符数

返回值:

​ 返回 dest 的副本。

注:

​ 因为 strncat 需要在每次调用时找到 dest 的结尾,故用 strncat 将多个字符串连接成一体是低效的

6.1 strncat使用

# include<stdio.h>

int main()
{
	char arr1[20] = "abcdefg";
	char arr2[] = "hujfdjs";
	strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

6.2 strncat模拟实现

 strncat函数的使用和模拟实现
# include<stdio.h>
# include<assert.h>
# include<string.h>

char* my_strncat(char* destination, const char* source, size_t num)
{
	assert(destination && source != NULL);
	char* ret = destination;
	int count = (int)strlen(source);
	while (*destination)
	{
		destination++;  // 这里要格外注意,不可以将++放在while中
	}
	while (num)
	{
		if (count >= 0)
			*destination++ = *source++;
		else
			*destination++ = '\0';
		num--;
		count--;
	}
	return ret;
}

int main()
{
	char arr1[20] = "abcdefg";
	char arr2[] = "hujfdjs";
	//strncat(arr1, arr2, 3);
	// strncat 函数的模拟实现
	my_strncat(arr1, arr2, 3);
	printf("%s\n", arr1);
	return 0;
}

7、strncmp函数

string.h 中定义

int strncmp(const char* lhs, count char* rhs, size_t count);

描述:

​ 比较两个可能空终止的数组的至多 count 个字符。按字典序进行比较。不比较后随空字符的字符。

结果的符号是被比较的数组中首对字符(都转译成 unsigned char)的值间的差的符号。

若出现越过 lhs 或 rhs 结尾的访问,则行为未定义。若 lhs 或 rhs 为空指针,则行为未定义。

参数:

  • lhs,rhs-指向要比较的可能空终止的数组的指针
  • count - 要比较的最大字符数

返回值:

​ 若地址中 lhs 先于 rhs 出现则为负值。

​ 若 lhsrhs 比较相等,或若 count 为零,则为零。

​ 若地址中 lhs 后于 rhs 出现则为正值。

7.1 strncmp使用

# include<stdio.h>
int main()
{
	char arr1[20] = "abcdefg";
	char arr2[] = "abcfdjs";
	int ret = strncmp(arr1, arr2, 4);
	if (ret > 0)
		printf(">");
	if (ret < 0)
		printf("<");
	else
		printf("=");
	return 0;
}

7.2 strncmp模拟实现

// strncmp函数的使用和模拟实现
# include<stdio.h>
# include<string.h>
# include<assert.h>

int my_strncmp(const char* str1, const char* str2, size_t num)
{
	assert(str1 && str2);
	while(*str1 == *str2)
	{
		if (*str1 == '\0')
			return 0;
		str1++;
		str2++;
		num--;
		if (num == 0)
			return 0;
	}
	return *str1 - *str2;
}

int main()
{
	char arr1[20] = "abcdefg";
	char arr2[] = "abcfdjs";
	int ret = my_strncmp(arr1, arr2, 4);
	if (ret > 0)
		printf(">");
	if (ret < 0)
		printf("<");
	else
		printf("=");
	return 0;
}

8、strstr函数

string.h 中定义

char* strstr(const char* str, const char* substr);

描述:

查找 `substr` 所指的空终止字节字符串在 `str` 所指的空终止字节字符串中的首次出现。不比较空终止字符。若 `str` 或 `substr` 不是指向空终止字节字符串的指针,则行为未定义。

参数:

  • str:指向要检验的空终止字节字符串的指针
  • substr:指向要查找的空终止字节字符串的指针

返回值:

​ 指向于 str 中找到的子串首字符的指针,或若找不到该子串则为空指针。若 substr 指向空字符串,则返回 str

8.1 strstr使用

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

int main()
{
	char arr1[20] = "cmdfgabcdrty";
	char arr2[] = "abc";
	char* ret = strstr(arr1, arr2);
	if (ret == NULL)
		printf("没有找到。\n");
	else
		printf("找到了:%s\n", ret);
	return 0;
}

8.2 strstr模拟实现

// strstr函数的使用和模拟实现
# include<stdio.h>
# include<assert.h>
# include<string.h>

char* my_strstr(const char* destination, const char* source)
{
	assert(destination && source != NULL);
	const char* str1 = destination;
	const char* str2 = source;
	const char* nul = destination; // 标记起始地址
	while (*nul)
	{
		str1 = nul;
		str2 = source;
		while (*str1 && *str2 && *str1 == *str2)
		{
			str1++;
			str2++;
		}
		if (*str2 == 0)
			return nul;
		nul++;
	}
	return NULL;

}

int main()
{
	char arr1[20] = "cmdfgabcdrty";
	char arr2[] = "abc";
	// char* ret = strstr(arr1, arr2);
	// strstr函数的模拟实现
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
		printf("没有找到。\n");
	else
		printf("找到了:%s\n", ret);
	return 0;
}

9、strtok 函数

string.h 中定义

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

描述:

寻找 str 所指向的空终止字节字符串中的下个记号。由 delim 所指向的空终止字节字符串鉴别分隔字符。

此函数被设计为进行多次调用以从同一字符串获得相继的记号。

  • str 不是空指针,则调用被当做 strtok 对此特定字符串的首次调用。函数搜索首个含于 delim 的字符。

  • 若找不到这种字符,则 str 中完全没有记号,而函数返回空指针

  • 若找到这种字符,则它是记号的起始。然后函数从该点搜索首个含于 delim 的字符。

  • 若找不到这种字符,则 str 只有一个记号,而将来对 strtok 的调用将返回空指针

  • 若找到这种字符,则用空字符 ‘\0替换它,并将指向下个字符的指针存储于静态位置,以为后继调用所用

  • str 为空指针,则将调用当做对 strtok 的后继调用,函数从先前调用中它剩下的位置开始。行为如同将先前存储的指针作为 str 传递。

    strdelim 不是指向空终止字节字符串的指针,则行为未定义。

参数:

  • str : 指向要记号化的空终止字节字符串的指针
  • delim:指向标识分隔符的空终止字节字符串的指针

返回值:

​ 返回指向下个记号起始的指针,或若无更多记号则返回空指针。

注解:

​ 此函数是破坏性的:它写入 ‘\0’ 字符到字符串 str 的元素。特别是,不能以字符串字面量为 strtok 的首参数。每次对 strtok 的调用都会修改静态对象:它不是线程安全的。不同于大多数其他记号化器,strtok 中的分隔符能对于后继记号不同,而且甚至能依赖于先前记号的内容。

// strtok的使用
# include<stdio.h>
# include<assert.h>
# include<string.h>

int main()
{
	char arr1[] = "zpengwei@yeah.net";
	char arr2[] = "192@168#110.123";
	char sep[] = "@#.";
	char buf[40] = { 0 };
	strcpy(buf, arr1);
	char* p = NULL;

	for (p = strtok(buf, sep); p != NULL; p = strtok(NULL, sep))
	{
		printf("%s\n", p);
	}
	return 0;
}

10、strerror函数

string.h 中定义

char* strerror( int errnum );

描述:

​ 返回指向系统错误码 errnum 的文本表示的指针,它等同于 perror() 会打印的描述。

errnum 通常从 errno 变量获得,不过函数接受可任何 int 类型值。字符串的内容特定于本地环境。

程序必须不修改返回的字符串,但对 strerror 函数的后继调用可能重写该字符串。不要求 strerror 为线程安全。实现可以返回指向静态只读字符串字面量的不同指针,或反复返回指向静态缓冲区的同一指针,strerror 放置字符串于该缓冲区中。

返回值:

  • 指向对应 errno 错误码 errnum 的空终止字节串的指针。

  • 若整个消息完整存储于 buf 则为零,否则为非零。

// strerror函数的使用
# include<stdio.h>
# include<string.h>
# include<errno.h>

int main()
{
	int i = 0;
	for (i = 0; i <= 10; i++)
	{
		printf("%2d : %s\n",i, strerror(i));
	}
	return 0;
}

//int main()
//{
//	FILE* pf = fopen("test.txt", "r");
//	if (pf == NULL)
//	{
//		perror("文件打开失败,原因是");
//	}
//	return 0;
//}

//int main()
//{
//	FILE* pf = fopen("test.txt","r");
//	if (pf == NULL)
//	{
//		printf("打开失败的原因是:%s\n", strerror(errno));
//	}
//	return 0;
//}


内存函数

动态内存分配允许程序在运行时根据需要分配内存,而不是在编译时固定大小。这使得程序能够处理不确定大小的数据结构,如动态数组、链表等。

11、内存函数

string.h 中定义

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

  • 函数memcpy从source的位置开始向后复制num个字节的数据到destination指向的内存位置。
  • 这个函数在遇到 ‘\0’ 的时候并不会停下来。
  • 如果source和destination有任何的重叠,复制的结果都是未定义的。

11.1memcpy使用

# include<stdio.h>
# include<string.h>
int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9,10};
	int arr2[10] = {5,6,7};
	memcpy(arr2, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

11.2 memcpy模拟实现

// memcpy用函数的使用和模拟实现
// memcpy 是按字节复制,不考虑数据的类型或内容。这个函数在遇到 '\0' 时候不会停下
// strncpy 是按字符复制,并且会考虑字符串的结束符。
// void* memcpy(void * destination, const void * source, sizeof_t num)
# include<stdio.h>
# include<string.h>
# include<assert.h>

void* my_memcpy(void* destination, const void* source, size_t num)
{
	assert(destination && source != NULL);
	void* ret = destination;
	while (num--)
	{
		*(char*)destination = *(char*)source;
		(char*)destination += 1;
		(char*)source += 1;
	}
	return ret;
}

int main()
{
	int arr1[] = {1,2,3,4,5,6,7,8,9,10};
	int arr2[10] = {5,6,7};
	//memcpy(arr2, arr1, 5 * sizeof(int));
	//memcpy函数的模拟实现
	my_memcpy(arr2, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	return 0;
}

12、memmove函数

string.h 中定义

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

描述:

​ 从 src 所指向的对象复制 count 个字节到 dest 所指向的对象。两个对象都被转译成 unsigned char 的数组。对象可以重叠(这是与memcpy的重要区别):如同复制字符到临时数组,再从该数组到 dest 一般进行复制。 若在 dest 数组末尾之后发生访问则行为未定义。若 destsrc 为非法或空指针则行为未定义。

参数:

  • dest:指向复制目标对象的指针
  • src:指向复制来源对象的指针
  • count : 要复制的字节数

返回值:

​ 返回 dest 的副本,本质为更底层操作的临时内存地址,在实际操作中不建议直接使用此地址,操作完成以后,真正有意义的地址是dest本身。

12.1 memmove使用

# include<stdio.h>
# include<string.h>
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 5,6,7 };
	memove(arr1+3, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;

	return 0;
}

12,2 memmove模拟实现

 memmove函数的模拟实现, memmove和memcpy相差无几,
 唯一的区别是memmove可以很好地处理destination和source相同地情况
 也即是说 memmove支持重叠内存区域
# include<stdio.h>
# include<string.h>
# include<assert.h>

void* my_memove(void* des, const char* sour, size_t num)
{
	assert(des && sour != NULL);
	void* ret = des;
	if ((char*)des >= (char*)sour + num || (char*)sour >= (char*)des + num)
	{
		while (num--)
		{
			*(char*)des = *(char*)sour;
			(char*)des += 1;
			(char*)sour += 1;
		}
	}
	else
	{
		(char*)des = (char*)des + num ;
		(char*)sour = (char*)sour + num ;
		while (num--)
		{
			*(--(char*)des) = (*(--(char*)sour));
		}
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 5,6,7 };
	my_memove(arr1+3, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

12,2 memmove模拟实现

 memmove函数的模拟实现, memmove和memcpy相差无几,
 唯一的区别是memmove可以很好地处理destination和source相同地情况
 也即是说 memmove支持重叠内存区域
# include<stdio.h>
# include<string.h>
# include<assert.h>

void* my_memove(void* des, const char* sour, size_t num)
{
	assert(des && sour != NULL);
	void* ret = des;
	if ((char*)des >= (char*)sour + num || (char*)sour >= (char*)des + num)
	{
		while (num--)
		{
			*(char*)des = *(char*)sour;
			(char*)des += 1;
			(char*)sour += 1;
		}
	}
	else
	{
		(char*)des = (char*)des + num ;
		(char*)sour = (char*)sour + num ;
		while (num--)
		{
			*(--(char*)des) = (*(--(char*)sour));
		}
	}
	return ret;
}

int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[10] = { 5,6,7 };
	my_memove(arr1+3, arr1, 5 * sizeof(int));
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;

	return 0;
}

在这里插入图片描述


未完待续!!!!

评论 13
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值