当前位置:首页 » 《随便一记》 » 正文

❤爆肝万字C进阶字符串+内存函数的介绍+库函数模拟实现+笔试题分析❤,建议收藏!!!_/少司命的博客

8 人参与  2021年11月07日 17:43  分类 : 《随便一记》  评论

点击全文阅读


 目录这里排版出现问题了,改了很长时间也没改好,只能图片代替,望读者见谅

一,前言 

C语言中对字符和字符串的处理很是频繁,但是C语言本身是没有字符串类型的,字符串通常放在 常量字符串中或者字符数组中。字符串常量 适用于那些对它不做修改的字符串函数。

二,字符串和内存函数

1,strlen

获取字符串的长度。

字符串已经 '\0' 作为结束标志,strlen函数返回的是在字符串中 '\0' 前面出现的字符个数(不包含 '\0' )。
参数指向的字符串必须要以 '\0' 结束。
注意函数的返回值为size_t,是无符号的( 易错 )

int main()
{
	char* p = "hello";
	char arr1[] = "hello";
	char arr2[] = { 'h', 'e', 'l', 'l', 'o' };
	printf("%d\n", strlen(p));
	//结果为5,strlen求的是字符串的长度,不包含\0
	printf("%d\n", strlen(arr1));
	//结果为5,strlen求的是字符串的长度,不包含\0
	printf("%d\n", strlen(arr2));
	//因为字符型不包含\0,但字符串需要找到\0才可结束,所以在'o'之后继续向后读取直到找到\0,所以是一个随机值
}
#include <stdlib.h>
#include <string.h>
int main()
{
	char arr[] = "abcdef";
	printf("%d\n", strlen(arr)); // 6
	printf("%d\n", strlen(arr + 1)); // 5
	printf("%d\n", strlen(&arr + 1)); //类型不匹配
	printf("%d\n", strlen(&arr)); // 类型不匹配
	printf("%d\n", strlen(&arr[0])); // 6
	printf("%d\n", strlen(*&arr)); // 6
	printf("%d\n", strlen(&*arr)); // 6
	printf("%d\n", strlen(&arr[1] + 1));//4
	return 0;
}
#include <stdlib.h>
#include <string.h>
int main()
{
	char* p = "abcdef";
	printf("%d\n", strlen(p)); // 6
	printf("%d\n", strlen(p + 1)); // 5
	printf("%d\n", strlen(*p)); // 类型不匹配
	printf("%d\n", strlen(p[1])); // 类型不匹配
	printf("%d\n", strlen(&p)); // 类型不匹配. 
	printf("%d\n", strlen(&p[1] + 1)); // 4
	printf("%d\n", strlen(*&p)); // 6
	printf("%d\n", strlen(&*p)); // 6
	return 0;
}
int main()
{
	//3-6 - -3
	//size_t
	if (strlen("abc") - strlen("abcdef") > 0)
	{
		printf("hehe\n");
	}
	else
	{
		printf("haha\n");
	}
	return 0;
}

//strcpy
//strcat
//strcmp
//长度不受限制,不稳定

2,strcpy

复制一个字符串。

源字符串必须以 '\0' 结束。
会将源字符串中的 '\0' 拷贝到目标空间。
目标空间必须足够大,以确保能存放源字符串
目标空间必须可变

