抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

Kelecn

“唯爱与科技不可辜负!”

简介: 嵌入式笔试相关题目、考点。

一、static关键词


1)、用于声明函数体内的变量为静态局部变量,存储在静态数据存储区,在函数被调用过程中维持其值保持不变。
2)、在文件内(函数体外)被声明为静态的变量,可以被文件内的所有函数访问,但不能被其他文件的函数访问,是一个本地的局部变量。
3)、在文件内,被声明为静态的函数只可被文件内的其他函数调用,但不能被其他文件的函数调用。

二、const关键词


常类型是指使用类型修饰符const说明的类型,常类型的变量或对象的值是不能被更新的。不管出现在任何上下文都是为这个目的而服务的。
注意:非const变量默认为extern。要使const变量能够在其他文件中访问,必须在文件中显式地指定它为extern。

1
2
3
const int a;
//同上面的代码行是等价的,都表示一个常整形数。
int const a;
1
2
3
4
5
6
7
8
9
10
11
12
13
/* const具有"左结合"性,即const修饰*,那么,不难理解,该句表示一个指向整数的常指针,
a指向的整数可以修改,但指针a不能修改。
*/
int *const a;

/* 同理,下面的这两行,根据"左结合"性,const修饰的是(*a),也即是一个整数,
所以,这两句表示指针指向一个常整数。
*/
const int *a;
int const *a;

//根据"左结合"性质,第一个const修饰(*),第二个const修饰(a),因此,这句话表示一个指向常整数的常指针。
int const *a const;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
const char *p; //*p是const,p可变
const (char *) p;//p是const,*p可变
char* const p; //p是const,*p可变
const char* const p; //p和*p都是const
char const * p;// *p是const,p可变
(char*) const p;//p是const,*p可变
char* const p;// p是const,*p可变
char const* const p;// p和*p都是const
//例如
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
//const修饰一个变量为只读
const int a = 10;
//a = 100; //err

//指针变量, 指针指向的内存, 2个不同概念
char buf[] = "aklgjdlsgjlkds";

//从左往右看,跳过类型,看修饰哪个字符
//如果是*, 说明指针指向的内存不能改变
//如果是指针变量,说明指针的指向不能改变,指针的值不能修改
const char *p = buf;
// 等价于上面 char const *p1 = buf;
//p[1] = '2'; //err
p = "agdlsjaglkdsajgl"; //ok

char * const p2 = buf;
p2[1] = '3';
//p2 = "salkjgldsjaglk"; //err

//p3为只读,指向不能变,指向的内存也不能变
const char * const p3 = buf;

return 0;
}

三、数组越界问题


1
2
3
4
5
6
void test()
{
char string[10];//应该是char string[11];//字符串数组最后一位是\0
char*str="0123456789";
strcpy(string,str);//err
}

四、C语言中宏定义的使用


==预处理==命令可以改变程序设计环境,提高编程效率,它们并不是 C 语言本身的组成部分,不能直接对 它们进行编译,必须在对程序进行编译之前,先对程序中这些特殊的命令进行“预处理” 。经过预处理后,程序就不再包括预处理命令了,最后再由编译程序对==预处理==之后的源程序进行==编译==处理,得到可供执行的目标代码。C 语言提供的预处理功能有三种,分别为==宏定义==、文件包含和条件编译。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 不带参数的宏定义
#define MAX 10
/*带参宏定义*/
#define M(y) y*y+3*y

/*宏调用*/
k=M(5);

//宏定义最大值
#include <iostream>

using namespace std;

#define max(a,b) (((a)>(b))?(a):(b))

int main(){
cout << max(1+1,2+2) << endl;
return 0;
}
//这个面试题主要考查面试者对宏定义的使用,宏定义可以实现类似于函数的功能,但是它终归不是函数,而宏定义中括弧中的“参数”也不是真的参数,在宏展开的时候对“参数”进行的是一对一的替换。
/*
#define MAX( x, y ) ( ((x) > (y)) ? (x) : (y) )//输入a++,a=a+2 调用两次
#define MIN( x, y ) ( ((x) < (y)) ? (x) : (y) )
#define MAX( a, b ) ({int _a = a;int _b = b;(_a) > (_b)? (_a) :(_b); })
*/
/*程序员对宏定义的使用要非常小心,特别要注意两个问题:
  (1)谨慎地将宏定义中的“参数”和整个宏用用括弧括起来。所以,严格地讲,下述解答:
#define MIN(A,B) (A) <= (B) ? (A) : (B)
#define MIN(A,B) (A <= B ? A : B )
  都应判0分;
  (2)防止宏的副作用。
  宏定义#define MIN(A,B) ((A) <= (B) ? (A) : (B))对MIN(*p++, b)的作用结果是:
((*p++) <= (b) ? (*p++) : (*p++))
  这个表达式会产生副作用,指针p会作三次++自增操作。
  除此之外,另一个应该判0分的解答是:
#define MIN(A,B) ((A) <= (B) ? (A) : (B)); 不能在后面加冒号。

参考文章:宏定义正确处理a++

五、递归算N!


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>
long fac(int n)
{
long f;
if(n<0)
printf("n<0,data error!");
else if(n==0,n==1)
f=1;
else f = fac(n-1)*n;
return (f);
}
int main()
{
long fac(int n);
int n,y;
printf("input an integer number:");
scanf("%d",&n);
y= fac(n);
printf("%d!=%ld\n",n,y);
return 0;
}

六、中断服务子程序(ISR)


中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展-让标准C支持中断。其代表事实是,产生了一个新的关键字 interrupt(51即如此)。下面的代码就使用了interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
__interrupt double compute_area (double radius) 
{

double area = PI * radius * radius;

printf("/nArea = %f", area);

return area;

}
/*
中断服务程序需要满足如下要求:

(1)不能返回值;

(2)不能向ISR传递参数; (嵌入式中的ISR指的是中断服务处理)

(3) ISR应该尽可能的短小精悍;

(4) printf(char * lpFormatString,…)函数会带来重入和性能问题,不能在ISR中采用。
这个函数有太多的错误了,以至让人不知从何说起了(前提是**非操作系统**下的中断服务函数):

**1)ISR 不能返回一个值(都应该为void类型)。如果你不懂这个,那么你不会被雇用的。**

**2)ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。**

3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额外的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是**短而有效率**的,在ISR中做浮点运算是不明智的。另外中断服务程序是运行在内核态的(linux),**内核通常是不支持浮点运算的**。
*/

