嵌入式C语言注意点

预处理器(Preprocessor

1 . 
预处理指令#define 明一,用以表明1年中有多少秒(忽略问题
         #define SECONDS_PER_YEAR  (60 * 60 * 24 * 365)UL
我在
想看到几件事情:
1) #define 
法的基本知(例如:不能以分号结束,括的使用,等等)
2)
预处理器将为你计算常式的,因此,直接是如何算一年中有多少秒而不是算出实际,是更晰而有代价的。
3) 
这个使一16位机的整型溢出-因此要用到整型符L,诉编译这个是的整型
4) 
如果
的表式中用到UL(表示无符号长整型),那么你有了一好的起点。住,第一印象很重要。

2 . 
""MIN 这个两个参数并返回小的一
        #define MIN(A,B) 
((A <= (B) ? (A) : (B)) 
这个测试下面的目的而的:
1) 
标识#define在宏中用的基本知是很重要的。因  嵌入(inline)操作符 变为标C的一部分之前,宏是方便生嵌入代的唯一方法,于嵌入式系统来说了能到要求的性能,嵌入代码经常是必的方法。
2)
三重
件操作符的知这个操作符存在C言中的原因是使得编译器能生比if-then-else化的代,了解这个用法是很重要的。
3) 
得在宏中小心地把参数用括括起
4) 
我也用这个问题开讨论宏的副作用,例如:当你写下面的代码时会发生什事?
        least = MIN(*p++, b);

3. 
预处理器标识#error的目的是什
如果
不知道答案,考文1这问题对区分一正常的伙计和一个书呆子是很有用的。只有呆子才会读C本的附去找出象这种问题的答案。然如果不是在找一个书呆子,那么应试者最好希望自己不要知道答案。


死循
Infinite loops

4. 
嵌入式系
常要用到无限循你怎么样C编写死循呢?
这个问题用几方案。我首的方案是:

while(1)
{

}

一些程序
更喜如下方案:

for(;;)
{

}

这个实现方式为难,因为这个语有确切表到底怎么回事。如果一个应试这个方案,我这个去探究他们这样做的基本原理。如果他的基本答案是:"我被这样做,但从没有想到过为"这会给我留下一坏印象。

第三方案是用 goto
Loop:
...
goto Loop;
应试者如出上面的方案,这说明或者他是一个汇编语言程序是好事)或者他是一入新域的BASIC/FORTRAN程序


明(Data declarations 

5. 
a出下面的定
a) 
整型An integer 
b)
指向整型的指 A pointer to an integer 
c)
指向指的的指指向的指是指向一整型 A pointer to a pointer to an intege
d)
10整型数组 An array of 10 integers 
e) 
10数组是指向一整型的。(An array of 10 pointers to integers 
f) 
指向有10整型数数组的指 A pointer to an array of 10 integers 
g) 
指向函的指有一整型参数并返回一整型A pointer to a function that takes an integer as an argument and returns an integer 
h) 
10数组指向一有一整型参数并返回一整型 An array of ten pointers to functions that take an integer argument and return an integer 

答案是: 
a) int a; // An integer 
b) int *a; // A pointer to an integer 
c) int **a; // A pointer to a pointer to an integer 
d) int a[10]; // An array of 10 integers 
e) int *a[10]; // An array of 10 pointers to integers 
f) int (*a)[10]; // A pointer to an array of 10 integers 
g) int (*a)(int); // A pointer to a function a that takes an integer argument and returns an integer 
h) int (*a[10])(int); // An array of 10 pointers to functions that take an integer argument and return an integer 

们经声称这里有几个问题是那要翻一下才能回答的问题,我同意这种说法。写这篇文章了确定法的正确性,我的确了一下。但是我被面候,我期望被这个问题(或者相近的问题)。因在被面时间里,我确定我知道这个问题的答案。应试者如果不知道所有的答案(或至少大部分答案),那也就为这次面做准,如果为这次面做准,那他又能出准呢?

Static 

6. 
关键static的作用是什
这个简单问题很少有人能回答完全。在C言中,关键static有三的作用:
1)
在函
体,一为静态量在一函程中持其
2) 
在模
块内(但在函体外),一为静态量可以被模块内所用函数访问,但不能被模外其数访问是一本地的全局量。
3) 
在模
块内,一为静态的函只可被一模块内的其数调用。那就是,这个被限制在的模的本地范围内使用。

大多
数应试者能正确回答第一部分,一部分能正确回答第二部分,同是很少的人能得第三部分。是一个应试者的重的缺点,因然不得本地化据和代的好和重要性。