int main()
{
	char arr1[] = "xxx";
	//const char* p = "xxxxxxxxxxxxxx";
	char arr2[] = "hello\0abc";
	//char arr2[] = { 'a', 'b', 'c' };
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

3,strcat

附加一个字符串

源字符串必须以 '\0' 结束。
目标空间必须有足够的大,能容纳下源字符串的内容。
目标空间必须可修改

int main()
{
	//strcpy字符串拷贝\0是停止的标志
	//stract字符串连接
	char arr1[20] = "abc";//保证数据可以放进去
	char arr2[20] = { 'd','e','f' };//无\0程序将会出现问题
	char arr3[20] = { 'd','e','f' ,'\0'};//正确书写
	strcat(arr1, arr2);
	strcat(arr1,"dssrg");
	printf("%s\n", arr1);
	return 0;
}
​
#include<string.h>
#include<stdio.h>
#include<assert.h>
my_strcat(char* dest, const char* src)
{
	assert(dest && src);

	// a b c \0
	// d e f \0
	//1,找到目标字符串的末尾\0
	//2,追加字符串直到\0
	//返回类型是char*,stract返回目标空间的起始地址
	char* ret = dest;
	while (*dest)
	{
		dest++;
	}
	//与strcpy追加相等
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
	//返回类型是char*,stract返回目标空间的起始地址
}

int main()
{
	//strcpy字符串拷贝\0是停止的标志
	//stract字符串连接
	char arr1[20] = "abc";//保证数据可以放进去
	char arr2[20] = { 'd','e','f' };//无\0程序将会出现问题
	char arr3[20] = { 'd','e','f' ,'\0' };//正确书写
	my_strcat(arr1, arr3);

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

​

4,strcmp

比较字符串。

标准规定:
第一个字符串大于第二个字符串,则返回大于0的数字
第一个字符串等于第二个字符串,则返回0
第一个字符串小于第二个字符串,则返回小于0的数字

int main()
{
	char arr1[] = "asihvw";
	char arr2[] = "asns";
	//字符串一一比较
	//返回值有三种可能性
	//发现相等\0,停下来,结果为0;
	//不同比较的是字符串对应的ascii码值
	int ret = strcmp(arr1, arr2);
	if (ret == 0)
	{
		printf("=\n");
	}
	else if (ret < 0)
	{
		printf("<\n");
	}
	else
	{
		printf(">\n");
	}
	printf("%d\n", ret);
	return 0;
}

#include<string.h>
#include<stdio.h>
#include<assert.h>
//不修改加const
int my_strcmp(const char* s1,const char* s2)
{
	assert(s1 && s2);
	// a b c d e \0
	// a d n \0
	//c与n不相等,比较ascii值
	// a b c \0
	// a b c \0
	//相等
	while (*s1 == *s2)
	{
		if (*s1 == '\0')
		{
			return 0;
		}
		s1++;
		s2++;
	}
	return *s1 - *s2;//第一个字符串小于第二字符串,返回负数

}

int main()
{
	char arr1[] = "asihvw";
	char arr2[] = "asns";
	//字符串一一比较
	//返回值有三种可能性
	//发现相等\0,停下来,结果为0;
	//不同比较的是字符串对应的assic码值
	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;
}

//strncpy
//strncat
//strncmp
//长度受限制的字符串,稳定

5,strncpy

将一个字符串的字符复制到另一个字符串。

拷贝num个字符从源字符串到目标空间。
如果源字符串的长度小于num,则拷贝完源字符串之后,在目标的后边追加0,直到num个

int main()
{
	char arr1[20] = "sho\0isfohf";
	char arr2[] = "svnv";
	//相对来说比较稳定
	strncpy(arr1, arr2, 2);
	// strncpy(arr1, arr2, 6);不够追加,前面正常拷贝,到后面凑\0,个数一定要注意限制
	printf("%s\n", arr1);
	return 0;
}

6,strncat

附加字符串的字符。

将源的第一个num字符加上终止的null字符追加到目标。

如果源中C字符串的长度小于num,则仅复制到终止的nullcharacter之前的内容

int main()
{
	char arr1[20] = "abc\0sojcss";
	char arr2[] = "chcda";
	strncat(arr1, arr2, 3);//遇到\0就停止追加
	return 0;
	
}

7,strncmp

比较两个字符串的字符。

比较到出现另个字符不一样或者一个字符串结束或者num个字符全部比较完。

int main()
{
	char arr1[] = "schcss";
	char arr2[] = "schsrgvv";
	int ret = strncmp(arr1, arr2, 3);//前三个相等,返回值为0
	printf("%d\n", ret);
	return 0;
}

8,strstr

找到一个子串。

返回指向str1中第一个出现的str2的指针,如果str2不是str1的一部分,则返回空指针

strstr字符串查找函数
帮我们在一个字符串查找子字符串是否存在
int main()
{
	char arr1[] = "i am student";
	char arr2[] = "student";
	//查找arr1中arr2第一次出现的位置
	char* ret = strstr(arr1, arr2);
		if(ret == NULL)
		{
			printf("找不到\n");
		}
		else
		{
			printf("找到了\n");
		}
	return 0;
}
char* my_strstr(const char* str1, const char* str2)
{
	//建议画图分析比较好理解
	assert(str1 && str2);
	//abcdef
	//bcd
	//找匹配的可能性,相等后一直比较是否相等,遇到\0后结束匹配
	//abbbcdef
	//bbc
	//当起始位置不相等,跳过不相等的字符,类似于第一个比较,匹配,
	//为了方便匹配,我们需要创建指针,记录从哪开始匹配,子串要回归匹配,字串\0就应该表示查找完
	//三种情况停止匹配
	char* s1;
	char* s2;
	char* cp;//cp 最开始指向abbcdef中的a,与s1匹配对应的b,跳过a,到第二个相等时,++,匹配不成功,s2回到原点b
	if (*str2 == '\0')
	{
		return str1;
	}
	while (*cp)
	{
		s1 = cp;
		s2 = str2;//比较
		while (*s1 != '\0'&& * s2 != '\0' && *s1 == *s2)
		{
			s1++;        
			s2++;
		}
		if (*s2 == '\0')
		{
			return cp;  
		}
		cp++;
	}

	return NULL;//找不到的情况
}

int main()
{
	char arr1[] = "i am student";
	char arr2[] = "student";
	//查找arr1中arr2第一次出现的位置
	char* ret = my_strstr(arr1, arr2);
	if (ret == NULL)
	{
		printf("找不到\n");
	}
	else
	{
		printf("找到了\n");
	}
	return 0;
}

9,strtok

在字符串中查找下一个标记。

sep参数是个字符串,定义了用作分隔符的字符集合
 第一个参数指定一个字符串,它包含了0个或者多个由sep字符串中一个或者多个分隔符分割的标记。
strtok函数找到str中的下一个标记,并将其用 \0 结尾,返回一个指向这个标记的指针。(注:strtok函数会改
变被操作的字符串,所以在使用strtok函数切分的字符串一般都是临时拷贝的内容并且可修改。)
strtok函数的第一个参数不为 NULL ,函数将找到str中第一个标记,strtok函数将保存它在字符串中的位置。
strtok函数的第一个参数为 NULL ,函数将在同一个字符串中被保存的位置开始,查找下一个标记。
如果字符串中不存在更多的标记,则返回 NULL 指针

//strtok
//对一个字符串切割

int main()
{
	char arr1[] = "cscwocsoe.dj";
	char arr2[100] = { 0 }; //临时数据
	char sep[] = "s.";
	strcpy(arr2, arr1);//分割字符串
	char* ret = NULL;
	for (ret = strtok(arr2, sep); ret != NULL;ret = strtok(NULL,sep))
	{
		printf("%s\n", ret);
	}
	/*strtok(arr2, sep);
	strtok(NULL, sep);*/
	return 0;
}

10,strerror

获取系统错误消息(strerror)或打印用户提供的错误消息(strerror)

返回错误码,所对应的错误信息
字符分类函数:

函数如果他的参数符合下列条件就返回真
iscntrl任何控制字符
isspace空白字符:空格‘ ’,换页‘\f’,换行'\n',回车‘\r’,制表符'\t'或者垂直制表符'\v'
isdigit十进制数字 0~9
isxdigit十六进制数字,包括所有十进制数字,小写字母a~f,大写字母A~F
islower小写字母a~z
isupper大写字母A~Z
isalpha字母a~z或A~Z
isalnum字母或者数字,a~z,A~Z,0~9
ispunct标点符号,任何不属于数字或者字母的图形字符(可打印)
isgraph任何图形字符
isprint任何可打印字符,包括图形字符和空白字符
#include <string.h>
//显示错误代码0至9的错误原因描述
int main()
{
    int i;
    for (i = 0; i < 10; i++)
    {
        printf("%d : %s\n", i, strerror(i));
    }
    return 0;
}

 11,memcpy

在缓冲区之间复制字符,内存拷贝。

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

int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7 };
	int arr2[20] = { 0 };
	memcpy(arr2, arr1, 40);//拷贝的是整型数据
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d", arr2[i]);
	}
	return 0;
}
my_memcpy(void* dest, const void* src, size_t count)
{
	void* set = dest;
	assert(dest && src);
	while (count--)
	{
		*(char*)dest = *(char*)src;
		dest = (char*)dest + 1;
		src = (char*)src + 1;
	}

	return  set;
}


