嵌入式学习-c基础(中)

前言

        本章节主要介绍一下c中的函数以及存储布局的一些操作。本章节在c基础中属于比较重要的知识内容,无论后续我们后续使用c来干什么项目,这些知识毫无疑问都是不可或缺的。

1.函数

1.1 函数的定义

定义:返回值类型 函数名(形参列表){......}

注意:

        如果你不写返回值类型,默认是int类型,在使用的时候,如果没有加函数的声明,那么我们必须将函数定义在main函数之前。如果定义的函数的返回值类型不为void,那么我们必须返回一个与返回值类型一致的值。

案例:

#include <stdio.h>
int add(int a, int b)
{
    printf("--");
    return a + b;
}

int main()
{
    int a = 10;
    int b = 20;
    int c = add(a, b);
    printf("c = %d\n", c);
    return 0;
}

1.2 函数的声明

声明方式:返回值类型 函数名(形参列表);

声明位置:main函数之前

案例:
 

#include <stdio.h>

int add(int a, int b);

int main()
{
    int a = 10;
    int b = 20;
    int c = add(a, b);
    printf("c = %d\n", c);
    return 0;
}

int add(int a, int b)
{
    return a + b;
}

1.3 函数的调用

参考以上两个案例中关于add()函数的调用

1.4分程序编程

简单来说,就是将一个模块的所有函数都放在一个.c文件中,并且在.h文件中存放函数的声明和类型的定义。

案例:

创建三个文件math_utils.h,math_utils.c, main.c

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

// 宏定义
#define PI 3.1415926

// 函数声明
int add(int a, int b);
double circle_area(double radius);

// 类型定义
typedef struct {
    double x;
    double y;
} Vector;

#endif
 
// math_utils.c
#include "math_utils.h"

int add(int a, int b) {
    return a + b;
}

double circle_area(double radius) {
    return PI * radius * radius;
}
 
// main.c
#include <stdio.h>
#include "math_utils.h"

int main() {
    int a = 10;
    int b = 20;
    int c = add(a, b);
    printf("c = %d\n", c);
    double radius = 5.0;
    double area = circle_area(radius);
    printf("area = %f\n", area);
    return 0;
}

编译命令:

gcc -o main main.c math_utils.c

1.5 编译多文件

vim 文件1 文件2 ... -p

1.6 多文件程序编译

gcc -o 别名 所有要编译的.c文件

1.7 变参函数

return_value_type func(第一个参数不可变, ...);

例如:常用的scanf()和printf()函数

1.8 预处理指令(宏函数)

定义:

#define 宏定义 定义值

注意:

  • 宏没有分号
  • gcc预处理阶段做替换
  • 宏语句需要在一行,如果过长可以使用续航符(\)进行换行
  • 优势
    • 函数的调用会降低程序的执行效率,函数调用期间会保存程序执行现场,切换函数的地址到调用函数,当函数调用结束后再恢复保护的现场。
    • 宏函数既可以模块化,在执行的时候也不会降低效率,因为宏函数仅仅是纯替换,没有所有的调用过程。
  • 宏函数不适合功能很复杂的定义

2. c的存储布局

2.1 物理地址和虚拟地址

2.1.1物理地址

存储在内存上的地址

2.1.2 虚拟地址

虚拟地址由程序生成,在进程中,内核会独立分配4G虚拟地址,虚拟地址并不是数据真正存储的地址,而是我们程序访问处理的地址。

虚拟地址空间布局

虚拟地址空间作用声明周期
堆区存储局部变量函数内
栈区动态开辟函数内
bss段未初始化的静态变量以及全局变量(其中变量的初始值一定为0)整个程序运行期间
data数据段已经初始化的全局变量以及静态变量整个程序运行期间
text文本段存储代码整个程序运行期间

2.2 修饰变量的关键字

2.2.1 static

作用:修饰静态变量

语法:static 数据类型 数据名称

案例:

        static int a;        //未初始化的

        static int a = 0;        //初始化的

注意:

        以上两个定义的结果虽然a的值相同,但是存储的位置并不相同

2.2.2 const

作用:只读变量,用于修饰指针。

语法:const 数据类型 数据名称(= 值)

案例:const int max_value = 100;

注意:const修饰的值不可改变,所以使用的时候我们会进行初始化,否则会得到一个不可改变的脏数据

2.2.3 auto

作用:自动变量,定义局部变量默认修饰关键字

语法:auto 变量名 = 值;

案例:

        auto a = 10; // a 被推导为 int

        auto b = 3.14; // b 被推导为

        double auto c = 'a'; // c 被推导为

        char auto d = true; // d 被推导为 bool

2.2.4 register

作用:寄存器变量,主要用于提示编译器这个变量可能会被频繁使用,尽可能将其存储在 CPU 寄存器中,以提高访问速度

语法:register 变量类型 变量名 = 值;

案例:
        register int a = 1;

2.2.5 volatile

作用:易失变量,主要用于硬件控制开发,方便编译器对定义的变量优化

语法:volatile 变量类型 变量名 = 值;

案例:

        volatile bool flag = false;

2.2.5 extern

作用:外部声明,用于实现跨文件的变量 / 函数共享。

语法:extern 变量类型 变量名 = 值;

案例:

        fun.c文件中定义,,main.c中使用

// func.c

int add(int a, int b) {return a + b; } // 定义函数 




// main.c

#include <stdio.h>

extern int add(int a, int b);

// 声明:add 函数在其他地方定义

int main() { 
    printf("%d\n", add(2, 3)); // 调用外部函数,输出 5

    return 0;
}

2.3 变量类型

案例

#include <stdio.h>

static int c;
int b = 10;
void demo(int a) {
 
    printf("a = %d, b = %d, c = %d\n", a, b, c);
}

int main() {
    int a = 10; 
    demo(a);
    return 0;
}

2.3.1 局部变量

定义在函数体内的变量,函数的形参也是。如案例中的变量a

2.3.2 全局变量

定义在函数体外边的变量,如b

2.3.3 静态变量

使用static修饰的变量,根据位置可以分为全局静态变量和局部静态变量,如果定义的是全局静态变量,则只能在本文件中使用。

3.结束语

        本文到此结束,针对这些存储变量以及函数的声明定义,本文并没有详细展开来介绍,只把常用的场景进行了一下说明。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值