嵌入式开发知识点概括

226 阅读16分钟

c语言知识点

栈区;编译器自动分配

堆区:动态内存分配函数,由程序员去控制释放和分配

 main(){}//默认返回int类型数据

&地址运算符,通过它获取变量地址,格式%p

马斯洛需求层次理论

 关于就业后命名方面,一般公司都会有自用的前缀,因需要引入外部库,恐有重名风险。

1. 知识点

在声明指针变量的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。

在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。

 

2. 指针

函数指针是指向函数的指针变量。函数指针可以像一般函数一样,用于调用函数、传递参数。

int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型

逗号运算符(,)它允许在单个表达式中多个操作。

++a,a++  -->先加后执行,还是先执行后加

对于星号的位置并没有准确的规范,完全是根据个人习惯来的

int* a,b,c;

 我们会误以为abc都是int*类型的指针变量。但其实只有a是指针变量,而bc只是普通的int类型的变量。

3. ub行为

格式:%zu  返回size_t的格式

UB (未定义行为) 是指编程语言中那些由编译器平台定义的行为,这些行为在标准中并未被明确指定。 使用未定义的行为可能会导致代码的可移植性降低,并且在环境、编译器或平台发生任何变化时,可能会随机破坏您的代码。这种行为有时可能会看起来可行,但理论上它可以使恶魔从你的鼻子中飞出,因此被称为“鼻恶魔”。因此,使用未定义的行为几乎总是一个坏主意

注:切记不要在同一个地方对同一个变量进行操作,写法不合规,可能导致多种运行结果发生

4.字符串

main函数有三个参数,argc、argv和envp表示。

int argc,用于存放命令行参数的个数。

char *argv[],是个字符串的数组,每个元素都是一个字符指针,指向一个字符串,即命令行中的每一个参数。

char *envp[],也是一个字符串的数组,这个数组的每一个元素是指向一个环境变量的字符指针。envp存放了当前程序运行环境的参数。

在实际开发中,main函数一般都需要参数,没有参数的情况极少。

5.inline

C语言中的inline关键字是C99标准的关键字,它的作用是将函数展开,把函数的代码复制到每一个调用该函数的地方。 这样调用该函数的地方就可以直接执行函数代码,而不发生跳转、压栈等一般性函数操作。可以节省时间,也会提高程序的执行速度。

使用inline关键字修饰的函数就是内联函数。

在C语言中,如果一些函数被频繁的调用,尤其是这些函数在递归函数中被调用,那么如果递归过程很长,就有可能出现递归还没完成,但是栈空间被用完了。此时如果将这些函数定义为内联函数,那么在递归函数中,内联函数是不会发生函数跳转和压栈操作的,因此也就不存在栈溢出的情况了。

注意:数组只是一个指针(地址,系统在本函数运行时,是不知道数组所表示的地址有多大的数据存储空间 这里只是告诉函数:一个数据存储空间首地址),所以结果是占内存大小

在编程中获取数组长度都在外部获取,不要在函数内部获取!!!

6.异或

1. 归零律:

2. 恒等律:

3. 交换律: 

4. 结合律:

5. 自反:

重点记124,面试可能涉及:不使用临时变量,将两个数交换数值,可使用异或运算

7.字符串

字符串默认跟\0,编译器打印时遇到它就会停下来,当需要使用字符数组来表示字符串时,记得打上\0.否则它会继续打印下一个字符串,直到遇到\0

8.指针

指针里面存储的是另一个变量的地址。 指针做比较时,比较的是内存地址的大小,而非该地址的值的大小。 int *p1; p1 =&a; p1 = 1; 注意p1就是到指针所存储的值那里去,然后修改值,a=1;

函数指针

