第一章-main函数传参
第二章-C数据类型及语句
负数存储方式:补码
格式化输出字符
第三章-数组
若干个相同类型的变量在内存中有序存储的集合
1)存储的数据类型必须是相同的
2)在内存会开辟一块连续的空间
初始化:开辟空间额同时并且给变量赋值
字符数组的定义方式
1)char c1[]={‘c’,’ ‘,‘p’,‘r’,‘o’,‘g’}
2) char c2[]=“c prog”;
用2)字符串方式赋值比用1)字符逐个赋值要多占1个字节,用于存放字符串结束标志’\0’
第四章-函数
函数是 c 语言的功能单位, 实现一个功能可以封装一个函数来实现。
定义函数的时候一切以功能为目的, 根据功能去定函数的**参数和返回值 **
在定义函数的时候, 关于函数的参数和返回值是什么情况, 完全取决于函数的功能。
使用函数的好处?
1、 定义一次, 可以多次调用, 减少代码的冗余度。
2、 使咱们代码, 模块化更好, 方便调试程序, 而且阅读方便
第五章-预处理、动态库、静态库
- 预处理编译
gcc -E hello.c hello.i
将.c中的头文件展开、宏展开
生成的文件是.i文件
- 编译
gcc -S hello.i hello.s
将预处理后的.i文件生成.s汇编文件
- 汇编
gcc -c hello.s hello.o
将.s汇编文件生成.o目标文件
- 链接
gcc hello.o -o hello.elf
将.o文件链接成目标文件
include 和define的区别
注意: 预处理只是对 include 等预处理操作进行处理并不会进行语法检查
这个阶段有语法错误也不会报错, 第二个阶段即编译阶段才进行语法检查。
宏的好处: 只要修改宏定义, 其他地方在预编译的时候就会重新替换。
带参宏和带参函数的区别
- 带参宏被调用多少次就会展开多少次, 执行代码的时候没有函数调用的过程, 不需要压栈弹栈。 所以
带参宏, 是浪费了空间, 因为被展开多次, 节省时间。 - 带参函数, 代码只有一份, 存在代码段, 调用的时候去代码段取指令, 调用的时候要, 压栈弹栈。 有
个调用的过程。所以说, 带参函数是浪费了时间, 节省了空间。 - 带参函数的形参是有类型的, 带参宏的形参没有类型名。
选择性编译
#ifndef AAA
代码段一
#else
代码段二
#endif
和第一种互补。
这种方法, 经常用在防止头文件重复包含。
动态库静态库
一: 动态编译
动态编译使用的是动态库文件进行编译
gcc hello.c -o hello
默认的咱们使用的是动态编译方法
二: 静态编译
静态编译使用的静态库文件进行编译
gcc -static hello.c -o hello
三: 静态编译和动态编译区别
1: 使用的库文件的格式不一样
动态编译使用动态库, 静态编译使用静态库
注意:
1: 静态编译要把静态库文件打包编译到可执行程序中。
2: **动态编译不会把动态库文件打包编译到可执行程序中,它只是编译链接关系 **
第六章-指针
指令的相关概念
操作系统给每个存储单元分配了一个编号,从0x00 00 00 00~0xff ff ff ff
- 这个编号称为地址
- 指针就是地址
指针变量:是个变量,存放地址编号,32位操作系统大小占4个字节
内存是以字节为单位来存储数据的,咱们可以将程序中的虚拟寻址空间,看成一个很大的一维字符数组
- 字符变量 char ch,占一个字节,有一个地址编号,这个地址编号就是变量ch的地址
- 整型变量int a, 占4个字节,占有4个字节的存储单元,有4个地址编号,a的地址就是存储单元编号最小的编号
指针的定义方式
数组的名字就是数组的首地址,即第0个元素的地址,是个常量
指针数组:一般遇到这样的叠词,本质就是后者
是个数组,若干个相同类型的指针变量构成的集合
指针的指针
字符串和指针
在C语言里面必须先开辟空间,再初始化,C++可以开辟空间的时候初始化
数组指针
本质是个指针,指向一个数组,+1跳一个数组,主要用来传二维数组的首地址
指针和函数的关系
1)值传递:实参的值传递给形参,形参的改变影响不了实参
2)地址传递:将实参的地址传递给形参,形参的改变影响实参
指针函数:指针作为函数的返回值
函数指针-指针保存函数的地址
函数指针数组:本质是一个数组
容易混淆的指针
特殊指针void*
特殊指针NULL
第七章:动态内存申请
内存分区基础知识
1)物理内存
实实在在的存储设备,比如内存条
2)虚拟内存
操作系统虚拟出来的内存,会在物理内存和虚拟内存之间做映射
写应用程序,看到的都是虚拟地址。
3G的用户空间:当前进程所私有的
- 堆区:动态申请的内存
- 栈区:局部变量
- 静态全局区:静态变量,全局变量
- 代码区:程序代码
- 文字常量区:常量
1G的内核空间:一个系统中所有进程所公有的
为什么要动态申请内存
动态分配函数
free 函数(释放内存函数)
头文件:#include<stdlib.h>
函数定义:void free(void *ptr)
函数说明: free 函数释放 ptr 指向的内存。
注意 ptr 指向的内存必须是malloc、calloc、relloc动态申请的内存
char *p=(char *)malloc(100);
free(p);
1)free 后, 因为没有给 p 赋值, 所以 p 还是指向原先动态申请的内存。 但是内存已经不能再用了,
p 变成野指针了。
2)一块动态申请的内存只能 free 一次, 不能多次 free
内存泄漏
申请的内存, 首地址丢了, 找不了, 再也没法使用了, 也没法释放了, 这块内存就被泄露了。
int main(){
char *p;
p=(char *)malloc(100);
//接下来,可以用 p 指向的内存了
p="hello world";//p 指向别的地方了
//从此以后,再也找不到你申请的 100 个字节了。则动态申请的 100 个字节就被泄露了
return 0;
}
void fun(){
char *p;
p=(char *)malloc(100);
//接下来,可以用 p 指向的内存了
free(p);
}
int main() {
fun();
fun();
return 0;
}
void fun(){
char *p;
p=(char *)malloc(100);
//接下来,可以用 p 指向的内存了
}
int main(){
fun();
fun();
return 0;
} //每调用一次 fun 泄露 100 个字节
内存泄露例2解决方法
char * fun(){
char *p;
p=(char *)malloc(100);
//接下来,可以用 p 指向的内存了
return p;
}
int main(){
char *q;
q=fun();
//可以通过 q 使用 ,动态申请的 100 个字节的内存了
//记得释放
free(q);
return 0;
}
**总结: 申请的内存, 一定不要把首地址给丢了, 在不用的时候一定要释放内存 **
第八章-字符串处理函数
- 测字符串长度函数:size_t strlen(const char *s);
- 字符串拷贝函数:char *strcpy(char *dest, const char *src);
- 字符串追加函数 :char *strcat(char *dest, const char *src);
- 字符串比较函数 :int strcmp(const char *s1, const char *s2);
- 字符查找函数:char *strchr(const char *s, int c);
- 字符串匹配函数:char *strstr(const char *haystack, const char *needle);
- 空间设定函数:void* memset(void *ptr,int value,size_t num);
- 字符串转换数值:int atoi(const char *nptr);
- 字符串切割函数:char *strtok(char *str, const char *delim);
- 格式化字符串操作函数:
- const
第九章-结构体共用体枚举
结构体概念
结构体定义
1)结构体变量的地址编号和结构体第一个成员的地址编号相同, 但指针的类型不同
2)结构体数组的地址就是结构体数组中第 0 个元素的地址
结构体内存分配
- 结构体变量大小:它所有成员的大小之和。因为结构体变量是所有成员的集合。
- 为什么要有字节对齐?用空间来换时间, 提高 cpu 读取数据的效率
共用体
枚举
将变量的值都列举出来, 变量的值只限于列举出来的值的范围内
枚举类型也是个构造类型的, 类型定义类似结构体类型的定义。
使用枚举的时候, 得先定义枚举类型, 再定义枚举变量