c语言(五)
静态分配内存
在程序编译或运行过程中,按事先规定大小分配内存空间的分配方式。int a [10]
必须事先知道所需空间的大小。
分配在栈区或全局变量区,一般以数组的形式。
按计划分配。
动态分配内存
在程序运行过程中,根据需要大小自由分配所需空间。
按需分配。
分配在堆区,一般使用特定的函数进行分配。
动态分配函数
头文件#include<stdlib.h>
malloc 函数
函数原型: void *malloc(unsigned int size);
在内存的动态存储区(堆区)中分配一块长度为size 字节的连续区域,用来存放类型说明符指定的类型。
函数原型返回void*指针,使用时必须做相应的强制类型转换,函数的返回值:返回申请的内存的首地址(申请成功),返回NULL(申请失败)
在调用malloc 之后,一定要判断一下,是否申请内存成功。
如果多次malloc 申请的内存,第1 次和第2 次申请的内存不一定是连续的
最后使用free函数释放指针变量指向的内存
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p,i;
p = (int *)malloc(20); //开辟20个字节
if (p==NULL)
{
printf("开辟内存失败\n");
return 0;
}
printf("p的值%s\n", p);
memset(p,0,20); //如果分配的内存空间内容不确定,一般使用memset 初始化。
printf("p的值%s\n", p);
for (i = 0; i < 5; i++) //int类型一个元素占4个字节
{
p[i] =i;
printf("p[%d]=%d\n", i, p[i]);
}
free(p);
return 0;
}
free 函数释放内存函数
一块动态申请的内存只能free 一次,不能多次free
calloc 函数
函数定义:void * calloc(size_t nmemb,size_t size);
char *p=(char *)calloc(3,100);堆中申请了3 块,每块大小为100 个字节,即300 个字节连续的区域。
size_t 实际是无符号整型,它是在头文件中,用typedef 定义出来的。在内存的堆中,申请nmemb 块,每块的大小为size 个字节的连续区域。函数的返回值:返回申请的内存的首地址(申请成功),返回NULL(申请失败)
malloc 申请的内存,内存中存放的内容是随机的,不确定的,而calloc 函数申请的内存中的内
容为0
#include<stdio.h>
#include<stdlib.h>
int main()
{
int* p,i;
p = (int *)calloc(1,20); //开辟1块20个字节
if (p==NULL)
{
printf("开辟内存失败\n");
return 0;
}
printf("p的值%s\n", p);
memset(p,0,20); //如果分配的内存空间内容不确定,一般使用memset 初始化。
printf("p的值%s\n", p);
for (i = 0; i < 5; i++) //int类型一个元素占4个字节
{
p[i] =i;
printf("p[%d]=%d\n", i, p[i]);
}
free(p);
return 0;
}
realloc 函数(重新申请内存)
malloc 和calloc 函数,单次申请的内存是连续的,两次申请的两块内存不一定连续。
malloc 或者calloc 申请了一块内存,在原先内存的基础上挨着继续申请内存,或者释放后边的一部分内存
函数的定义:void* realloc(void *s,unsigned int newsize);
在原先s 指向的内存基础上重新申请内存,新的内存的大小为new_size 个字节,如果原先内存后面有足够大的空间,就追加,如果后边的内存不够用,则relloc 函数会在堆区找一个newsize 个字节大小的内存申请,将原先内存中的内容拷贝过来,然后释放原先的内存,最后返回新内存的地址。
char *p;
p=(char *)malloc(100)
p=(char *)realloc(p,50);//重新申请内存,大小为50 个字节.100 个字节的后50 个字节的存储空间就被释放了
malloc calloc relloc 动态申请的内存,只有在free 或程序结束的时候才释放。
内存泄露
申请的内存,首地址丢了,找不着了,没法使用了,也没法释放了,这块内存就被泄露了。
char *p;
p=(char *)malloc(100);//申请100个字节的内存,p指向这100个内存
p="hello world";//p 指向别的地方了,原来申请的100个字节的内存还存在,但找不到了。
p=(char *)malloc(10);//申请10个字节的内存,p指向这10个内存
p=(char *)malloc(100);//又申请100个字节的内存,p指向这100个内存,原来10个内存就找不到了
解决办法,p在指向别的地方之前,用free函数释放掉。
所以申请的内存,一定不要把首地址给丢了,在不用的时候一定要释放内存。
字符串处理函数
#include <string.h> 头文件
#pragma warning(disable:4996) 在c 文件开始处写上这句话,即告诉编译器忽略4996 警
告,strcpy、scanf 等一些不安全的标准c 库函数在vs 中可以用了。
strlen
函数定义:size_t strlen(const char *s);
测字符指针s 指向的字符串中字符的个数,不包括’\0
’,返回值:字符串中字符个数
#include<stdio.h>
#include<string.h>
int main()
{
char a[] = "caicai";
printf("sizeof:%d\n",sizeof a);
printf("字符串长度:%d",strlen(a));//caicai是6个字节,加上字符串结束标志\0是7
return 0;
}
strcpy
函数定义:char *strcpy(char *dest, const char *src);
拷贝src 指向的字符串到dest 指针指向的内存中,’\0’
也会拷贝,函数的返回值:目的内存的地址
在使用此函数的时候,必须保证dest 指向的内存空间足够大,否则会出现内存污染。
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
char a[] = "hellow a shu";
printf("sizeof%d,%s\n", sizeof a, a);
strcpy(a, "nihao");
printf("sizeof%d,%s\n",sizeof a,a);//拷贝‘\0’
int i;
for (i = 0; i < 13; i++)
{
printf("%c* ",a[i]);
}
return 0;
}
strncpy
char *strncpy(char *dest, const char *src, size_t n);
将src 指向的字符串前n 个字节,拷贝到dest 指向的内存中,返回值:目的内存的首地址
strncpy 不拷贝‘\0’
如果n 大于src 指向的字符串中的字符个数,则在dest 后面填充n-strlen(src)个’\0’
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
char a[] = "hellow.a.shu";
printf("sizeof%d,%s\n", sizeof a, a);
strncpy(a, "nihao",7);
printf("sizeof%d,%s\n",sizeof a,a);
int i;
for (i = 0; i < 13; i++)
{
printf(" *%c* ",a[i]); //第6,第7,第13个字节的字符值是空的,即都是’\0’
}
return 0;
}
strcat
char *strcat(char *dest, const char *src);
strcat 函数追加src 字符串到dest 指向的字符串的后面。追加的时候会追加’\0’
,保证dest 指向的内存空间足够大。追加字符串的时候会追加’\0’
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4996)
int main()
{
char a[30] = "cai\0caicaicai";
char* p = "hello";
strcat(a, p); //p有6个字节,一定要保证a剩余字节足够大
printf("%s\n", a);
return 0;
}
strncat
char *strncat(char *dest, const char *src, size_t n);
追加src 指向的字符串的前n 个字符,到dest 指向的字符串的后面。注意如果n 大于src 的字符个数,则只将src 字符串追加到dest 指向的字符串的后面,追加的时候会追加’\0’
#include <stdio.h>
#include <string.h>
#pragma warning(disable:4996)
int main()
{
char a[30] = "cai\0caicaicai";
char* p = "ha";
strncat(a, p,5);
printf("%s\n", a);
return 0;
}
strcmp与strncmp
int strcmp(const char *s1, const char *s2);
比较s1 和s2 指向的字符串的大小,
比较的方法:逐个字符去比较ascII 码,一旦比较出大小返回。
如果所有字符都一样,则返回0
返回值:
如果s1 指向的字符串大于s2 指向的字符串返回1
如果s1 指向的字符串小于s2 指向的字符串返回-1
如果相等的话返回0
int strncmp(const char *s1, const char *s2, size_t n);
比较s1 和s2 指向的字符串中的前n 个字符
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "hello cai";
char *b = "hello shu";
if (strcmp(a, b) == 0) //
printf("a==b\n");
else if (strcmp(a, b) > 0)
printf("a>b\n");
else
printf("a<b\n");
if (strncmp(a, b, 5) == 0) //比较前5个字符都是hello
printf("a==b\n");
else if (strncmp(a, b, 5) > 0)
printf("a>b\n");
else
printf("a<b\n");
return 0;
}
strchr
char *strchr(const char *s, int c);
在字符指针s 指向的字符串中,找ascii 码为c 的字符,是首次匹配,如果过说s 指向的字符串中有多个ASCII 为c 的字符,则找的是第1 个字符.
返回值:找到了返回找到的字符的地址,找不到返回NULL,
char *strrchr(const char *s, int c);
末次匹配
在s 指向的字符串中,找最后一次出现的ASCII 为c 的字符,
返回值;找到了,末次匹配的字符的地址。找不到,返回NULL
#include<stdio.h>
#include<string.h>
#pragma warning(disable:4996)
int main()
{
char* p = "caicaicai";
char* q;
q = strchr(p, 'i');
if (q == NULL)
{
printf("NULL\n");
return 0;
}
printf("p:%p\n", p);
printf("q:%p\n", q);
printf("p-str=%d\n", q - p);//差了两个字节
return 0;
}
strstr
char *strstr(const char *haystack, const char *needle);
在haystack 指向的字符串中查找needle 指向的字符串,也是首次匹配
返回值:找到了返回找到的字符的地址,找不到返回NULL,
#include <string.h>
#include <stdio.h>
int main()
{
char str1[30] = "jfsdjklsd43$#$53jklj$#$4t5";
char str2[20] = "$#$";
char* result;
result =(char *) strstr(str1, str2);
printf("%s\n", result);
printf("%p\n", result);
printf("%p\n", str1);
printf("%d\n", result - str1);
return 0;
}
memset
void* memset(void *ptr,int value,size_t num);
memset 函数是将ptr 指向的内存空间的num 个字节全部赋值为value
ptr:指向任意类型的指针,即指向我们需要修改的内存
value:给ptr 指向的内存空间的赋的值。
num:确定将ptr 所指的内存中的num 个字节全都用value 代替
返回值;目的内存的首地址,即ptr 的值
#include <string.h>
#include <stdio.h>
#pragma warning(disable:4996)
int main()
{
char result[] = "hello a shu";//char *result指向的字符串保存在字符常量区是不可被修改的
printf("%s\n", result);
memset(result, 'c', 6);
printf("%s\n", result);
return 0;
}
字符串转数值
atoi/atol/atof //字符串转换功能
int atoi(const char *nptr);
将nptr 指向的字符串转换成整数,返回
转换后的整数,此值由将输入字符作为数字解析而生成。如果该输入无法转换为该类型的值,则atoi
的返回值为0
long atol(const char *nptr);
double atof(const char *nptr);
#include <string.h>
#include <stdio.h>
#pragma warning(disable:4996)
int main()
{
char result[] = "1234";
int num,num1;
num = atoi(result);
num1 = atoi("qwer");
printf("%d\n", num);
printf("%d\n", num1);
return 0;
}
strtok
char *strtok(char *str, const char *delim);
字符串切割,按照delim 指向的字符串中的字符,切割str 指向的字符串。其实就是在str 指向的字符串中发现了delim 字符串中的字符,就将其变成’\0’
,调用一次strtok 只切割一次,切割一次之后,再去切割的时候strtok 的第一个参数传NULL,意思是接着上次切割的位置继续切。
如果str 字符串中出现了连续的几个delim 中的字符,则只将第一个字符变成’\0’
#include<string.h>
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char str[100] = "hello,,,a;;;shu";
char* p= ",;";
char* q[10];
q[0] = strtok(str, p);
printf("%s",q[0]);
int i = 0, j;
while (q[i] != NULL) //切割完毕,回传NULL
{
i++;
q[i] = strtok(NULL, p); //从上一次切割的位置继续切,,,,a将第一个,变成\0,下一次切割的时候,跳过,,从a开始切割。
}
for (j = 0; j < i; j++)
{
printf("q[%d]: %s\n", j, q[j]);
}
printf("%s\n", q[0]+6);//只将第一个字符变成’\0’,
return 0;
}
sprintf与sscanf
int sprintf(char *buf, const char *format, …);
输出到buf 指定的内存区域
int sscanf(const char *buf,const char *format, …);
从buf 指定的内存区域中读入信息
#include<string.h>
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char p[100];
sprintf(p,"%s;%d;%d","hello", 23, 34);
printf("%s\n",p);
int a, b, c;
sscanf("2022:8:18", "%d:%d:%d",&a,&b,&c);
printf("a:%d\n",a);
printf("b:%d\n", b);
printf("c:%d\n", c);
return 0;
}
sscanf跳过数据:%*s 或%*d
#include<string.h>
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char buf[100];
sscanf("hello world", "%*s %s", buf);//跳过hello ,然后隔一个空格获取字符串
printf("%s\n", buf);
return 0;
}
读指定宽度的数据:%[width]s
#include<string.h>
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char buf[100];
sscanf("hello world", "%5s", buf);//从字符串中获取字符串,只要5个字节,存放在buf 中
printf("%s\n", buf);
return 0;
}
支持集合操作:只支持获取字符串
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char buf[100];
sscanf("qweqewr123qwer123asf", "%[a-z]", buf);//从字符串中获取输入只要’a’和’z’之间的字符碰到不
在范围内的,就终止了
printf("%s\n", buf);//结果为qweqewr
}
%[aBc] 匹配a、B、c 中一员,贪婪性,碰到除了aBc就停止
%[^aFc] 匹配非a Fc 的任意字符,贪婪性,碰到aFc就停止
%[^a-z]表示读取除a-z 以外的所有字符
获取# @号之间的字符串abc#def@ghi
#include<stdio.h>
#pragma warning(disable:4996)
int main()
{
char buf[20];
sscanf("asdf#sdjd@djfkd", "%*[^#]%*[#]%[^@]", buf);//跳过非#的字符,跳过#,直到@
printf("%s\n", buf);
}
const
修饰普通变量,代表只读的意思
const int a=100;//定义了一个只读变量a 值为100,后面就不能再给a 赋值了
const 修饰指针
const char *str
意思是str 指向的内存的内容不能通过str 来修改,用来保护str 指向的内存的内容,但是str 的指向是可以改变的。
char * const str
意思是str 是只读的变量,str 不能指向别的地方,但是str 指向的内存的内容,是有可能可以修改的。
const char * const str
str 不能指向别的地方,指向的内存的内容也不能通过str 去修改