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