C语言字符串

 

目录

1、字符串的几种定义格式

2、和整型数组在存储上的区别

3、sizeof和strlen的区别

4、动态开辟字符串

(1)malloc和calloc函数

(2)realloc函数

(3)free函数

(4)memset函数

5、字符串几种常用的API


1、字符串的几种定义格式

#include <stdio.h>

int main(){
    //1、和整型一样
    int data[]={1,2,3,4,5,6};
    char str1[]={'a','b','c','d','e','f'};
	for(int i=0;i<sizeof(str1);i++){
		printf("%c",str1[i]);
	}
	printf("\n");
    //2、改进
    char str2[]="abcdef";
    printf("2--%s\n",str2);
    //3、常用方法
    char *str3="abcdef";
    printf("3--%s\n",str3);
    return 0;
}

 运行结果

 三种方式都可以,但是第2种和第3种有区别,2是字符串变量,3是字符串常量是不允许被修改的,注意对指针的操作,可以保存地址修改指向但是不能对其进行直接赋值,对野指针的内存空间操作也不行

2、和整型数组在存储上的区别

 这里我们可以先回忆如何计算数组的大小及数组元素的个数,

#include <stdio.h>

int main(){
    int demo[]={1,2,3,4,5};
    char str1[]={'h','a','p','p','y'};
    char str2[]="happy";
    char *str3="happy";
    
    int len=sizeof(demo)/sizeof(demo[0]);
    printf("demo=%d\n",len);
    
    len =sizeof(str1)/sizeof(str1[0]);
    printf("str1=%d\n",len);

    len =sizeof(str2)/sizeof(str2[0]);
    printf("str2=%d\n",len);

    len =sizeof(str3)/sizeof(*str3);
    printf("str3=%d\n",len);
    return 0;
}

运行结果

 demo一共有五个整长度为5,str1一共存储了5个字符长度为5,而str2存储了5个字符但长度为6,为什么呢?因为字符串再存储时系统会自动在字符串末尾加上结束标志'\0'结束标志,而使用字符数组一个一个存储时不会自动在末尾加上结束标志,需要自己手动加,最后一个str3输出的8是指指针变量所占的字节数 

3、sizeof和strlen的区别

 strlen是一个函数,它用来计算指定字符串str的长度,但不包括结束字符,其原型如下

size_t strlen(char const* str);

#include <stdio.h>
#include <string.h>
int main (){
    char *str="happy";
    printf("str的长度%d\n",strlen(str));

    return 0;
}

很显然,上面的代码运行结果为5,因为strlen计算时不会包含结束字符\0。这里需要特别注意的是,函数 strlen 返回的是一个类型为 size_t 的值, size_t 类型(即无符号整型),所以返回结果不可能为负;

关键字 sizeof 是一个单目运算符,而不是一个函数。与函数 strlen 不同,它的参数可以是数组、指针、类型、对象、函数等,

#include <stdio.h>
#include <string.h>
int main (){
   // char *str="happy";
    char str[]="happy";
//注意这样写的话求的就会是指针变量所占的字符大小
   // printf("str的长度%d\n",sizeof(str));

     printf("str的长度%d\n",sizeof(str));
    return 0;
}

 相对于函数 strlen,这里的示例代码运行结果为 6(因为它包括结束字符 null)。同时,对 sizeof 而言,因为缓冲区已经用已知字符串进行了初始化,其长度是固定的,所以 sizeof 在编译时计算缓冲区的长度。也正是由于在编译时计算,因此 sizeof 不能用来返回动态分配的内存空间的大小。

直接对比

#include <stdio.h>
#include <string.h>
int main(){
	//sizeof与strlen的区别
	char str[128]="happy";
	
	printf("sizeof  :%d\n",sizeof(str));//128--输出数组所占空间字符大小
	printf("strlen  :%d\n",strlen(str));//5--输出有效字符所占空间大小
	
	char *p = "happy";
	//p是一个char* ,sizeof来计算的时候,得出的是计算机用多少个字节来表示一个地址
	//而strlen来计算,得出的是指针变量指向的字符计算机用了多少个字节来表示
	//注意strlen只能用于字符串
	printf("sizeof  :%d\n",sizeof(p));//8
	printf("strlen  :%d\n",strlen(p));//5
	
	printf("sizeof  :%d\n",sizeof(char));//1

	printf("sizeof  :%d\n",sizeof(int));//4

	return 0;
}

4、动态开辟字符串

(1)malloc和calloc函数

 malloc函数。其原型void *malloc(unsigned int size);malloc是用来动态开辟的

参数size为要申请的空间大小需要手动计算,如char *p= (char *)malloc(20*sizeof(char)),如果编译器默认char 为1字节存储的话,一次就申请了个20Byte的连续空间,并将空间基地址强制转换为char 类型,赋值给指针p,此时申请的内存值是不确定的。

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

int main(){
    char *str=(char*)malloc(sizeof(char)*20);
    if(str==NULL){
        exit(-1);
    }
    for(int i=0;i<20;i++){
        printf("str = %c  ",str[i]);
    }
    return 0;
}

