
形式
作用:将函数分类,每一类写到文件中,方便管理
源文件 c3.c:
- 包含头文件
- 放定义:函数定义,全局变量
头文件 c3.h
- 放声明:函数声明,结构体类型声明,宏,typedef
- 源文件和头文件是一一对应的,名字都是一样的
主文件 cmain.c
- 包含头文件,自己的头文件一般用“”,系统的头文件一般用<>
- 主函数调用源文件的内容

main.c
- /#include "c3.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "c3.h"
int main(void) {
printf("%d,%d\n",sum(1, 2),mul(2,3)) ;//0,6
return 0;
}
c3.h
- 放声明:函数声明,结构体类型声明,宏,typedef
- 源文件和头文件是一一对应的,名字都是一样的
//头文件 声明
int sum(int a, int b);
int mul(int a, int b);
c3.c
- 放定义:函数定义,全局变量
- /#include "c3.h"
#include "c3.h"
int sum(int a, int b) {
return a + b;
}
int mul(int a, int b) {
return a * b;
}
include 预处理指令,带“#”
预处理指令在程序编译的过程起作用。
- 本质:单纯的替换,比如上面的“#include c3.h”,将c3.h的内容全部替换到这个位置,其实就是个声明的作用,
- 作用:使声明更加方便。
""和<>的区别
#include "c3.h" :
- 自己写的
- 先在工程文件所在的目录下查找
- 如果没有,则会去系统默认目录下查找
#include <stdlib.h> :
- C提供的标准的系统头文件
- 直接去系统默认目录下查找
所以#include "stdlib.h" 这样写没有任何问题,用<>省区查找工程目录,这样查找的快
相对路径和绝对路径
绝对路径:文件的完整路径“D:\ \c3.h” (两个反斜杠)、或者 “D:/c3.h”
相对路径:不是完整路径,比如“c3.h”
文件的上一层文件夹:../
头文件重复包含 与 头文件互相包含
1.头文件重复包含
类型声明这种,可以在不同文件下出现相同的,但不能在一个文件下出现一模一样的。
头文件重复包含引起的问题:重定义
那么怎么解决?
#ifndef D4 //if not define
#define D4
#include "c3.h"
int sum(int a, int b);
int mul(int a, int b);
#endif
2. 头文件互相包含
c3.h包含d4.h,d4.h包含c3.h
引起的问题:报错提示产生大量文件。死循环
那么怎么解决?
也是这些
#ifndef D4 //if not define
#define D4
#include "c3.h"
//头文件
int sum(int a, int b);
int mul(int a, int b);
#endif
存储类说明符 auto extern static register typedef
注意:一个变量不能出现两个存储类说明符
自动变量(又叫局部变量/栈区变量)、
静态变量、
寄存器变量、
typedef
自动变量(又叫局部变量/栈区变量)
-
特征:定义在{}内,也就是代码段。
比如函数内,函数参数内,if for while switch dowhile
注意⚠️:结构体的花括号成员不是局部变量的意义 -
种类:
- int a;
- int a[2];
- int *p; //malloc()的时候:指针的地址是存在栈区对,指针指向的数据存在堆区。malloc的空间不是变量
- struct AAA a[3];
- 声明周期:所在花括号,花括号结束,该变量被自动释放
- 作用域:在本花括号内,出来花括号,名字就没用了。局部起作用
- auto:存储说明符
- auto :自动默认有auto修饰,由于平时写的太多,每次都写也是浪费时间,所以默认不写跟写是一样的,自动有auto。这个auto就叫存储说明符,他说明a是栈区存储。
- 函数形参也是局部变量。
- 所以函数不能返回局部变量地址。
- 同一个作用域内,变量不能重名。
- 不同的作用域内,可以重名。
作用域嵌套:在小作用域内,小的起作用,覆盖大的。内部覆盖外部呗。
下面*p找不着地址了,地址b的地址被释放了,非法操作。

静态变量
特别:
- 内存区域:静态区/静态全局区/静态存储区
- 内存特点:会被自动初始化为0;声明周期与程序共存亡;运行时,在加载资源阶段分配空间。「加载资源阶段之后,才执行main」
全局变量/外部变量
结构体等里面的变量也是全局变量
- 位置:写在全局位置。
- 位置的注意点:
全局变量在全局位置初始化的时候:只能用常量进行初始化,不能写执行语句。
- 全局变量会初始化成0
- 全局变量和局部变量重名,在局部位置,局部变量有效。
- 声明周期:与程序共存亡。
- 作用域:所有文件函数可见,跟函数一样,只能出现一个定义,声明可以出现多个。由此,声明可以放到头文件中
- 特点:
- 多文件中的全局变量:
-- 定义:手动初始化,只能出现一个初始化,其他文件只能声明。所以定义不能放在头文件,只能放源文件,其他文件声明。
-- 声明:没有手动初始化,这种可以放头文件,所有文件使用的都是一个全局变量,没有初始化,默认是0.
- extern 存储说明符号 ⚠️
- 修饰变量/函数声明,表示是外部变量,实体在别处,也就是显式的表面该出使用的是全局变量或者函数。
- 修饰:
1.变量声明:
全局变量本身就是对外开发的,所以加不加都行。
局部位置声明变量{extern int a;},这个不能定义,只能声明,表示a是使用的全局变量。好处:代码逻辑清晰,一看看出a是全局变量
2.函数声明:加不加都行
//❌ *p=malloc(4),初始值设定项不是常量

