http://blog.csdn.net/linxiaowu66/article/details/7639250
1、C++中为什么用模板类。
答:(1)可用来创建动态增长和减小的数据结构
(2)它是类型无关的,因此具有很高的可复用性。
(3)它在编译时而不是运行时检查数据类型,保证了类型安全
(4)它是平台无关的,可移植性
(5)可用于基本数据类型
2、CSingleLock是干什么的。
答:同步多个线程对一个数据类的同时访问
3、函数模板与类模板有什么区别?
答:函数模板的实例化是由编译程序在处理函数调用时自动完成的,而类模板的实例化
必须由程序员在程序中显式地指定。
4、引用与指针有什么区别?
5、全局变量和局部变量在内存中是否有区别?如果有,是什么区别?
6、什么是平衡二叉树?
7、堆栈溢出一般是由什么原因导致的?
8、冒泡排序算法的时间复杂度是什么?
9、写出float x 与“零值”比较的if语句。
10、用预处理指令#define 声明一个常数,用以表明1年中有多少秒(忽略闰年问题)
#defineSECONDS_PER_YEAR (60 * 60 * 24 * 365)UL
(1). 意识到这个表达式将使一个16位机的整型数溢出-因此要用到长整型符号L,告诉编译器这个常数是的长整型数。
(2). 如果你在你的表达式中用到UL(表示无符号长整型),那么你有了一个好的起点。记住,第一印象很重要。
11、
#define MIN(A,B)((A) <= (B) (A) : (B))
12、
#error:停止编译并显示错误信息
13、
方案1是:
while(1)
{
}
方案2:
for(;;)
{
}
第三个方案是用goto
Loop:
...
goto Loop;
14、
a) 一个整型数(An integer)
b) 一个指向整型数的指针(A pointer to an integer)
c) 一个指向指针的的指针,它指向的指针是指向一个整型数(A pointer to a pointer to aninteger)
d) 一个有10个整型数的数组(An array of 10integers)
e) 一个有10个指针的数组,该指针是指向一个整型数的(An array of 10 pointers tointegers)
f) 一个指向有10个整型数数组的指针(A pointer to an array of 10integers)
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数(A pointer to a function that takes aninteger as an argument and returns an integer)
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数(Anarray 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 aninteger argument and returns an integer
h) int (*a[10])(int); // An array of 10 pointers to functions thattake an integer argument and return an integer
15、关键字static的作用是什么?
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。(本地化数据和代码范围的好处和重要性。)
16、关键字const是什么含意?
const int a;
int const a;
const int *a;
int * const a;
int const * a const;各是什么意思?
答:“只读”。前两个的作用是一样,a是一个常整型数。第三个意味着a是一个指向常整型数的指针(也就是,整型数是不可修改的,但指针可以)。第四个意思a是一个指向整型数的常指针(也就是说,指针指向的整型数是可以修改的,但指针是不可修改的)。最后一个意味着a是一个指向常整型数的常指针(也就是说,指针指向的整型数是不可修改的,同时指针也是不可修改的)。(const的作用:
1). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。)
2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。
3). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。)
17、关键字volatile有什么含意并给出三个不同的例子。
一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:
1). 并行设备的硬件寄存器(如:状态寄存器)
2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)
3). 多线程应用中被几个任务共享的变量
附加问题: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;
}
18、嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。
1)用bit fields。Bit fields是被扔到C语言死角的东西,它保证你的代码在不同编译器之间是不可移植的,同时也保证了的你的代码是不可重用的。我最近不幸看到Infineon为其较复杂的通信芯片写的驱动程序,它用到了bit fields因此完全对我无用,因为我的编译器用其它的方式来实现bit fields的。从道德讲:永远不要让一个非嵌入式的家伙粘实际硬件的边。
2). 用 #defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:
#define BIT3 (0x1<<3)
static int a;
void set_bit3(void)
{
a |= BIT3;
}
void clear_bit3(void)
{
a &= ~BIT3;
}
19、嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。
为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。
int *ptr;
ptr = (int *)0x67a9;
*ptr = 0xaa55;
20、中断是嵌入式系统中重要的组成部分,这导致了很多编译开发商提供一种扩展—让标准C支持中断。具代表事实是,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论一下这段代码的。
__interrupt double compute_area (doubleradius)
{
double area = PI * radius * radius;
printf(" Area = %f", area);
return area;
}
这个函数有太多的错误了,以至让人不知从何说起了:
1). ISR 不能返回一个值。如果你不懂这个,那么你不会被雇用的。
2). ISR 不能传递参数。如果你没有看到这一点,你被雇用的机会等同第一项。
3). 在许多的处理器/编译器中,浮点一般都是不可重入的。有些处理器/编译器需要让额处的寄存器入栈,有些处理器/编译器就是不允许在ISR中做浮点运算。此外,ISR应该是短而有效率的,在ISR中做浮点运算是不明智的。
4). 与第三点一脉相承,printf()经常有重入和性能上的问题。
21、下面的代码输出是什么,为什么?
void foo(void)
{
unsigned int a = 6;
int b = -20;
(a+b > 6) puts("> 6") :puts("<= 6");
}
这无符号整型问题的答案是输出是 “>6”。原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。
22、评价下面的代码片断:(处理器字长的重要性)
unsigned int zero = 0;
unsigned int compzero = 0xFFFF;
对于一个int型不是16位的处理器为说,上面的代码是不正确的。应编写如下:
unsigned int compzero = ~0;
c/c++笔试题(2)(转载的)
1.尽管不像非嵌入式计算机那么常见,嵌入式系统还是有从堆(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
2. 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 两个指针。
晦涩的语法
3.C语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?
int a = 5, b = 7, c;
c = a+++b;
这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:
c = a++ + b;
因此, 这段代码持行后a = 6, b = 7, c =12。
如果你知道答案,或猜出正确答案,做得好。如果你不知道答案,我也不把这个当作问题。我发现这个问题的最大好处是:这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。
4、用递归算法判断数组a[N]是否为一个递增数组。
递归的方法,记录当前最大的,并且判断当前的是否比这个还大,大则继续,否则返回false结束:
bool fun( int a[], int n )
{
if( n= =1 )
return true;
if( n= =2 )
return a[n-1] >= a[n-2];
return fun( a,n-1) && ( a[n-1]>= a[n-2] );
}
5、编写算法,从10亿个浮点数当中,选出其中最大的10000个。
用外部排序,在《数据结构》书上有
《计算方法导论》在找到第n大的数的算法上加工
6.单连表的建立,把'a'--'z'26个字母插入到连表中,并且倒叙,还要打印!
方法1:
typedef struct val
{
}*p;
void main(void)
{
}
}
方法2:
node *p = NULL;
node *q = NULL;
node *head = (node*)malloc(sizeof(node));
head->data = '';head->next=NULL;
node *first = (node*)malloc(sizeof(node));
first->data ='a';first->next=NULL;head->next =first;
p = first;
int longth = 'z' - 'b';
int i="0";
while ( i<=longth )
{
node *temp = (node*)malloc(sizeof(node));
temp->data ='b'+i;temp->next=NULL;q=temp;
head->next = temp;temp->next=p;p=q;
i++;
}
print(head);
测试程序
#include <stdio.h>
struct A{
};
struct B{
}__attribute__((aligned));
struct C{
}__attribute__((aligned(1)));
struct D{
}__attribute__((aligned(4)));
struct E{
}__attribute__((aligned(8)));
struct F{
}__attribute__((packed));
int main(int argc, char **argv)
{
}
在fedora 7下的测试结果:
A = 28, B = 32, C = 28, D = 28, E = 32, F = 20
A:不使用__attribute__ 默认4字节对齐
B:__attribute__((aligned))
C:__attribute__((aligned(1))) 不支持,除了packed 不能减小对齐字节数,以默认对齐方式对齐
D:__attribute__((aligned(4))) 四字节对齐
E:__attribute__((aligned(8)))八字节对齐
F:__attribute__((packed))
Here is a structure in which the field x is packed, so that itimmediately follows a:
cygwin下的测试结果:
A = 32, B = 32, C = 32, D = 32, E = 32, F =20
从测试结果上看默认8字节对齐?或是只支持packed,未知
8、一个递规反向输出字符串的例子,可谓是反序的经典例程.
void inverse(char *p)
{
return;
}
int main(int argc, char *argv[])
{
}
9、输出和为一个给定整数的所有组合
例如n=5
5=1+4;5=2+3(相加的数不能重复)
则输出
1,4;2,3。
答案:
#include<stdio.h>
void main()
{
unsigned long int a,i=1;
scanf("%d",&a);
if(a%2==0)
{
}
else
for(i=1;i<=a/2;i++)
}
10、在对齐为4的情况下
struct BBB
{
}*p;
p=0x1000000;
p+0x200=____;
(Ulong)p+0x200=____;
(char*)p+0x200=____;
答案:假设在32位CPU上,
sizeof(long) = 4 bytes
sizeof(char *) = 4 bytes
sizeof(short int) = sizeof(short) = 2 bytes
sizeof(char) = 1 bytes
由于是4字节对齐,
sizeof(struct BBB) = sizeof(*p)
= 4 + 4 + 4((2 + 1 )+ 1补齐为4)+ 12(2*5 + 2补齐为12) = 24 bytes
p=0x1000000;
p+0x200=____;
(Ulong)p+0x200=____;
(char*)p+0x200=____;
11、写一段程序,找出数组中第k大小的数,输出数所在的位置。例如{2,4,3,4,7}中,第一大的数是7,位置在4。第二大、第三大的数都是4,位置在1、3随便输出哪一个均可。函数接口为:int find_orderk(const int*narry,const int n,const int k)
要求算法复杂度不能是O(n^2)
答案:可以先用快速排序进行排序,其中用另外一个进行地址查找
代码如下,在VC++6.0运行通过。给分吧^-^
//快速排序
#include<iostream>
usingnamespacestd;
intPartition (int*L,intlow,int high)
{
inttemp = L[low];
intpt = L[low];
while (low < high)
{
while (low < high &&L[high] >= pt)
--high;
L[low] = L[high];
while (low < high &&L[low] <= pt)
++low;
L[low] = temp;
}
L[low] = temp;
returnlow;
}
voidQSort (int*L,intlow,int high)
{
if (low < high)
{
intpl = Partition (L,low,high);
QSort (L,low,pl - 1);
QSort (L,pl + 1,high);
}
}
intmain ()
{
intnarry[100],addr[100];
intsum = 1,t;
cout << "Input number:"<< endl;
cin >> t;
while (t != -1)
{
narry[sum] = t;
addr[sum - 1] = t;
sum++;
cin >> t;
}
sum -= 1;
QSort (narry,1,sum);
for (int i = 1; i <= sum;i++)
cout << narry[i]<< '/t';
cout << endl;
intk;
cout << "Please input place youwant:" << endl;
cin >> k;
intaa = 1;
intkk = 0;
for (;;)
{
if (aa == k)
break;
if (narry[kk] != narry[kk + 1])
{
aa += 1;
kk++;
}
}
cout << "The NO."<< k <<"number is:" << narry[sum - kk]<< endl;
cout << "And it's place is:" ;
for (i = 0;i < sum;i++)
{
if (addr[i] == narry[sum - kk])
cout << i<< '/t';
}
return0;
}
intmain(void)
{
int *a = (int *)malloc(MAX * sizeof(int));
int *b;
FILE *fp1;
FILE *fp2;
fp1 = fopen("a.txt","r");
if(fp1 == NULL)
{printf("error1");
}
if(fp2 == NULL)
{printf("error2");
}
int i = 0;
while(fscanf(fp1,"%d",&a[i]) != EOF)
{
i++;
j++;
if(i >= MAX)
{
MAX = 2 * MAX;
b = (int*)realloc(a,MAX * sizeof(int));
if(b == NULL)
{
printf("error3");
exit(-1);
}
a = b;
}
}
for(;--j >= 0;)
fclose(fp1);
fclose(fp2);
return 0;
}
12、运行的结果为什么等于15
#include"stdio.h"
#include "string.h"
void main()
{
char aa[10];
printf("%d",strlen(aa));
}
答案:sizeof()和初不初始化,没有关系;strlen()和初始化有关。
13、分析一下
#include<iostream.h>
#include<string.h>
#include<malloc.h>
#include<stdio.h>
#include<stdlib.h>
#include<memory.h>
typedef struct
{
}AA;
void main()
{
}
答案: -16和1
首先sizeof(AA)的大小为4,b1和b2分别占5bit和2bit.
经过strcpy和memcpy后,aa的4个字节所存放的值是:
0,1,2,3的ASC码,即00110000,00110001,00110010,00110011
所以,最后一步:显示的是这4个字节的前5位,和之后的2位
分别为:10000,和01
因为int是有正负之分,所以是-16和1
14、求函数返回值,输入x=9999;
int func ( x )
{
}
结果呢?
答案:知道了这是统计9999的二进制数值中有多少个1的函数,且有
9999=9×1024+512+256+15
9×1024中含有1的个数为2;
512中含有1的个数为1;
256中含有1的个数为1;
15中含有1的个数为4;
故共有1的个数为8,结果为8。
1000 - 1 = 0111,正好是原数取反。这就是原理。
用这种方法来求1的个数是很效率很高的。
不必去一个一个地移位。循环次数最少。
15、int a,b,c 请写函数实现C=a+b ,不可以改变数据类型,如将c改为long int,关键是如何处理溢出问题
答案:bool add (int a, int b,int *c)
{
*c=a+b;
return (a>0 &&b>0&&(*c<a ||*c<b) || (a<0&& b<0&&(*c>a ||*c>b)));
}
16、改错:
#i nclude<stdio.h>
int main(void) {
}
答案:搞错了,是指针类型不同,
int **p; //二级指针
&arr; //得到的是指向第一维为100的数组的指针
应该这样写#i nclude<stdio.h>
int main(void) {
int **p, *q;
int arr[100];
q = arr;
p = &q;
return 0;
标准答案示例:
const float EPSINON = 0.00001;
if ((x >= - EPSINON)&& (x <=EPSINON)
void *p = malloc(100 );
请计算
sizeof ( p ) =
17、在C++ 程序中调用被 C编译器编译后的函数,为什么要加extern“C”?
答:C++语言支持函数重载,C语言不支持函数重载。函数被C++编译后在库中的名字与C语言的不同。假设某个函数的原型为:voidfoo(int x, int y);
该函数被C编译器编译后在库中的名字为_foo,而C++编译器则会产生像_foo_int_int之类的名字。
C++提供了C连接交换指定符号extern“C”来解决名字匹配问题。
18、
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str, "hello world");
printf(str);
}
请问运行Test函数会有什么样的结果?
答:试题传入GetMemory( char *p)函数的形参为字符串指针,在函数内部修改形参并不能真正的改变传入形参的值,执行完
char *str = NULL;
GetMemory( str );
后的str仍然为NULL;
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory();
printf(str);
}
请问运行Test函数会有什么样的结果?
答:可能是乱码。
的p[]数组为函数内的局部自动变量,在函数返回后,内存已经被释放。这是许多程序员常犯的错误,其根源在于不理解变量的生存期。
void GetMemory2(char **p, int num)
{
*p = (char *)malloc(num);
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100);
strcpy(str, "hello");
printf(str);
}
请问运行Test函数会有什么样的结果?
答:
(1)能够输出hello
(2 )Test函数中也未对malloc的内存进行释放。
(3)GetMemory避免了试题1的问题,传入GetMemory的参数为字符串指针的指针,但是在GetMemory中执行申请内存及赋值语句
*p = (char *) malloc( num );
后未判断内存是否申请成功,应加上:
if ( *p == NULL )
}
{
char *str = (char *) malloc(100);
printf(str);
}
}
请问运行Test函数会有什么样的结果?
答:执行
char *str = (char *) malloc(100);
后未进行内存是否申请成功的判断;另外,在free(str)后未置str为空,导致可能变成一个“野”指针,应加上:
str = NULL;
19、写出运行结果
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(int argc,char *argv[]) {
/0
c
b
a
f
e
d
}
a slen = 3,b slen =6
a
a = abc, b = defabc
asize len = 4, bsize len = 3
注:栈分配原则:从高地址->低地址分配;
b
It is not the aboveresult when I test in Vmware Linux.
4.说出错误
void test() {
}
注:数组越界
20、说出错误
void test() {
//modif
}
21、写出运行结果
#include <stdio.h>
#include <string.h>
#define STRCPY(a,b)
#define STRCPY1(a, b)
intmain(void)
}
var1 = bbbb
var2 = var1
22、宏中"#"和"##"的用法
一、一般用法
我们使用#把宏参数变为一个字符串,用##把两个宏参数贴合在一起.
用法:
#include<cstdio>
#include<climits>
using namespace std;
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
int main()
{
printf(STR(vck)); // 输出字符串"vck"
printf("%d/n", CONS(2,3)); // 2e3 输出:2000
return 0;
}
二、当宏参数是另一个宏的时候
需要注意的是凡宏定义里有用'#'或'##'的地方宏参数是不会再展开.
a, 非'#'和'##'的情况
#define TOW (2)
#define MUL(a,b) (a*b)
printf("%d*%d=%d/n", TOW, TOW,MUL(TOW,TOW));
这行的宏会被展开为:
printf("%d*%d=%d/n", (2), (2), ((2)*(2)));
MUL里的参数TOW会被展开为(2).
b,当有'#'或'##'的时候
#define A (2)
#define STR(s) #s
#define CONS(a,b) int(a##e##b)
printf("int max: %s/n", STR(INT_MAX)); // INT_MAX#include<climits>
这行会被展开为:
printf("int max: %s/n", "INT_MAX");
printf("%s/n",CONS(A, A)); // compile error
这一行则是:
printf("%s/n", int(AeA));
A不会再被展开, 然而解决这个问题的方法很简单. 加多一层中间转换宏.
加这层宏的用意是把所有宏的参数在这层里全部展开, 那么在转换宏里的那一个宏(_STR)就能得到正确的宏参数.
#define A (2)
#define _STR(s) #s
#define STR(s) _STR(s) // 转换宏
#define _CONS(a,b) int(a##e##b)
#define CONS(a,b) _CONS(a,b) // 转换宏
printf("int max: %s/n", STR(INT_MAX)); //INT_MAX,int型的最大值,为一个变量#include<climits>
输出为: int max: 0x7fffffff
STR(INT_MAX)--> _STR(0x7fffffff) 然后再转换成字符串;
printf("%d/n", CONS(A, A));
输出为:200
CONS(A, A) --> _CONS((2), (2)) -->int((2)e(2))
三、'#'和'##'的一些应用特例
1、合并匿名变量名
#define ___ANONYMOUS1(type, var, line) type var##line
#define __ANONYMOUS0(type, line) ___ANONYMOUS1(type, _anonymous,line)
#define ANONYMOUS(type) __ANONYMOUS0(type, __LINE__)
例:ANONYMOUS(static int); 即: static int _anonymous70; 70表示该行行号;
第一层:ANONYMOUS(static int);--> __ANONYMOUS0(static int,__LINE__);
第二层:--> ___ANONYMOUS1(static int, _anonymous,70);
第三层:--> static int_anonymous70;
即每次只能解开当前层的宏,所以__LINE__在第二层才能被解开;
2、填充结构
#define FILL(a) {a, #a}
enum IDD{OPEN, CLOSE};
typedef struct MSG{
IDD id;
const char * msg;
}MSG;
MSG _msg[] ={FILL(OPEN), FILL(CLOSE)};
相当于:
MSG _msg[] = {{OPEN, "OPEN"},
{CLOSE, "CLOSE"}};
3、记录文件名
#define _GET_FILE_NAME(f) #f
#define GET_FILE_NAME(f) _GET_FILE_NAME(f)
static char FILE_NAME[] = GET_FILE_NAME(__FILE__);
4、得到一个数值类型所对应的字符串缓冲大小
#define _TYPE_BUF_SIZE(type) sizeof #type
#define TYPE_BUF_SIZE(type) _TYPE_BUF_SIZE(type)
char buf[TYPE_BUF_SIZE(INT_MAX)];
--> char buf[_TYPE_BUF_SIZE(0x7fffffff)];
--> char buf[sizeof"0x7fffffff"];
这里相当于:
char buf[11];-
第3题:考查递归调用
int foo ( int x ,int n) { int val; val =1;
这段代码对x和n完成什么样的功能(操作)?
(a) x^n (x的n次幂)
(b) x*n(x与n的乘积)
(c) n^x(n的x次幂)
(d) 以上均不是
c/c++笔试题(3)(转载的)
第1题目:考查逗号表达式
main(){ int a, b,c, d; a=3; b=5; c=a,b; d=(a,b); printf("c=%d" ,c);printf("d=%d" ,d);}
这段程序的输出是:
(a) c=3 d=3
(b) c=5 d=3
(c) c=3 d=5
(d) c=5 d=5
第1题:(c)
考查逗号表达式,逗号表达式的优先级是很低的,比 赋值(=)的优先级低.逗号表达式的值就是最后一个元素的值
逗号表达式的还有一个作用就是分割函数的参数列表..
第2题:考查自加操作(++)
main(){ int i=3; int j; j =sizeof(++i+ ++i); printf("i=%d j=%d", i,j);}
这段程序的输出是:
(a) i=4 j=2
(b) i=3 j=2
(c) i=3 j=4
(d) i=3 j=6
第2题: (b)
sizeof操作符给出其操作数需要占用的空间大小,它是在编译时就可确定的,所以其操作数即使是一个表达式,也不需要在运行时进行计算.( ++i + ++ i )是不会执行的,所以
i 的值还是3
第3题:考查形式参数,实际参数,指针和数组
void f1(int *, int); void f2(int *, int); void(*p[2]) ( int *,int);main(){ int a; int b; p[0] = f1; p[1] = f2; a=3; b=5;p[0](&a , b); printf("%d/t %d/t" , a ,b);p[1](&a , b); printf("%d/t %d/t" , a ,b);}void f1(int* p , int q){ int tmp; tmp =*p; *p = q; q= tmp;}void f2( int* p, int q){ int tmp; tmp =*p; *p = q; q=tmp;}
这段程序的输出是:
(a) 5 5 5 5
第4题:考查自减操作(--)
void e(int );
这段程序的输出是:
(a) 0 1 2 0
第5题:此题考查的是C的变长参数,就像标准函数库里printf()那样,这个话题一般国内大学课堂是不会讲到的,不会也情有可原呵呵,
#include<stdarg.h>
int ripple ( int , ...);
main()
{
}
int ripple (int n, ...)
{
}
这段程序的输出是:
(a) 7
(b) 6
(c) 5
(d) 3
第5题:(c)
在C编译器通常提供了一系列处理可变参数的宏,以屏蔽不同的硬件平台造成的差异,增加程序的可移植性。这些宏包括va_start、 va_arg和va_end等。
采用ANSI标准形式时,参数个数可变的函数的原型声明是:
type funcname(type para1, type para2,...)
这种形式至少需要一个普通的形式参数,后面的省略号不表示省略,而是函数原型的一部分。type是函数返回值和形式参数的类型。
不同的编译器,对这个可变长参数的实现不一样 ,gcc4.x中是内置函数.
关于可变长参数,可参阅
http://www.upsdn.net/html/2004-11/26.html
http://www.upsdn.net/html/2004-11/24.html
程序分析
va_list p;
va_start( p , n);
for (; j<n; ++j)
{
++k;
当我们调用ripple函数时,传递给ripple函数的参数列表的第一个参数n的值是3 .
va_start 初始化 p指向第一个未命名的参数(n是有名字的参数),也就是 is 5(第一个).
每次对va_arg的调用,都将返回一个参数,并且把p 指向下一个参数.
va_arg 用一个类型名来决定返回的参数是何种类型,以及在 var_arg的内部实现中决定移动多大的距离才到达下一个 参数
(; i; i&=i-1) k++
5用二进制表示是 (101) 2
7用二进制表示 (111)3
所以 k 返回5(2+3),也即本题应该选c
因为i与i-1的最右边的那位(最低位)肯定是不同,如果i1,i-1肯定是0,反之亦然.
6. char str1[] = "abc";
char str2[] = "abc";
const char str3[] ="abc";
const charstr4[] = "abc";
const char *str5 ="abc";
const char*str6 = "abc";
char *str7 ="abc";
char *str8= "abc";
cout<< ( str1 == str2 )<< endl;
cout << (str3 == str4 ) <<endl;
cout<< ( str5 == str6 )<< endl;
cout<< ( str7 == str8 )<< endl;
解答:结果是:0 0 1 1
str1,str2,str3,str4是数组变量,它们有各自的内存空间;而str5,str6,str7,str8是指针,它们指向相同的常量区域
-----------------------------------------------
7. 以下代码中的两个sizeof用法有问题吗?
void UpperCase( char str[] )// 将 str中的小写字母转换成大写字母
{
}
char str[] ="aBcDe";
cout<< "str字符长度为: " <<sizeof(str)/sizeof(str[0]) <<endl;
UpperCase(str );
cout<< str<< endl;
答:函数内的sizeof有问题。
根据语法,sizeof如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。
函数外的str是一个静态定义的数组,因此其大小为6,
函数内的str实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof作用于上只将其当指针看,一个指针为4个字节,因此返回4。
-------------------------------------------------
8. main()
{
inta[5]={1,2,3,4,5};
int *ptr=(int*)(&a+1);
printf("%d,%d",*(a+1),*(ptr-1));
}
答案:输出:2,5
*(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
&a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
int*ptr=(int *)(&a+1);
则ptr实际是&(a[5]),也就是a+5
原因如下:
&a是数组指针,其类型为 int (*)[5];
而指针加1要根据指针类型加上一定的值,不同类型的指针+1之后增加的大小不同。
a是长度为5的int数组指针,所以要加 5*sizeof(int)
所以ptr实际是a[5]
但是prt与(&a+1)类型是不一样的(这点很重要)
所以prt-1只会减去sizeof(int*)
a,&a的地址是一样的,但意思不一样
--------------------------------------------
9. 请问以下代码有什么问题:
int
{
char a;
char *str=&a;
strcpy(str,"hello");
printf(str);
return 0;
}
答案:没有为str分配内存空间,将会发生异常。问题出在将一个字符串复制进一个字符变量指针所指地址。虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。
---------------------------------------------
10. char*s="AAA";
printf("%s",s);
s[0]='B';
printf("%s",s);
有什么错?
答案:
"AAA"是字符串常量。s是指针,指向这个字符串常量,所以声明s的时候就有问题。
cosnt char* s="AAA";
然后又因为是常量,所以对是s[0]的赋值操作是不合法的。
---------------------------------------------
11. int (*s[10])(int)表示的是什么?
答案:int (*s[10])(int)函数指针数组,每个指针指向一个int func(intparam)的函数。
---------------------------------------------
12. 有以下表达式:
int a=248;b=4;
int constc=21;
const int*d=&a;
int *const e=&b;
int const *f const=&a;
请问下列表达式哪些会被编译器禁止?为什么?
*c=32;d=&b;*d=43;e=34;e=&a;f=0x321f;
答案:
*d 说了是const, 禁止
e =&a 说了是const 禁止
const *fconst =&a; 禁止
------------------------------------------
13. #include<stdio.h>
#include<stdlib.h>
void getmemory(char*p)
{
p=(char*) malloc(100);
strcpy(p,"hello world");
}
int main()
{
char*str=NULL;
getmemory(str);
printf("%s/n",str);
free(str);
return 0;
}
答案:程序崩溃,getmemory中的malloc 不能返回动态内存, free()对str操作很危险
博 主:getmemory中p是形参,是一个指针变量,getmemory(str)调用后,传入的是指针变量保存的对象地址,p=(char *)malloc(100)实际上是把申请的动态内存空间的首地址付给p指向的地址(即str指向的地址null),这个是错误的。应该修改成指向指针的指针 void getmemory(char **p),这样malloc返回的地址付给*p(即str变量本身)。
-----------------------------------------
14. char szstr[10];
答案:长度不一样,会造成非法的OS
------------------------------------------
15.要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;
答案:*((void (*)( ))0x100000 )( );
首先要将0x100000强制转换成函数指针,即:
(void(*)())0x100000
然后再调用它:
*((void(*)())0x100000)();
用typedef可以看得更直观些:
typedefvoid(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
------------------------------------------
16. 分析下面的程序:
void GetMemory(char **p,intnum)
{
*p=(char *)malloc(num);
}
int main()
{
GetMemory(&str,100);
strcpy(str,"hello");
free(str);
if(str!=NULL)
{
}
printf("/n str is%s",str);
getchar();
}
问输出结果是什么?
答案:输出str isworld。
free 只是释放的str指向的内存空间,它本身的值还是存在的.所以free之后,有一个好的习惯就是将str=NULL.
此时str指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,
尽管这段程序确实是存在大大的问题(上面各位已经说得很清楚了),但是通常会打印出world来。
这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的。
当你malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的。
-------------------------------------------
17.char a[10];
strlen(a)为什么等于15?
#include"stdio.h"
#include "string.h"
voidmain()
{
charaa[10];
printf("%d",strlen(aa));
}
答案:sizeof()和初不初始化,没有关系;
strlen()和初始化有关。
--------------------------------------------
18.char(*str)[20];
char*str[20];/*str是一个指针数组,其元素为指针型数据.
19.以下代码有什么问题?
struct Test
{
Test( int ) {}
Test() {}
void fun() {}
};
void main( void )
{
Test a(1);
a.fun();
Test b();
b.fun();
}
答:变量b定义出错。按默认构造函数定义对象,不需要加括号。
20.以下代码有什么问题?
cout <<(true?1:"1") <<endl;
答:三元表达式“?:”问号后面的两个操作数必须为同一类型。
21.以下代码能够编译通过吗,为什么?
unsigned int const size1 =2;
char str1[ size1];
unsigned int temp =0;
cin >>temp;
unsigned int const size2 =temp;
char str2[ size2];
答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。
During my test in linux environment.The above code could be compiled successfully. But if we initializethe defined arrary like this “char str2[ size2 ] = {0};”, therewould be a compile error informing that “variable-sized object`str2' may not be initialized”.
22.以下反向遍历array数组的方法有什么错误?
vector array;
array.push_back( 1);
array.push_back( 2);
array.push_back( 3);
for( vector::size_typei=array.size()-1; i>=0; --i ) //反向遍历array数组
{
cout <<array[i] << endl;
}
答:首先数组定义有误,应加上类型参数:vector<int>array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i为0时再减1就会变成最大的整数,导致循环失去控制。
23.以下代码中的输出语句输出0吗,为什么?
struct CLS
{
int m_i;
CLS( int i ) : m_i(i){}
CLS()
{
CLS(0);
}
};
CLS obj;
cout <<obj.m_i << endl;
答:不能。在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。
24. C++中的空类,默认产生哪些类成员函数?
答:
class Empty
{
public:
Empty(); //缺省构造函数
Empty( const Empty&); //拷贝构造函数
~Empty(); //析构函数
Empty& operator=(const Empty& ); //赋值运算符
Empty* operator&();//取址运算符
const Empty*operator&() const; //取址运算符const
};
25.以下两条输出语句分别输出什么?
float a = 1.0f;
cout <<(int)a << endl;
cout <<(int&)a <<endl;
cout <<boolalpha << ( (int)a ==(int&)a ) << endl;//输出什么?
float b = 0.0f;
cout <<(int)b << endl;
cout <<(int&)b <<endl;
cout <<boolalpha << ( (int)b ==(int&)b ) << endl;//输出什么?
答:分别输出false和true。注意转换的应用。(int)a实际上是以浮点数a为参数构造了一个整型数,该整数的值是1,(int&)a则是告诉编译器将a当作整数看(并没有做任何实质上的转换)。因为1以整数形式存放和以浮点形式存放其内存数据是不一样的,因此两者不等。对b的两种转换意义同上,但是0的整数形式和浮点形式其内存数据是一样的,因此在这种特殊情形下,两者相等(仅仅在数值意义上)。
注意,程序的输出会显示(int&)a=1065353216,这个值是怎么来的呢?前面已经说了,1以浮点数形式存放在内存中,按ieee754规定,其内容为0x0000803F(已考虑字节反序)。这也就是a这个变量所占据的内存单元的值。当(int&)a出现时,它相当于告诉它的上下文:“把这块地址当做整数看待!不要管它原来是什么。”这样,内容0x0000803F按整数解释,其值正好就是1065353216(十进制数)。
通过查看汇编代码可以证实“(int)a相当于重新构造了一个值等于a的整型数”之说,而(int&)的作用则仅仅是表达了一个类型信息,意义在于为cout<<及==选择正确的重载版本。
26、
①链表反转
单向链表的反转是一个经常被问到的一个面试题,也是一个非常基础的问题。比如一个链表是这样的:1->2->3->4->5通过反转后成为5->4->3->2->1。
最容易想到的方法遍历一遍链表,利用一个辅助指针,存储遍历过程中当前指针指向的下一个元素,然后将当前节点元素的指针反转后,利用已经存储的指针往后面继续遍历。源代码如下:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
还有一种利用递归的方法。这种方法的基本思想是在反转当前节点之前先调用递归函数反转后续节点。源代码如下。不过这个方法有一个缺点,就是在反转后的最后一个结点会形成一个环,所以必须将函数的返回的节点的next域置为NULL。因为要改变head指针,所以我用了引用。算法的源代码如下:
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
②已知String类定义如下:
class String
{
public:
String(const char *str = NULL);//通用构造函数
String(const String&another); //拷贝构造函数
~ String(); //析构函数
String & operater=(const String &rhs); //赋值函数
private:
char *m_data; //用于保存字符串
};
尝试写出类的成员函数实现。
答案:
String::String(const char*str)
{
if ( str == NULL )//strlen在参数为NULL时会抛异常才会有这步判断
{
m_data = new char[1];
m_data[0] = '/0' ;
}
else
{
m_data = new char[strlen(str) +1];
strcpy(m_data,str);
}
}
{
m_data = newchar[strlen(another.m_data) + 1];
strcpy(m_data,other.m_data);
}
String&String::operator =(const String&rhs)
{
if ( this ==&rhs)
return *this ;
delete []m_data;//删除原来的数据,新开一块内存
m_data = new char[strlen(rhs.m_data)+ 1];
strcpy(m_data,rhs.m_data);
return *this ;
}
String::~String()
{
delete []m_data ;
}
还有:~~~~~~~~~~~
1.要使引用pr代表变量“char*p”,则pr的初始化语句为__________
参考答案
char * &pr=p;
2.表达式8&3的结果是__________。
参考答案
0
3.设int x;,则经过(
A int * px;
C int * const px=&x D const int *px=&x;
参考答案C
4.写出下列程序的执行结果。
# include <iostream.h>
void fun(int,int,int * );
{
int x,y,z;
fun (2,3,&x);
fun (4,x,&y);
fun (x,y,&z);
cout<<x<<','<<y<<','<<z<<endl;
}
void fun(int a,int b,int * c )
{
b*=a;
*c=b-a;
}
参考答案
4,12,44
linux笔试题参考答案(4)
(部分题目还没找到答案,改天在贴出)
1. 嵌入式linux和wince操作系统的特点和特性?
2. 嵌入式linux中tty设备驱动的体系结构?
3. 嵌入式设备,为加快启动速度,可以做哪些方面的优化?
4. USB设备的枚举过程?
5. PSRAM、SDRAM、DDR、DDR2的时序特性?
6. I2C触摸屏芯片与CPU的数据传输流程?画出相关图例?(这题目记得不是太清楚了,大概是考查I2C设备驱动的数据传输过程)
12.Linux有内核级线程么。
答:线程通常被定义为一个进程中代码的不同执行路线。从实现方式上划分,线程有两
种类型:“用户级线程”和“内核级线程”。用户线程指不需要内核支持而在用户程序
中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度
和管理线程的函数来控制用户线程。这种线程甚至在象DOS 这样的操作系统中也可实现
,但线程的调度需要用户程序完成,这有些类似Windows3.x 的协作式多任务。另外一
种则需要内核的参与,由内核完成线程的调度。其依赖于操作系统核心,由内核的内部
需求进行创建和撤销,这两种模型各有其好处和缺点。用户线程不需要额外的内核开支
,并且用户态线程的实现方式可以被定制或修改以适应特殊应用的要求,但是当一个线
程因I/O 而处于等待状态时,整个进程就会被调度程序切换为等待状态,其他线程得不
到运行的机会;而内核线程则没有各个限制,有利于发挥多处理器的并发优势,但却占
用了更多的系统开支。
Windows NT和OS/2支持内核线程。Linux 支持内核级的多线程
14.使用线程是如何防止出现大的波峰。
答:意思是如何防止同时产生大量的线程,方法是使用线程池,线程池具有可以同时提
高调度效率和限制资源使用的好处,线程池中的线程达到最大数时,其他线程就会排队
等候
22.TCP/IP建立连接的过程?(3-way shake)
答:在TCP/IP协议中,TCP协议提供可靠的连接服务,采用三次握手建立一个连接。
第一次握手:建立连接时,客户端发送syn包(syn=j)到服务器,并进入SYN_SEND状
态,等待服务器确认;
第二次握手:服务器收到syn包,必须确认客户的SYN(ack=j+1),同时自己也发送一个
SYN包(syn=k),即SYN+ACK包,此时服务器进入SYN_RECV状态;
第三次握手:客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=k+1)
,此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。
23.ICMP是什么协议,处于哪一层?
答:Internet控制报文协议,处于网络层
27.IP组播有那些好处?
答:Internet上产生的许多新的应用,特别是高带宽的多媒体应用,带来了带宽的急剧
消耗和网络拥挤问题。组播是一种允许一个或多个发送者(组播源)发送单一的数据包
到多个接收者(一次的,同时的)的网络技术。组播可以大大的节省网络带宽,因为无
论有多少个目标地址,在整个网络的任何一条链路上只传送单一的数据包。所以说组播
技术的核心就是针对如何节约网络资源的前提下保证服务质量。
Embedded Software Design Engineer
1 读程序段,回答问题
int main(int argc,char *argv[])
{
int c=9,d=0;
c=c++%5;
d=c;
printf("d=%d\n",d);
return 0;
}
a) 写出程序输出
b) 在一个可移植的系统中这种表达式是否存在风险?why?
#include "stdio.h"
int a=0;
int b;
static char c;
int main(int argc,char *argv[])
{
char d=4;
static short e;
a++;
b=100;
c=(char)++a;
e=(++d)++;
printf("a=%d, b=%d, c=%d, d= %d, e=%d",a,b,c,d,e);
return 0;
}
a) 写出程序输出
b) 编译器如果安排各个变量(a,b,c,d)在内存中的布局(eg. stack,heap,data section,bsssection),最好用图形方式描述。
2 中断是嵌入式系统中重要的组成部分,这导致了许多编译开发商提供一种扩展:让标准C支持中断,产生了一个新的关键字__interrupt。下面的代码就使用了__interrupt关键字去定义了一个中断服务子程序(ISR),请评论以下这段代码。
__interrupt double compute_area(double radius)
{
double area = PI * radius *radius;
printf("nArea = %f", area);
return area;
}
3 C/C++基础知识问题
a) 关键字volatile在编译时有什么含义?并给出三个不同使用场景的例子(可以伪代码或者文字描述)。
b) C语言中static关键字的具体作用有哪些?
c) 请问下面三种变量声明有何区别?请给出具体含义
int const *p;
int* const p;
int const* const p;
4 嵌入式系统相关问题
a) 对于整形变量A=0x12345678,请画出在little endian及big endian的方式下在内存中是如何存储的。
b) 在ARM系统中,函数调用的时候,参数是通过哪种方式传递的?
c) 中断(interrupt,如键盘中断)与异常(exception,如除零异常)有何区别?
5 设周期性任务P1,P2,P3的周期为T1,T2,T3分别为100,150,400;执行时间分别为20,40,100。请设计一种调度算法进行任务调度,满足任务执行周期及任务周期。
6 优先级反转问题在嵌入式系统中是一中严重的问题,必须给与足够重视。
a) 首先请解释优先级反转问题
b) 很多RTOS提供优先级继承策略(Priorityinheritance)和优先级天花板策略(Priorityceilings)用来解决优先级反转问题,请讨论这两种策略。
参考答案:
1 5 //我感觉答案应该是4,但标准答案给的是5...
存在风险,因为c=c++%5;这个表达式对c有两次修改,行为未定义,c的值不确定
int a=0; // data section
int b;
static char c; // BSS
int main(int argc,char *argv[])
{
}
a=2,b=100,c=2,d=6,e=5
2 a)ISR不能返回一个值;
b)ISR不能传递参数;
c)浮点一般都是不可重入的;
d)printf函数有重入和性能上的问题。
3 a) 用volatile关键字定义变量,相当于告诉编译器,这个变量的值会随时发生变化,每次使用时都需要去内存里
重新读取它的值,并不要随意针对它作优化。
建议使用volatile变量的场所:
(1) 并行设备的硬件寄存器
(2) 一个中断服务子程序中会访问到的非自动变量(全局变量)
(3) 多线程应用中被几个任务共享的变量
b) 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
访问。它是一个本地的全局变量。
模块的本地范围内使用。
c) 一个指向常整型数的指针
4
a) 0x12345678
little endian
高地址--〉0x12
低地址--〉0x78
b)参数4的通过压栈方式传递
c)
所谓中断应该是指外部硬件产生的一个电信号,从cpu的中断引脚进入,打断cpu当前的运行;
所谓异常,是指软件运行中发生了一些必须作出处理的事件,cpu自动产生一个陷入来打断当前运行,转入异常处理流程。
异步与同步的区别`
5
6 高优先级任务需要等待低优先级任务释放资源,而低优先级任务又正在等待中等优先级任务的现象叫做优先级反转
优先级继承策略(Priorityinheritance):继承现有被阻塞任务的最高优先级作为其优先级,任务退出临界区,恢
复初始优先级。
优先级天花板策略(Priorityceilings):控制访问临界资源的信号量的优先级天花板。
优先级继承策略对任务执行流程的影响相对教小,因为只有当高优先级任务申请已被低优先级任务占有的临界资源
这一事实发生时,才抬升低优先级任务的优先级。
1
现在才发现在c中,这是不行的
在采用段式内存管理的架构中,BSS段(bss
4
1. 同步中断是当指令执行时由 CPU 控制单元产生,之所以称为同步,是因为只有在一条指令执行完毕后 CPU 才会发出中断,而不是发生在代码指令执行期间,比如系统调用。
2. 异步中断是指由其他硬件设备依照 CPU 时钟信号随机产生,即意味着中断能够在指令之间发生,例如键盘中断。
所谓中断应该是指外部硬件产生的一个电信号,从cpu的中断引脚进入,打断cpu当前的运行;
所谓异常,是指软件运行中发生了一些必须作出处理的事件,cpu自动产生一个陷入来打断当前运行,转入异常处理流程。
6
复初始优先级。
这一事实发生时,才抬升低优先级任务的优先级。
via的考题--继续
2007-06-29 11:09
1.via的考题一道
这个程序设计的思想是这样的:
为了测试32位机上的浮点数的运算精度,作如下考虑:
当1.5 == 1时(二进制1.1==1)精度为1个小数位
当1.25 == 1时(二进制1.01==1)精度为2个小数位
继续判断测试,直到两者相等,从而得到精度。
所以程序代码如下:
int main()
{ int nCount;
}
问题是,结果为多少?
经测试得64或者53或者24(稍加改动)。。。
得53和24已基本得出答案,主要是ieee 754标准中规定单双精度数字的底数指数 符号位所置。。。64的目前还没有得出结论。。。
2.改错题
void mymul(double *p)
{
}
int main(int argc, char *argv[])
{
}
直接运行,结果为6.00000
这个题目很简单,可以有很多种改法,如:a.把float f=6.0;改为double f=6.0;睛面的mymul句不要强制类型转化b.把所有的数都当成float型来处理等等
但往细的方面想,float型默认4字节,double型默认8字节,虽然在vc6下能勉强运行(运行时报出调试窗口),但单步跟踪发现在mymul()中并没有得到正确执行,而是:Access Violation,这个错误常常在计算机用户运行的程序试图存取未被指定使用的存储区时遇到中,可见mymyl()这个函数并没有得到正确的执行。。。同样,用gcc来直接编译上述程序,虽然没有报错,但结果仍然为6.00000,可能gcc也是在执行mymul()时没有正确执行(不过我没拿gdb来跟踪看)
以下内容是补充,主要是一些基础的知识
1.解释命令ls -a | more具体含义. ls -a 是将当前目录下的文件名输出到终端,而加入后面的| more 是将输出结果分页显示 2.LINUX中的管道指什么重定向又指什么管道命令操作符是:”|”,它仅能处理经由前面一个指令传出的正确输出信息,也就是standard output 的信息,对于 stdandard error 信息没有直接处理能力。然后,传递给下一个命令,作为标准的输入 Linux重定向是指修改原来默认的一些东西,对原来系统命令的默认执行方式进行改变,比如说简单的我不想看到在显示器的输出而是希望输出到某一文件中就可以通过Linux重定向来进行这项工作。区别是: 1)、左边的命令应该有标准输出| 右边的命令应该接受标准输入 左边的命令应该有标准输出 > 右边只能是文件 左边的命令应该需要标准输入 < 右边只能是文件 2)、管道触发两个子进程执行"|"两边的程序;而重定向是在一个进程内执行3.GCC-g -o test.elf test.c的具体含义将test.c 文件编译输出为test.elf 带上调试信息,可以用GDB调试用4.浅述GCC编译器在编译时都有哪几个过程要经历四个相互关联的步骤:预处理(也称预编译,Preprocessing)、编译(Compilation)、汇编(Assembly)和连接(Linking)。预处理(Preprocessing):命令gcc首先调用cpp进行预处理,在预处理过程中,对源代码文件中的文件包含(include)、预编译语句(如宏定义define等)进行分析。编译(Compilation):接着调用cc进行编译,这个阶段根据输入文件生成以.o为后缀的目标文件。汇编(Assembly):汇编过程是针对汇编语言的步骤,调用as进行工作,一般来讲,.s为后缀的汇编语言源代码文件和汇编.s为后缀的汇编语言文件经过预编译和汇编之后都生成以.o为后缀的目标文件。连接(Linking):当所有的目标文件都生成之后,gcc就调用ld来完成最后的关键性工作,这个阶段就是连接。在连接阶段,所有的目标文件被安排在可执行程序中的恰当位置,同时,该程序所调用到的库函数也从各自所在的档案库中连到合适的地方。 6.说明uclinux 和linux的区别uclinux不具有的功能:
1). 没有虚存管理
2). 不能运行时增加进程栈
3). 不支持分页
5). 不能用fork, 而是用vfork
6). RAMDISK
7解释下面一组GDB命令的含义.
12.编写一个hello world程序,要求以创建进程的方式打印hello world.编写hello.c文件如下
{ fork();
printf("hello world!/n");
exit(0);
}
编译hello.c生成hello可执行文件在shell中执行#fork hello 便可以看到打印结果
13.浅谈bootloader,kelnel,filesystem三者之间的关系.嵌入式是linux启动过程如下。bootloader->kernel->filesystem->application先是bootloader,它是linux-kernel移植的基石,Bootloader是在系统启动之后、Kernel运行之前所执行的第一段代码,其任务是为调用Kernel准备必要的软硬件环境。完成bootlaoder的移植后,就是kernel的移植。主要包括添加特定模块的驱动,针对具体要求对内核进行配置。这里有两点要注意:一是有些参数要与所用的bootloader向对应,如nand的分区参数。二是bootlaoder对特定模块的驱动在进入kernel后便会有kernel接管,并有kernel重新驱动文件系统主要是建立根文件和一些系统功能的实现,如bash。用busybox很容易搞定。
3.Linux系统下.ko文件是什么文件?.so文件是什么文件?Linux下面文件名不代表什么。但是从常识上讲,.ko代码是驱动编译成的格式,.so文件一般是动态库文件4.二维数组AA [ 3 ][ 7 ]的另外一种表示方法:我想可以用指针来表示,如果数组元素为int型,则可以用int (*p)[7]来表示,p++后移动向下一行。5.请写出下列代码的输出内容 #include “stdio.h”
main()
{
int a,b,c,d;
a=10;
b=a++;
c=++a;
d=10a++;
printf("b,c,d:%d,%d,%d",b,c,d);
return 0;
}
输出结果为:b, c, d: 10, 12, 130