七、位运算符


1
2
3
4
5
6
7
8
9
10
11
12
//5=>0101,7=>0111
1)& 按位与//5&7==>0101,也就是5

2)| 按位或//5|7==>0111,也就是7

3)^ 按位异或//5^7==>0010,也就是2

4)~ 按位取反

5)<< 左移

6)>> 右移

八、register 关键词


寄存器存在于CPU内部,运算速度非常快, 因为内存中的数据必须载入寄存器才能计算。如果直接定义一个变量为寄存器变量,则少了载入等过程自然会快。对于频繁使用的变量可以把它放在寄存器中来提速度。
1.寄存器变量可以用来优化加速c语言程序
2.声名只需在类型前多加register 即可,eg register int quick; (quick 就是一个整形的寄存器变量)
3.register只是一个建议型关键字,能不能声名成功还取决于编译器(建议型的关键字还有c++中的 inline),若不幸没有请求成功,则变量变成一个普通的自动变量。
4.是无法对一个register变量取地址的(因为寄存器变量多放在寄存器而非内存中,内存有地址,而寄存器是无地址的)

1
2
3
4
5
//全局变量最好不要占用寄存器,会影响程序的速度
register int num = 1000;//err
//只有局部自动变量和形参才可以定义为寄存器变量。因为寄存器变量属于动态存储方式,凡需要采用静态存储方式的量都不能定义为寄存器变量,包括:模块间全局变量、模块内全局变量、局部static变量。
//静态变量无法定义为寄存器变量,静态变量存在静态区
register static double res = 0.0;//err

九、volatile 关键词


volatile的作用是告知编译器,它修饰的变量随时都可能被改变,因此,编译后的程序每次在使用该变量的值时,都会从变量的地址中读取数据,而不是从寄存器中获取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/*volatile的本意是“易变的”
由于访问寄存器的速度要快过RAM,所以编译器一般都会作减少存取外部RAM的优化。比如:
*/
static int i=0;
int main(void)
{
...
while (1)
{
if (i) do_something();
}
}
/* Interrupt service routine. */
void ISR_2(void)
{
i=1;
}
/*程序的本意是希望ISR_2中断产生时,在main当中调用do_something函数,但是,由于编译器判断在main函数里面没有修改过i,因此可能只执行一次对从i到某寄存器的读操作,然后每次if判断都只使用这个寄存器里面的“i副本”,导致do_something永远也不会被调用。如果变量加上volatile修饰,则编译器保证对此变量的读写操作都不会被优化(肯定执行)。此例中i也应该如此说明。
*/

一般说来,volatile用在如下的几个地方:
1、中断服务程序中修改的供其它程序检测的变量需要加volatile;
2、多任务环境下各任务间共享的标志应该加volatile;
3、存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
另外,以上这几种情况经常还要同时考虑数据的完整性(相互关联的几个标志读了一半被打断了重写),在1中可以通过关中断来实现,2中可以禁止任务调度,3中则只能依靠硬件的良好设计了。

十、C 库函数 - memset()


C 库函数 void *memset(void *str, int c, size_t n) 复制字符 c(一个无符号字符)到参数 str 所指向的字符串的前 n 个字符。

1
2
3
4
5
void *memset(void *str, int c, size_t n)
//str -- 指向要填充的内存块。
//c -- 要被设置的值。该值以 int 形式传递,但是函数在填充内存块时是使用该值的无符号字符形式。
//n -- 要被设置为该值的字符数。
//该值返回一个指向存储区 str 的指针。

十一、printf函数从右到左压栈


注意:函数printf从右到左压栈,然后将先读取放到栈底最后读取的放在栈顶,处理时候是从栈顶开始的,所以我们看见的结果是,从右边开始处理的。

1
2
3
4
5
6
7
#include <stdio.h>

int main(){
int i=10;
int a=i;
printf("%d %d",(a+i),(a=2*a));
}//30,20

十二、排序算法


冒泡排序:基本思想:比较相邻的两个数,如果前者比后者大,则进行交换。每一轮排序结束,选出一个未排序中最大的数放到数组后面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<stdio.h>
//冒泡排序算法
void bubbleSort(int *arr, int n) {
for (int i = 0; i<n - 1; i++)
for (int j = 0; j < n - i - 1; j++)
{
//如果前面的数比后面大,进行交换
if (arr[j] > arr[j + 1]) {
int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp;
}
}
}
int main() {
int arr[] = { 10,6,5,2,3,8,7,4,9,1 };
int n = sizeof(arr) / sizeof(int);
bubbleSort(arr, n);
printf("排序后的数组为:\n");
for (int j = 0; j<n; j++)
printf("%d ", arr[j]);
printf("\n");
return 0;

参考文章:七大经典排序算法总结(C语言描述)

评论




博客内容遵循 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 协议

总访问量为 访客数为

粤ICP备2021157327号

载入天数...载入时分秒...