重点:

1.malloc函数在开辟空间后需要判断开辟空间是否成功,若开辟成功会返回开辟好的空间的指针,开辟失败会返回空指针。

2.malloc函数并不会对开辟空间进行初始化,空间内容为随机数

calloc函数 原型:void* calloc (size_t num, size_t size);

calloc函数与malloc类似 ,calloc可以看作malloc+memset

参数num是开辟空间的元素个数,参数size是元素的大小,单位:字节。

函数的功能是为 num 个 size大小 的元素开辟一块空间,并且把空间的每个字节初始化为0。

calloc函数与malloc函数不同点在于:会将开辟的空间都初始化为0(按字节初始化为0)。而且calloc不需要人为的计算空间的大小

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

int main(){
    char *str=(char*)calloc(20,sizeof(char));
    if(str==NULL){
        exit(-1);
    }
    for(int i=0;i<20;i++){
        //printf("str = %c  ",str[i]);这里要改成%d输出整数类型,如果是输出字符类型就会不显示
        printf("  %d",str[i]);
    }
    return 0;
}

运行结果

(2)realloc函数

realloc函数让动态内存管理变得更加灵活,空间不够时可以对动态开辟的空间扩容

函数原型:void* realloc(void* ptr, size_t szie);

参数ptr为需要扩充动态内存分配的空间地址

size是你需要增加多大的内存,返回的参数为调整后的初始位置

下面是malloc和realloc的联合使用

#include <stdio.h>
#include <stdlib.h>
int main(){
   int *p =(int*)malloc(sizeof(int)*5);
   if(p==NULL){
    exit(-1);
    } 
    p=realloc(p,sizeof(int)*5);
   if(p==NULL){
    exit(-1);
    } 
    for(int i=0;i<10;i++){
        printf("%d  ",p[i]);
}
    return 0;
}

运行结果

 

从运行结果可以看出,realloc函数也不会对空间进行初始化 ,所以realloc与malloc相似,都不会初始化,这其实也是realloc和malloc的一个特性,当要扩容的对象为空时,我们可以把realloc当作malloc函数使用

(3)free函数

free函数是专门用来对动态开辟内存的回收和释放的。当我们不需要再使用动态开辟的空间时,一定要free释放空间,因为 calloc、malloc 或 realloc 所分配的内存空间是在堆上开辟的空间,所以不会随着出了作用域而销毁,需要我们手动free释放,避免内存泄漏,并置空(将指针置为NULL),避免形成野指针。内存释放是标记删除, 只会修改当前空间的所属状态,并不会清除空间内容。

函数原型:void free (void* ptr);

如果参数ptr指向的内存空间不是动态内存开辟的,那free函数的行为是未定义的,就会报错,所以free函数是针对动态开辟的空间,如果ptr是 NULL空指针,那么free函数什么都不做。

 malloc、free、realloc三个函数一起的应用----这里可以不看主要是我自己的学习笔记

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
	
	char *p;//野指针:没有明确的指向一个内存空间
	//*p='s';//这里对野指针进行赋值时会出现一个段错误,因为野指针没有一个具体指向,没有指向任何一个空间
	
	//函数原型  void *malloc(size_t size)按需开辟
	p=(char*)malloc(1);//这里让p指向malloc函数在堆空间开辟的一个字节大小的空间,就让野指针有了一个具体的内存指向
	if(p==NULL){//这里要判断内存是否开辟成功,如果开辟失败就会返回空指针
		printf("malloc error\n");
		exit(1)
	}
	*p='o';
	printf("%c\n",*p);
	puts("end\n");
	//这样就可以正确输出内容了
	//在下面我们又重新开辟了一个内存空间让指针变量p指向它,这就让前面开辟的空间失去了指向没有地址,是无用的空间,悬挂在堆上
	//堆里面的空间要等程序运行结束才会释放,这里我们用free函数来释放内存空间,以防内存泄露,防止悬挂指针,野指针的一种
	free(p);//C 库函数 void free(void *ptr) 释放之前调用 calloc、malloc 或 realloc 所分配的内存空间。
	p=NUll;//将指针置于空
	p=(char*)malloc(1);
	printf("%d\n",sizeof(*p));
	//*p="asdgfghkh";这里不能直接赋值,需要用到拷贝函数strcpy:函数原型char *strcpy(char* dest, const char *src);
	strcpy(p,"asdfghjkl");
	printf("%d\n",sizeof(p));
	puts(p);
	//通过strcpy拷贝进指针变量里的数据输出时不需要用取值符号直接用指针变量名即可
	printf("%s\n",p);
	p=realloc(p,strlen("qwertyuiopasdfghfffffffffff")+1);//加一加的是结束标志
		if(p==NULL){//这里要判断内存是否追加成功
		printf("malloc error\n");
		exit(1)
	}
	strcpy(p,"qwertyuiopasdfghfffffffffff");
	//这里假如拷贝的内容超出了原先定义的空间字符大小则我们需要对开辟的空间进行扩容
	puts(p);
	printf("%d\n",sizeof(*p));
	printf("%s\n",p);
	printf("end\n");

	return 0;
}

