多文件

作者: 晓晓桑 | 来源:发表于2020-04-21 22:17 被阅读0次
image.png

形式

作用:将函数分类,每一类写到文件中,方便管理

源文件 c3.c:
  • 包含头文件
  • 放定义:函数定义,全局变量
头文件 c3.h
  • 放声明:函数声明,结构体类型声明,宏,typedef
  • 源文件和头文件是一一对应的,名字都是一样的
主文件 cmain.c
  • 包含头文件,自己的头文件一般用“”,系统的头文件一般用<>
  • 主函数调用源文件的内容
image.png

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

自动变量(又叫局部变量/栈区变量)

  1. 特征:定义在{}内,也就是代码段。
    比如函数内,函数参数内,if for while switch dowhile
    注意⚠️:结构体的花括号成员不是局部变量的意义

  2. 种类:

  • int a;
  • int a[2];
  • int *p; //malloc()的时候:指针的地址是存在栈区对,指针指向的数据存在堆区。malloc的空间不是变量
  • struct AAA a[3];
  1. 声明周期:所在花括号,花括号结束,该变量被自动释放
  2. 作用域:在本花括号内,出来花括号,名字就没用了。局部起作用
  3. auto:存储说明符
  • auto :自动默认有auto修饰,由于平时写的太多,每次都写也是浪费时间,所以默认不写跟写是一样的,自动有auto。这个auto就叫存储说明符,他说明a是栈区存储。
  • 函数形参也是局部变量。
  • 所以函数不能返回局部变量地址。
  • 同一个作用域内,变量不能重名。
  • 不同的作用域内,可以重名。
    作用域嵌套:在小作用域内,小的起作用,覆盖大的。内部覆盖外部呗。

下面*p找不着地址了,地址b的地址被释放了,非法操作。


image.png

静态变量

特别:

  • 内存区域:静态区/静态全局区/静态存储区
  • 内存特点:会被自动初始化为0;声明周期与程序共存亡;运行时,在加载资源阶段分配空间。「加载资源阶段之后,才执行main」

全局变量/外部变量

结构体等里面的变量也是全局变量

- 位置:写在全局位置。
- 位置的注意点:

全局变量在全局位置初始化的时候:只能用常量进行初始化,不能写执行语句。

- 全局变量会初始化成0
- 全局变量和局部变量重名,在局部位置,局部变量有效。
- 声明周期:与程序共存亡。
- 作用域:所有文件函数可见,跟函数一样,只能出现一个定义,声明可以出现多个。由此,声明可以放到头文件中
- 特点:
- 多文件中的全局变量:

-- 定义:手动初始化,只能出现一个初始化,其他文件只能声明。所以定义不能放在头文件,只能放源文件,其他文件声明。
-- 声明:没有手动初始化,这种可以放头文件,所有文件使用的都是一个全局变量,没有初始化,默认是0.

- extern 存储说明符号 ⚠️
  • 修饰变量/函数声明,表示是外部变量,实体在别处,也就是显式的表面该出使用的是全局变量或者函数。
  • 修饰:
    1.变量声明:
    全局变量本身就是对外开发的,所以加不加都行。
    局部位置声明变量{extern int a;},这个不能定义,只能声明,表示a是使用的全局变量。好处:代码逻辑清晰,一看看出a是全局变量
    2.函数声明:加不加都行

//❌ *p=malloc(4),初始值设定项不是常量


image.png

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


image.png image.png

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


image.png
image.png
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,那就是我们不能通过这个名字修改了,但是可以通过地址修改


image.png

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


image.png

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;


image.png

restrict 可优化的操作

image.png

内存分区

栈区

局部变量,
特点:默认1M。
系统申请内存,系统释放内存。

堆区

nalloc的空间。
特点:理论上是物理内存的大小。很大
程序员申请,程序员释放,不释放的话也是跟程序共存亡。(内存泄漏)

静态全局区

全局变量、static变量(静态全局、静态局部)
特别:会被自动初始化为0。
生命周期与程序共存亡。
作用域:
静态全局变量:所在文件可以见
静态局部变量:所在的作用域可见
全局变量:所有文件可见

字符常量区

装的是:常量数字12 、’a ‘、“qwe”、12.3
特点:只读
空间也是系统申请和释放
生命周期:
字符串常量与程序共存亡。
数值常量:12 、’a ‘、12.3 ,不占用额外的存储空间,他们在立即数存储里面存储。
全局const变量:存储在常量区,
局部const变量:不存储在常量区,存储在栈区

image.png
代码区

代码
特点:只读,由系统管理

命令行参数 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

相关文章

  • 多文件

    形式 作用:将函数分类,每一类写到文件中,方便管理 源文件 c3.c: 包含头文件 放定义:函数定义,全局变量 头...

  • 多文件上传

    package com.test.test;import java.io.File; import java.io...

  • 多文件上传

    index js

  • 多文件编程

    随着程序的慢慢变大,单一文件必然不能满足我们的需求,一个普遍的做法是多文件编程,这样做的目的也是为了项目组织结构更...

  • 多文件上传

    多文件上传实现

  • 多配制文件

    1、application.properties 2、application-dev.properties 3、a...

  • awk 多文件

    awk 如何运算多个文件中的内容? 文件1:1.txt (id,name) 文件2:2.txt (id,age) ...

  • iOS 上传多个文件

    上传文件格式 多文件文件上传

  • 2018-05-07 阶梯书包数据库设计,接口设计

    表设计:用户购买书包多对多,书包和资源多对多,一个资源对应对多文件表,用户文件表多对多记录观看文件,用户已读资源多...

  • 16 C多文件开发

    1、多文件开发 1.1、新建ds.h文件 1.2、新建ds.c文件 1.3、新建main.c文件 2、多文件开发中...

网友评论

      本文标题:多文件

      本文链接:https://www.haomeiwen.com/subject/qbxaphtx.html