C语言:字符串的拷贝与反转、sprintf格式化字符串、calloc和realloc的使用、ssacnf的使用

一、字符串的几个特点

字符串:字符串或串是由数字、字母、下划线组成的一串字符。一般以0或者’\0’结尾,数字0和’\0’等价。 它是编程语言中表示文本的数据类型。
字符串的几个特点:
1.字符数组输出的时候,从开始位置直到找到0或’\0’结束; 如下:

	char str1[] = {'h','e','l','\0','l','o'};
	printf("%s\n",str1);

输出结果为:hel
在这里插入图片描述
2.字符数组在部分初始化后,后面的元素自动赋值为’\0’;

	char str2[100] = {'h','e','l','l','o'};
	printf("%s\n",str2);

输出为:hello,后面默认为0;
在这里插入图片描述
3.如果以字符串初始化忘了写’\0’,那么编译器默认会在字符串尾部添加’\0’;

	char str2[10] = {'h','e','l','l','o'};
	printf("%s\n",str2);

输出为:hello,默认在最后补齐:’\0’。
4.sizeof计算数组大小,包含’\0’字符;strlen计算时,不包含’\0’字符,碰到\0就结束了;
实例1:sizeof(str4))为100;strlen(str4))为5。

	char str4[100] = "hello";
	printf("sizeof(str4) = %d\n",sizeof(str4));//100
	printf("strlen(str4) = %d\n",strlen(str4));//5

实例2:sizeof(str5))为12;strlen(str5))为5。

	char str5[] = "hello\0world";
	printf("%s\n",str5);
	printf("sizeof(str5) = %d\n",sizeof(str5));//12
	printf("strlen(str5) = %d\n",strlen(str5));//5

实例3:sizeof(str6))为12;strlen(str6))为11。

	char str6[] = "hello\012world";
	printf("%s\n",str6);
	printf("sizeof(str6) = %d\n",sizeof(str6));//12
	printf("strlen(str6) = %d\n",strlen(str6));//11

输出结果为:
在这里插入图片描述
为什么输出会这样呢?
“hello\012world”:hello为5个字符,world为5个字符,但是此时碰到了\0为转义字符,为8进制,\012表示为8进制下12这么一个数字;因此sizeof输出为:hello(5)+\012(1)+world(5)+\n(1)=12;strlen输出为hello(5)+\012(1)+world(5)=11;
那么为什么输出hello再输出world呢?
因为/012为八进制,将它转换为10进制下即为10,10再对应ASCII码中的10刚好为换行,所有输出会换行。

二、字符串的拷贝和反转

字符串拷贝:
方式1:利用下标方式进行拷贝

//字符串拷贝
//参数1:目标字符串,参数2源字符串
//需求:将源字符串拷贝到目标字符串中
void CopyString1(char* dest,char* source)//第一种,利用下标方式进行拷贝
{
	int len = strlen(source);
	int i = 0;
	for (i; i<len; i++)
	{
		dest[i] = source[i];
	}
	dest[len] = '\0';
}
void test1()
{
	char *str = "hello wolrd";
	char buf[1024];
	CopyString1(buf,str);
	printf("%s\n",buf);
}
int main()
{
	test1();

	return 0;
}

拷贝成功:
在这里插入图片描述
方式2:利用字符串指针进行拷贝

void CopyString2(char* dest,char* source)//第二种,利用字符串指针进行拷贝
{
	while (*source != '\n')
	{
		*dest = *source;
		*dest++;
		*source++;
	}
	*dest = '\n';//拷贝完让它结束
}

拷贝成功:
在这里插入图片描述
方式3:利用字符串指针进行拷贝,同方法二,但写法最简单

void CopyString3(char* dest,char* source)//第三种,写法最简单
{
	while (*dest++ = *source++){}
}

拷贝成功:
在这里插入图片描述
字符串反转:
方式1:利用下标方式进行反转