(4)memset函数

# include <string.h>
void *memset(void *s, int c, unsigned long n);

 memset()函数主要功能是初始化内存,通常为新申请的内存进行初始化工作。它是直接操作内存空间

#include <stdio.h>
#include <string.h>
int main(){
    char str[12];
    memset(str,0,sizeof(str));
    for(int i =0 ;i<12;i++){
        printf("%d  ",str[i]);
    }
    return 0;
}

运行结果

 

如果不初始化的化是 

 

具体功能直接参考下面,这是我找的讲的比较仔细的memset函数及其用法,C语言memset函数详解 (biancheng.net))

5、字符串几种常用的API

(1)输出字符串:puts();、printf("%s",p);这里注意puts会自动换行,而printf需要我们自己手动换行

(2)获取字符串:scanf("%s",p);

gets();函数原型是char * gets ( char * str );这里有个返回值char*,注意因为本函数可以无限读取,易发生溢出。如果溢出,多出来的字符将被写入到堆栈中,这就覆盖了堆栈原先的内容,破坏一个或多个不相关变量的值

(3)计算长度:strlen前面有讲解

(4)字符串的拷贝

strcpy函数原型:char *strcpy(char* dest, const char *src);参数dest是指向用于存储复制内容的目标数组。参数src是要复制的字符串。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
    char *p;
    p=malloc(sizeof(char)*10);
    p=strcpy(p,"happy");
    printf("%s\n",p);
    return 0;
}

 运行结果

strncpy函数的原型: char *strncpy(char *dest, const char *src, int n)

表示把src所指向的字符串中以src地址开始的前n个字节复制到dest所指的数组中,并返回被复制后的dest,这里都是指针变量

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(){
    char *p;
    char *str;
    p=malloc(sizeof(char)*10);
	str=malloc(sizeof(char)*10);
    p=strcpy(p,"happy");
    str=strncpy(str,p,3);
    printf("%s\n",p);
    printf("%s\n",str);
    return 0;
}

 运行结果

(5)断言assert

函数原型:void assert(int expression);

assert的作用是先计算表达式expression的值为假(即为0),那么它就先向stderr打印一条出错信息,然后通过条用abort来终止程序;简而言之就是条件不成立就终止程序,条件成立就继续运行

具体用法我们可以参考文章:C语言C++中assert的用法 - 知乎 (zhihu.com)

(6)拼接strcat

函数原型: char *strcat(char *dest, const char *src);

把src所指向的字符串(包括“\0”)复制到dest所指向的字符串后面(删除*dest原来末尾的“\0”)。要保证*dest足够长,以容纳被复制进来的*src。*src中原有的字符不变。返回指向dest的指针。

函数的使用 

(7)比较字符串strcmp和strncmp

strcmp函数原型: int strcmp(const char *s1,const char *s2);

若str1=str2,则返回零;若str1<str2,则返回负数;若str1>str2,则返回正数,这里说一个strcmp函数的缺点,strcmp函数是根据字符串第一个不相同的字母来比较大小的,出现了一个不同的字符之后指针就不会往后偏移了,它会根据这个不同的字符来比较字符串的大小

strncmp函数原型:int strncmp ( const char * str1, const char * str2, size_t n )

功能是把 str1 和 str2 进行比较,比较前 n 个字节,若str1与str2的前n个字符相同,则返回0;若s1大于s2,则返回大于0的值;若s1 小于s2,则返回小于0的值。

(8)查找字符串strchr

strchr函数原型:char *strchr(const char *str, int c)

在参数 str 所指向的字符串中搜索第一次出现字符 c(一个无符号字符)的位置

如果在字符串 str 中找到字符 c,则函数返回指向该字符的指针,如果未找到该字符则返回 NULL。

(9)字符串分割strtok

strtok函数模型:char *strtok(char *str, const char *delim)

str -- 要被分解成一组小字符串的字符串。delim -- 包含分隔符的 C 字符串。

函数的功能:

        字符串切割,按照delim指向的字符串中的字符,切割str指向的字符串。其实就是在str指向的字符串中发现了delim字符串中的字符,就将其变成,调用一次strtok只切割一次,切割一次之后,再去切割的时候strtok的第一个参数传NULL,意思是接着上次切割的位置继续切割,如果切割结束了他就会返回一个NULL

注意如果str字符串中出现了连续的几个delim中的字符,则只将第一个字符变成'\0',下次切割的时候它会自动跳过那些分隔符,分隔符可以有多个

#include <stdio.h>
#include <string.h>
int main(){
	//这里假如用指针变量会出错,因为指针变量定义字符串它是常量
    char str[128]="gskjhg,,sgd..agf//da,sgsrfef&sggs dfasng";
    char *str2;
    str2 =strtok(str,".,&/ ");
    while(str2!=NULL){
         printf("%s\n",str2);
         str2 =strtok(NULL,".,& /");

    }
	return 0;
}

扩展:野指针没有明确的指向一个内存空间的指针

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值