Const 

7
关键const有什含意?
我只要一听到被面
"const意味着常",我就知道我正在和一个业余者打交道。去年Dan Saks在他的文章里完全括了const的所有用法,因此ESP(者:Embedded Systems Programming)的每一位应该非常熟悉const能做什和不能做什.如果你从没到那篇文章,只要能const意味着""就可以了。这个答案不是完全的答案,但我接受正确的答案。(如果想知道更详细的答案,仔细读一下Saks的文章。)
如果
应试者能正确回答这个问题,我将问他一附加的问题
下面的
明都是什意思?

const int a;
int const a;
const int *a;
int * const a;
int const * a const;

 

/******/
两个的作用是一a是一常整型。第三意味着a是一指向常整型的指(也就是,整型是不可修改的,但指可以)。第四意思a是一指向整型的常指(也就是,指指向的整型是可以修改的,但指是不可修改的)。最后一意味着a是一指向常整型的常指(也就是,指指向的整型是不可修改的,同也是不可修改的)。如果应试者能正确回答问题,那他就我留下了一好印象。顺带提一句,也许你可能会问,即使不用关键 const,也是能很容易出功能正确的程序,那么还要如此看重关键const呢?我也如下的几下理由:
1) 
关键const的作用是为给读你的人传达非常有用的信息,实际上,明一个参数为常量是了告了用户这个参数用目的。如果曾花很多时间清理其人留下的圾,很快学会谢这点多余的信息。(然,得用const的程序很少留下的让别来清理的。)
2) 
过给优化器一些附加的信息,使用关键const生更紧凑的代
3) 
合理地使用
关键const可以使编译器很自然地保那些不希望被改参数,防止其被无意的代修改。而言之,这样可以bug的出


Volatile 

8. 
关键volatile有什含意?并给出三不同的例子。
义为volatile量是说这变量可能被意想不到地改这样编译器就不去假设这个变量的了。精确地就是,化器在用到这个变每次都小心地重新这个变量的,而不是使用保存在寄存器里的备份。下面是volatile量的几例子:
1) 
设备的硬件寄存器(如:状态寄存器)
2) 
子程序中会访问到的非自动变(Non-automatic variables)
3) 
线用中被几共享的

回答不出
这个问题的人是不被雇的。我认为这C程序和嵌入式系程序的最基本的问题嵌入式的家伙们经常同硬件、中RTOS等等打交道,所有些都要求用到volatile量。不volatile将会带来
被面者正确地回答了问题怀疑是否这样),我稍微深究一下,看一下是不是直正volatile完全的重要性。
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
可能被意想不到地该变,因此ab可能是不同的。果,段代可能返不是所期望的平方!正确的代如下:

long square(volatile int *ptr) 
{
    int a;
    a = *ptr;
    return a * a;
}

位操作(Bit manipulation 

9. 
嵌入式系
统总是要用户对变量或寄存器行位操作。定一整型a写两段代,第一个设abit 3,第二个清bit 3。在以上两个操作中,要保持其位不
对这个问题有三基本的反
1)
不知道如何下手。被面者从没任何嵌入式系的工作。
2) 
bit fieldsBit fields是被
C言死角的西,证你的代在不同编译器之是不可移植的,同也保了的的代是不可重用的。我最近不幸看到 Infineon较复杂的通信芯片驱动程序,用到了bit fields因此完全我无用,因我的编译器用其的方式来实现bit fields的。道德:永不要非嵌入式的家实际硬件的

 

3)  #defines  bit masks 操作。是一高可移植性的方法,是应该被用到的方法。最佳的解方案如下:

#define BIT3 (0x1 << 3)
static int a;

void set_bit3(void) 
{
    a |= BIT3;
}
void clear_bit3(void) 
{
    a &= ~BIT3;
}

一些人喜
欢为设置和而定一些明常也是可以接受的。我希望看到几要点:明常|=&=~操作。


