目录
第一节:字符和字符串函数
字符和字符串函数的头文件是 string.h,它们与字符串和内存有关。
1-1.strlen 函数和 sizeof 关键字
strlen 函数和 sizeof 关键字都可以计算字符串的长度,它们的区别是 strlen 函数不会计算字符串的结束标志 '\0' 而 sizoef 关键字会计算在内,请阅读以下代码:
#include <stdio.h>
#include <string.h>
int main()
{
char ch[] = "Hello world";
printf("strlen函数得到的长度:%u\n", strlen(ch));
printf("sizeof函数得到的长度:%u\n", sizeof(ch));
return 0;
}
因为 strlen 和 sizoef 得到的都是一个无符号的整型,所以用%u接收更加恰当。
streln 的原理:
它的函数原型如下:
strlen 的原理就是遍历字符串,只要遇到 '\0' 就结束,所以不管 '\0' 后是否还有数据,它都会结束,请阅读以下代码:
#include <stdio.h>
#include <string.h>
int main()
{
char ch[] = "Hello world\0Hello world";
printf("strlen函数得到的长度:%u\n", strlen(ch));
return 0;
}
它在遇到第一个 '\0' 就结束了,实际上只计算了 "Hello world" 的长度。
sizeof 的原理:
sizeof 是通过类型大小得出数值,比如 int 类型就是4,实际上它的数值与字符串的内容无关,请阅读以下代码:
#include <stdio.h>
#include <string.h>
int main()
{
char ch_1[] = "Hello world";
char ch_2[100] = "Hello world";
printf("ch_1的大小:%u\n", strlen(ch_1));
printf("ch_2的大小:%u\n", sizeof(ch_2));
return 0;
}
因为 ch_2 的类型是 char[100],大小是100个字节,所以 sizoef(ch_2) 的值是100;
而 ch_1 虽然没有写大小,但是它会由初始化的字符串被推到成 char[11] ,所以 sizeof(ch_1)的值是11。
1-2.memcpy 内存拷贝函数
memcpy 函数的功能是将一个空间的任意大小的数据拷贝到另一个空间中,它的函数原型如下:
其中destination就是目的地址的指针,source 就是源地址的指针,num就是拷贝的数据大小(单位:字节)。
它的用法如下:
#include <stdio.h>
#include <string.h>
int main()
{
char ch_1[] = "Hello world";
char ch_2[100];
memcpy(ch_2,ch_1,sizeof(ch_1));
printf("ch_2:%s",ch_2);
return 0;
}
此时 ch_1 的内容就被 ch_2 拷贝了一份,注意这里使用 sizoef 而不用 strlen 是因为字符串的结束标志 '\0' 也要拷贝过去。
1-3.memmove 内存拷贝函数
memmove 函数的功能与 memcpy 函数相同,用法也相同,函数原型如下:
memmove 与 memcpy 在语义上也不同,一个是移动,一个是拷贝;
memmove 与 memcpy 的语义还体现在对重叠内存的处理,这个区别会在下一次加餐中具体阐明。
1-4.memset 内存设置函数
memset 函数可以让一块空间存储自定义的字符,它的函数原型如下:
ptr 就是目标空间的指针,value 就是要存储的值,num 就是要设置的空间大小(单位:字节)
它的用法如下:
#include <stdio.h>
#include <string.h>
int main()
{
char str[10];
memset(str, 'a', 9);
str[9] = '\0';
printf("%s\n", str);
return 0;
}
这样就成功将 9 个字节的空间赋值成 a 了。
1-5.strtok 字符串切割函数
strtok 函数可以对字符串按照给定的分隔符进行切割,每次调用strtok,它就会沿着上一次切割的位置继续切割,并且返回一个切割好的字符串,函数原型如下:
str 就是目标字符串,delimiters 就是分隔符,如果没有分隔符了就会返回NULL。
它的用法如下:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "123#456#789";
// 第一次调用时要传目标字符串
char* ptr = strtok(str,"#");
while (ptr!=NULL)
{
printf("%s\n", ptr);
// 之后调用不传目标字符串,自动根据上次的位置剪切
ptr = strtok(NULL, "#");
}
return 0;
}
分隔符不仅可以传一个,还可以传多个:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "123#456#789&abc&def";
// 第一次调用时要传目标字符串
char* ptr = strtok(str,"#&");
while (ptr!=NULL)
{
printf("%s\n", ptr);
// 之后调用不传目标字符串,自动根据上次的位置剪切
ptr = strtok(NULL, "#&");
}
return 0;
}
1-6.strerrno 和 perror 错误码函数
当使用库函数时,如果发生了错误会有一个名为 errno 的全局变量被设置,它的类型是 int ,不同的值代表不同的错误:
strerror 函数的功能就是把错误码翻译成字符串,把错误码作为参数后,它会返回错误信息的地址,比如打开一个不存在的文件:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
int main()
{
FILE* fd = fopen("text.txt","r");
perror("打开文件失败");
return 0;
}
我们还可以用 perror 函数代替 printf("errno:%d ,errno info:%s\n",errno,strerror(errno)),
perror 函数就是 printf + strerror(errno):
perror 使用起来更加的方便快捷
第二节:动态内存分配
2-1.内存分配
在C语言中有许多不同的值:变量、常量,它们都存储在内存中,但是在内存的不同分区中,具体的情况如下:
除了堆区之外,其他区域都有系统自动创建和释放,我们可以调用一些函数来创建和释放堆区空间,这就是动态内存分配。
2-2.堆区函数
堆区函数的头文件是 stdlib.h。
2-2-1.malloc 函数
malloc 函数之前我们已经见过了,它可以在堆区开辟一块空间,函数原型如下:
size 就是要开辟的空间的大小(单位:字节),然后返回这块空间的地址,它的使用方法如下:
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(4);
return 0;
}
因为它返回的指针是 void* 类型(无类型指针),所以要用(指针类型)把它强转成我们需要的指针类型。
如果申请堆区空间失败,它会返回NULL,所以每次申请堆区空间后都判断一下是否成功,这是一个良好的代码习惯:
#include <stdlib.h>
int main()
{
int* ptr = (int*)malloc(4);
if (ptr == NULL)
{
perror("malloc fail");
return 0;
}
return 0;
}
为了代码看起来简单,后面的代码我就不判断了,但是自己写是一定要加上。
2-2-2.calloc 函数
calloc 函数也能申请堆区空间,函数原型如下:
num 是申请的个数,size 是每个申请多少字节,总共申请 num*size 字节的空间,,如果申请空间失败就返回NULL,它的用法如下:
#include <stdlib.h>
int main()
{
int* ptr2 = (int*)calloc(3,4);
return 0;
}
它与 malloc 除了传参上的不同外,calloc还会将申请空间全部置0:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr1 = (int*)malloc(4);
int* ptr2 = (int*)calloc(3,4);
printf("%d\n", *ptr1);
printf("%d\n", *ptr2);
return 0;
}
用调试窗口可以看得更清楚:
2-2-3.realloc 函数
realloc 也是申请堆区空间的函数,但是它一般不单独使用,它可以申请一块新空间后,又将数据拷贝到新空间中,函数原型如下:
ptr 是旧空间的地址,realloc 用它拷贝数据,size 表示新空间的大小(单位:字节)
,如果申请空间失败就返回NULL,它的用法如下:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int* ptr1 = (int*)malloc(4);
ptr1[0] = 3; // 之前说过 ptr[0]的本质是 *(ptr+0)
// 扩容
int* ptr2 = (int*)realloc(ptr1, 8);
// 释放旧空间,旧指针置空
ptr1 = NULL;
// 检查数据是否拷贝
printf("%d\n", ptr2[0]);
return 0;
}
注意调用 realloc 函数时,它会自动释放旧空间(注意不要再调用 free 了)。
2-2-4.free 函数
free 函数用来释放堆区的空间,如果一块堆区空间已经无用了却不释放,这块空间就被浪费掉了,这叫做 内存泄漏。
对于需要一直运行的程序(比如服务器进程),如果频繁出现内存泄漏,就是导致程序无堆区空间可用,导致进程退出。
free 函数的原型如下:
ptr 就是需要释放空间的地址,它的使用方法如下:
#include <stdlib.h>
int main()
{
int* ptr1 = (int*)malloc(4);
free(ptr1);
return 0;
}
有几点需要注意:
(1)程序退出时,系统也会自动释放未释放的堆区空间
(2)free 函数可以传NULL,但是不能传已经释放了空间的指针:
(3)free 不能从中间分几次释放堆区空间(申请几次就释放几次,申请到哪个指针就释放哪个) :
下期预告:
下一次是第八章指针的进阶学习,将介绍以下内容:
字符指针、指针数组、数组指针、数值指针数组、函数指针等
传送门:C语言-第八章:指针进阶