C 知识点整理

Qt 安装

Section 1

基础阶段

C语言的编译步骤
  • 预处理:头文件展开,不做语法检查
  • 编译:翻译成汇编(要做语法检查)
  • 汇编:生成目标文件(已经是二进制文件,但是还不能运行,还要链接一些启动文件,库)
  • 链接:生成可执行文件
    在这里插入图片描述
    • 在linux里查看文件依赖的动态库命令ldd
    • 在这里插入图片描述
    • 在windows可以用Depends.exe查看需要的dll(动态库)
    • 在这里插入图片描述
      在这里插入图片描述
交换文件说明
  • vi写的文件没有保存关闭,会生成.swp文件,用来保存之前的内容
  • .swp是隐藏文件,在文件恢复后,可以自行删除
寄存器
  • 寄存器是最小的存储单元
  • 运算的时候数据在cpu的寄存器中
  • 由于数据从内存拿到寄存器太消耗时间,所以引出三级缓存
  • 在这里插入图片描述
2.10.7大端对齐与小端对齐
  • 计算机的内存最小单位是什么?是BYTE,是字节,一个大于BYTE的数据类型在内存中存放的时候要有先后顺序。
  • 高内存地址放整数的高位,低内存地址放整数的低位,这种方式叫到着放,术语叫小端对齐。X86和ARM都是小端对齐的。
  • 高内存地址放整数的低位,低内存地址放整数的高位,这种方式叫正着放,术语叫大端对齐,很多unix服务器的CPU是大端对齐的。

在这里插入图片描述

在这里插入图片描述
头文件和函数文件
  • 头文件放的是函数的声明,不是函数,如果是放函数,2个以上的地方引用了头文件就会报错(重复定义)。
  • gcc 的第一个步骤预处理:就是把头文件的替换成函数的声明
  • 头文件重复包含不会错(但是多余),防止可以用:
    • 在这里插入图片描述
    指针
    • 变量的指针,存的是变量的首地址。
      在这里插入图片描述
有关指针的概念
  • 指针和指针变量:
    • 指针也是一种设计类型,例如:int *
    • 指针是地址;
    • 指针变量是存放地址的变量;
  • int *p :表示整型指针p
  • *p:表示取指针指向内存的值
野指针
  • 原则:不可以自己设定地址,只能通过定义后的变量,这个变量的地址才是合法的,这是系统分配的地址(系统才有权利分配地址)。操作系统没有授权的内存会出现段错误(运行时的异常);
    在这里插入图片描述#### 空指针
  • 因为定义一个指针变量,然后又不赋值系统就会赋个随机值,这样就是野指针,所以为了避免野指针,就强行把定义的时候把指针赋值为null
    在这里插入图片描述
指针大小
  • 在这里插入图片描述
多级指针
  • 在这里插入图片描述
p[0] 数组符号去指针地址
  • []和*是等价的,a[0] 等价 *a
万能指针
  • 不可以定义void类型的普通变量,因为不能确定类型;
    在这里插入图片描述
const 修饰的指针变量
  • int const *p和const int *p修饰之后代表指针指向的空间不能修改(只读),指针本身的地址值可以修改
  • int *const p:就是常量指针,p变量的值(地址)不能动。
    在这里插入图片描述
数组名指向数组首元素地址。
  • 数组名是常量不允许修改:int * const p,p值不能改,但是*p就是p指向的位置可以修改。
混淆概念
  • 指针数组 和 数组指针
    • 指针数组:是数组:char *p[]
    • 数组指针:是指针
字符串常量
  • 字符串是常量,他的地址不会变
  • 字符串指的是首元素地址,所以可以直接赋值给一个指针。
    在这里插入图片描述
字符串
  • 放在文字常量区(程序结束才释放)

在这里插入图片描述

注意区别两种接收方式
  • 第一种是创建在文字常量区的不能改 char const *p
  • 第二种是字符串数组首地址不能改 char * const P
    在这里插入图片描述
  • 理解
  • 在这里插入图片描述