访问固定的存位置(Accessing fixed memory locations 

10. 
嵌入式系
统经常具有要求程序访问某特定的存位置的特点。在某工程中,要求置一绝对地址0x67a9的整型量的值为0xaa66编译器是一个纯粹的ANSI编译器。去完成一任
问题测试你是否知道访问绝对地址把一整型强制转换typecast一指是合法的。问题实现方式格不同而不同。典型的似代如下:
    int *ptr;
    ptr = (int *)0x67a9;
    *ptr = 0xaa55;

 A more obscure approach is: 
个较的方法是:

    *(int * const)(0x67a9) = 0xaa55;

即使
的品味更接近第二方案,但我建议你在面试时使用第一方案。

Interrupts 

11. 
是嵌入式系中重要的成部分,这导致了很多编译开发商提供一种扩让标C支持中。具代表事是,生了一新的关键 __interrupt。下面的代就使用了__interrupt关键字去定了一子程序(ISR)请评论一下段代的。

__interrupt double compute_area (double radius) 
{
    double area = PI * radius * radius;
    printf("/nArea = %f", area);
    return area;
}

这个有太多的错误了,以至人不知起了:
1)ISR 
不能返回一
个值。如果懂这个,那么你被雇用的。
2) ISR 
不能
传递参数。如果你没有看到一点,被雇用的机等同第一
3) 
多的理器/编译器中,浮点一般都是不可重入的。有些理器/编译器需要让额处的寄存器入,有些理器/编译器就是不允ISR中做浮点算。此外,ISR应该是短而有效率的,在ISR中做浮点算是不明智的。
4) 
第三点一脉相承,printf()常有重入和性能上的问题。如果你丢掉了第三和第四点,我不为难你的。不用,如果能得到后点,那么你的被雇用前景越越光明了。


例子(Code examples

12 . 
下面的代
码输出是什

void foo(void)
{
    unsigned int a = 6;
    int b = -20;
    (a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否C言中的整动转换,我发现有些开发西。不管如何,无符整型问题的答案是出是 ">6"。原因是式中存在有符号类型和无符号类所有的操作都自动转换为无符号类型。因此-20成了一非常大的正整,所以算出的果大于6一点应当频繁用到无符号数型的嵌入式系统来说是丰常重要的。如果这个问题也就到了得不到这份工作的边缘

13. 
价下面的代

unsigned int zero = 0;
unsigned int compzero = 0xFFFF; 
/*1's complement of zero */

 



于一int型不是16位的理器为说,上面的代是不正确的。应编写如下:

unsigned int compzero = ~0;

问题真正能揭露出应试者是否理器字的重要性。在我的经验里,好的嵌入式程序非常准确地明白硬件的细节的局限,然而PC机程序往往把硬件作无法避免的烦恼
到了
这个阶段,应试者或者完全垂头丧气了或者信心满满志在必得。如果应试者不是很好,那么这个测试就在束了。但如果应试者做得不,那我就出下面的追加问题问题是比较难的,我想仅仅非常秀的应试者能做得不。提出问题,我希望更多看到应试问题的方法,而不是答案。不管如何,这个娱乐吧...

 

动态内存分配(Dynamic memory allocation 

14. 
管不像非嵌入式算机那,嵌入式系统还是有堆(heap)中动态分配存的程的。那嵌入式系中,动态分配存可能生的问题是什
里,我期望应试者能提到存碎片,碎片收集的问题量的持行时间等等。这个ESP志中被广泛地讨论过了(主要是 P.J. Plauger, 他的解释远远里能提到的任何解),所有回过头看一下让应试入一种虚假的安全感后,我拿出这么目:
下面的代
片段的出是什

char *ptr;
if ((ptr = (char *)malloc(0)) == NULL) 
    puts("Got a null pointer");
else
    puts("Got a valid pointer");

是一有趣的问题。最近在我的一同事不意把0值传给了函malloc,得到了一合法的指之后,我才想到这个问题就是上面的代出是"Got a valid pointer"。我用这个来开讨论这样的一问题,看看被面者是否想到例程这样做是正确。得到正确的答案固然重要,但解决问题的方法和定的基本原理更重要些。

Typedef 
 
15 Typedef 
C
言中繁用以明一存在的型的同字。也可以用预处理器做似的事。例如,思考一下下面的例子:

#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 两个





16 . C
言同意一些令人震结构,下面的结构是合法的,如果是做些什

int a = 5, b = 7, c;
c = a+++b;

这个问题将为这个测验的一愉快的尾。不管相不相信,上面的例子是完全合乎法的。问题编译器如何?水平不高的编译作者实际会争论这个问题,根据最理原编译应当可能所有合法的用法。因此,上面的代理成:

c = a++ + b;

因此
段代持行后a = 6, b = 7, c = 12
如果
知道答案,或猜出正确答案,做得好。如果不知道答案,我也不把这个当问题。我发现这个问题的最大好是一个关于代码编写风格,代的可性,代的可修改性的好的话题


 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值