int main()
{
	int arr1[10] = { 1,2,3,4,5,6,7 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 40);//拷贝的是整型数据
	int i = 0;
	for (i = 0; i < 20; i++)
	{
		printf("%d", arr2[i]);
	}
	return 0;
}

12,memmove

将一个缓冲区移动到另一个缓冲区。

和memcpy的差别就是memmove函数处理的源内存块和目标内存块是可以重叠的。
如果源空间和目标空间出现重叠,就得使用memmove函数处理。

#include<stdio.h>
#include<string.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	//            1,2,1,2,3,4,7,8,9,10
	//memcpy(arr+2, arr, 16);
	//memcpy - 只要完成了不重叠的内存拷贝就完成任务
	memmove(arr + 2, arr, 16);
	//内存拷贝时,出现内存重叠的现象,应该使用memmove
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 13,memcmp

比较两个缓冲区中的字符

 比较从ptr1和ptr2指针开始的num个字节

memcmp函数是用于比较字符串的,比较内存前N个字节;

该函数在头文件<string.h>中,函数定义为:int memcmp (const void *s1, const void *s2, size_t n);

字符串大小的比较是以ASCII 码表上的顺序来决定,次顺序亦为字符的值。memcmp()首先将s1 第一个字符值减去s2 第一个字符的值,若差为0 则再继续比较下个字符,若差值不为0 则将差值返回。例如,字符串"Ab"和"ba"比较则会返回字符'A'(65)和'b'(98)的差值(-33)。

返回值:两个字符串内容完全一样,返回0;若S1大于S2,则大于0,反之则小于0;