形参中的数组
  • 下图中三种定义的形式参数都是等价的;
  • 数组作为函数的参数,会退化为指针- 形参中的数组不是数组
  • 二维数组不是二级指针
  • 在这里插入图片描述
返回局部变量的地址
  • 指针函数 (返回值是指针):int * fun(){}
  • 野指针错误
  • 在linux平台地址返回的是0(就是没返回)
  • 在windows平台会返回,但是不能操作(因为系统已经收回了该内存,没有权限)
  • 可以采用返回全局变量的地址来规避这个问题
    在这里插入图片描述
返回全局变量的地址

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

  • 所以有:-在这里插入图片描述
    在这里插入图片描述-
    -理解:
  • 在这里插入图片描述

内存管理- 1. 普通局部变量 :放在栈区在这里插入图片描述

  • 2 静态局部变量 :数据放在data区

    • 只能用常量初始化
      在这里插入图片描述
  • 在这里插入图片描述

  • 普通全局变量
    在这里插入图片描述

    • 缺陷:(可以强制声明 加extern关键字)
    • 在这里插入图片描述
  • 所以建议:

    • 在这里插入图片描述
  • static全局变量(与普通全局变量的区别就是作用域不一样)
    static是内部链接,一个文件只能有一个
    普通全局变量是外部链接,全部文件只能有一个

  • 同理普通函数和static函数是以道理

  • 总结:

  • 在这里插入图片描述

有关一个文件,被多个文件include,导致变量被多次引用的问题
  • 就是头文件(.h)只声明,不定义(定义到具体文件定义)
    在这里插入图片描述
内存分区

在这里插入图片描述

栈越界
  • linux 查看栈空间大小:ulimit -a
几个函数
  • memset的使用-在这里插入图片描述
    – memcpy的使用 (不会因为\0结束)
    在这里插入图片描述
    在这里插入图片描述
    -比较memcmp(不只是比字符串)
    在这里插入图片描述

  • _指针指向栈区空间(纠正野指针)
    在这里插入图片描述

  • 指针指针堆区空间(纠正野指针)
    在这里插入图片描述

  • 用完释放内存,清空指针
    在这里插入图片描述

值传递 和 址传递
  • 指针在实际开发中的价值往往是用来作为函数的参数
  • 值传递:不管变量是什么类型,只要是变量本身传递就是值传递(即时变量本身就是地址,那也是值传递),结果就是形式参的改变不会改变实参的值,但是如果传的是指针,指针的值(地址)不变,但是可以改变指针指向地址内存的值;
  • 址传递:传递变量的地址,对变量取地址
经典内存问题分析

在这里插入图片描述

  • 纠正:
    在这里插入图片描述
结构体

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 定义变量,使用
  • 在这里插入图片描述
  • 并且可以这样赋值
  • 在这里插入图片描述
  • 结构体互相赋值:与面向对象语言中的对象赋值不一样,他是直接复制一份同样的内容到自己空间(是独立的内存)
    在这里插入图片描述
  • 结构体套一级指针
    • 非法使用内存导致的错误说明
      在这里插入图片描述
  • 解决方法,不能往未分配的地址写入值(操作野指针),但是可以给指针赋值,即:
    • 定义为str[]的要:用strcpy往定义的空间写(改变地址指向空间的值);不可以改变地址值,因为str是数组,是常量
    • 定义为char * 的要:直接赋值一个字符串,(改变地址值);不能往空间写,因为是野指针,会内存污染。
  • 如图:
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
共用体
  • union关键字
    • 共用体大小为最大成员的大小
    • 共用体公用一块内存,所有成员地址都一样
枚举
  • enum关键字
	enumspectrum { red, yellow, green, blue, white, black };
	enum spectrum color;
	color = black;
	if (color != red)
  • 默认时,枚举列表中的常量被指定为0,1,2等
typedef(起别名)
  • 与宏定义不同:typedef是编译器处理的,而不是预处理
  • 比如:
    在这里插入图片描述

Section 2 文件操作

