首页 > 其他分享 >c语言笔记5

c语言笔记5

时间:2023-08-23 09:02:04浏览次数:25  
标签:const 语言 int 笔记 char 内存 字符串 函数

c语言笔记5(动态内存申请,字符串处理函数,const与指针的关系)

1. 动态内存申请

现状:数组长度是预先定义好的,在整个程序中固定不变

问题:但是在实际的编程中,往往会发生这种情况,即所需的内存空间取决于实际输入的数据,而无法预先确定

解决办法:为了解决上述问题,c语言提供了一些内存管理函数,这些内存管理函数可以按需要动态的分配内存空间,也可以把不再使用的空间回收后再次利用

1.1 静态分配和动态分配

静态分配

1.在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式
2.必须事先知道所需空间的大小
3.按计划分配

动态分配

1.在程序运行过程中,根据需要大小自由分配所需空间
2.按需分配
3.分配在堆区,一般使用特定的函数进行分配

1.2 动态分配相关的函数

引入头文件<stdlib.h>

1.2.1 malloc()函数

原型:void *malloc(unsigned int size);

功能说明:

在内存的动态存储区(堆区)中分配一块长度为size字节的连续区域,用来存放类型说明符指定的类型。函数原型返回void*指针,使用时必须做相应的强制类型转换,分配的内存空间内容不确定,一般使用memset初始化

返回值:分配空间的起始地址(分配成功),NULL(分配失败)

1.2.2 free函数

原型:void free(void* ptr);

功能:free函数释放ptr指向的内存

【注意】ptr指向的内存必须是malloc、colloc、relloc三个函数之一的动态申请的内存,一块动态申请的内存只能free一次,不能多次free

1.2.3 calloc函数

void *calloc(size_t nmemb,size_t size);

功能:在内存的堆中,申请nmemb块,每块的大小为size个字节的连续区域

malloc和calloc函数都是用来申请内存的,他们的区别:

malloc 申请的内存,内存中存放的内容是随机的,不确定的
calloc 函数申请的内存中的内容为0

1.2.4 realloc函数(重新申请内存)

使用场景:扩大或缩小内存空间

void *realloc(void *s,unsigned int newsize);

在原先s指向的内存基础上重新申请内存,新的内存大小为new_size

个字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不够用,则realloc函数会在堆区找一个newsize个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址

1.2.5 memset函数

初始化内存空间的数据,一般在动态申请空间之后,使用。

部分编译器中,动态申请空间时,内容不确定,因此需要进行初始化

#include <string.h>

void *memset(void *s,int c,size_t n);
s表示指针的首地址(内存空间的首地址)
c初始值
n字节大小

1.3 内存泄露

从堆区中分配(申请)的内存空间,在使用之后,没有释放free;或者不存在有效的指针来指向此内存空间

1.3.1 分配的内存空间没有释放

如:主函数中没有释放p指针指向的堆内存空间

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


int *create(int n)
{
    int *p=(int*)malloc(n*4);
    memset (p,0,n*4);
    if(NULL == p)
    {
        perror("malloc");
    }
    return p;
}
int main()
{
    int *p=create(s);
    p[0]=100;
    printf("%d\n",*p);
    //用完之后释放
    free(p);
    return 0;
}

1.3.2 分配的空间没有引用

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

void create(char *p,int n)
{
    p = (char *)malloc(n);
    memset(p,'\0',n);
    if(NULL == p)
    {
        perror("malloc");
    }
}
int main(){
    char *p=NULL;
    create(p,32);
    scanf("%s",p);
    printf("==>%s\n",p);
    free(p);
    return 0;
}

【注】申请的内存,一定不要把首地址给丢了,在不用的时候一定要释放内存

2. 字符串处理函数

字符串处理的相关函数都在<string .h>头文件中

2.1 strlen求长度函数

size_t strlen(const char*s);

返回字符串中字符个数,到'\0'结束

自定义函数,实现strlen

int str_len(const char*str)
{
    //const作用:防止str指针在函数内被指向其他地方
    int len = 0;
    while(str[len++]);
    return len-1;//不需要'\0'
}
int main()
{
    while(1)
    {
        printf("data:");
        scanf("%s",content);
        if(content[0]=='q')
            break;
        printf("%s len is%d\n",content,str_len(content));
        //printf("%s len is %ld\n",content,strlen(content));
    }
    return 0;
}

2.2 strcpy复制函数

将src的内容复制到前面的字符串中,包含'\0'内容,返回dest目标的地址

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

自定义复制函数,在复制内容之前先清空,再复制内容

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

void strcp(char *dest,char *src)
{
    int len = 0;
    while(src[len++]);
    //复制时,不包含'\0'
    for (int i = 0;i<len-1;i++)
    {
        *(dest + i) = *(src + i);
    }
    //dest原内容中多余的要清空
    while (dest[len-1])
    {
        dest[len-1]=0;
        len++;
    }
}

2.3 strcat 拼接函数

将src的内容追加到dest字符串的后面,要求dest字符串的空间足够大