//字符串反转
void ReverseString(char *str)//方法一:利用下标进行反转
{
	int len = strlen(str);
	int start = 0;//定义开始下标
	int end = len-1;//定义末尾下标

	while (start < end)
	{
		char temp = str[start];
		str[start] = str[end];
		str[end] = temp;
		start++;
		end--;
	}
}
void test()
{
	char str[] = "abcdefg";
	ReverseString(str);
	printf("%s\n",str);
}
int main()
{
	test();
	return 0;
}

反转成功:
在这里插入图片描述
方式2:利用指针进行反转

void ReverseString2(char* str)//方法二:利用指针进行反转
{
	int len = strlen(str);
	char* start = str;//定义开始下标
	char* end = str + (len-1);//定义末尾下标

	while(start < end)
	{
		char temp = *start;
		*start = *end;
		*end = temp;
		start++;
		end--;
	}
}

反转成功:
在这里插入图片描述

三、sprintf格式化字符串

sprintf:可以将想要的结果输出到指定的字符串中,也可作为缓冲区,而printf只能输出到命令行上;sprintf()根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中去,直到出现字符串结束符’\0’为止。

函数原型:int sprintf(char* str,const char* format,...)
参数:
str:字符串首地址。
format:字符串格式,用法和printf()一样
...:拼接的参数内容,可选参数个数不定,根据实际情况使用
返回值:
成功:实际格式化的字符个数
失败:-1

实际功能1:格式化字符串(即将指定内容输入到字符串中)

//1.格式化字符串
void test()
{
	char buf[1024];
	memset(buf,0,1024);
	sprintf(buf,"今天是%d年%d月%d日",2020,2,15);
	printf("%s\n",buf);
}

输出结果:
在这里插入图片描述
实际功能2:拼接字符串

void test()
{
	char buf[1024];
	char str1[] = "hello";
	char str2[] = "world";
	int len = sprintf(buf,"%s%s",str1,str2);//sprintf返回的是字符串的长度
	printf("buf = %s\nlen = %d\n",buf,len);
}

输出结果:
在这里插入图片描述
实际功能3:数字转为字符串

//3.数字转为字符串
void test()
{
	char buf[1024];
	int num = 100;
	sprintf(buf,"%d",num);//sprintf返回的是字符串的长度
	printf("buf = %s\n",buf);
}

输出结果:转换成为了一个字符串100
在这里插入图片描述
实际功能4:设置宽度,右对齐与左对齐(用的少)

//4.设置宽度,右对齐与左对齐
void test()
{
	char buf[1024];
	int num = 100;
	sprintf(buf,"%8d",num);//右对齐
	printf("buf1 = %s\n",buf);
	sprintf(buf,"%-8d",num);//左对齐
	printf("buf2 = %sa\n",buf);
}

输出结果:算上100总共占8个空位
在这里插入图片描述
实际功能5:转换为16进制小写或8进制(用的少)

//5.转换为16进制小写或8进制
void test()
{
	char buf[1024];
	int num = 100;
	sprintf(buf,"0X%x",num);//转换为16进制小写
	printf("buf1 = %s\n",buf);
	sprintf(buf,"%o",num);//转换为8进制
	printf("buf2 = %s\n",buf);
}

输出结果:
在这里插入图片描述

四、calloc和realloc的使用

calloc的使用

calloc: 在内存动态存储区中分配rmemb块长度为size字节的连续区域。calloc自动将分配的内存置0。

函数原型:void* calloc(size_t nmemb,size_t size);
参数:
nmemb:所需内存单元数量
size:每个内存单元的大小(单位:字节)
返回值:
成功:分配空间的起始地址
失败:NULL

calloc使用时:需要两个参数,并且会自动将分配的内存置0;但同样是在堆区分配的内存,还需要手动释放
我们先使用malloc在堆区分配一块内存,不初始化它的值为0

