指针面试题详解,字符串函数详解:strcmp、strcpy、strlen、strcat、strstr、strtok、strerror、memcpy、memmove

目录

一、指针高难度面试题详解

二、计算字符串长度strlen函数拟写和易错点:

三、字符串复制函数strcpy函数拟写:

四、字符串追加函数strcat的拟写:

五、字符串比较函数strcmp:

六、字符串匹配函数strstr:

七、字符串分隔函数strtok:

八、错误码信息函数:strerror

九、内存拷贝函数memcpy(不重叠的拷贝):

十、内存拷贝函数memmove(同一个数组里面重叠数字的拷贝):


一、指针高难度面试题详解

#include<stdio.h>
int main()
{
	char* c[] = { "ENTER","NEW","POINT","FIRST" };
	char** cp[] = { c + 3,c + 2,c + 1,c };
	char*** cpp = cp;
	printf("%s\n", **++cpp);
	printf("%s\n", *-- * ++cpp + 3);
	printf("%s\n", *cpp[-2] + 3);
	printf("%s\n", cpp[-1][-1] + 1);
	return 0;
}

首先我们可以根据代码画出下图:

1、初始数据解读: 

c时一个char*类型的指针数组,里面分别存有不同的字符串,cp是一个char**类型的指针数组,里面放的是指针c的地址+n的结果(c+3==c[3],其他相同),cpp是一个char***类型的指针,指向cp的首地址。(特别注意,以下的cpp指针指向的改变会随着printf程序中的使用而改变,不是独立存在的!!)

2、第一个printf解读

 *操作符和++操作符相比,++操作符优先计算,所有可以得到如图所示:

 解释:cpp先++指向了cp中的c+2,通过一个*解引用操作符,可以得到cp中的值,这个值就是c+2,再通过一个解引用,可以得到cp指针中c+2指向的对象,即使POINT,所以第一个输入就会输入POINT。

3、第二个printf程序解读

 原理同上,++,--操作符优先考虑,但是+号最好计算,即可得到如图:

解释: ++cpp优先执行,cpp指向了cp[2]的位置,即使c+1的位置,通过解引用操作符(*),可以得到cp[2]中的值(c+1),再通过--使c+1变成了c,再通过解引用操作符(*),获得了cp[3]即是c指向的值ENTER,最好因为后面的+3使指针cp[3]向后走三个元素,就到了E的位置,所以最后输出的是ER;(注意指针指向的是首元素地址,+操作使其向后偏移步长值)。

4、第三个printf程序解读

 本题中注意,cpp[-2]==*(cpp-2),意思是活得cpp[-2]中的地址然后解引用,特别注意,cpp的指向并不会改变,改变的是他指向cp后,步长-2,从而得到了cp[0]的地址,可得如图:

 解释:cpp重新因为cpp[-2]指向了cp[0]的位置,再因为cpp[-2]取得了cp[0]中的c+3的地址,再通过解引用操作符(*)取得了c+3指向的内容的地址,c+3指针指向FIRST,+3使其向后偏移3个步长值,最后走到S的位置,所以最后输出ST;

5、第四个printf程序解读:

 本题注意cpp[-1][-1]==*(*(cpp-1)-1),注意同上,cpp指向不改变,改变的是指向后的cp中取得的地址的位置,可得到如图:

 解释:cpp指向cp[2],通过-1,这里减的是cp数组中的下标,cp[2-1]=cp[1],cp[1]中是c+2,再解引用,得到了c+2的地址,c+2,再次通过-1得到了c+1的地址,这里是对cp中的元素进行-1,c+2-1=c+1,再通过解引用使指针指向了NEW,+1使其向后偏移一个字符,最后输出EW。

最后输出结果:

 大家可以多动手画图编码,调试、试试,会有不一样的体验。


二、计算字符串长度strlen函数拟写和易错点:

#include<stdio.h>
#include<string.h>
int main()
{
	if(strlen("abcde")-strlen("abc")>0)
	{
		printf(">");
	}
	else
	{
		printf("<");
	}
	//因为strlen的返回值是size_t,是无符号数,所以他不会<0;
}

#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* str)
{
	assert(str);//断言str不为空指针
	char* end = str;
	while (*end != '\0')
	{
		end++;
	}
	return end-str;
}
int main()
{
	char arr[] = "abcdef";
	int len = my_strlen(arr);
	printf("%d", len);
	return 0;
}

解释:通过end指针后移去找\0,最后返回end指针-str首地址指针的值就可以得到两个指针之间的元素,注意要用size_t类型的,因为不都是int类型。


三、字符串复制函数strcpy函数拟写:

#include<stdio.h>
#include<assert.h>
char* my_strcpy(char* dest, const char* src)
{
	assert(dest);
	assert(src);
	char* ret = dest;
	while(*dest++=*src++)
	{
		;
	}	
	return ret;
}
int main()
{
	char a1[20] = { "abc" };
	char a2[] = { "ni hao ya" };
	my_strcpy(a1, a2);
	printf("%s", a1);
	return 0;
}

