字符串-字符数组和字符串常量

学习字符串时先要对字符串常量和字符数组有个明确的概念

字符数组

  • 用来存放字符的数组
char str1[] = {'a','b','c','d','e'};
char str2[] = "abcdefg";
char buf1[] = {'a','b','c','d','e'};//默认元素个数
char buf2[4] = {'a','b','c','d'};//指定元素个数
char buf3[4] = {'a','b'};//指定4个元素,其余两个自动补0

用字符串来初始化数组

char buf1[] = {'a','b','c','\0'};
char buf2[] = {"abcdefg"};
char buf3[] = "abcdefg";
char buf4[10] = "abcd";

  • 在c语言中使用字符数组来模拟字符串
  • c语言中字符串都是以‘\0’结束的字符数组
  • 字符串可以在栈、堆或只读存储区分配空间
  • strlen()求字符串长度,字符串长度不包括‘\0’
  • sizeof(buf3)字符串类型的大小,包括‘\0’,如上buf3 sizeof(buf3) = 8

字符串常量

  • c语言中没有字符串这个类型,但是存在字符串常量,以‘\0’来结尾
char *str = "hello world";

字符串常量和指针的关系

由上面可以看出,字符串常量保存的是一个地址,是字符串常量首字符的地址,而不是这个字符串本身。因此在c语言中要想访问一个字符串通常声明一个char *类型的指针并初始化一个字符串常量。

如何访问字符串中的每一个字符,以为字符串都是以‘\0’结尾的。因此可以循环遍历,如果不是‘\0’,则地址下移。

while (*str)\\ *str != '\0'
{
    printf("%c\n",*str);
    str++;
}

字符串相关一级指针内存模型

char buf[10]= "abcde";
char buf2[] = "aaabb";
char *p1 = "hello world";
char *p2 = malloc(20); strcpy(p2, "abcd");

操作字符串的两种方法

  1. 数组下标
  2. 指针
  3. 字符数组名,代表字符数组首元素的地址,不代表整个数据的地址
char buf[] = "abcdefgh";
//数组下标
int i = 0;
int count = strlen(buf);
for ( i = 0; i < count; i++)
{
    printf("buf[%d] = %c\n",i,buf[i]);
}
    
//指针
for ( i = 0; i < count; i++)
{
    printf("buf[%d] = %c\n",i,*(buf+i));
}

int index = 0;
while (buf[index])
{
    printf("buf[%d] = %c\n",index,*(buf+index));
    index++;
}

字符串做函数参数

void Copy_Str1(char *from,char *to)
{
    for(;*from != '\0';from++,to++)
    {
        *to = *from;
    }
    *to = '\0';
}

//上下对比
int Copy_Str2(const char *from,char *to)
{
    if(from == NULL || to == NULL)
    {
        printf("throw error\n");
        return -1;
    }

    while ((*to++ = *from++) != '\0')
    {
        ;
    }
    return 0;
}

        对比上面两个方法,都是拷贝字符的方法,但是推荐使用第二种Copy_Str2(),首先被拷贝者来说只进行简单的读取操作,因此使用const关键字来提高数据的安全性。对拷贝者来说是要对其进行读写操作,所以不需要加const。添加判断机制防止空指针异常,使用while语法可以减少代码行数,提高了编译效率。

常见字符串处理函数

gets()

  • 从标准输入读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止。

#include <stdio.h>
//s:字符串首地址
char *gets(char *s);

//返回值:
//	成功:读入的字符串
//	失败:NULL

char str[100];
printf("请输入str: ");
gets(str);
printf("str = %s\n", str);

gets(str)与scanf(“%s”,str)的区别:

  • gets(str)允许输入的字符串含有空格​​​​​​​
  • scanf(“%s”,str)不允许含有空格

注意:由于scanf()和gets()无法知道字符串s大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字符数组越界(缓冲区溢出)的情况。

fgets()

  • 从stream指定的文件内读入字符,保存到s所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
#include <stdio.h>
//s:字符串
//size:指定最大读取字符串的长度(size - 1)
//stream:文件指针,如果读键盘输入的字符串,固定写为stdin
char *fgets(char *s, int size, FILE *stream);