void test()
{
	int* p = (int*)malloc(sizeof(int)*10);

	int i = 0;
	for (i; i<10; i++)
	{
		printf("%d\n",p[i]);
	}
}

我们发现,使用malloc如果不初始化的话,会出现随机值
在这里插入图片描述
此时,我们使用calloc在堆上分配内存;

void test()
{
	int*p = (int*)calloc(10,sizeof(int));

	int i = 0;
	for (i; i<10; i++)
	{
		printf("%d\n",p[i]);
	}
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}
}

分配好后,会自动将内存置0;同样也需要手动释放内存;
在这里插入图片描述

realloc的使用

realloc: 重新分配用malloc或者calloc函数在堆中分配内存空间的大小; realloc不会自动清理增加的内存,需要手动清理,如果指定的地址后面没有空间,那么realloc会重新分配新的连续内存把旧内存的值拷贝到新内存,同时释放旧内存。

函数原型:void* *realloc(ptr,size_t size);
参数:
ptr:为之前用malloc或者calloc分配的内存地址,如果此参数等于NULL,那么和realloc与malloc功能一致。
size:为重新分配内存的大小(单位:字节)
返回值:
成功:新分配的堆内存地址
失败:NULL

realloc机制:
1.如果原来的空间后面有足够大的空闲空间,那么直接在原来的后面继续开辟,返回原有的首地址。
2.如果原来的空间后续没有足够大的空闲空间,那么系统会直接分配一个空间,这个空间就是需要的内存空间。并将原有空间下的数据拷贝到新空间下,并且会将原有空间释放返回新空间的首地址,但是后续开辟的空间并未进行初始化。。

在这里插入图片描述
我们来验证一下这两种机制:
验证1:如果原来的空间后面有足够大的空闲空间,那么直接在原来的后面继续开辟,返回原有的首地址。

void test()
{
	int* p = (int*)malloc(sizeof(int)*10);
	printf("%d\n",p);//打印p的地址
	for (int i = 0; i<10; i++)
	{
		p[i] = i;//0-9
	}
		
	p = (int*)realloc(p,sizeof(int)*11);//情况1:有可能够用
	for (int i = 0; i<10; i++)
	{
		printf("%d\n",p[i]);
	}
	printf("%d\n",p);//再次打印p的地址
}

打印结果:原有地址与分配完地址相同,机制1成立;
在这里插入图片描述
验证2:如果原来的空间后续没有足够大的空闲空间,那么系统会直接分配一个空间,这个空间就是需要的内存空间。并将原有空间下的数据拷贝到新空间下,并且会将原有空间释放返回新空间的首地址,但是后续开辟的空间并未进行初始化。。
将此行改为20,后续空间就可能不够大了

void test()
{
	int* p = (int*)malloc(sizeof(int)*10);
	printf("%d\n",p);//打印p的地址
	for (int i = 0; i<10; i++)
	{
		p[i] = i;//0-9
	}
		
	p = (int*)realloc(p,sizeof(int)*20);//情况2:后续空间可能不够大了,但是后续开辟的空间并未初始化
	for (int i = 0; i<20; i++)
	{
		printf("%d\n",p[i]);
	}
	printf("%d\n",p);//再次打印p的地址
	if (p != NULL)
	{
		free(p);
		p = NULL;
	}
}

输出结果:前后地址不一样了,机制2也成立。但是后续开辟的空间并未进行初始化。
在这里插入图片描述

五、ssacnf的使用

sscanf: 从一个字符串中读进于指定格式相符的数据。利用它可以从字符串中取出整数、浮点数和字符串;从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
sscanf和scanf的区别: scanf是以键盘作为输入源,sscanf是以字符串作为输入源。

函数原型:int sscanf(const char* str,const char* format,...);
参数:
str:指定的字符串首地址
format:字符串格式,用法和scanf()一样
返回值:
成功:返回参数数目
失败:-1

用法1:% * s或% * d,忽略数据。
忽略数字:如图

