1、C语言执行过程需要经历哪几个步骤?
C语言源码到可执行文件需要经历四个步骤:预处理阶段、编译阶段、汇编阶段、链接阶段
(1)预处理:展开头文件、定义常量以及条件编译等;
(2)编译:将预处理后的源文件转换为机器可执行的目标代码,生成中间代码或汇编代码;
(3)汇编:将汇编代码转化为机器指令,这些指令是计算机能够直接执行的二进制码;
(4)链接:讲多个目标文件合并成最终的可执行文件,该步骤需要使用到库函数和系统调用等外部功能。
2、C语言中const的作用是什么?
const关键字具有多种用途,主要作用是定义常量以及修饰变量:
(1)定义常量:使用
const
可以定义常量,常量是指在程序运行期间不能被修改的值。(2)修饰变量:使用
const
可以修饰变量,这使得变量的值在初始化后不能被修改。(3)修饰指针:使用
const
修饰指针可以限制指针的行为。(4)修饰函数参数和返回值:使用
const
可以修饰函数的参数和返回值,这可以确保函数不会修改这些值。(5)修饰函数指针:使用
const
修饰函数指针可以限制函数指针的行为。例如:指向常量的函数指针表示该函数不能修改任何参数。(6)修饰数组:使用
const
可以修饰数组,表示数组的内容是常量,不能被修改。
3、C语言中statict的作用是什么?
static
关键字具有多种用途,可以用来控制变量的生命周期、作用范围、可见性等。(1)在函数内部定义变量:变量的生命周期将延长到整个程序的运行时间。它将在程序开始执行时被初始化,并且在程序结束时才销毁。这个变量只会初始化一次,但每次函数被调用时都会保留其值。
(2)修饰全局变量时:这个变量只在定义它的源文件中可见。
(3)修饰函数时:函数范围被限定于定义函数的文件中,其他文件不能够调用。
(4)使用
static
修饰数组时,数组的大小必须在编译时确定,不能在运行时改变.(5)使用
static
修饰指针时,可以用来声明一个指向静态变量的指针,或者声明一个指向函数的指针
4、变量的声明和定义有什么区别?
变量的声明:告诉编译器变量的名称和类型。它使编译器知道变量存在,并为其预留存储空间。
变量的定义:定义不仅告诉编译器变量的名称和类型,还为变量分配内存空间。
5、C语言中常用的变量类型及大小
整型(Integer Types):
int
:通常占用2或4字节,具体取决于系统和编译器。在许多现代系统上,它通常是4字节(32位)。short
:通常占用2字节。long
:通常占用4或8字节,具体取决于系统和编译器。在许多现代系统上,它是4字节(32位)。long long
:通常占用8字节。字符型(Character Types):
char
:通常占用1字节。浮点型(Floating Point Types):
float
:通常占用4字节。double
:通常占用8字节。long double
:通常占用更多字节,具体取决于系统和编译器,但通常大于double
。
6、结构体可以直接赋值吗?
在C语言中,结构体可以直接赋值。你可以将一个结构体的值赋给另一个结构体,这被称为结构体的浅复制。需要注意的是,如果结构体中包含指针,那么复制的只是指针本身,而不是指针所指向的数据。
7、简述sizeof和strlen的区别
(1)sizeof
是一个运算符,而strlen
是一个函数。
(2)sizeof
返回的是数据类型或对象的大小,而strlen
返回的是字符串的长度,以“\0”结束,长度不包含“\0”。
(3)sizeof
可以用于各种数据类型和对象,而strlen
只能用于字符串。
(4)sizeof
操作符的结果单位是字节,而strlen
的结果单位是字符。
8、简述volatile的作用
在C语言中,
volatile
关键字用于告诉编译器,变量的值可能会在外部被改变,因此不能进行优化。使用volatile
可以确保每次访问该变量时都会从其原始地址中读取,而不是从寄存器或缓存中读取。
9、全局变量和局部变量有什么区别?
(1)作用域不同:全局变量的作用域为整个程序,而局部变量的作用域为当前函数或循环等。
(2)内存存储方式不同:全局变量存储在静态数据区中,局部变量存储在栈区。
(3)生命期不同:全局变量的生命期和主程序一样,随程序的销毁而销毁,局部变量在函数内部或循环内部,随函数的退出或循环退出就不存在了。
(4)使用方式不同:全局变量在声明后程序的各个部分都可以用到,但是局部变量只能在局部使用。
10、C语言程序内存划分为哪几部分?
(1)代码区(Code Segment):也称为文本区,存储程序的二进制代码。这部分内存是只读的,以防止程序意外地修改其指令。
(2)全局/静态区(Global/Static Storage):存储全局变量和静态变量。全局变量是在函数之外定义的变量,静态变量(即关键字
static
定义的变量)既可以是在函数之外定义的,也可以是在函数内部定义的。全局/静态区的变量在程序的生命周期内一直存在。(3)堆区(Heap):动态内存分配区域,如使用
malloc
,calloc
,realloc
等函数分配的内存。程序在运行时可以向系统申请特定大小的内存,并在不再需要时释放它。(4)栈区(Stack):存储局部变量、函数调用的返回地址等。每当一个函数被调用时,系统会在栈上为其分配一块内存来存储它的参数、局部变量等。当函数返回时,它的内存就会被自动回收。
11、简述指针和引用的区别?
(1)性质不同:指针是一个实体,而引用是一个别名。
(2)是否可以初始化不同:指针可以不初始化,通过赋值可以指向任意同类型的内存;引用必须初始化,而且引用一经引用一块内存,再也不能引用其它内存了,即引用不能被改变。
(3)在sizeof操作时不同:sizeof指针在32位系统下永远是4个字节,而sizeof引用计算的是它所引用内存的大小。
(4)底层实现不同:引用的底层是以指针的方式实现的,定义一个引用变量,相当于就是定义了一个指针,然后把引用内存的地址写到这个指针里面。
12、#include <>和#include“”区别是什么?
(1)使用场景不同
#include <> 一般用于包含系统头文件,诸如 stdlib.h 、stdio.h 、iostream等;
#include " " 一般用于包含自定义头文件,如test.h、declare.h等
(2)查找的目录不同
#include<> : 编译器直接从系统类库目录里查找头文件;
#include "" : 默认从项目当前目录查找头文件,所谓项目当前目录,就是项目工程文件(*.vcxproj) 所在的目录,如果在项目当前目录下查找失败,再从项目配置的头文件引用目录查找头文件。
13、简述GPIO的8种工作模式
(1) GPIO_Mode_AIN 模拟输入;
用于连接模数转换器(ADC)或其他模拟输入设备;
(2)GPIO_Mode_IN_FLOATING 浮空输入;
此模式最常用的是检测按键,可以接收高低电平。但容易被干扰。
(3)GPIO_Mode_IPD 下拉输入;
此模式检测到电平默认为低,可以检测到由低到高的电平变化。
(4)GPIO_Mode_IPU 上拉输入;
此模式检测到电平默认为高,可以检测到由高到低的电平变化。
(5)GPIO_Mode_Out_OD 开漏输出;
开漏输出用于输出低电平,高电平靠外部上拉电阻电压决定,适用于快速切换电压的外部电路结构。
(6) GPIO_Mode_Out_PP 推挽输出;
推挽输出用于输出高低电平,是最常用的模式。
(7) GPIO_Mode_AF_OD 复用开漏输出;
复用 IIC 时候选择复用开漏输出,因为开漏输出可以“线与”。
(8)GPIO_Mode_AF_PP 复用推挽输出
其他复用比如 SPI 等可以选择复用推挽输出。
14、什么是中断,以及中断的流程
中断是一种机制,用于在程序执行过程中暂停当前任务,转而执行其他优先级更高的任务。中断可以是外部中断,如按键触发的中断,也可以是内部中断,如定时器中断。