1,常量赋值问题
请问以下代码有什么问题
char* s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什么错?
"AAA" 是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。cosnt char* s="AAA";然后又因为是常量,所以对是s[0] 的赋值操作是不合法的。
2.指针数组,数组指针,函数指针,指针函数:
用变量a给出下面的定义
(1)一个有10个指针的数组,该指针是指向一个整型数
的;
int *a[10]; //[]”的优先级比“*”要高。a先与“[]”结合,构成一个数组的定义
(2) 一个指向有10个整型数数组的指针;
int (*a)[10];//在这里“()”的优先级比“[]”高,“*”号和a 构成一个指针的定义
(3)一个指向函数的指针,该函数有一个整型参数并返回一个整型数;
int (*a)(int);//在这里(*a)构成一个指针
(4) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数;
int (*a)[10](int);
3,union和struct的大小
以下定义和说明
typedef union {long i; int k[5]; char c;} DATE;
struct data { int cat; DATE cow; double dog;} too;
DATE max;
则语句printf("%d",sizeof(struct date)+sizeof(max)); 的
执行结果是:_52____
DATE是一个union, 变量公用空间. 里面最
大的变量类型是int[5], 占用20个字节. 所以它的大小是
20
data 是一个struct, 每个变量分开占用空间. 依次为int4 +
DATE20 + double8 = 32.
所以结果是20 + 32 = 52.
4.头文件中的ifndef/define/endif 干什么用?
防止该头文件被重复引用
5.#include <filename.h> 和#include “filename.h” 有什么区别?
对于#include <filename.h> ,编译器从标准库路径开始搜索filename.h ;
对于#include “filename.h” ,编译器从用户的工作路径开始搜索filename.h 。
6.const有什么用途?
可以定义const常量
const 可以修饰函数的参数、返回值,甚至函数的定义体。被const 修饰的东西都受到强制保护,可以预防意外的变动,能提高程序的健壮性。
7.static有什么用途?
限制变量的作用域(static全局变量);
设置变量的存储域(static局部变量)。
8.堆栈溢出一般是由什么原因导致的?
没有回收垃圾资源。
9.队列和栈有什么区别?
队列先进先出,栈后进先出
10.Heap与stack的差别
Heap是堆,stack是栈。
Stack的空间由操作系统自动分配/释放,Heap上的空间手动分配/释放。
Stack空间有限,Heap是很大的自由存储区
C 中的malloc 函数分配的内存空间即在堆上
程序在编译期对变量和函数分配内存都在栈上进行,且程序运行过程中函数调用时参数的传递也在栈上进行。
11.写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
#define Min(X, Y) ((X)>(Y)?(Y):(X))
12.宏定义和函数
1)宏缺乏类型检查,不如函数调用检查严格
2)宏展开可能会产生意想不到的副作用,函数一般不会
3)宏形式写的代码难以调试,难以打断点,不利于定位问题
4)宏如果调用很多,会造成代码空间的浪费,不如函数空间效率高
13.inline函数
如果编译器支持inline,可以采用inline函数,编译时不用展开,代码size小,同时可以加断点,易于定位问题
14.进程和线程的区别
进程:是并发执行的程序在执行过程中分配和管理资源的基本单位,是一个动态概念,竞争计算机系统资源的基本单位。
线程:是进程的一个执行单元,是进程内科调度实体。比进程更小的独立运行的基本单位。线程也被称为轻量级进程。
一个程序至少一个进程,一个进程至少一个线程
进程线程的区别:
- 地址空间:同一进程的线程共享本进程的地址空间,而进程之间则是独立的地址空间。
- 资源拥有:同一进程内的线程共享本进程的资源如内存、I/O、cpu等,但是进程之间的资源是独立的。
一个进程崩溃后,在保护模式下不会对其他进程产生影响,但是一个线程崩溃整个进程都死掉。所以多进程要比多线程健壮。
进程切换时,消耗的资源大,效率高。所以涉及到频繁的切换时,使用线程要好于进程。同样如果要求同时进行并且又要共享某些变量的并发操作,只能用线程不能用进程
- 执行过程:每个独立的进程程有一个程序运行的入口、顺序执行序列和程序入口。但是线程不能独立执行,必须依存在应用程序中,由应用程序提供多个线程执行控制。
- 线程是处理器调度的基本单位,但是进程不是。
- 两者均可并发执行。
15.进程和线程的优缺点
优缺点:
线程执行开销小,但是不利于资源的管理和保护。线程适合在SMP机器(双CPU系统)上运行。
进程执行开销大,但是能够很好的进行资源管理和保护。进程可以跨机器前移。
16.堆栈溢出一般是由什么原因导致的?
1)函数调用层次太深。函数递归调用时,系统要在栈中不断保存函数调用时的现场和产生的变量,如果递归调用太深,就会造成栈溢出,这时递归无法返回。再有,当函数调用层次过深时也可能导致栈无法容纳这些调用的返回地址而造成栈溢出。
2)动态申请空间使用之后没有释放。由于C语言中没有垃圾资源自动回收机制,因此,需要程序主动释放已经不再使用的动态地址空间。申请的动态空间使用的是堆空间,动态空间使用不会造成堆溢出。
3)数组访问越界。C语言没有提供数组下标越界检查,如果在程序中出现数组下标访问超出数组范围,在运行过程中可能会内存访问错误。
4)指针非法访问。指针保存了一个非法的地址,通过这样的指针访问所指向的地址时会产生内存访问错误。
17.以下代码是合法的吗?
int a = 5, b = 7, c;
c = a+++b;
上面的代码是合法的,但是会被编译器处理成c = a++ + b,可读性不强
18.进程间通讯方式
常见的通信方式
1)管道pipe:管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
2)命名管道FIFO:有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。
3)消息队列MessageQueue:消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
4)共享存储SharedMemory:共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号量,配合使用,来实现进程间的同步和通信。
5)信号量Semaphore:信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
6)套接字Socket:套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
7)信号 ( sinal ) : 信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
19.以下代码实现了平方运算,请问这段代码有什么问题
#define SQR(X) X*X
int main(void)
{
int a=10,k=2,m=1;
a=SQR(k+m)/SQR(k+m)
printf("a=%d",a);
return 0;
}
由于宏定义是整体替换,所以a=k+m*k+m/k+m*k+m=2+1*2+1/2+1*2+1=7
如果改成 #define SQR(X) (X)*(X)
如果改成#define SQR(X) ((X)*(X))
20.嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
int * ptr;
ptr= (int *)0x67a9;
*ptr = 0xaa55;
21.要对绝对地址0x100000赋值,我们可以用以下代码
(unsigned int*)0x100000 = 1234;
那么要想让程序跳转到绝对地址0x100000去执行,要如何操作?
*((void(*)())0x100000)();
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
22.以下代码运行会出现什么问题
void getmemory(char *p)
{undefined
p=(char *) malloc(100);
strcpy(p,“hello world”);
}
int main( )
{undefined
char *str=NULL;
getmemory(str);
printf(“%s/n”,str);
free(str);
return 0;
} 会出现什么问题?
程序崩溃,getmemory中的malloc 不能返回
动态内存,free ()对str操作很危险。
23.执行以下代码会出现什么问题
char szstr[10];
strcpy(szstr,"0123456789");
产生什么结果?为什么?
=====》出现段错误,因为拷贝长度越界
24.在不应加地址运算符&的位置加了地址运算符。
char str[20];
scanf(“%s”,&str);
C语言编译系统对数组名的处理是:数组名代表该数组的起始地址,且scanf函数中的输入项是字符数组名,不必要再加地址符&。应改为:
scanf(“%s”,str);
25.输入数据时,企图规定精度。
scanf(“%7.2f”,&a);
这样做是不合法的,输入数据时不能规定精度
26.将字符常量与字符串常量混淆。
char c;
c=”a”;
在这里就混淆了字符常量与字符串常量,字符常量是由一对单引号括起来的单个字符,字符串常量是一对双引号括起来的字符序列。C规定以“\0”作字符串结束标志,它是由系统自动加上的,所以字符串“a”实际上包含两个字符:‘a’和‘\0’,而把它赋给一个字符变量是不行的。
27.用变量a给出下面的定义
a) 一个整型数(An integer)----int a;
b)一个指向整型数的指针( A pointer to an integer)-----int *a;
c)一个指向指针的的指针,它指向的指针是指向一个整型数( A pointer to a pointer to an integer)-----int **a;
d)一个有10个整型数的数组( An array of 10 integers)----int a[10];
e) 一个有10个指针的数组,该指针是指向一个整型数的。(An array of 10 pointers to integers)
----int *a[10];
f) 一个指向有10个整型数数组的指针( A pointer to an array of 10 integers)----int (*a)[10];
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes an integer as an argument and returns an integer)----int (*a)(int);
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数( An array of ten pointers to functions that take an integer argument and return an integer )
----int (*a[10])(int);
28 . 下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ">6"。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的
类似的
0x96=150.
29. 评价下面的代码片断:(这一问题真正能揭露出是否懂得处理器字长的重要性)
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;//只写了2个字节,16位的才符合 。
/*1's complement of zero */
讲解:
对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:
unsigned int compzero = ~0;
32位的可以写:unsigned int compzero = 0xFFFFFFFF;
但unsigned int compzero = ~0;更安全,不管有多少位,直接取反,把所有的0都变成1了。
30.switch()语句的括号内的表达式必须是整型,(不能是实型)它要与case 里的值呼应
31.中断(Interrupts)
中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (double radius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
这个函数有太多的错误了,以至让人不知从何说起了:
1). ISR 不能返回一个值。
2). ISR 不能传递参数。
3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4). 与第三点一脉相承,printf()经常有重入和性能上的问题。
32.位操作
嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a的bit 3。在以上两个操作中,要保持其它位不变
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
33.指针优先级的
以下输出什么?
char *ptr="Linux";
printf("\n[%c]\n",*ptr++);
printf("\n[%c]\n",*ptr);
[L]
[i]
因为*和++ 的优先级一样,*ptr++ 相当于*(ptr++) ,即先执行ptr++, ptr++先使用值再加加,所以操作结果是“L”。第二个结果是“i”。
34,sizeof 和strlen
char text[100]= "Sample test";
printf("%d,%d\n", strlen(text), sizeof(text));//11 100
char str[]="hello";
char *p=str;
int n=10;
printf("%d,%d,%d\n",sizeof(str),sizeof(p),sizeof(n));//6 4 4
在字符数组中,strlen()返回字符数,而sizeof()返回按字符数组占用的字节数
如果char str[5]="hello" sizeof(str)=5
如果char str[6]="hello" sizeof(str)=6
如果char str[]="hello" sizeof(str)=6
在没有指定数组大小的话 加上含有'\0'结束符,指定了数组大小sizeof(str)就是数组的大小
35.if(表达式)的考察
在keil编译器上if(i=1) if(i=2)只是警告
如果加上if(i=3) j=6 打印的还是6
36.反码和补码
反码:对原码除符号位外的其余各位逐位取反就是反码
补码:负数的补码就是对反码加1
正数的原码.反码.补码都一样
37.ROM.RAM.SRAM.DRAM.SDRAM
ROM是只读存储器,掉电不丢失
RAM是读写存储器,掉电丢失
SRAM:CPU的缓存就是SRAM,静态的,加点情况下,不需要刷新,数据不会丢失.
DRAM:动态随机存取存储器最为常见的系统内存,需要不断刷新,才能保存数据
SDAM:同步动态随机存储器,即数据的读取需要时钟来同步.
38.signed char的取值范围
-128~127
39.指针指向地址
int *a=(int *)2;//a为int型指针 指向地址2
printf("%d",a+3);答案是2+3*4=14,int类型地址加1,相当于加4个字节,即a指向地址0X0E.
40,sscanf()函数的用法
1,sscanf("123456","%4s",buf);//1234取指定长度的字符串
2,sscanf("123456 asdfga","%[^ ]",buf);//123456//取到指定字符为止的字符串,取遇到空格为止字符串
3,sscanf("123456aafsdfADDEFF","%[1-9a-z]",buf);//123456aafsdf keil仿真不支持
4,sscanf("123afsdfADJKLJ","%[^A-Z]",buf);//123afsdf
int a,b,c;
5,sscanf("2015.04.05", "%d.%d.%d", &a,&b,&c); //取需要的字符串
printf("a=%d,b=%d,c=%d",a,b,c); // a=2015,b=4,c=5
6,给定一个字符串“abcd&hello$why”,现在我想取出&与$之间的字符串怎么办呢
sscanf("abcd&hello$why", "%*[^&]&%[^$]", str );//str="hello"