震惊,C语言字符串还有这么多坑?

C语言学习笔记第十一天

  • 一、字符串

字符
在计算机中字符都是以整数形式存储的,当需要显示成字符时,就会根据ASCII码表中的对应关系显示出相应的符号或图案

字符(字面值)ascall码值
‘\0’0空字符
‘0’48
‘A’65
‘a’97

:是一种数据结构,有一组连续的若干类型相同的数据组成,末尾一定有一个结束标志
对于这种数据结构的处理都是批量性的,从一开始的地方一直处理到结束标志停止

字符串:由字符组成的串行结构的结束标志是 ‘\0’

  • 二、字符串的存在形式

字符数组:

char str[5]={'a','b','c','d'};

由char类型组成的数组,需要为‘\0’预留位置,使用的是栈内存,数据可以中途修改的

字符串字面值:
“有双引号包含的若干个字符”

char str="abcd";

特点是末尾会自动补零,字符串字面值以地址形式存在,字符数据存在代码段,如果修改一定会产生段错误
常用方式:字符数组[]="字符串字面值",会自动给‘\0’预留位置,可以修改内容,初始化简单,赋值完成后字符串存在两份,一份在代码段,一份在栈内存
字符串本身是一个地址,但是sizeof算出来是字符串在代码段中占用空间(包括‘\0’),所以字符串跟数组的属性很像。

注意:完全相同的字符串字面值,在代码段中只存在一份

  • 三、字符串的输入输出

字符串输入:

char str[256]={};

scanf方法

scanf("%s",str);

注意scanf不能接收空格,遇到空格输入结束,scanf(“%s”,str);会在输入数据后默认加入‘\0’

gets方法

格式:char* gets(char* s);

可以输入带有空格的字符串,缺点是不限制输入长度,有安全隐患,编译器会有警告

返回值为s,为了链式调用,一个函数的返回值当作另一个函数的参数,

fgets方法

格式:char *fgets(char *s, int size, FILE *stream);

功能:从stream中最多输入size-1个字符到s中

s:最多接受的字符个数+1

size:最多接收的字符个数+1

stream:stdin相当于键盘文件,固定写

返回值:链式调用

如果输入超过size-1个字符,多出来不接受

如果输入不足size-1,‘\n’也会接收

使用方法:fgets(str,10,stdin);

字符串输出

printf方法

printf("%s",str);

int puts(const char *s);

功能:输出字符串

返回值:成功打印的个数

注意:会自动打印换行符

练习1:实现一个判断字符串是否为回文串的函数

练习2:实现一个函数,把一个由数字字符组成的字符串转换成对应的整数

练习3:实现一个函数,把字符串逆序

个人答案如下:

//判断回文数
bool is_palindrome_str(const char* str,int len)
{
    for(int i=0;i<=len/2;i++)
    {   
        if(*(str+i)!=*(str+len-i-1))
        {
            return 0;   
        }
    }   
    return 1;
}
//转换成对应的整数
int change(const char* str)
{
    int num=0;
    while(*str)
    {   
        num=num*10+(*str++)-'0';    
    }   
    return num;
}
//字符串逆序
char* contrary_arr(char* str,int len)
{
    char* str_start=str;
    char t;
    for(int i=0;i<len/2;i++)
    {
        t=*(str+i);
        *(str+i)=*(str+len-i-1);
        *(str+len-i-1)=t;
    }
    return str_start;
}

  • 四、输入缓冲区(次重点)

程序中要输出显示的的内容并不会立即显示到屏幕上,而是先存到输出缓冲区中,当满足一定条件时才会从缓冲区中显示内容到屏幕上

条件:

  1. 遇到‘\n’后
  2. 遇到输入语句后
  3. 当输入缓冲区满了会将所有的数据输出
  4. 程序结束时
  5. 手动刷新 fflash(stdout);
  • 五、输入缓存区(重点)