解释:返回值为char*类型,返回他的首地址来进行打印,src在函数中是被复制的值,不会被改变,所以需要用const修饰,先是断言空指针,定义一个char*类型的ret来存放复制到的位置的首地址,通过解引用操作符(*)把src的值赋给dest,再分别指针++,dest++,src++寻找下一个,直到寻找到'/0'就停止。


四、字符串追加函数strcat的拟写:

#include<stdio.h>
char* my_strcat(char* des, const char* src)
{
	char* cur = des;
	//先找到需要追加的字符串中'\0'的位置
	while (*cur != '\0')
	{
		cur++;
	}
	//从'\0'的位置开始一个个字符串粘贴过去,通过赋值的方法
	while(*cur++=*src++)
	{
		;
	}
	//返回被追加的字符串的首地址
	return des;
}
int main()
{
	char a1[20] = { "hello " };
	char a2[] = {"world"};
	my_strcat(a1, a2);
	printf("%s\n", a1);
	return 0;
}

解释:先通过被追加字符串首地址的指针++,找到他的‘\0’,再从'\0'开始复制字符串,从而完成字符串的追加。


五、字符串比较函数strcmp:

#include<stdio.h>
#include<assert.h>
int my_strcmp(const char* s1, const char* s2)
{
	assert(s1 && s2);
	while (*s1 == *s2)
	{
		s1++;
		s2++;
	}
	return *s1 - *s2;
}
int main()
{
	char a1[] = "abc";
	char a2[] = "abc";
	int ret = my_strcmp(a1, a2); 
	if(ret>0)
	{
		printf("a1>a2");
	}
	else if(ret<0)
	{
		printf("a1<a2");
	}
	else
	{
		printf("a1==a2");
	}
	return 0;
}

解释:首先断言空指针情况,通过while循环首先排除相等的字符,然后到了不同的字符跳出循环,通过*s1-*s2就可以得到哪个字符大,再通过int类型的返回值,得到整形数据,就可以完成字符串的比较了。


六、字符串匹配函数strstr:

在一个字符串中寻找另一个字符串是否存在

存在:返回字符串所在的地址

不存在:返回NULL

#include<stdio.h>
char* my_strstr(const char* s1, const char* s2)
{
	char* p = s1;
	//创建一个char*类型的p接受s1的位置
	char* a = s1;
	//创建a指向s1的位置
	char* b = s2;
	//创建b指向s2的位置
	while (*p)
		//p是s1和s2相等的位置,如果p一直指导了\0还没找到一样的就直接退出循环
	{
		a = p;
		//每次循环让a的位置指向p
		b = s2;
		//让b的位置在s2处
		while (*a != '\0' && *b != '\0' && (*a == *b))
			//a和b中指向内容相同就进入循环继续寻找下一个相等的,直到不相等就退出循环
		{
			a++;
			b++;
		}
		if(*b=='\0')
		//如果b(b在a中寻找相同的字符串)中走到了\0,证明已经找到了相同的字符串,直接返回第一个相等的位置
		{
			return p;
		}
		p++;
		//每次a和b不相等,直接p向后找
	}
	//循环完都没找到就返回空指针
	return NULL;
}
int main()
{
	char a[] = "abcdeeedcba";
	char b[] = "ee";
	char* p = my_strstr(a, b);
	if (p == NULL)
	{
		printf("不存在");
	}
	else
	printf("%s\n", p);
	return 0;
}

解释:这个strstr函数就是在前一个字符串中找与后一个字符串相等的片段,如果有相等的直接输出相等位置后面所有的字符串,自我实现大概要注意,首先选择一个两个字符第一次相等的位置p,记录下来,相等时p不动,a和b++向后走,如果b走到了\0,则返回p位置,主函数就直接输出p后面的所有字符了,如果b没走到\0,则p++,重新进入while循环寻找,最后走到了*p==\0,则返回空指针,证明前一个字符串没有后面字符串的内容。


七、字符串分隔函数strtok:

这个函数主要通过分隔符号对对应字符串进行分割

#include<stdio.h>
#include<string.h>
int main()
{
	char a[] = "3478611368@qq.com";
	char b[200] = { 0 };
	char c[200] = { 0 };
	strcpy(b, a);
	strcpy(c, a);
	//因为strtok函数会改变原来字符串内容,将分隔符变为\0,所以我们需要用一个临时变量来测试
	char* p = "@.";
	//char*类型指针存在分隔符号
	char* str = strtok(b, p);
	//第一次分割b,使指针指向首元素地址,向后寻找\0停止
	printf("%s\n", str);
	str = strtok(NULL, p);
	//第二次分割直接传入NULL,在strtok函数中会自动寻找上次记忆留下的\0,从\0后面开始寻找分隔符号变为\0,
	printf("%s\n", str);
	str = strtok(NULL, p);
	//同上
	printf("%s\n", str);
	//高级优雅做法
	char* set = NULL;
	//首先定义空指针
	for (set = strtok(c, p); set != NULL; set = strtok(NULL, p))
		//第一步赋值第一个分隔符前的首元素地址给set,再通过判断非空循环,第一次以后都是传入NULL,所以可以用循环直接写
		//不需要自己多次手写第一次以后的内容,更加方便简洁;
	{
		printf("%s\n", set);
	}
	return 0;
}