全局变量声明的作用:
在main里面直接调用的fun(),在main里面有全局变量e=23,没有传通过函数传参数,而是在函数的文件里面声明一个e,就直接可以使用,传值。定义有一个,声明可以有多个。


如下 下面就错了❌,因为e定义了两次,e只能定义一次才对


int e =12;
int e;//可以
int e=23;//❌,定义只有一次
e=23;//❌,定义只有一次
在c中,全局变量 名字类型相同,他就是同一个东西,全局变量是公共的。看下面例子就知道了:
main.c
#include "c3.h"
int main(void) {
//虽然e在不同的地方改了值,但是e是同一个
//在c中,全局变量 名字类型相同,他就是同一个东西
fun();//12
fun2();//66
fun();//66
e = 97;
fun();//97
fun2();//66
return 0;
}
c3.c
#include "c3.h"
int e = 12;//这里初始化了
void fun() {
printf("%d\n", e);
}
void fun2() {
e = 66;
printf("%d\n", e);
}
c3.h
#ifndef C3
#define C3
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
//这里声明
int e; //如果别的地方要定义或者赋值,头文件一定不要初始化。
void fun();
void fun2();
#endif
extern 局部位置声明变量 :
void fun() {
int e;//局部变量
{
extern int a;//全局变量// 默认成0
}
}
int main(void) {
fun();
return 0;
}
#include "c3.h"
void fun5() {
int e;//局部变量
{
//1。局部位置声明全局变量
extern int e;//全局变量
printf("%d",e);//打印12 ,因为在c3.c中全局变量已经定义了int e = 12;
//2。有块范围的e只能声明不能初始化
extern int e=23;//❌
//3。
int e;//局部变量
}
}
int main(void) {
fun5();
return 0;
}
静态全局变量 static
- 生命周期:与程序共存亡
- 作用域:只有在所在文件内有效,别的文件用不了。
- 会被初始化成0
- 只能用常量初始化
ps:全局变量作用域:所有文件函数可见。
下面结果是:
45
33
45
分析:经过fun后,a没改变,因为a是静态全局变量,只能作用在main.c这个文件里
//main.c
#include "c3.h"
static int a = 45;
int main(void) {
printf("%d\n", a);
fun();
printf("%d\n", a);
return 0;
}
//c3.c
#include "c3.h"
static int a = 33;
void fun() {
printf("%d\n", a);
}
//c3.h
#ifndef C3
#define C3
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
void fun();
#endif
静态局部变量/局部静态变量
- 就是在局部变量前加static
- 会被初始化成0
- 只能用常量初始化
- 生命周期:与程序共存亡:相当于把局部变量的生命周期加长里,内存特别修改。
- 作用域:和局部变量的作用域一样
- 不同的作用域内,可以重名:
作用域嵌套:在小作用域内,小的起作用。
int main(void) {
//静态局部变量
// 默认成0
static int b;
return 0;
}
经典例子:
#include "c3.h"
void fun5() {
int a = 1;
static int b = 1;
a++;
b++;
printf("%d,%d\n", a, b);
}
int main(void) {
//分析:a局部变量,调用一次函数,a释放内存,下一次调用,重新走一遍a=1.
//而b是局部静态变量,生命周期跟程序共存亡, 所以函数调用完内存不会释放,值一直有
fun5();//2,2
fun5();//2,3
fun5();//2,4
fun5();//2,5
fun5();//2,6
return 0;
}
静态函数
作用域:仅在所在文件内有效,其他文件不认识,跟静态全局变量大作用域一样
static void fun5() {
int a = 1;
int b = 1;
printf("%d,%d\n", a, b);
}
寄存器变量register
register int a;
- 无法取地址:&a是错的(❌)
-
不能修饰全局变量
image.png
cpu取数据处理:
物理内存先进到寄存器,cpu再从寄存器取数据。
寄存器:寄存器和cpu速度相当,空间比较小在kb级别
高度缓存:在寄存器和物理内存中间,装的是物理内存使用频率高的数据,寄存器取数据效率更高。空间可以达到m
物理内存:内存比缓存要慢,但是空间可以达到GB级别
硬盘(固态和磁盘):硬盘这个速度更慢,但是价格也是便宜,空间也大。运行大时候加载到物理内存,如何再到cpu
其他外设:u盘、硬盘、光盘,这个就慢了。
类型限定符 const volatile restrict
const 常量修饰符
作用:被修饰的变量变成常量,不能被二次修改
1.休闲基本数据类型变量
const int a=12;//常量 相当于final
int const a=12;//这样写也行
作用:保护数据不被修改。
本质:const修饰了a,那就是我们不能通过这个名字修改了,但是可以通过地址修改