void test()
{
	char* str = "12345abcde";
	char* buf[1024] = {0};
	sscanf(str,"%*d%s",buf);//忽略数字,%*表示忽略
	printf("%s\n",buf);
}

输出结果:
在这里插入图片描述
忽略字符串方式1:中间加空格(但一般不允许改动字符串)

char* str = "abcde 12345";//忽略数字方式1:中间加空格

忽略字符串方式2:中间加\t(但一般不允许改动字符串)

char* str = "abcde\t12345";//忽略数字方式2:制表符忽略

忽略字符串方式3:%*[a-z]%s方法

void test()
{
	char* str = "abcde12345";
	char* buf[1024] = {0};
	sscanf(str,"%*[a-z]%s",buf);//忽略数字,%*表示忽略
	printf("%s\n",buf);
}

成功将字符串[a-z]忽略:
在这里插入图片描述
用法2:%d[width]s 读取指定宽度的数据

void test()
{
	char* str = "abcde12345";
	char* buf[1024] = {0};
	sscanf(str,"%6s",buf);//读取6个宽度
	printf("%s\n",buf);
}

如图:读取了6个宽度的数据
在这里插入图片描述
用法3:%[a-z]匹配a-z中任意字符(尽可能多的匹配)

void test()
{
	char* str = "12345abcde";
	char* buf[1024] = {0};
	sscanf(str,"%*d%[a-c]",buf);//匹配a-c的数据
	printf("%s\n",buf);
}

成功匹配a-c的数据:
在这里插入图片描述
用法4:%[aBz]匹配a、B、c中的一员,贪婪性:只要有一个匹配失败,后面都不会匹配

void test()
{
	char* str = "aabcde12345";
	char* buf[1024] = {0};
	sscanf(str,"%[aBc]",buf);//只要有一个匹配失败,后面都不会匹配
	printf("%s\n",buf);
}

输出如图:只要有一个匹配失败,后面都不会匹配
在这里插入图片描述
用法5:%[^a]匹配非a的任意字符,贪婪性

void test()
{
	char* str = "abcde12345";
	char* buf[1024] = {0};
	sscanf(str,"%[^c]",buf);//只要有一个匹配失败,后面都不会匹配
	printf("%s\n",buf);
}

输出如图:匹配非a的字符,只要有一个匹配失败,后面都不会匹配
在这里插入图片描述
用法6:%[^a-z]匹配非a-z的任意字符

void test()
{
	char* str = "abcde12345";
	char* buf[1024] = {0};
	sscanf(str,"%[^0-9]",buf);
	printf("%s\n",buf);
}

输出如图:
在这里插入图片描述
案例1: 利用sscanf截取数字。 利用sscanf将一个本机ip地址分别截取到4个num中。

void test()
{
	char* ip = "127.0.0.1";
	int num1 = 0;
	int num2 = 0;
	int num3 = 0;
	int num4 = 0;

	sscanf(ip,"%d.%d.%d.%d",&num1,&num2,&num3,&num4);
	printf("num1 = %d\n",num1);
	printf("num2 = %d\n",num2);
	printf("num3 = %d\n",num3);
	printf("num4 = %d\n",num4);
}

截取成功:
在这里插入图片描述
案例2: 提取字符串中有效的名称。

void test()
{
	char* str = "abcde#hello@12345";
	char* buf[1024] = {0};
	sscanf(str,"%*[^#]#%[^@]",buf);
	printf("%s\n",buf);
}

提取成功:可以有多种方式提取
在这里插入图片描述
案例3:给定字符串:helloworld@itcast.cn实现helloworld与itcast.cn的输出:

void test()
{
	char* str = "helloworld@itcast.cn";
	char* buf1[1024] = {0};
	char* buf2[1024] = {0};
	sscanf(str,"%[a-z]%*[@]%s",buf1,buf2);
	printf("buf1 = %s\nbuf2 = %s\n",buf1,buf2);
}

成功分离:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值