scaf()读入键盘,先要读入缓存区
printf 把内存的值放入到屏幕
文件指针
  • FILE *fp:(不同平台都叫FILE是用typedef定义别名,本来是不一样的)
    • 是一个结构体类型(不同平台,成员不一样,不用纠结),
    • fp指针,调用fopen()就砸堆区分配空间,把地址返回给fp
    • fp指针不是指向文件,fp指针和文件关联,fp的内部成员保存了文件的状态;所以用*fp操作不了文件。关联文件的是fp的内部成员fd:
    • 在这里插入图片描述
    • 在这里插入图片描述
  • 文件指针*fp不是指向文件本身,是指向内存的一个结构体变量(关联)。(文件是放在硬盘上的,怎么能指,笑脸)

Section 3

文件分类
  • 设备文件:stdout(标准输出文件)
  • 磁盘文件
    • 二进制文件:图片,视频
    • 文本文件
文件操作流程
  • 文件操作:fopen()
  • 读写文件:
    • 按字符:fget,fput
    • 按字符串:fgets,fputs
    • 判断结尾:feof
  • 关闭文件:fclose()
标准文件指针
  • 输入:stdin —scanf 写入标准输入
  • 输出:stdout —printf 向标准输出写入
  • 错误:stderr—perror 向标准错误写入
    注意:标准文件指针关闭了,可以自己关联文件???
    在这里插入图片描述
有关打开方式w r a
  • 注意在windows下要按照二进制打开要加b,否则会当成文本处理,采用fread也没有(fread只是方便处理二进制文件,并不是确定文件以二进制方式打开),比如:wb、rb;
  • 但是在linux中不需要加上这些- 在这里插入图片描述 #### 等价的打开方式
    在这里插入图片描述
文件打开时选择相对路径,那么是相对于可执行程序

在这里插入图片描述

feof()不局限文本文件(文本文件没有负数),如果仅仅只是用-1字符表示文件结尾,在二进制文件中,就会出现错误。

在这里插入图片描述

从键盘接收输入制作vi命令
  • gets:不能有回车
  • scanf:忽略空格
  • fgets:完美匹配
fgets和fscan
  • fgets:

    • 读取文件的时候会读取换行符;
    • 会一直读取到feof才会结束;
    • 在这里插入图片描述
  • fscan:

    • 会按照固定的格式读取,如果没有匹配的格式不会读取;
  • 总结:用什么格式写入就用什么格式读取;
    在这里插入图片描述

文本文件操作函数(按字符,字符串)
  • printf,scanf
  • sprintf(格式化写入到字符串),sscanf(从字符串按格式扫描出变量)
  • fprintf(格式化写入到文件),fscanf(从文件按格式扫描出变量)
  • fgetc,fputc
  • fgets(从文件读取到字符串),fputs(把字符串写入文件)
  • fread,fwrite
  • fseek,ftell
  • 下面两个等级价
  • 在这里插入图片描述
  • 在这里插入图片描述
二进制文件操作函数(按块大小)
  • fread:
  • 在这里插入图片描述
  • fwrite:
    在这里插入图片描述
    在这里插入图片描述
Section 4

注意有关结构体(二进制文件)的野指针错误

  • 当结构体包含char *的时候,里面存的是地址
  • 如果写进文件,这个时候写进去的是地址,程序如果结束,这个地址指向的内存会被释放掉
  • 然后调用文件读取,这是会指针指向的位置已经被释放,操作的是野指针
  • 解决方式:
  • 写入在这里插入图片描述
  • 读取:读取的时候,读取结构体;但是指针变量重新赋值(自己创建新的堆区空间,然后把name写入堆区空间)
经典疑问
  • 有关结束符
    在这里插入图片描述
    在这里插入图片描述
  • 有关二维数组,二级指针
    二维数组定义好即分配了空间,默认就在栈空间
    二级指针,要指向定义好的变量或者自己开辟空间才有空间,否则是野指针
提高篇
数据类型

在这里插入图片描述