//返回值:
//	成功:成功读取的字符串
//	读到文件尾或出错: NULL

char str[100];
printf("请输入str: ");
fgets(str, sizeof(str), stdin);
printf("str = \"%s\"\n", str);

        fgets()在读取一个用户通过键盘输入的字符串的时候,同时把用户输入的回车也做为字符串的一部分。通过scanf和gets输入一个字符串的时候,不包含结尾的“\n”,但通过fgets结尾多了“\n”。fgets()函数是安全的,不存在缓冲区溢出的问题。

puts()

  • 标准设备输出s字符串,在输出完成后自动输出一个'\n'。
#include <stdio.h>
//s:字符串首地址
int puts(const char *s);

//返回值:
//	成功:非负数
//	失败:-1

int main()
{
    puts("hello world");
    printf("Hello World");
    return 0;
}

fputs()

  • 将str所指定的字符串写入到stream指定的文件中 字符串结束符 '\0'  不写入文件。
#include <stdio.h>
//str:字符串
//stream:文件指针,如果把字符串输出到屏幕,固定写为stdout
int fputs(const char * str, FILE * stream);

//返回值:
//	成功:0
//	失败:-1

int main()
{
    printf("hello world\n");
    puts("hello world");
    fputs("hello world", stdout);
    return 0;
}

        fputs()是puts()的文件操作版本,但fputs()不会自动输出一个'\n'

​​​​​​​strcpy()

  • 把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
#include <string.h>
//dest 目的字符串首地址
//src  源字符首地址
char *strcpy(char *dest, const char *src);

//返回值:
//	成功:返回dest字符串的首地址
//	失败:NULL


char src[] = "abcdef";
char dest[10] = "12345";//dest的内存空间要足够大,否则会造成缓冲溢出

strcpy(dest,src);
printf("%s\n",dest);

 

 strcpy前       strcpy后 

注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

strncpy()

  • 把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'

#include <string.h>
//dest:目的字符串首地址
//src: 源字符首地址
//n:   指定需要拷贝字符串个数
char *strncpy(char *dest, const char *src, size_t n);

//返回值:
//	成功:返回dest字符串的首地址
//	失败:NULL


char src[] = "abcdefd";
char dest[10] = "12345";

strncpy(dest,src,4); 
dest[4] = '\0';//把src中的前四个字符拷贝给了dest,没有以'\0'结尾,手动添加。
printf("%s\n",dest);

 strcpy前       strcpy后 

memcpy()

  • 拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。

#include <string.h>
//dest:目的内存首地址
//src:源内存首地址,注意:dest和src所指的内存空间不可重叠
//n:需要拷贝的字节数
void *memcpy(void *dest, const void *src, size_t n);

//返回值:dest的首地址

int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int b[10];
	
memcpy(b, a, sizeof(a));
int i = 0;
for (i = 0; i < 10; i++)
{
    printf("%d, ", *(b +i));
}

strset()

  • 将字符串str中所有的字符都设置成为指定的字符。
#include <string.h>
//_Str 目标字符串
//_Val 虽然参数为int,但必须是unsigned char , 范围为0~255
char *__cdecl strset(char *_Str,int _Val)

//返回值:
//	成功:返回被替换后的str首地址
//	失败:NULL


char str[10] = "12345";
char symbol = 'b';
strset(str,symbol);
printf("%s\n",str);

strnset()

  • 将一个字符串中的前n个字符都设为指定字符。
#include <string.h>
//_Str 目标字符串
//_Val 虽然参数为int,但必须是unsigned char , 范围为0~255
//_MaxCount 最大字符数范围
char *__cdecl strnset(char *_Str,int _Val,size_t _MaxCount)

//返回值:
//	成功:返回被替换后的str首地址
//	失败:NULL

char str[30] = "123asd45sdasd";
char symbol = 'k';
strnset(str,symbol,10);
printf("%s\n",str);

memset()

  • 将s的内存区域的前n个字节以参数c填入

#include <string.h>
//s:需要操作内存s的首地址
//c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
//n:指定需要设置的大小
void *memset(void *s, int c, size_t n);

//返回值:s的首地址

char a[10];