程序并不是立即从键盘获取输入的内容,而是当按下回车后,终端输入的内容会先存储到缓冲区中,然后输入函数再从输入缓冲区中读取数据到内容中。

  1. 当想要读取整形或浮点型数据,但是缓存区中的数据是符号或字符时,读取会失败,数据会残留在缓存区中,影响接下来所有数据的读取

    解决:在%c前面加上一个空格,将缓存区的\n冲掉。判断scanf的返回值是否全部输入数据,如果不是,先清理输入缓冲区再重新输入,直到全部输入正确。

  2. fgets可以接受指定size-1个字符,如果有多余的字符会残留到缓冲区中,可能会影响接下来的输入

  3. 当先输入整形浮点型数据,在输入字符或者字符串时,前一次输入中会残留\n,影响了后面字符字符串的输入。

    解决1:

    1. 先判断‘\n’在不在字符串内,如果不在,则说明在缓冲区中,则执行2和3步骤,否则不执行。
    2. scanf("%*[^\n]"),从缓存区中读任意字符并丢弃,直到遇到\n,(\n没有读取掉)
    3. scanf("%*c");从缓存区中读取一个字符,并且丢弃。

    解决2:

    ​ 把输入缓冲区中的当前位置指针,移动到缓冲区末尾,相当于清理了输入缓冲区(只能Linux/Unix下使用)

    1. stdin->_IO_read_ptr = stdin->_IO_read_end;

    "%*[^\n]"涉及到正则表达式,感兴趣上网搜,不必深究

  • 六、字符串相关操作函数

头文件:inclulde <string.h>

原型:size_t strlen(const char *s);

作用:求s的字符长度,遇到‘\0’结束,但是‘\0’不计入长度

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

作用:src拷贝给dest,连同’\0’一起,但是后面字符不改变

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

作用:src的n个字符拷贝给dest,连同’\0’一起,但是后面字符不改变

返回值用来链式调用

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

作用:将src追加到dest末尾,相当于合并两个字符串

返回值用来链式调用

int strcmp(const char *s1, const char *s2);

作用:比较两个字符串的大小

从头开始,每个字符一对一进行比较,按照字典序,谁出现在谁前面谁就小(谁ascall码大谁就大),一旦比较出结果,立即返回结果

以下s1和s2表示字符串(方便表示)

0s1=s2
<0s2<s2
>0s1>s2

作业1:自己实现strlen\strcpy\strcat\strcmp

其他小知识,包含头文件<unistd.h>后可以使用sleep和usleep(线程睡眠)

作业个人代码(老师还没讲):

/************************************
> 作者:杭电羊皮卷
> QQ:2997675141
> weixin:QQ2997675141
************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int strlen_t(const char* s)
{
	int i=0;
	while(s[i])i++;
	return i;
}
char* strcpy_t(char* dest,const char* src)
{
	char* start=dest;
	while((*dest++=*src++)!='\0');
	return start;
}
char* strcat_t(char* dest,const char* src)
{
	char* p=dest;
	while(*p){p++;};
	while((*p++=*src++)!='\0');
	return dest;
}
int strcmp_t(const char* s1,const char* s2)
{
	while(*s1!='\0'&& *s2!='\0' && *s1++==*s2++);
	if(*s1-*s2<0)	return -1;
	else if(*s1-*s2>0)	return 1;
	else return 0;
}
int main(int argc,const char* argv[])
{
	char str[]="abcde";
	char str1[]="1234";
	char str2[]="1234";
	char strdest[30]="123456";
	char strdest1[30]="123456";

	printf("===========test1=================\n");
	printf("strlen = %d\n",strlen_t("abcdef"));
	printf("strlen = %d\n",strlen("abcdef"));
	printf("===========test2=================\n");
	printf("strcat: %s\n",strcat_t(strdest,str));
	printf("strcat: %s\n",strcat_t(strdest1,str));
	printf("===========test3=================\n");
	printf("strcmp: %d\n",strcmp_t("abcde","abcd"));
	printf("strcmp: %d\n",strcmp("abcde","abcd"));
	printf("===========test4=================\n");
	printf("strcpy: %s\n",strcpy_t(str1,"abcd"));
	printf("strcpy: %s\n",strcpy(str2,"abcd"));


	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值