在dest的第一个\0的位置上追加src的内容,包含src的'\0'

char *strcat(char *dest,const char*src);
char *strncat(char *dest,const char *src,size_t n);

自定义strcat的功能

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


char *strjoin(char *dst,char *src)
 //char *strjoinn(char *dst,char *src,int n)
{
    int insert_pos = 0;
    while(dst[insert_pos])
        insert_pos++;
    //在dst的第一个'\0'位置上开始拼接src的内容
    for(int i =0;i<=strlen(src);i++)
 // for(int i=0;i<n;i++)      
    {
        dst[insert_pos + i]=*(src+i);
    }
    return dst;
}

2.4 strcmp比较函数

比较两个字符串的内容是否相等,按位置从左到右依次比较相同位置的字符,如果相同继续比较,如果不同返回(两个字符的差值为正数),-1(两个字符的差值为负数)

返回值:

0 s1 == s2

1 s1 > s2

-1 s1 < s2

int strcmp(const char *s1,const char *s2);
int strncmp(const char *s1,const char *s2,size_t n);

自定义strcmp函数的功能

int cmp(char *s1,char *s2)
//cmpn(char *s1,char *s2,int n)    
{
    int i=0;
    while(s2[i]&&s1[i]==s2[i])
  //while(i<n-1&&s1[i]&&s1[i]==s2{i})      
        i++;
    int delta = s1[i]-s2[i];
    if(delta == 0)
        return 0;
    else if(delta > 0)
        return 1;
    else 
        return -1;
}

2.5 strchr和strstr查找函数

查找字符或字符串的函数

char *strchr(const char*s,int c);
从s字符串中查找是否存在c字符,如果存在,则返回字符在s中的第一次出现的位置(地址),反之,不存在时,返回NULL。
char *strrchr(const char*s,int c);
查找c,最后一次在s中出现的位置(地址)。如果不存在,则返回NULL
char *strstr(const char *haystack,const char *needle);
从haystack中查找首次出现needle(子字符串)的内容位置(地址)

2.6 字符串转数值的函数

<stdlib.h>库文件

int atoi(const char *nptr);  将字符串的数字转化为数值 int
long atol(const char *nptr);   将字符串的数组转化为长的数值 long
double atof(const char *nptr);   将字符串的数字和小数点转化为浮点型 double

2.7 strtok切割函数

按照delim指向的字符串中的字符,切割str指向的字符串

其实就是在str指向的字符串中发现了delim字符串中的字符,就将其变成'\0'

调用一次strtok只切割一次,切割一次之后,再去切割的时候strtok的第一个参数传NULL,意思是接着上次切割的位置继续切。

char *strtok(char *str,const char *delim);

int main(int argn,char *argv[])
{
    char str[100] = "小明:21,,,.男.女,北京:haidian";
    int i = 0;
    char *delim = ":,.";
    char *ret[10];
    ret[i] = strtok(str,delim);//返回第一次切割的内容
    while (ret[i] != NULL)
    {
        i++;
        //第二次切割时,目标为NULL时,表示接着上一次继续切割
        ret[i] = strtok(NULL,delim);
    }
    char *title[10]={"姓名","年龄","性别","爱好","城市","区"};
    for(int j=0;j<i;j++)
    {
        printf("%s:%s\n",title[j],ret[j]);
    }
    return 0;
}

2.8 格式化字符串

  1. 输出到字符串

    int sprintf(char *buf,const char *format,...);
    将format格式及后面的数据,输出到buf字符串,并返回buf的字符长度
    基于此函数,可以实现数值转字符串,字符串拼接
    如:
       int ret = sprintf(buf,"%4d年%2d月%2d日",2023,7,18);2.
    
  2. 从字符串获取

    int scanf(const char *buf,const char *format,...);
    从buf字符串按format格式提取数据并赋值给后面的变量地址
    

    1.一般用法
    sscanf(content,"%5s,%11s,%s",name,phone,qq);
    printf("name:%s\nphone:%s\nqq:%s\n",name,phone,qq);
    2.高级用法
    1)跳过格式:%*,%*N+s|d|f,(贪婪模式,如%*s,跳过的字符尽量多,遇到\0、空格、换行符则停止)
    char *content="hello,12345678910,12345678910";
    char phone[12]="";
    sscanf(content,"%*6s%11s",phone);
    2)支持范围(集合)选择或跳过(具有贪婪性)
    %[a-z]  提取所有的小写字母
    %[abc]  提取所有a,b,c三个字符的内容
    %[^abc] 提取非a,b,c三个字符之外的所有内容
    %[^a-z] 提取非所有小写字母
    (以上遇到不符合规则的,则停止)
    char *content = "abc#def@ghi";
    char s1[20]=""; char s2[20]="";
    sscanf(content,"%[^#]#%[^#]",s1,s2);
    sscanf(content,"%[abc]#%s",s1,s2);
    sscanf(content,"%[abc#%[^A-Z]]",s1,s2);
    sscanf(content,"%[a-z]#%[a-z@]",s1,s2);
    

