字符串

本文详细介绍了C语言中字符与字符串的概念,包括ASCII表、字符和字符串的存储方式、输入输出方法,以及如何处理输入缓冲区的问题。讨论了字符串处理函数如strlen、strcpy、strcat、strcmp等的实现和使用,并提供了实际代码示例,帮助读者理解字符串操作的细节和潜在问题。
摘要由CSDN通过智能技术生成

字符:

	在计算机中字符都是以整理形式存储的,当需要显示时根据ASCII表中的对应关系显示出相应的符号和密度
	'\0'   0
	'0'    48
	'A'    65
	'a'    97

串:

	是一种数据结构,是由一组连续的相同类型若干个数据组成,末尾有结束标志
	对这种数据结构进行处理都是批量性处理,从开头位置到结束标志为止。

字符串:

	由字符组成的串行结构,他的结束标志是'\0'

字符串的输入:

char str[256];
scanf (“%s”, 地址) //自带/0,回车即停止
注意:不能接收空格

char *gets(char *s);
功能:输入字符串到s中,可以接收空格
返回值:链式调用
注意:gets及scanf printf 不检查长度,不接收长度,会一直存储,会覆盖掉后面的额内容。输入超过定义长度后依旧输出

链式调用:把一个函数的返回值,作为另一个函数的参数
gets(str) = printf("%s", gets(str));

#include <stdio.h>
int main(int argc,const char* argv[])
{
	char user[20] = {};
	char pass[20] = "1234";
	gets(user);
	printf("%s",pass);
}

在这里插入图片描述
虽然定义的密码是“1234”,但是因为输入唱过20个字符,gets不接收长度,一直存储数据,因为user[20]和pass[20]是连续的,覆盖掉了pass的内容。

char *fgets(char *s, int size, stdin); //stdin是固定的意思
fgets(str,size,stdin)
例:fgets(user,20,stdin);//接收输入的19个,留一个给\0
功能:可以设置输入的字符串的长度为size-1,超出长度时会在末尾给'\0'预留位置,超出部分会不接收
注意:如果长度不足size-1,则会接收最后输入的'\n'换行

输入缓冲区:

程序并不会立即获取在终端输入的数据,先由终端保管。当按下回车时程序才会从输入缓冲区中读取数据

1、当读取的是整型或者浮点型数据时,而缓冲区中的数据是字母或符号时,此时从缓冲区获取数据失败,并不会从缓冲区中拿走字母或字符,数据会残留在输入缓冲区中,就会影响接下来的数据的读取

int a,b,c;
	scanf("%d%d%d",&a,&b,&c);
	printf("%d %d %d\n",a,b,c);

在这里插入图片描述
当输入字母时,获取数据失败,导致数据残留在缓冲区,影响接下来的输入

int a,b,c;
	while(3 > scanf("%d%d%d",&a,&b,&c))
	{
		printf("输入的数据格式有误,请重新输入:");	
		stdin->_IO_read_ptr = stdin->_IO_read_end;
	}
	printf("%d %d %d\n",a,b,c);

解决方法:根据scanf的返回值,判断变量是否从缓冲区内读取成功,如果失败则先清空缓冲区再重新输入
stdin->I0_read_ptr = stdin>IO_read_end;//跳过所有垃圾数据 ,_IO_read_ptr是一个指针,指向ptr指向当前要读的位置即开头,挪到末尾end,即跳过中间的数据
注意:只能在LIUNX系统上使用,把缓冲区的当前位置指针移动到缓冲区末尾,相当于清理缓冲区

2、fgets可以指定读取size-1个字节,如果超出范围,则多余的字符会残留在输入缓冲区中,会影响接下来的读取

char str[20] = {};
	fgets(str,20,stdin);
	char str2[20] = {};
	scanf("%s",str2);
	puts(str);
	puts(str2);

在这里插入图片描述
输入的元素长度不够时则会自动接收最后输入的‘\n’,fgets的特性
输入的元素长度超过规定的长度时。会将在缓冲区的数据自动scanf存入输入中

