【常用函数列表】
字符串常用函数
【新手常见问题】
1.二级指针与二维数组
2.char* pText[] 和 char** ppText
【9-3 变量生命周期和修饰符】
生命周期
函数的声明周期
起于调用,结束于调用结束
局部变量的生命周期
起于调用,结束于调用结束
main函数的生命周期
main开始 == 进程的开始 main函数的结束 == 进程的结束
全局变量的生命周期
起于main调用,结束于main调用结束
修饰符
auto
含义:只能用于修饰局部变量,表示该变量存储于栈stack 特点: 随用随开,用完消失 C中默认的局部变量,就是auto类型,所以通常将其省略 c++ auto自动类型
register
含义:只能修饰局部变量,原则上,将内存中的变量升级到CPU寄存器中存储,这样访问速度会更快,但由于CPU寄存器数量有限,通常会在程序优化阶段,被优化为普通auto类型,可以通过汇编代码来查看,优化过程(与平台和编译有关) 特点:可以避免cpu与内存的频繁交互 一般程序从内存读取数据 放到寄存器 进行运算 运算结果写入到内存
关于各个存储部件中读写速度
硬盘:7200转 内存:频率1333MHZ 1600MHZ 带宽 = 频率*64/8 缓存: CPU:
extern
含义:只能用于修饰全局变量,全局变量本身是全局可用的,但是由于文件是单个完成编译, 并且编译是自上而下的, 所以说,对于不是在本范围内定义的全局变量,要想使用必须用extern 进行申明, 如果不加上可能会造成重定义。 特点: 1.可以省略,声明在前,定义在后,不论局部还是全局。 main.c : int a; other.c : int a =200;编译不会报错 2.外延性 某文件定义的全局变量 可在其他文件使用
c语言编译是跨文件的
编译过程:单文件编译 单文件每个编译成xxx.o 链接所有.o文件和lib.so文件 生成可执行文件xxx.out
【9-4 static】
修饰局部变量
特点:1.初始化值为0,且只初始化一次 2.生命周期等同于程序
修饰全局变量
特点:1.全局的外延性消失,变成文件内部的全局变量 也适用于函数 2.存储在data段
小结问题:
- extern int a;
int a= 200;
是不是同一个a? - 局部变量和全局变量储存的位置有什么不同?
- static修饰的变量值所保存的位置在哪里 才导致是累计变化的?
保存在data段中
【9-5 字符串常量——9-7字符串的输入输出】
定义
是双引号括起的任意字符序列
字符串大小
看到的大小,比我们实际字面量要多一个, 最后的字符’\0’,我们称为字符串结束字符,是系统对双引号引起的字符串自动加设 的,而非手动干预。
字符串存储
字符串的存储,不像其它普通类型的常量自变量的存储一样,普通类型自变量通常存储 在代码段(text), 而字符串,则存储在数据段,且是只读数据段。 也就是通常意义上的常量区,但是常量区这种说法,是不太精确的,也不提倡。
拓展
栈区(stack):临时变量,局部变量,生命周期结束即销毁 堆区(heap):一些new出来的变量 存储的地方 特定销毁时间统一销毁,析构函数 数据段(data):用来存放程序中已初始化的全局变量的一块内存区域,属于静态内存分配。 代码段(text):用来存放程序执行代码的一块内存区域,只读,且不可修改。可能包含一些只读的常数变量,例如字符串常量等。 bbs段:bss段(bss segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域
字符数组和字符串区别
1.字符串数组(栈区)是将text区的常量字符串 拷贝到栈区 字符串值的改变实际上是栈的值改变 字符串指针 是直接指向 text区常量字符串地址 故不能改变text区的值 2.相同点:字符数组长度大于等于字符串长度
字符串的输入输出
puts函数:printf的自带换行 scanf函数:默认遇到空格结束输入,scanf("%[\n]s")遇到回车结束输入,但不检查输入长度,不安全 gets函数:空格也可以读入,但不检查输入长度,不安全。 fgets函数:参数列表(接收变量,长度,输入流)
tips
1.printf(“”) 空串有换行功能 2.gets();不检查预留存储区是否能够容纳实际输入的数据, 换句话说,如果输入的字符数目大于数组的长度,gets 无法检测到这个问题,就会发生内存越界,所以编程时建议使用 fgets()。 char *fgets(char *s, int size, FILE *stream); fgets() 虽然比 gets() 安全,但安全是要付出代价的, 代价就是它的使用比 gets() 要麻烦一点,有三个参数。 它的功能是从 stream 流中读取 size 个字符存储到字符指针变量 s 所指向的内存空间。它的返回值是一个指针,指向字符串中第一个字符的地址。 3. c中允许 c++不允许 char a[2] = "china"; printf("%s",a); 输出结果:ch 4.char * p = "china"; printf("p = %p p+1 = %p p[0]=%c 0[p] =%c\n", p, p + 1, p[0], 0[p]); printf(" = %p = %p =%c =%c\n", "china", "china"+1, "china"[0], 0["china"]); 输出结果:地址不相等 与视频不符合
问题?
1.视频说 " 字符串,则存储在数据段" • 1
而 这篇文章说了
在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
2.字符串指针和字符数组的区别? 字符数组可修改内容,有个拷贝过程,data段常量区拷贝到栈区,字符数组实际是栈区 字符串指针如果指向data段字符串常量 是不可修改地址的,若是指向字符数组反而可以
【10-2 从字符串常量到字符数组】
【10-3 原生字符串处理】
strlen(char *) 字符串长度
strcat(char *head,char *tail) 连接两个字符串
注意:被连接的串必须有足够空间
but vs 可行…
char firstName[] = "jim"; char familyName[30] = "tim"; strcat(firstName, familyName); printf("%s", firstName); 输出:jimtim
优化操作:
原本代码
while(*p) p++; char *p,*q; while(1){ p * =q*; if(p* == '\0'){ break; } p++;q++; } 优化后: while(*p) p++; while(*p++ = *q++);
Tip
- int* 和 char* ,float * 长度 是多少?
指针统一长度为4byte
int a = 1; int* ap = &a; printf("%d \n", sizeof(ap)); char * c = "123"; printf("%d \n", sizeof(c)); float d = 1.0f; float * dp = &d; printf("%d \n", sizeof(dp)); 输出:4 4 4
- char *ac = “123”;
printf("%p \n", &ac);
printf("%p \n", “123”);
为什么输出地址不同?
3.字符串长度和大小是不同的
长度是指字符长度且不包含\0,大小是指字节大小包含\0
【10-7 字符串指针数组入门】
指针数组的本质是数组,数组指针的本质是指针。
Tips
1.运行这段代码 设置字符串池优化
char * pa = "china"; char *pb = "america"; char*pc = "canada"; char*pd = "japan"; char * cpArr[4] = { pa,pb,pc,pd }; printf("pa = %p \n", pa); printf("pb = %p \n", pb); printf("pc = %p \n", pc); printf("pd = %p \n", pd); for (int i = 0; i < 4; i++) { printf("%p \n",cpArr[i]); } printf("----------------\n", pd); char * cpArr2[4] = {"china","america","canada", "japan" }; for (int i = 0; i < 4; i++) { printf("%p \n", cpArr2[i]); }
- NULL ,nullptr,0区别
【11-1 栈内存和堆内存的基本概念】
概念
源程序:源代码
程序:可执行文件
进程:时间概念可执行性文件被拉起,到结束的这一段过程,称为进程
进程空间:可执行文件 被拉起以后 在内存中的分布情况
栈内存
栈存储的特点
栈中存放任意类型的变量,但必须是 **auto** 类型修饰的,即自动类型的局部变量,随用随开,用完即消。 内存的分配和销毁系统自动完成,不需要人工干预。 分配顺序,由高地址到低地址。
栈的大小
栈的大小并不大,他的意义并不在于存储大数据,而在于数据交换。
常见栈溢出
局部变量申请过多,过大,char[1024*1024*10]; 递归层数太多 例如10000层递归
堆内存
栈存储的特点
堆内存可以存放任意类型的数据,但需要自己申请与释放。 分配顺序,由低地址到高地址。
堆大小
堆大小,想像中的无穷大,对于栈来说,大空间申请,唯此,无它耳。但实际使用中,受限于实际内存的大小和内存是否连续性。(基本最大为用户空间大小)
Tips
1.memset(pm,1,10*sizeof(int);
为每个字节赋值为1;一个int 实际4个字节 4个0x01 printf("%#x"); %#表示的输出提示方式,如果是8进制,在前面加0,如果是十进制,不加任何字符,如果是十六进制,会加上0x
2.calloc和malloc
calloc在动态分配完内存后,自动初始化该内存空间为零,而malloc不做初始化,分配到的空间中的数据是随机数据
3.realloc
void *realloc(void *ptr, size_t size);
功能:扩容(缩小)原有内存的大小。通常用于扩容,缩小会会导致内存缩去的部分数据丢失。
参数:
void *ptr:ptr 表示待扩容(缩小)的指针, ptr 为之前用 malloc 或 者 calloc 分配的内存地址。或 ptr==NULL,则该函数等同于 malloc。
size_t:size 表示扩容(缩小)后内存的大小。
返回值:
成功返回非空指针指向申请的空间 ,失败返回 NULL。返回的指针,可能与 ptr 的值相同,也有可能不同。若相同,则说明在原空间后面申请,否则,则可能后续空间不足,重新申请的新的连续空间,原数据拷贝到新空间,原有空间自动释放。
原理:先判断当前的指针是否有足够的连续空间,如果有,扩大mem_address指向的地址,并且将mem_address返回,如果空间不够,先按照newsize指定的大小分配空间,将原有数据从头到尾拷贝到新分配的内存区域,而后释放原来mem_address所指内存区域(注意:原来指针是自动释放,不需要使用free),同时返回新分配的内存区域的首地址。即重新分配存储器块的地址。
malloc 使用流程 申请 判空 使用 释放 置空
问题1:指针接收 创建的内存空间(malloc),释放时怎么知道释放(free)多少内存空间的
heap的每个块儿头有保存本块的大小以及本块分配的大小。
free并不知道那个指针所指空间的大小,它要先查找当前heap的头部,然后整个块儿释放掉。
问题2:如何用malloc/new 定义一维,二维,三维数组?用free/delete销毁呢?malloc/free和new/delete区别?
问题3:malloc/calloc/realloc 区别与不同?
问题4:常见的内存泄露和检测内存泄露的常见方式?内存泄漏和野指针?