3. const与指针的关系

3.1 const修饰*

const修饰变量时,变量的值不能被修改(通过变量的方式来修改)

const int m=0;
//m=100;//报错
int *p=&m;
*p=100;
printf("%d\n",m);

如果const修改指针的*,表示 *p只读的,p指针读写(修改指向的位置或地址)

const int m=10;
const int *p=&m;
//*p=100;//报错
int n =50;
p=&n;//p指向新的地址
//*p=200;//报错

3.2 const修饰指针变量

如果const修饰指针变量时,指针变量是只读(不能指向新的地址)

int * const p=&n; const修饰的是p,p是只读的,*p读写的

const int n=10;
int *const p =&n;//必须给初始化的地址 p只读,*p读写
*p = 20;
printf("%d %d\n",n,*p);
int m = 100;
//p = &m;//报错,p只读的,不能指向新的地址了
return 0;

3.3 const修饰*和指针变量

const即修饰*,又修饰指针变量p,则 *p和p都是只读的

const int*const p =&n

int m =100;
const int * const p =&m;
*p += 10;//error
int n =90;
p=&n;//error

标签:const,语言,int,笔记,char,内存,字符串,函数
From: https://www.cnblogs.com/dijun666/p/17650113.html

相关文章

  • C语言数组(3)--- 一维数组的内存存储
    一.引入我们前面已经介绍了一维数组的创建以及使用,下面我们来探究一下一维数组在内存中的存储#define_CRT_SECURE_NO_WARNINGS1#include<stdio.h>intmain(void){ intarr[]={1,2,3,4,5,6,7,8,9,10}; intsz=sizeof(arr)/sizeof(arr[0]); for(inti=0;i<sz;i++......
  • 《深入理解Java虚拟机》读书笔记: 类加载器
                                     类加载器   虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到Java虚拟机外部去实现,以便让应用程序自己决定如何去获取......
  • dp学习笔记
    前言:因为本人\(dp\)实在太差了,故此挖个新坑。\(dp\)的一般套路是:设计状态,要注意一定要不重不漏,所有能影响到答案的数据都要包含到状态里面。初始化,基本上是第一项转移,要注意无后效性,面面俱到。可以关注数据范围,有时候范围会给我们以提醒。基本技巧:状态设计:......
  • 学习笔记:DSTAGNN: Dynamic Spatial-Temporal Aware Graph Neural Network for Traffic
    DSTAGNN:DynamicSpatial-TemporalAwareGraphNeuralNetworkforTrafficFlowForecastingICML2022论文地址:https://proceedings.mlr.press/v162/lan22a.html代码地址:https://github.com/SYLan2019/DSTAGNN一个用于时空序列预测的交通流量预测模型。可学习的地方:提出......
  • Jmeter(二十八)加密接口测试笔记
    一、加密接口测试场景1、例如登录操作,输入账号密码,返回token,token是需要加密的2、Jmeter本身没有加解密函数工具二、加密接口和普通接口有什么区别1、发送出去的数据需要进行额外处理,接口测试工具通常不具备这个功能三、如何测试加密接口1、测试数据准备......
  • 统计数据源(NLP/AI/ML): Indeed.com(全球超过60个市场28种语言的招聘站:可视化统计数
    Indeed.com:全球招聘站可视化统计数据:(全球超过60个市场28种语言的招聘站:可视化统计数据https://www.hiringlab.org/data/)Indeedhaswebsitesinover60marketsand28languages.Thefulllistofmarketsishere:https://www.indeed.com/worldwide.Wehaveeconom......
  • Markdown学习笔记
    标题语法标准语法要创建标题,只需要在单词或者短语钱添加井号#。井号的个数代表标题的级别,支持1~6个级别可选语法可以在文本下方添加任意数量的=号来标识一级标题,或者-号来标识二级标题最佳实践为了兼容各类应用程序#和标题之间使用一个空格来分割段落(段落1)使用......
  • Go 语言基础知识
    有道云分享链接 什么是Go语言Go是一门并发支持、垃圾回收的编译型系统编程语言,旨在创造一门具有在静态编译语言的高性能和动态语言的高效开发之间拥有良好平衡点的一门编程语言。 一些设计思想不要通过共享内存来通信,要通过通信来共享内存 Go的主要特......
  • 学习C语言第一天
    循环语句和分支语句#include<stdio.h>intmain(){ //输出1~100之间的奇数循环语句的两种表达方式 inti=1; //for(inti=1;i<=100;i++) //{ // if(i%2==1) // { // printf("%d\n",i); // } //} while(i<=100) { if(i%2==1) printf("......
  • PyTorch数据处理工具箱-新手笔记
    数据下载和预处理是机器学习、深度学习实际项目中耗时又重要的任务,尤其是数据预处理,关系到数据质量和模型性能,往往要占据项目的大部分时间。PyTorch提供了专门的数据下载,数据处理包,可以极大提高开发效率及数据质量。数据处理工具箱概述torch.utils.data工具包:Dataset:一个抽象类......