函数指针的定义: 函数的返回值类型(指针名)(函数的参数列表类型 函数名称前面加:代表函数指针,它返回一个指针。

  • 函数指针 —存放函数地址的指针;
  • &函数名 —得到的就是一个函数的地址;
  • 函数名是等于函数地址
void Add(int x, int y){
return x+y;}
int main(){
int (*pf)(int,int)=&Add;
int ret=(*pf)(3,5);

c语言获取当前时间

#include <stdlib.h>
#include <time.h>
 
int main() {
    time_t currentTime; // 定义存放当前时间的变量
    
    // 获取当前时间
    currentTime = time(NULL);
    
    // 将当前时间转换为本地日期和时间格式
    struct tm *localTime = localtime(&currentTime);
    
    printf("当前时间:%d年 %d月 %d日 %02d:%02d:%02d\n",
           (1900 + localTime->tm_year),
           (1 + localTime->tm_mon),
           localTime->tm_mday,
           localTime->tm_hour,
           localTime->tm_min,
           localTime->tm_sec);
    
    return 0;
}

数组指针和指针数组

数组指针:指向数组的指针 指针数组:数组中所有元素都是指针

结构体

结构体存在内存对齐概念,结构体会找到占用内存最大的类型,然后按该类型去分配每个结构体里的变量。 排序也会影响所占内存,性能优化可以从此下手。

枚举

优点:

  • 易于维护
  • 安全性
  • 代码可读性

static

static局部变量

一般局部变量在运行完后都会销毁,加上static则不会,它会存在与程序的整个生命周期。 static int x = 11;x是存在静态区域的。

static申明全局变量以及函数

在函数外定义的全局静态变量,表示该变量不对外分享,只能在该文件查询。 全局函数用法一致,也不可对外分享。

extern

声明外部变量 extern int x=0;该变量外部文件可访问 函数默认外部是可以访问的。

宏定义

宏定义的作用域:宏定义的作用域从它被定义的地方开始,到它被取消定义的地方结束,或 者到文件结束。

为了避免宏定义重复 #undef指令 取消宏定义 #ifndef指令 如果没有定义

#ifndef和#define、#endif指令组合,通常组合用于头文件。

  1. 宏通常比函数快,它在编译时进行了文本替换,没有函数调用的开销。
  2. 函数更安全,因为编译器会检查数据类型,但宏是不会的。
  3. 代码膨胀

typedef

和宏定义不同,typedef是需要分号的

typedef unsige int us_int;

字符处理

C 语言中的字符串是以字符数组的形式存储的,所以在将字符串赋值给字符数组时,应该使用字符指针而不是单个字符变量。

char* strcpy(char* des,const char* source) 该函数把\0结束的字符串复制到另一个内存地址,写法有一下几种:你可以使用malloc

char *sSecond = (char*)malloc((len + 1) * sizeof(char));

去申请一块内存地址,并把它付给指针变量。

当然你也可以写死,直接神奇一个足够大的char数组,或者用更聪明的做法,strlen(s)来获取原字符串的长度,记得长度+1,strlen 函数会计算字符串中的字符数,但不包括空字符 '\0',为了后面操作方便,建议长度+1

int_

面试题:宏与函数的比较

  1. 速度宏通常比函数快,因为宏在编译时进行文本替换,没有函数调用的开销。
  2. 类型安全函数更安全,因为编译器会检查类型。宏不会进行类型检查,可能导致类型不匹 配的错误。
  3. 调试函数更容易调试,因为调试器可以跳到函数内部。宏由于是文本替换,调试时可能不 那么直观。
  4. 代码膨胀宏可能导致代码膨胀,因为每次使用宏时,它的替换文本都会被插入到代码中。 而函数只会在内存中有一个副本。

总的来说,宏和函数各有优缺点,应根据具体情况选择使用。

面试题:const修饰指针

const 也可以用于修饰指针,但这里有几种不同的情况: 指针指向的内容是常量,不能通过指针修改内容。

const int *p = &a; // p 是一个指向 int 的指针,但它指向的
内容不能通过 p 修改 *p = 30;/不可以 int b = 9; *p = &b; 
指针本身是常量,不能改变指针指向的地址。 int * const q = &a; 
// q 是一个指向 int 的指针,并且 q 自身的值(即地址)不能改变 
指针本身和它所指向的内容都是常量。 const int * const r = &a; 
// r 是一个指向 const int 的指针,并且 r 自身的值也不能

AOP编程

面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

常见的条件编译指令

1、#if:如果条件为真,则执行相应的操作。 2、#elif:类似于 elseif 的用法,当前面条件为假,再判断该条件是否为真,如果是真,则执行相应操作。 3、#else:如果前面所有条件均为假,则执行相应操作。 4、#ifdef:如果该宏已定义,则执行相应操作。 5、#ifndef:如果该宏没有定义,则执行相应操作。 6、#endif :结束对应的条件编译指令。(不能省略)

网络接口

(聚合数据-api免费接口)测接口工具Apifox

4. http特点无连接(一次请求一次连接,后续释放一次连接,HTTP/1.1已经实现长连接),无状态(每次请求都是独立的)

5. 请求/响应模型:客户端请求,服务器响应。

网络请求的工作原理?
RESTFUL是什么?

算法

有一个很著名的公式 “程序=数据结构+算法”。

算法的基本特性:一个算法必须具备明确性、有限性、输入项、输出项和有效性。

算法,从字面意义上解释,就是用于计算的方法,通过该这种方法可以达到预期的计算结果。目前,被广泛认可的算法专业定义是:算法是模型分析的一组可行的,确定的,有穷的规则。

排序算法


4 int temp = *xp;

5 *xp = *yp;

6 *yp = temp;

7 }void selectionSort(int arr[], int n) {

int i, j, min_idx;

// 遍历所有数组元素

for (i = 0; i < n-1; i++) {

// 找到未排序部分中的最小元素

min_idx = i;

for (j = i+1; j < n; j++)

if (arr[j] < arr[min_idx])

min_idx = j;

// 将找到的最小元素与arr[i]交换

swap(&arr[min_idx], &arr[i]);

}

}

思路理解: 先假定数组第一位为最小值,制定两个函数,一个函数用于交换元素(必须是交换,因为不是最小值的数值还要进行后续的排序),再制定一个函数寻找最小值(两个for),第一个for遍历2所有元素,第二个for遍历第一个for当前遍历元素的后续元素,判断后续数值是否小于当前数值,如果是则将他的下标保存,传给另一个函数进行交换

算法规则

  • 1.算法函数种的常熟可以忽略
  • 2.算法函数种最高次幂的常熟因子可以忽略
  • 3.算法函数中最高次幂越小,算法效率越高。

大O算法表示

大O()体现算法时间复杂度

  • 1.用常数1取代运行时间中的所有加法常数
  • 2.在修改后的运行次数中,只保留高阶项
  • 3.如果最高阶存在,且常数因子不为1,则去除这个项相乘的常数
  • 算法一:3次
  • 算法二:n+3次
  • 算法三:n^2+2次 大O表示法 O(1)、O(n)、O(n^2)

malloc申请的内存,在函数调用结束后不会自动释放,需使用free来手动释放,该内存是建立在堆上的,而普通函数变量建立在栈上。

image.png

assert关键字

assert是一个非常有用的关键字,能够帮助程序员在编写程序时更快地发现问题并进行修复,
从而提高程序的安全性和可靠性。

语法: assert(condition); condition:要判断的条件 说明: a、如果condition判断结果为false,则程序就会停止执行。 b、当程序停止执行时,assert就会断言失败,并输出一条程序的错误信息到控制台或日志文件中。 c、assert关键字常常用于调试程序阶段,可以用来帮助程序员在程序出现异常或错误时,快速定位问题所在,以便于检查和就修改完善。 d、而在程序正常部署后,assert一般就会被自动关闭。

char *a="12"; 这种方式申明的变量,12保存在只读区域内的,而a存放在栈里面,如果修改它,会报错。
printf("测试%c",*a);  
printf("测试%c",*a+1);  
printf("测试%s",a);

数据库

非关系数据库和关系数据库举例: MySQL中,SQL本身不区分大小写(Liunx区分)

utf8mb4、utf-8、utf8的区别

CHARACTER SET utf8mb4;(utf-8,utf8) MySQL在5.5.3之后增加了这个utf8mb4的编码,mb4就是most bytes 4的意思,专门用来兼容四字节的unicode。好在utf8mb4是utf8的超集,除了将编码改为utf8mb4外不需要做其他转换。当然,为了节省空间,一般情况下使用utf8也就够了。可以简单的理解utf8mb4是目前最大的一个字符编码,支持任意文字。

两种删除手法的区别

delet删除:逐条删除 Truncate删除:直接全表删除,然后新建一个表,旧表的元素,新表都有。 DISTINCT:过滤重复内容,譬如查询全部性别。

聚合函数

SUM、MIN、AVG、COUNT、MAX

SELECT MIN(age) FROM 表名;

分页

LIMIT:分页,用于限制从查询结果 OFFSET:检索,它从0开始计算。

约束

SQL约束是用于保证数据完整性的机制,防止非法和不合理的数据被插入到表中。

例如:主键约束,自增约束(默认从1开始)、唯一约束(可以为空,主键约束不能为空)

记住复合主键!!!

线性表

顺序表

顺序存储结构以连续的存储单元 顺序存放线性表的元素,可以直接访问元素,查找和遍历较快。

顺序表具有随机存取的优点,可以直接按照下标访问任意位置的元素。但是因为删除或插入操作时需要移动大量元素,所以其插入和删除 操作效率较低。

单链表(火车)

  1. 线性表的链式存储结构,即链表。
  2. 每个节点都有一个挂钩,代表指向下一个车厢的指针。
  3. 头节点为车头,要访问某个节点,必须从头节点一辆一辆往后找。
  4. 需删除某节点,必需将它的前一个结点指向它之后的节点
  5. 单链表的元素有序,每个节点只能方位它后一个节点,自由度和效率较低。

单链表和顺序表的优缺点

单链表优点:

删除和插入节点时,只需要改变指针的方向,时间复杂度为O(1) 单链表可以动态分配内存空间,不受固定大小的限制。 单链表可以轻松地实现链表的翻转、查找中间节点等操作。

缺点:

  1. 单链表不支持随机访问,查找节点需要遍历整个链表,时间复杂度为O(n)。
  2. 单链表的每个节点需要额外的指针域来存储下一个节点的地址,占用额外的存储空间。
  3. 单链表在访问某个节点之前,必须先访问它的前一个节点,不便于一些操作的实现。

顺序存储结构的优点

  1. 顺序存储结构支持随机访问,可以通过下标直接访问某个元素,时间复杂度为O(1)。
  2. 顺序存储结构不需要额外的指针域存储节点关系,占用空间较小。
  3. 顺序存储结构对于一些简单的线性表操作,如插入、删除、排序等,实现比较简单。

顺序存储结构的缺点

  1. 在插入和删除元素时,需要移动其他元素,时间复杂度为O(n)。
  2. 顺序存储结构的大小是固定的,不能动态调整,不适用于动态变化的应用。
  3. 当需要改变存储空间时,需要进行大量的数据搬移,效率较低。、

liunx(docker指令)

docker images 查看系统已有docker镜像 docker pull 镜像 拉去制定的docker镜像

保存退出文件:esc +(shift+:)+输入wq+回车

liunx编译c文件: blog.csdn.net/haovin/arti…

num = 23
num += 221
这里的+=表示追加,和c里面的赋值不一样

面试涉及

c-V2X ISO汽车安全等级

课下了解

博士工程技术、CAN\高速以太网