memset(a, 0, sizeof(a));
strcpy(a,"abcd");
int i = 0;
for (i = 0; i < strlen(a); i++)
{
    printf("%c\n", a[i]);
}

strpbrk()

  • 比较字符串str1和str2中是否有相同的字符,如果有,则返回该字符在str1中的位置的指针。
#include <string.h>
//_Str        待比较的字符串
//_Control    指定被搜索的字符串
char *__cdecl strpbrk(const char *_Str,const char *_Control);

//返回值 返回指针,搜索到的字符在_Str中的索引位置的指针。

char *str1 = "hello world!";
char *str2 = "world";

char *str3 = strpbrk(str1,str2);
printf("%s\n",str3);

strtok()

  • 分解字符串 str 为一组字符串,delim 为分隔符。

#include <string.h>
//str   要被分解成一组小字符串的字符串。
//delim 按照那种字符进行分割
char *strtok(char *str, const char *delim)

//返回值  该函数返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。

char str[] = "this is strtok function";
char *s = " ";

char *result = strtok(str, s);//(位置1)
printf("%s\n",str);//特别要注意分割处理后原字符串 str 会变,变成第一个子字符串

while (result)
{
    printf("%s\n",result);
    result = strtok(NULL,s);//(位置2)
}

注意:strtok(NULL,s)中的NULL怎么理解。这里的strtok涉及到了两个指针pointer_a和pointer_b,pointer_a是用来指向返回的字符串的指针;pointer_b是指向匹配字符的位置。因此上面代码(位置1)处执行完后pointer_a指向了“this”的首部元素位置,pointer_b指向了“this is”中间的空格位置,代表pointer_b之前的位置已经进行过查找匹配了。这样,在while循环中的(位置2)执行时,只要把strtok()的第一个参数设置为NULL,就可以直接从pointer_b位置开始进行查找匹配了。因此这里传入NULL是告诉编译器,之前的位置都匹配过了,接下来从pointer_b开始继续向下匹配。

第一次执行完strtok时,源字符串会被分割处理,变成第一个子字符串。

还有很多字符串处理函数,这里就不一一列举了,其实用的的时候可以百度一下。

const理解

在指针中学过了,指针常量和常量指针。这里在详细了解一下,const关键字是定义一个常量意味着这个变量函数只读,

const char *a;
char * const b;
const char * const c;
  • a 是一个指向常整形数的指针(所指向的内存数据不能被修改,但是本身可以修改)
  • b 是常指针(指针变量不能被修改,但是它所指向内存空间可以被修改)

  • c 是一个指向常整形的常指针(指针和它所指向的内存空间,均不能被修改)

合理的使用const可以有效提高代码质量,减少bug,对函数参数使用const可以清楚的分清参数的输入和输出特性

 

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
C语言中,字符串常量是由一对双引号括起来的字符序列,例如"hello world"。字符串常量在内存中以字符数组的形式存储,以空字符'\0'作为结束标志。可以通过printf函数使用%s格式输出字符串常量。\[2\] 字符数组是由一组字符组成的数组,可以用来存储字符串字符数组的定义和初始化可以使用字符数组的形式,例如char s1\[\]="hello world",也可以逐个字符赋值,例如char s2\[20\]; s2\[0\]='h'; s2\[1\]='e'; s2\[2\]='l'; s2\[3\]='l'; s2\[4\]='o'; s2\[5\]=' '; s2\[6\]='w'; s2\[7\]='o'; s2\[8\]='r'; s2\[9\]='l'; s2\[10\]='d'; s2\[11\]='\0';。字符数组可以通过printf函数使用%c格式输出单个字符,也可以使用puts函数输出整个字符串。\[3\] 需要注意的是,字符常量字符数组在赋值和输入时的方式是不同的。字符常量可以直接赋值给字符变量,例如char c='a';而字符数组需要使用strcpy函数或者逐个字符赋值的方式进行赋值。在输入时,字符常量可以使用scanf函数直接输入,例如scanf("%c",&a);而字符数组需要使用scanf函数逐个字符输入,或者使用gets函数一次输入整个字符串。\[1\]\[3\] #### 引用[.reference_title] - *1* *2* *3* [C语言字符常量字符串常量字符数组使用](https://blog.csdn.net/guorongyi/article/details/117827664)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值