char str[20] = {};
	fgets(str,20,stdin);
	char str2[20] = {};
	
	scanf("%*[^\n]");   
	scanf("%*c");

	scanf("%s",str2);
	puts(str);
	puts(str2);

在这里插入图片描述
输入中“%※”丢弃接下来输入的,叫做字符输入格式化,^取反的意思,
“%*[^\n]”的意思就是会一直丢弃字符到遇到回车时停止
“%*c”的意思就是丢弃一个字符类型

char str[20] = {};
	fgets(str,20,stdin);
	int len = 0;
	while(str[len]) len++;
	if('\n' != str[len-1])
	{
		scanf("%*[^\n]");
		scanf("%*c");
	}
	char str1[20] = {};
	scanf("%s",str1);
	puts(str);
	puts(str1);

在这里插入图片描述

解决方法:
判断fgets读取到的字符串的最后一个字符是否是\n,如果不是则说明缓冲区内有残留数据,才需要清理,必须确保缓冲区内有垃圾数据,否则程序会停下来等待接收一个\n
因此可以再清理缓冲区前,判断str末尾是不是\n,是则不用清理,否则需要清理
scanf(%*[^\n]"); 表示从缓冲区中读取并丢弃数据,只要不是\n就一直丢弃,遇到\n停止
scanf(“%*c”); 从缓冲区中读取并丢弃一个字符数据,也就是丢弃剩余的\n
注意:当不超过规定的长度时,会出现需要多输入一行的错误

3、当先输入的是整型或浮点型时,再输入字符、字符串时,会读取到上一次最后输入的残留的’\n’,会影响字符、字符串的输入

int num = 0;
char ch = 0;
scanf("%d%c", &num, &ch);
printf("--%d--%c--\n", num, ch);

在这里插入图片描述
显示在输入100后敲回车,‘\n’被看做是字符输入,字符与scanf配合有问题

int num = 0;
char ch = 0;
scanf("%d%s", &num, &ch);
printf("--%d--%s--\n", num, ch);

在这里插入图片描述
表示字符串和scanf配合没有问题

int num = 0;
char ch = 0;
scanf("%d", &num);
gets(ch);
printf("--%d--%s--\n", num, ch);

在这里插入图片描述
‘\n’都没有读到就直接结束

	注意:scanf("%s", str) 不会影响字符串的输入
	     gets(str)  残留的\n会影响字符串的输入

解决方法:
输入字符 scanf(" %c", &ch); %C前加空格,会顶掉上一句的‘\n’,上一句没有也没有影响
输入字符串 scanf("%d ",&num);//空格加在后面 gets(str);

字符串的输出:

printf %s 地址
int puts(const char *s);  //const保护指针变量,指针时共享的
功能:输出一个字符串,会在末尾自动打印一个'\n',而printf不接收换行
返回值:成功输出的字符个数 (+1有一个'\0')

输出缓冲区:

程序输出的数据并不会立即写入到“文件”中,而是先存储在输出缓冲区中,只有满足一定的条件时才会写入到文件中。
	1、遇到\n后悔刷新所有缓冲区的数据
#include <stdio.h>
#include<unistd.h>

printf("hello world!");   
printf("hello world!\n");  
for;;;
sleep(3);

for(;;) ---- 加了‘\n’后可以输出,不加不会输出,一直死循环
sleep(3) —— 加了’\n’后可以立即输出,而不加会在3s后输出

printf("hello world!");  
printf("hello world!"); 
sleep(3);
printf("hello world!\n");  

会在3s后瞬间输出三条hello world,这就是输出缓冲区

	2、当遇到输入语句时也会立刻刷新
	例:printf
	    printf    第二个printf不会出现
	而  printf
	    printf
	    scanf     第二个printf会出现
<unistd.h>
sleep()  usleep()
	3、程序结束时
	4、当缓冲区满4k时
	5、手动刷新 fflush(stdout)
缓冲区机制可以提高数据的读写速度,并且可以让低速的输入输出设备与高速的CPU能够协调工作。

字符串的存在形式:

字符数组

char str[n] = (‘a’,‘b’,‘c’,……);
由char类型组成的数组,要记得为’\0’预留位置,赋值的数量不要超过n-1,赋值较麻烦
但使用的是栈内存,数据是可以修改的
例:str[0] = ‘A’;,可以直接改值

#include <stdio.h>

int main(int argc,const char* argv[])
{
	char str[5] = {'a','b','c','d'};
	char str1[5] = {'a','b','c','d','e'};
	printf("%s\n",str);
}

在这里插入图片描述
因为没有为‘\0’预留位置,没有结束标志,会一直找结束标志一直输出。

字符串字面值:

	代码段,只读,修改会产生段错误,地址
	“由双引号包含的若干个字符”  在末尾隐藏了一个'\0'
	printf(“hello world”) = printf("%s", "hello world")  
	所以字符串字面值就是以地址形式存在的,数据存储在代码段text里,如果修改则会产生段错误。
	
    char* str = "12341234";
	str[0] = '0';
	printf("%s", str); //产生段错误,无法更改字符串字面值。

printf("%d", sizeof(str)); // 输出为4,指针就是4
sizeof(str); 数组总字节数,与输入的字符串无关

printf("%d", sizeof(“12341234”)); // 9

printf("%d", strlen(“12341234”)); // 8,字符串的大小

	sizeof("strstr")   结果=字符个数+1;
	
	char* str1 = "hehe"两个一模一样的字符串字面值在代码段中只会存在一份,str1和st2是同一个地址
	char* str2 = "hehe"
	
	但是两个一模一样的数值存放的地址不同
	int num1= 10;   int num2 = 10;

常用方式:

		字符数值[] = "字符串字面值"   char str[] = "hehe world";
		【】空下会自动计算个数                                     sizeof(str);//输出11
		会自动给'\0'.预留位置
		赋值完成后字符串会存有两份,一份存储在代码段,另一份存储在栈内存(可修改)
		 char str[] = "hehe world";
		 char str1[] = "hehe world";   地址不同

练习1、实现一个函数,判断字符串是否是回文串

#include <stdio.h>
#include <stdbool.h>

bool back(char* str)
{
    size_t len=0;
    while(str[len])
    {   
        len++;
    }   
    for(int i=0;i<len/2;i++)
    {   
        if(str[i] != str[len-1-i])
        return false;
    }   
    return true;
}

int main(int argc,const char* argv[])
{
    char str[4096]={}; //一页
    //scanf("%s",str);
    gets(str);
    if(back(str))
    {   
        puts("yes");
    }   
   }   
    else
    {
        puts("NO");
    }

2、实现一个函数,把一个数字字符串转换为整数“12345” 12345

#include <stdio.h>

int back(const char* str)
{
	int num=0;
	while(*str)
	{
		if('0' <= *str && '9' >= *str)
		{
			num = num*10 + *str -'0';
			str++;
		}
	}
		return num;
	
}

int main(int argc,const char* argv[])
{
	char str[256]={};
	printf("%d\n", back(gets(str)));
}

字符串常用的处理函数

#include <string.h>

strlen

size_t strlen(const char *s);
功能:计算字符串长度,结果不包括'\0'  

对比sizeof二者的区别主要是以下四点:

1、sizeof()是运算符,strlen()是库函数 2、sizeof()在编译时计算好了,strlen()在运行时计算
3、sizeof()计算出对象使用的最大字节数,strlen()计算字符串的实际长度
4、sizeof()的参数类型多样化(数组,指针,对象,函数都可以),strlen()的参数必须是字符型指(传入数组时自动退化为指针)
具体而言,当参数分别如下时,sizeof返回的值表示的含义如下:

数组——编译时分配的数组空间大小; 指针——存储该指针所用的空间大小(在32位机器上是4,64位机器上是8);
类型——该类型所占的空间大小; 对象——对象的实际占用空间大小; 函数——函数的返回类型所占的空间大小。函数的返回类型不能是void

例子:
p=hello,strlen=5,sizeof=6
字符串hello的长度是5,但在字符串最后隐藏一个\0故sizeof是6。

p1=hello\0,strlen=5,sizeof=7
这里\0主动结束了字符串长度的计算,但隐藏的\0仍然有意义,故此时字符串中存在两个\0。

p2=hello\0,strlen=7,sizeof=8
这里的\转义了\,使得\0不再是一个整体,也不具有原来停止计算长度的作用而只是两个普通字符\和0。

p3=hello\\0,strlen=6,sizeof=8
这里第一个\只转义了第二个\,使得第二个\不能再转义后面的\,此时\0这个整体仍然有结束的作用。

p4 的结果:p4=hel\0lo,strlen=3,sizeof=7
strlen碰到\0停止输出和计算,所以结果是3。但sizeof不会停止而且两个\0都计算。

p5 的结果:p5=hel\0lo,strlen=7,sizeof=8
第一个\转义了紧接的\,使得\0变成两个普通字符\和0,sizeof比strlen多计算一个\0。

————————————————
版权声明:本文为CSDN博主「ly_1115」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/ly_6699/article/details/89482591

strcpy

char *strcpy(char *dest, const char *src);
功能:把src拷贝到dest,相当给dest赋值
返回值:dest的首地址,链式调用
    char *strncpy(char *dest, const char *src, size_t n);
只拷贝前n个字符
注意:数组离开初始化就只能一个一个赋值

strcat

char *strcat(char *dest, const char *src);
功能:把src追加到dest的末尾 相当于+=
返回值:dest的首地址,链式调用
    char *strncat(char *dest, const char *src, size_t n);
只追加前n个字符

strcmp

int strcmp(const char *s1, const char *s2);
功能:比较s1和s2两个字符串,按照字典序比较,谁在前谁小,比较出结果立即返回返回值
s1 >s2 正数 s1 = s2  0    s1 <s2负数
int strncmp(const char *s1, const char *s2, size_t n);

· 只比较前n个字符

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

int main(int argc,const char* argv[])
{
	char str[256] = "helloooooo";//范围给大一点

	printf("strlen: %d\n",strlen(str));
//	printf("strcpy: %s\n",strcpy(str,"world"));    //拷贝过去只是把“hello”覆盖了,后面的“ooooo”被‘\0’阻隔了不显示了
	printf("strncpy: %s\n",strncpy(str,"world",7));//拷贝只拷贝前n个字符时,只要不拷贝\0后面的字符也会打印出来,超出运行正常
	//str = "world";
	printf("strcat: %s\n",strcat(str,"12311111111111111111"));
//	printf("strncat: %s\n",strncat(str,"123",1));
//	printf("strcmp: %d\n",strcmp("aca","abcaaaaa"));
	printf("strncmp: %d\n",strncmp("aca","abcaaaaa",1));
	
	printf("atoi:%d\n",atoi("a123"));

}

在这里插入图片描述

const char *strstr(const char *haystack,const char *needle);
功能:查找haystack中是否存在needle
返回值:如果存在,则返回needle第一次在haystack中出现的位置,如果找不到,返回NULL
#include <stdio.h>
#include <string.h>

int main(int argc,const char* argv[])
{
//	puts(strstr("hello world","lli")); //产生段错误
	const char* str = strstr("hello world","lli");
	if(NULL ==  str)
	{
		printf("没找到\n");	
	}
	else
	{
		printf("找到了\n");	
	}
}
char *strchr(const char *s, int c);
功能:查找字符串S中是否存在c
返回值:如果存在返回C第一次在S中出现的位置,不存在返回NULL


int sprintf(char *str, const char *format, ...);
功能:把各种类型的数据输入到str中
返回值:字符串str的长度
#include <stdio.h>
#include <string.h>

int main(int argc,const char* argv[])
{
	int num = 10086;
	float f = 3.14;

	char str[256]={};
	int ret = sprintf(str,"num = %d\n",num);
	printf("ret=%d\n",ret);
	puts(str);
	for(int i=0; str[i]; i++)
	{
		printf("%d ",str[i]);	
	}
}

在这里插入图片描述

void *memcpy(void *dest, const void *src, size_t n);
功能:从src拷贝N个字节到dest中
#include <stdio.h>
#include <string.h>
#include <stdlib.h> 

int main(int argc,const char* argv[])
{
	int arr[5] = {1,2,3,4,5};

	int* p = malloc(sizeof(int)*5);

	memcpy(p,arr,sizeof(arr));//n小于5时再输出后面的为0,超出无影响
	for(int i=0; i<5; i++)
	{
		//p[i] = arr[i];
		printf("%d ",p[i]);
	}
}
int memcmp(const void *s1, const void *s2, size_t n);
功能:比较两个内存块,按字节比较,n指前几个字符,字母典谁在前谁小
返回值:
	s1 > s2 正数
	s1 = s2 0
	s1 > s2 负数
#include <stdio.h>
#include <string.h>

int main(int argc,const char* argv[])
{
	char* str1 = "abc";
	char* str2 = "abb";

	int ret = memcmp(str1,str2,3);
	printf("ret=%d\n",ret);
}

ret=1

#include <stdlib.h>

int atoi(const char *nptr) 
功能:字符串转int类型
	printf("atoi:%d\n",atoi("a123"));

把字符串“123”转换为数值123,”a123"转换为0,"12a3"转换为12

long atol(const char *nptr);
功能:字符串转long类型

long long atoll(const char *nptr)
功能:字符串转longlong类型

double atof(const char *nptr)
功能:字符串转double类型

#include<stdio.h>

int sscanf(const char *str, const char *format, ...);
功能:从str中解析出各种类型数据,成功读取到的变量个数
	char* str = "1999:2:20";
	int year month day
	sscanf(str,"%d %d %d", year,month,day);//字符串内什么格式就怎么读
	printf("%d%d%d", year,month,day);

考点

练习:自己实现strlen strcpy strcat strcmp 四个常用的字符串处理函数

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

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

size_t str_len(const char* str)
{
	assert(NULL != str);
	const char* tmp = str; //str为首地址
	while(*tmp) tmp++;//tmp指向末地址
	return tmp - str;//相减即是字节长度
}

char* str_cpy(char* dest,const char* src)
{
	assert(NULL != str && NULL != dest);//对来历不明的指针需要判断是否为空指针,用于错误检测,如果表达式结果为0,宏写错误信息到STDERR并退出程序执行,相当于断点
	char* tmp = dest;
	while(*tmp++ = *src++);  //=是赋值运算符,把src的值赋给tmp,而==是关系运算符,是如果src=tmp会在怎么怎么样,当src=‘\0’,循环结束
	return dest;			//如果src的长度超过了dest的长度,则会报错产生段错误,src长度小于dest则没有问题
}

char* str_cat(char* dest,const char* src)
{
	assert(NULL != str && NULL != dest);
	char* tmp = dest; //备份首地址
	while(*tmp) tmp++;//先走到末尾
	while(*tmp++ = *src++);//再从末尾地址开始赋值,如果追加后的长度超过dest的长度,则报错产生段错误
	return dest//返回首地址dest而不是返回末尾tmp
}

int str_cmp(const char* s1,const char* s2)
{
	assert(NULL != s1 && NULL != s2);
	while(*s1 && *s1 == *s2) s1++,s2++;
//	return *s1 - *s2;
	if(*s1 > *s2)
		return 1;
	if(*s1 < *s2)
		return -1;
	else
		return 0;
}

int main(int argc,const char* argv[])
{
	char str[256] = "hehe";
//	printf("%d\n",strlen(NULL));
	printf("strlen:%d\n",str_len(NULL));	
	printf("strcpy:%s\n",str_cpy(str,"ha"));
	printf("strcat:%s\n",str_cat(str,"qqq"));
	printf("strcmp:%d\n",str_cmp("abd","abc"));

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值