有关数组名
  • b[10]数组名b是数组的首地址,其值和&b相同
  • 取* 向前进一级 ,取&向后退一级
	int b[10];
	//相同
	printf("b:%d,&b:%d\n", b, &b);
	//不同
	//b是首个元素的地址
	//&b是整个数组的首地址
	printf("b+1:%d,&b+1:%d", b + 1, &b + 1);
有关void

在这里插入图片描述

万能指针void *
  • 类似于泛型,可以不固定要传入数据的类型。例如:memcpy可以接收多个类型,但是strcpy只能够接收字符串。
  • 在这里插入图片描述
    在这里插入图片描述
内存4区
  • 堆区(程序员管理):
    • 指针p指向的堆区内存被释放了,如果指针不置为null,那么指针还是原来的值,如果在堆指针释放就会报错;free(p)报错
  • 栈区(临时变量区):
  • 内存分析在这里插入图片描述在这里插入图片描述
  • 在这里插入图片描述
  • 全局区;全局变量(初始化、未初始化),静态变量,文字常量
    内存分析:
    在这里插入图片描述
    在这里插入图片描述
  • 代码区:
    在这里插入图片描述
函数调用模型

在这里插入图片描述

变量的操作
  • 生命周期
  • 作用域
数组 str[]=“sdfsfsd”;
  • 是把文字常量区的字符串,拷贝到栈区,str表示的是栈区的字符串首地址
栈的生长方向和内存的存放方向

在这里插入图片描述

指针的强化
  • 指针也是一种数据类型(就是固定大小内存块的别名)
  • 万能指针使用前要转换类型
    • 在这里插入图片描述
    • 在这里插入图片描述
为了防止头文件之间的死循环的include
  • 可以在头文件写上 pragma
    在这里插入图片描述
c项目兼容c++编译器

在这里插入图片描述####
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0和’\0’是同一个,都可以做字符串的结尾;但是‘0’和前两个不同;
    在这里插入图片描述
    在这里插入图片描述
有关char buf[]和char *p 中buf和p有什么区别
  • 相同都是地址,都可以+i,都可以*(取值)
  • 不同:buf是const,不能修改所以不能buf++
    在这里插入图片描述
  • 原因:因为buf是数组,是分配在栈 空间的。系统为了能够管理回收,所以设定buf不能修改;
  • const常量可以被间接修改 通过另一个指针修改
    在这里插入图片描述
二级指针的输入和输出
  • 二级指针在函数传参主要应用在输入(主调函数分配内存)
  • 二维数组

在这里插入图片描述

二维数组步长@增一维,*降一维
  • 针对二维数组:a[3][10]的a+i、 a[i] 和*(a+i)值是一样的;区别:
    • a[i] 和*(a+i)等价,但是a[i]+1或者*(a+i)+1,地址增长1
    • a+i+1地址增长10
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述

Section 5

  • 二级指针做输入:第一种内存模型(分配在文字常量区)
  • 二级指针做输入:第二种内存模型(分配在栈区)
    在这里插入图片描述
  • 二级指针做输入:第三种内存模型(分配在堆区)
    • 数组类型,也可以直接用指针直接指向
      在这里插入图片描述 - 动态打造二维空间
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
      在这里插入图片描述
如果有多个变量指向一块内存,内存释放只要一次,其他的指针,依次赋值为null就行了

在这里插入图片描述

数组
  • 一维数组
    • typedef定义:
      -在这里插入图片描述
数组指针
  • 定义方式1:
    在这里插入图片描述

  • 定义方式2(常用)
    在这里插入图片描述

  • 定义方式3(直接法,没有先用type确定类型名<匿名>):
    在这里插入图片描述

  • 总结 :
    在这里插入图片描述

  • 二维数组指针赋值辨析:
    -指向整个一维数组,所以p=&a 在这里插入图片描述

    • 一维数组指针,指向整个二维数组首行,所以p=a
      在这里插入图片描述
注意数组名在size函数中的区别
  • a+0代表是地址,与a不同,a表示整个数组
