收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
思考3:const修饰的常量与宏定义常量的区别
答案:
const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。
而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错
误。
试题4: 关键字volatile有什么含意?并给出三个不同的例子。
答案:
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假
设这个变量的值了。
精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是
使用保存在寄存器里的备份。
1) 并行设备的硬件寄存器(如:状态寄存器)
2) 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3) 多线程应用中被几个任务共享的变量保护,可以预防意外的变动,能提高程序的健壮性。
试题5:
1)一个参数既可以是const还可以是volatile吗?解释为什么。
2); 一个指针可以是volatile 吗?解释为什么。
3); 下面的函数有什么错误:
int square(volatile int \*ptr)
{
return \*ptr \* \*ptr;
}
答案:
1)是的。一个例子是只读的状态寄存器。它是volatile因为它可能被意想不到地改变。它是
const因为程序不应该试图去修改它。
2)是的。尽管这并不很常见。一个例子是当一个中服务子程序修该一个指向一个buffer的指
针时。
3) 这段代码有点变态。这段代码的目的是用来返指针\*ptr指向值的平方,但是,由于\*ptr
指向一个volatile型参数,编译器将产生类似下面的代码:
int square(volatile int \*ptr)
{
int a,b;
a = \*ptr;
b = \*ptr;
return a \* b;
}
由于\*ptr的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是
你所期望的平方值!正确的代码如下:
long square(volatile int \*ptr)
{
int a;
a = \*ptr;
return a \* a;
}
试题6. 关键字static的作用是什么?
答案:
在C语言中,关键字static有三个明显的作用:
1)在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2) 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被
模块外其它函数访问。它是一个本地的全局变量。
3) 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被
限制在声明它的模块的本地范围内使用。
总结:
static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他
文件单元中被引用;
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据
上一次结果值;
static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用
中维持一份拷贝.
试题7. 如何引用一个已经定义过的全局变量?
答案:
可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头
文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern
方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错。
试题8:在C++ 程序中调用被 C编译器编译后的函数,为什么要加 extern “C”?
例子:
#ifndef \_\_INCvxWorksh
#define \_\_INCvxWorksh
#ifdef \_\_cplusplus
extern "C" {
#endif
/\*...\*/
#ifdef \_\_cplusplus
}
#endif
#endif /\* \_\_INCvxWorksh \*/
答案:
由于c语言是没有重载函数的概念的,所以c编译器编译的程序里,所有函数只有函数名对应
的入口。而由于c++语言有重载函数的概念,如果只有函数名对应的入口,则会出现混淆,所
以c++编译器编译的程序,应该是函数名+参数类型列表对应到入口。
假设某个函数的原型为: void foo(int x, int y);
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类
的名字。
C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
思考9:如何判断一段程序是由C 编译程序还是由C++编译程序编译的?
(1)如果是要你的代码在编译时发现编译器类型,就判断_cplusplus或_STDC_宏。
#ifdef __cplusplus
cout<<"c++";
#else
cout<<"c";
#endif
如果要判断已经编译的代码的编译类型,就用nm查一下输出函数符号是否和函数名相同。
(2)注意,因为mian函数是整个程序的入口,所以mian是不能有重载的,所以,如果一个程序只
有main函数,是无法确认是c还是c++编译器
编译的可以通过nm来查看函数名入口
如一个函数
int foo(int i, float j)
c编译的程序通过nm查看
foo 0x567xxxxxx (地址)
c++编译程序,通过nm查看
foo(int, float) 0x567xxxxxx
另外,如果要在c++编译器里使用通过c编译的目标文件,必须通知c++编译器, extern "c"
foo;
2.预处理命令(宏)
试题10:什么是预编译,何时需要预编译?
答:
就是指程序执行前的一些预处理工作,主要指#表示的.
1)、总是使用不经常改动的大型代码体。
2)、程序由多个模块组成,所有模块都使用一组标准的包含文件和相同的编译选项。在这种情况
下,可以将所有包含文件预编译为一个预编译头。
试题11.说出以下 预处理器标识的目的意义
指令 用途
#
#include
#define
#undef
#if
#ifdef
#ifndef
#elif
#endif
#error
答案:
指令 用途
# 空指令,无任何效果
#include 包含一个源代码文件
#define 定义宏
#undef 取消已定义的宏
#if 如果给定条件为真,则编译下面代码
#ifdef 如果宏已经定义,则编译下面代码
#ifndef 如果宏没有定义,则编译下面代码
#elif 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码
#endif 结束一个#if……#else条件编译块
#error 停止编译并显示错误信息 编译时检测错误,
使程序员更好的掌握代码-
试题12:头文件中的 ifndef/define/endif 干什么用?
答:防止该头文件被重复引用。这样, 可以减少整个编译过程中打开这个文件的次数.
等同于:
#pragma once
试题13:#include “filename.h”和#include <filename.h>的区别?
答案:
对于#include <filename.h>编译器从标准库开始搜索filename.h
对于#include “filename.h”编译器从用户工作路径开始搜索filename.h
试题14: 用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
答案: #define SECONDS_PER_YEAR (60 \* 60 \* 24 \* 365UL)
剖析:
1) #define 语法的基本知识(例如:不能以分号结束,括号的使用,等等)
2)懂得预处理器将为你计算常数表达式的值,因此,直接写出你是如何计算一年中有多少秒
而不是计算出实际的值,是更清晰而没有代价的。
3) 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这
个常数是的长整型数。
4) 再思考进一步用到UL(表示无符号长整型)
试题15. 写一个"标准"宏MIN ,这个宏输入两个参数并返回较小的一个。
答案:
#define MIN(A,B) ((A) <= (B) ? (A) : (B))
剖析:
1) 标识#define在宏中应用的基本知识。这是很重要的。因为在 嵌入(inline)操作符 变
为标准C的一部分之前,
宏是方便产生嵌入代码的唯一方法,对于嵌入式系统来说,为了能达到要求的性能,嵌入式
代码经常是必须的方法。
2)三重条件操作符的知识。这个操作符存在C语言中的原因是它使得编译器能产生比
if-then-else更优化的代码,了解这个用法是很重要的。
3) 懂得在宏中小心地把参数用括号括起来
注:谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答是错误
的:
#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )
#define MIN(A,B) ((A) <= (B) ? (A) : (B)); //这个解答在宏定义的后面加“;”
试题16: #define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(\*p++, b)的有什么后果?
答案:
((\*p++) <= (b) ? (\*p++) : (\*p++))
这个表达式会产生副作用,指针p会作多次++自增操作。
试题17:宏定义的多语句错误,分析以宏定义
#define D(a,b) a+b;\
a++;
分析:
应用时:if(XXX)
D(a.b);
else
解决办法 用do{
} while(0)
#define D(a,b) do{a+b;\
a++;}while(0)
思考while(0)后没有分号
试题18:分析一下两个定义,哪种方法更好呢?(如果有的话)为什么?
#define dPS struct s \*
typedef struct s \* tPS;
分析:
以上两种情况的意图都是要定义dPS 和 tPS 作为一个指向结构s指针。
答案是:typedef更好。思考下面的例子:
dPS p1,p2;
tPS p3,p4;
第一个扩展为
struct s \* p1, p2;
上面的代码定义p1为一个指向结构的指,p2为一个实际的结构,这也许不是你想要的。第二
个例子正确地定义了p3 和p4 两个指针。
假如定义函数指针:
typedef void (\*fun)(void);
#define FUN(x) void(\*x)(void)
运算符·表达式·数据类型·优先级
试题19.分析以下代码,说出输出结果
#define swap(a,b) a=a+b;b=a-b;a=a-b;
void main()
{
int x=5, y=10;
swap (x,y);
![img](https://img-blog.csdnimg.cn/img_convert/21e64eed2c18092ea157d9bfe0fb543d.png)
![img](https://img-blog.csdnimg.cn/img_convert/f6baeb2c413b1cea04d6074138883fb1.png)
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**
存中...(img-IxgDsXDx-1715764855965)]
[外链图片转存中...(img-O7x53QpK-1715764855965)]
**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**
**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**
**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**
**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**