数组也不能通过aa去修改了,可以通过指针修改

const修饰指针
int main(void) {
int a = 12;
int b = 45;
const int *p = &a; //类型转换
// *p = 23;//❌ 编译不成功
p = &b;//p可以改变指向地址
int const *p1 = &a; //const int *p = &a;一样的
int *const p2 = &a;//*p可以变 p不可以变
*p2 = 13;
// p2= &b;//❌ 编译不成功
const int *const p3 = &a;// int const *const p;一样的
// *p3=&b;//❌ 编译不成功
// p3=12;//❌ 编译不成功
return 0;
}
const作为函数参数,函数返回值
const int* fun5(const int *p) {}
返回值不希望被修改,参数也不希望被修改。
标准来说:
const char *p="absd";
因为char p5 = "asdf"; 中p5不能修改
volatile 易变的变量
volatile int a=12;

restrict 可优化的操作

内存分区
栈区
局部变量,
特点:默认1M。
系统申请内存,系统释放内存。
堆区
nalloc的空间。
特点:理论上是物理内存的大小。很大
程序员申请,程序员释放,不释放的话也是跟程序共存亡。(内存泄漏)
静态全局区
全局变量、static变量(静态全局、静态局部)
特别:会被自动初始化为0。
生命周期与程序共存亡。
作用域:
静态全局变量:所在文件可以见
静态局部变量:所在的作用域可见
全局变量:所有文件可见
字符常量区
装的是:常量数字12 、’a ‘、“qwe”、12.3
特点:只读
空间也是系统申请和释放
生命周期:
字符串常量与程序共存亡。
数值常量:12 、’a ‘、12.3 ,不占用额外的存储空间,他们在立即数存储里面存储。
全局const变量:存储在常量区,
局部const变量:不存储在常量区,存储在栈区

代码区
代码
特点:只读,由系统管理
命令行参数 int main(int argc,char* argv[])
- 意义:通过带参数的主函数参数,传递命令行参数
- 作用:将应用外部的数据传递进程程序内部进程处理,就是用到命令行参数:一般就是传递文件路径
- 形式:传递参数
//int argv :命令行参数的个数
//char *arvg[] :参数数组
//int main(int argc, char **argv) //也可以这样写
int main(int argc, char *argv[]) {}
随机数
头文件 stdlib.h
#include <time.h>
#include "c3.h"
int main(int argc, char *argv[]) {
//随机数种子
//相当于产生随机数的基准数,在3的基础上随机
//如果基数是固定的srand(2),那么随机数产生的也一样
//所以传入当前时间,因为时间一直在变化
//不写scrand(),那么iu默认是1为基准数
srand(time(NULL));
//产生随机数
int a;
int i = 0;
while (i < 10) {
a = rand();
i++;
printf("%d\n", a);//随机产生10个数
}
return 0;
}
随机数应用
随机产生指定范围的一个数
产生一个0-78的随机数
思路:得到的随机数%79,任何一个数对79求余,就会得到0-78的数
#include <time.h>
#include "c3.h"
int main(int argc, char *argv[]) {
srand(time(NULL));
//产生随机数
int a;
int i = 0;
while (i < 10) {
a = rand();
int b = a % 79;
i++;
printf("%d\n", b);//随机产生10个数
}
return 0;
}
结果:
42
7
4
20
63
31
8
69
2
51
随机产生三位数(100-999)
思路:先产生0-899的随机数,然后再加100。
因为0-a的随机数,可以用a+1取余数来做。
#include <time.h>
#include "c3.h"
int main(int argc, char *argv[]) {
srand(time(NULL));
//产生随机数
int a;
int i = 0;
while (i < 10) {
a = rand();
int b = a % 900;
b + 100;
i++;
printf("%d\n", b);//随机产生10个数
}
return 0;
}
结果
490
229
200
416
122
744
870
704
136
727
随机产生50-120
产生0-70的随机数,然后+50.
#include <time.h>
#include "c3.h"
int main(int argc, char *argv[]) {
srand(time(NULL));
//产生随机数
int a;
int i = 0;
while (i < 10) {
a = rand();
int b = a % 71;
b + 50;
i++;
printf("%d\n", b);//随机产生10个数
}
return 0;
}
结果:
12
22
2
1
5
35
35
61
66
50
随机产生指定一组数组的一个
思路,随机产生数组下标。
#include <time.h>
#include "c3.h"
int main(int argc, char *argv[]) {
int arr[] = {12, 5, 67, 3, 8, 0, 12, 4, 55, 19};
srand(time(NULL));
//产生随机数
int a;
int i = 0;
while (i < 10) {
a = rand();
int b = a % 10;
i++;
printf("%d\n", arr[b]);//随机产生10个数
}
return 0;
}
结果:
3
19
5
3
5
4
67
3
12
8
网友评论