int main(void)
{
	int a[2][10];
	printf("--->  %d,--->  %d\n", sizeof(a), sizeof(a + 0));//80 4
	printf("--->  %d,--->  %d\n", sizeof(*(a+0)), sizeof(&a[0]));//40 4
	system("pause");
	return 0;
}
二维数组在形参中会退化为指针,但是指针的步长要一样才行
  • 针对于指针数组:
    • char * a[]={“111111”,“222222”,“asdsdfsdf”}
      -一个步长是一个char*,那么可以用char **来做形参接收
  • 针对于二维数组int a[2][10]:
    • 一个步长是10*4,
    • 要用一维数组指针int (*p)[10] 来做形参接收
结构体之间相互赋值
  • 这里可以理解其他语言中的强制类型转换的原理:
    • 涉及到的基本条件就是内存空间大小一样就可以强制转换
  • 类型相同即可以相互赋值
  • 结构体变量名和对象名不同:
    • 结构体名放的是结构体的地址,但是地址是在栈空间,结构体间相互赋值是重新复制一份,两者没关系
    • 类对象名的地址是在堆空间,相互赋值,只是改引用,不会重新复制一份
结构体变量深拷贝 和 浅拷贝
  • 浅拷贝只拷贝当前的一层(结构体里嵌套了指针),如果当前有指针变量,那么指针指向的空间不拷贝
  • 深拷贝则一直拷贝到底
    在这里插入图片描述
  • 注意并不想python中的深拷贝那么智能,完全靠自己手动strcpy
    • 在这里插入图片描述
结构体的内存对齐-在这里插入图片描述
  • 内部默认按照最长的变量来对齐(以空间换时间)
  • 如果结构体套结构体,那么按照所有结构体内最长变量来对齐
  • 数组也是复合类型,按一个元素的长度算
  • 到了子结构体,前面全部补齐,再开始;要一个新起点
  • 可以自己指定对齐长度pragma=package(2),但是指定超过最长,则为最长
文件加强
  • 标志操作的文件的结构体叫文件句柄,内部有个成员fd叫文件描述符:
    在这里插入图片描述
  • 文件操作api
  • 按 :字符-字符串-块-格式4种方式操作文件;
  • 为了更加灵活 还有 随机出来api fseek 和 ftell
    在这里插入图片描述
  • 打开方式
    在这里插入图片描述
  • 文件的写入有缓冲区:要满了,手动刷新,关闭文件,程序结束才会真正写入;
    • 但是缓冲区是针对普通文件,标准文件(stdin->键盘,stdout->屏幕,stderro->屏幕)默认自动刷新写入(一输入就显示在屏幕上)
fgets的使用

在这里插入图片描述

文件的加解密的时候,注意打开方式,一定要以二进制的方式,

在这里插入图片描述

Section 6

结构体 组成链表
  • 在这里插入图片描述

  • 静态链表:初始化好链表节点(栈中)

  • 动态链表:自己动态分配空间(堆中动态)

  • 指针函数 int*fun(){}:返回值是指针

函数指针:指向函数
  • 定义:
    • 先定义函数类型,在根据类型定义指针:
      在这里插入图片描述
    • 确定是指针还是函数,从左向右看,优先级高是谁就是谁,这就是为什么函数指针为什么要加上(),因为不加(),*的优先级要低于()
    • 在这里插入图片描述
    • 函数指针数组
      在这里插入图片描述
  • 回调函数:当传入不同的函数参数,得到的行为不一样。
  • 多态:调用同一个接口,产生不同的效果

在这里插入图片描述

链表内存四区图,还原原始图
  • 简洁图:

  • 在这里插入图片描述

  • 四区图:
    在这里插入图片描述

C语言处理的四个步骤:预处理,编译,汇编,链接
  • 在这里插入图片描述

  • 宏定义:

    • 只是替换
    • 宏定义不受局部影响,定义下面的代码都能用
  • 宏定义函数:
    在这里插入图片描述

  • 条件编译
    在这里插入图片描述

  • 防止重复包含头文件:
    在这里插入图片描述

  • 封装动态库

    • lib 是编译的时候使用
    • dll 是运行的时候使用
  • 日志打印:

  • 内存泄露检查

    • 使用三方的:memwatch
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值