#include <string.h>
int main() 
{
    char* a = "aBcDeF";
    char* b = "AbCdEf";
    char* c = "aacdef";
    char* d = "aBcDeF";
    printf("memcmp(a, b):%d\n", memcmp((void*)a, (void*)b, 6));
    printf("memcmp(a, c):%d\n", memcmp((void*)a, (void*)c, 6));
    printf("memcmp(a, d):%d\n", memcmp((void*)a, (void*)d, 6));
}

#include <stdio.h>
#include <string.h>
int main()
{
	char buffer1[] = "DWgaOtP12df0";
	char buffer2[] = "DWGAOTP12DF0";
	int n;
	n = memcmp(buffer1, buffer2, sizeof(buffer1));
	if (n > 0)
		printf("'%s' is greater than '%s'.\n", buffer1, buffer2);
	else if (n < 0)
		printf("'%s' is less than '%s'.\n", buffer1, buffer2);
	else
		printf("'%s' is the same as '%s'.\n", buffer1, buffer2);
	return 0;

}

二,库函数的模拟实现

1, assert()函数介绍

assert宏的原型定义在<assert.h>中,其作用是如果它的条件返回错误,则终止程序执行。

原型定义:

#include <assert.h>
void assert( int expression );

assert的作用是先计算表达式 expression ,如果其值为假(即为0),那么它先向stderr打印一条出错信息,然后通过调用 abort 来终止程序运行

#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
int main( void )
{
 FILE *fp;
 fp = fopen( "test.txt", "w" );//以可写的方式打开一个文件,如果不存在就创建一个同名文件
 assert( fp ); //所以这里不会出错
 fclose( fp );
 fp = fopen( "noexitfile.txt", "r" );//以只读的方式打开一个文件,如果不存在就打开文件失败
 assert( fp ); //所以这里出错
 fclose( fp ); //程序永远都执行不到这里来
 return 0;
}

2,模拟实现strlen

(1)计数器方式

int my_strlen(const char* str)
{
	int count = 0;
	while (*str)
	{
		count++;
		str++;
	}
	return count;
}

(2)不创建临时变量

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

(3)指针—指针

int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}

3,模拟实现strcpy

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char*dest, const char* src)
{
	assert(dest && src);
	char* ret = dest;
	while (*dest++ = *src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[] = "xxxxxxxx";
	char arr2[] = "abc";
	printf("%s\n", my_strcpy(arr1, arr2));
	return 0;
}

4,模拟实现strcat

#include<stdio.h>
#include<assert.h>
char* my_strcat(char* dest, const char* src)
{
	char* ret = dest;
	assert(dest != NULL);
	assert(src != NULL);
	while (*dest)
	{
		dest++;
	}
	while ((*dest++ = *src++))
	{
		;
	}
	return ret;
}

5,模拟实现strstr

#include<stdio.h>
#include<assert.h>
char* my_strstr(const char* str1, const char* str2)
{
	assert(str1);
	assert(str2);
	char* cp = (char*)str1;
	char* substr = (char*)str2;
	char* s1 = NULL;
	if (*str2 == '\0')
		return NULL;
	while (*cp)
	{
		s1 = cp;
		substr = str2;
		while (*s1 && *substr && (*s1 == *substr))
		{
			s1++;
			substr++;
		}
		if (*substr == '\0')
			return cp;
		cp++;
	}
}

6,模拟实现strcmp

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* src, const char* dst)
{
	int ret = 0;
	assert(src != NULL);
	assert(dst != NULL);
	while (!(ret = *(unsigned char*)src - *(unsigned char*)dst) && *dst)
		++src, ++dst;
	if (ret < 0)
		ret = -1;
	else if (ret > 0)
		ret = 1;
	return(ret);
}

7,模拟实现memcpy

#include<stdio.h>
#include<assert.h>
void* memcpy(void* dst, const void* src, size_t count)
{
	void* ret = dst;
	assert(dst);
	assert(src);
	//copy from lower addresses to higher addresses
	while (count--) {
		*(char*)dst = *(char*)src;
		dst = (char*)dst + 1;
		src = (char*)src + 1;
	}
	return(ret);
}

8,模拟实现memmove

#include<stdio.h>
void* memmove(void* dst, const void* src, size_t count)
{
	void* ret = dst;
	if (dst <= src || (char*)dst >= ((char*)src + count)) {
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst + 1;
			src = (char*)src + 1;
		}
	}
	else {
		dst = (char*)dst + count - 1;
		src = (char*)src + count - 1;
		while (count--) {
			*(char*)dst = *(char*)src;
			dst = (char*)dst - 1;
			src = (char*)src - 1;
		}
	}
	return(ret);
}

点击全文阅读


本文链接:http://zhangshiyu.com/post/30625.html

字符串  字符  函数  
<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1