运行结果:


八、错误码信息函数:strerror

#include<stdio.h>
#include<string.h>
int main()
{
	printf("%s\n", strerror(0));
	printf("%s\n", strerror(1));
	printf("%s\n", strerror(2));
	printf("%s\n", strerror(3));
	printf("%s\n", strerror(4));
	//相应数字分别对应相应的错误信息

	//将错误码信息记录到错误码的变量中
	//errno c语言提供的全局的错误变量
	FILE* pf = fopen("text.txt", "r");//打开文件text.txt,r表示用read的方式
	if (pf == NULL)
	{
		perror("fopen");//使用perror函数打印errno中所对应的错误信息
		printf("%s\n", strerror(errno));//打印错误码中的错误信息
		return 1;
	}
	//读文件
	fclose(pf);
	pf = NULL;
	return 0;
}

九、内存拷贝函数memcpy(不重叠的拷贝):

#include<stdio.h>
#include<assert.h>
void* my_memcpy(void* dest, void* src, size_t num)
//函数定义,返回值为void*指针,参数类型也为void*可以满足任意类型的参数。
{
	void* ret = dest;//定义一个void*类型变量存放首地址,因为后面dest指针要向后走
	assert(dest && src);//断言空指针
	while (num--)//循环结束条件是传入的字节--后为0
	{
		*(char*)dest = *(char*)src;//将两个指针强制转换为char*类型可以一个字节一个字节的赋值,从字节传值可以满足任意类型
		dest = (char*)dest + 1;//void*类型的指针不能自己++,只能将其强转为其他指针,又因为这里需要逐个字节寻找,
							   //所以需要使用char*类型一个字节一个字节的寻找
		src = (char*)src + 1;//这个src同上
	}
	return ret;//最后返回首元素地址
}
int main()
{
	int arr1[] = { 1,2,3,4,5,6,7,8,9,10 };
	int arr2[20] = { 0 };
	my_memcpy(arr2, arr1, 40);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr2[i]);
	}
	printf("\n");
	char* a = "abcde";
	char b[20] = { 0 };
	my_memcpy(b, a, 5);
	printf("%s\n", b);
	float c[] = { 1.0f,2.0f,3.0f,4.0f,5.0f };
	float d[20] = { 0 };
	my_memcpy(d, c, 20);
	for (int i = 0; i < 5; i++)
	{
		printf("%f ", d[i]);
	}
	return 0;
}

十、内存拷贝函数memmove(同一个数组里面重叠数字的拷贝):

#include<stdio.h>
void* my_memmove(void* dest, void* src, size_t num)
{
	void* ret = dest;
	if (dest < src)
		//如果是重叠的拷贝,被拷贝元素src>dest,就应该从前面开始拷贝,就不会出现后面有元素重叠的问题。
		//例如1 2 3 4 5 6 7 8 9 10中,要把4 5 6拷贝到2 3 4的位置,此时src=4,dest=2,src>dest,从前面拷贝
		// 如果从后面拷贝,先让4的位置变成了6,3的位置变成了5,到了2的位置时,就会出现拷贝到了6的情况,
		//因为4的位置已经被拷贝了6了,所以应该从前面开始拷贝,2变成4,3变成5,4变成6,这样就不会出现重复了
	{
		while (num--)
		{
			*((char*)dest) = *((char*)src);
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else
		//如果是被拷贝元素src<dest,就应该从两者的后面开始拷贝,就不会出现前面的元素重叠
		//例如:1 2 3 4 5 6 7 8 9 10中,要把1 2 3拷贝到 3 4 5的位置,此时src=1,dest=3,dest>src,就应该从后面开始拷贝
		//如果从前面开始拷贝,3的位置变成了1,4的位置变成了2,到了5的位置时就会拷贝到现在3的位置变成的1,就出现了重复
		//所以应该从后面开始拷贝。5变成3,4变成2,3变成1;
	{
		while (num--)//因为他们自己本身占了一个字节,所以此处--刚好使他们少偏移一个字节,正确找到区间末尾
		{
			*((char*)dest + num) = *((char*)src + num);//让他们字节加上偏移的字节量num-1,可以找到拷贝区间的最后一个。
		}
	}
	return ret;
}
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	my_memmove(arr + 2, arr, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

以上是对于字符串的详解,希望对大家有用~~~

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

程序员X.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值