嵌入式c语言常见笔试题或面试题

  • 格式:pdf
  • 大小:190.66 KB
  • 文档页数:7

下载文档原格式

  / 7
  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
9、动态内存分配
1)、在操作系统中,内存分配主要以下面三种方式存在: (1)静态存储区域分配。内存在程序编译的时候或者在操作系统初始化的时候就已经
分配好,这块内存在程序的整个运行期间都存在,而且其大小不会改变,也不会被重新分配。 例如全局变量,static 变量等。
(2)栈上的内存分配。栈是系统数据结构,对于进程/线程是唯一的,它的分配与释放 由操作系统来维护,不需要开发者来管理。在执行函数时,函数内局部变量的存储单元都可 以在栈上创建,函数执行结束时,这些存储单元会被自动释放。栈内存分配运算内置于处理 器的指令集中,效率很高,不同的操作系统对栈都有一定的限制。
long a=80000; printf("%d",a); 运行结果为 14464,因为 int 型允许的最大值为 32767,80000 超出此值,故结果取以 32768 为模的余数,即进行如下取余运算: (80000-32768)-32768=14464; 输出的数据类型与输出格式不符时常常发生错误,如: int d=9; printf("%f",d); 或 float c=3.2; printf("%d",c); 将产生错误的结果。
long square(volatile int *ptr) { int a; a = *ptr; return a * a; }
4、判断 cpu 大小端
int checkCPU( ) {
{ union w { int a; char b; } c; c.a = 1; return(c.b ==1);
在程序中将数据用 printf 函数以指定格式输出时,当要输出的盐据类型与输出格式不符 时,便自动进行类型转换,如一个 long 型数据用整型格式(%d)输出时,则相当于将 long 型 转换成整型(int)数据输出;一个字符(char)型数据用整型格式输出时,相当于将 char 型转 换 成 int 型输出。 注意:较长型数据转换成短型数据输出时,其值不能超出短型数据允许的值范围,否则 转 换时将出错。如:
由于频繁的进行动态内存分配会造成内存碎片的产生,影响系统性能,所以在不同的系 统中,对于动态内存管理,开发了许多不同的算法,不同的操作系统,有不同的实现方式, 为了程序的可移植性,一般在开发语言的库中都提供了统一接口。对于 C 语言,在标准 C 库和 Glib 中,都实现了以 malloc/free 为接口的动态内存分配功能。也就是说,malloc/free 库函索包装了不同操作系统对动态内存管理的不同实现,为开发者提供了一个统一的开发环 境。对于我们前面提到的一些嵌入式操作系统,因为实时系统的特殊要求(实时性要求和开 发者订制嵌入式系统),可能没有提供相应的接口。
} }
联合体 union 的存放顺序是所有成员都从低地址开始存放; 所谓的大端模式,是指数据的低位(就是权值较小的后面那几位)保存在内存的高地址 中,而数据的高位,保存在内存的低地址中;
所谓的小端模式,是指数据的低位保存在内存的低地址中,而数 据的高位保存在 内存的高地址中;
5、置位和取反:
#define BIT3 (0x1 < <3) static int a; void set_bit3(void) { a |= BIT3; } void clear_bit3(void) { a &= ~BIT3; }
(3) 堆上的内存分配,亦称动态内存分配。程序在运行的期间用 malloc 申请的内存, 这部分内存由程序员自己负责管理,其生存期由开发者决定:在何时分配,分配多少,并在 何时用 free 来释放该内存。这是唯一可以由开发者参与管理的内存。使用的好坏直接决定 系统的性能和稳定。 2)、 动态内存分配的实现
型。也就是说:
int_ptr1 a, b; //相当于 int * a, b; 只是简单的宏替换
int_ptr2 a,b //都指向 int 型
2)、#ifndef A #define A {...} #endif
避免重复引用头文件
3)do {...}while(0)
如果需要定义这样一个宏:
#define DOSOMETHING()\
3)、Linux 下动态内存分配的实现: 在 Linux 下,glibc 的 malloc 提供了下面两种动态内存管理的方法:堆内存分配和
mmap 的内存分配,此两种分配方法都是通过相应的 Linux 系统调用来进行动态内存管理 的。具体使用哪一种方式分配,根据 glibc 的实现,主要取决于所需分配内存的大小。一般 情况中,应用层面的内存从进程堆中分配,当进程堆大小不够时,可以通过系统调用 brk 来 改变堆的大小,但是在以下情况,一般由 mmap 系统调用来实现应用层面的内存分配:A、 应用需要分配大于 1M 的内存,B、在没有连续的内存空间能满足应用所需大小的内存时。
包含 long 与 unsigned long,32 位机器上都是 4 字节,所以均转换为 unsigned long. 包含 signed 与 unsigned int,signed 会转换为 unsigned。如果 int 恰好为负数,其结 果为对 unsigned 取值个数求模的结果。比如将-1 赋给 8 位的 unsigned char,呢么结果就 是 255(-1 对 256 取模后的值)。unsigned char 取值范围 0~255。 2)、赋值转换 进行赋值操作时,赋值运算符右边的数据类型必须转换成赋值号左边的类型,若右边的 数据类型的长度大于左边,则要进行截断或舍入操作。 下面用一实例说明:
if(a>0)
{
foo1();
foo2();
}
4)、#define
UL
UL 是标记该宏长整型 十进制 数据,而不是字符,也不是 int 型数据;C 语言中 默认宏中的数字是整型数据。
#define LENGTH (20UL*1024*1024) #define SECONDS_PER_YEAR 60*60*24*365UL 你要输出他的话就要这样:
如果仅仅使用{}将 foo1 和 foo2 包裹起来,展开后如下:
if(a>0)
ຫໍສະໝຸດ Baidu
{
foo1();
foo2();
}; //这是错的
所以,很多人才采用了 do{...}while(0);
#define DOSOMETHING() \
do{ \
foo1();\
foo2();\
}while(0)\
展开后得到:
6、中断(Interrupts)
判断如下代码: __interrupt double compute_area (double radius) { double area = PI * radius * radius; printf(" Area = %f", area);
return area; } 1)不能返回值; 2)不能传递参数; 3)在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处 的寄存器入栈,有些处理器/编译器就是不允许在 ISR 中做浮点运算。此外,ISR 应该是短 而有效率的,在 ISR 中做浮点运算是不明智的; 4)printf()经常有重入和性能上的问题;
printf(“LENGTH is %ld/n”,LENGTH); unsigned long int a = SECONDS_PER_YEAR; printf("a = %ld/n",a); 注意:不能将 #define SECONDS_PER_YEAR 60*60*24*365UL 写成 #define SECONDS_PER_YEAR (60*60*24*365)UL
1、#define:
1)、#typedef 和#define 的区别
typedef int * int_ptr1;
#define int_ptr2 int *
作用都是用 int_ptr 代表 int * ,但是二者不同,正如前面所说 ,#define 在预处理 时进
行简单的替换,而 typedef 不是简单替换 ,而是采用如同定义变量的方法那样来声明一种类
7、处理器字长
评价下面的代码片断: unsigned int zero = 0; unsigned int compzero = 0xFFFF; 对于一个 int 型不是 16 位的处理器为说,上面的代码是不正确的。应编写如下: unsigned int compzero = ~0;
8、C 语言中默认的隐式类型转换
2、#error 的作用
当预处理器预处理到#error 命令时将停止编译并输出用户自定义的错误消息。其目的就 是保证程序是按照你所设想的那样进行编译的。
如 #error Sorry,an error has occurred! 当程序比较大时,往往有些宏定义是在外部指定的(如 makefile),或是在系统头文件 中指定的,当你不太确定当前是否定义了 XXX 时,就可以改成如下这样进行编译: #ifdef XXX ... #error "XXX has been defined" #else #endif
1)、一个参数既可以是 const 还可以是 volatile,如只读状态寄存器 2)、Volatile 指针:一个例子是当一个中服务子程序修改一个指向一个 buffer 的指针时。 3)、int square(volatile int *ptr)
{ int a,b; a = *ptr; b = *ptr; return a * b; } 由于*ptr 的值可能被意想不到地该变,因此 a 和 b 可能是不同的。结果,这段代码 可能返不 是你所期望的平方值!正确的代码如下:
foo1();\
foo2();
这个宏的本意是,当调用 DOSOMETHING()时,函数 foo1()和 foo2()都会被调用。但是
如果你在调用的时候这么写:
if(a>0)
DOSOMETHING();
因为宏在预处理的时候会直接被展开,你实际上写的代码是这个样子的:
if(a>0)
foo1();
foo2();
(3)(ch/i) 和(f*d-i)进行加运算,由于 f*d-i 为 double 型,故 ch/i→double 型,ch/i+(f*d-i)→ double 型。 (4)由于 result 为 int 型,故 ch/i+(f*d-i)→double→int,即进行截断与舍入,最后取值为整型。 3)、输出转换
1)、算术转换 进行算术运算(加、减、乘、除、取余以及符号运算)时,不同类型数招必须转换成同
一类型的数据才能运算,算术转换原则为: 整型提升:对于所有比 int 小的类型,包括 char, signed char, unsigned char, short, unsigned short,首先会提升为 int 类型。 在进行运算时, 以表达式中最长类型为主,将其他类型位据均转换成该类型,如: (1)若运算数中有 double 型或 float 型,则其他类型数据均转换成 double 类型进行运算。 (2)若运算数中最长的类型为 long 型.则其他类型数均转换成 long 型数。 (3)若运算数中最长类型为 int 型,则 char 型也转换成 int 型进行运算。算术转换是在运算过 程中自动完成的。 特别注意的是有符号和无符号之间的转换:有符号------>>>无符号
3、Volatile
防止被编译器优化,在用到这个变量时必须每次都小心地重新读 取这个变量的值,而不是使用保存在寄存器里的备份。 1). 并行设备的硬件寄存器(如:状态寄存器) 2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables) 3). 多线程应用中被几个任务共享的变量 用法:
char ch; int i,result; float f; double d; result=ch/i+(f*d-i); (1)首先计算 ch/i,ch → int 型,ch/i → int 型。 (2)接着计算 f*d-i,由于最长型为 double 型,故 f→double 型,i→double 型,f*d-i→double 型。