vc面试题

2019-03-04 12:01

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);

二、数据声明(Data declarations) 用变量a给出下面的定义 a) 一个整型数

b) 一个指向整型数的指针

c) 一个指向指针的的指针,它指向的指针是指向一个整型数 d) 一个有10个整型数的数组

e) 一个有10个指针的数组,该指针是指向一个整型数的 f) 一个指向有10个整型数数组的指针

g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数

h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 答案是:

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的作用是什么?

在C语言中,关键字static有三个明显的作用:

1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。

2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,但不能被模块外其它函数访问。它是一个本地的全局变量。

3). 在模块内,一个被声明为静态的函数只可被这一模块内

1

的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

关键字const是什么含意?

1). 合理地使用关键字const可以使编译器很自然地保护那些不希望被改变的参数,防止其被无意的代码修改。简而言之,这样可以减少bug的出现。

2). 通过给优化器一些附加的信息,使用关键字const也许能产生更紧凑的代码。

3). 关键字const的作用是为给读你代码的人传达非常有用的信息,实际上,声明一个参数为常量是为了告诉了用户这个参数的应用目的。如果你曾花很多时间清理 其它人留下的垃圾,你就会很快学会感谢这点多余的信息。(当然,懂得用const的程序员很少会留下的垃圾让别人来清理的。) #include using namespace std;int main(){ const char *pa; char const *pb; char ca = 'a'; char cb = 'b'; char * const pc = &ca; const char * const pd = &cb; pa = &ca; pa = &cb; pb = &ca; pb = &cb; *pc = 'd'; printf(\经过以上测试 const char *pa; char const *pb;

上面两种定义方法一样都是 pa(pb)指向的变量的值不可改变,及*pa,*pb, 而pa,和pb本身是可变的,如: pa = &ca; //ok ×pa = 'c' //error char * const pc = &ca;

pc本身是不可变的(只能在定义时初始化),但指向的变量值是可变的,如 pc = &ca; //error *pc = 'd'; //ok

const char * const pd = &cb;

pd本身是不可变的,且指向的变量也是不可变的(只能在定义时初始化)

pd = &cb; //error *pd = 'c'; /error

通过以上总结,无论怎样定义p都是一指针

如果const在*左边,表示该指针指向的变量是不可变的 如果const在*右边,表示该指针本身是不可变得

关键字volatile有什么含意 并给出三个不同的例子。 一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精确地说就是,优化器在用到这个变量时必须每次都小心地重新读取这个变量的值,而不是使用保存在寄存器里的备份。下面是volatile变量的几个例子:

1). 并行设备的硬件寄存器(如:状态寄存器)

2). 一个中断服务子程序中会访问到的非自动变量(Non-automatic variables)

3). 多线程应用中被几个任务共享的变量

这是区分C程序员和嵌入式系统程序员的最基本的问题。嵌入式系统程序员经常同硬件、中断、RTOS等等打交道,所用这些都要求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的值可能被意想不到地该变,因此a和b可能是不同的。结果,这段代码可能返不是你所期望的平方值!正确的代码如下:

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

六、位操作(Bit manipulation)

嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,写两段代码,第一个设置a的bit 3,第二个清除a 的bit 3。在以上两个操作中,要保持其它位不变。 解答:采用#defines 和 bit masks 操作。这是一个有极高可移植性的方法,是应该被用到的方法。最佳的解决方案如下:

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

void set_bit3(void) { a |= BIT3; }

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

一些人喜欢为设置和清除值而定义一个掩码同时定义一些说明常数,这也是可以接受的。

主要考点:说明常数、|=和&=~操作。 七、访问固定的内存位置

嵌入式系统经常具有要求程序员去访问某特定的内存位置的特点。在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。编译器是一个纯粹的ANSI编译器。写代码去完成这一任务。

这一问题测试你是否知道为了访问一绝对地址把一个整型数强制转换(typecast)为一指针是合法的。这一问题的实现方式随着个人风格不同而不同。典型的类似代码如下: int *ptr;

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

一个较晦涩的方法是:

*(int * const)(0x67a9) = 0xaa55; 建议采用第一种方法;

1.下面的代码输出是什么,为什么?(考查有符号类型与无符号类型之间的转换) void foo(void) { unsigned int a = 6; int b = -20;

(a+b > 6) ? puts(\

2

)

这个问题测试你是否懂得C语言中的整数自动转换原则; 这无符号整型问题的答案是输出是“>6”。

原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。

2. 评价下面的代码片断:(考查是否懂得处理器字长) unsigned int zero = 0;

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

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

unsigned int compzero = ~0;

这一问题真正能揭露出应试者是否懂得处理器字长的重要性。好的嵌入式程序员非常准确地明白硬件的细节和它的局限,然而PC机程序往往把硬件作为一个无法避免的烦恼。 九、Typedef

Typedef作用是声明一个新的类型名代替已有的类型名; 也可以用预处理器做类似的事。例如,思考一下下面的例子:

#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 两个指针。

#define 在预编译时处理,只作简单的字符串替换; Typedef 在编译时处理,不是简单的字符串替换;

1. 改错(5分)

void test() {

char str[10];

char* str1 = \strcpy(str, str1); }

2.改错(5分) void test() {

char str[10], str1[10]; for( int = 0; i < 10; i++) {

str = 'a'; }

strcpy(str, str1); }

3.读程序,写出结果(5分) int sum(int a) {

int c = 0;

static int b = 3; c += 1; b += 2;

return (a + b + c); }

int main() {

int a = 2;

for(int i = 0; i < 5; i++) {

printf(\}

return 0; }

4.读程序,写出结果(5分) int main() {

int a[3]; a[0] = 0; a[1] = 1; a[2] = 2; int *p, *q; p = a; q = &a[2];

int c = a[q - p];

printf(\printf(\return 0; }

5.读程序,写出结果(10分,选做)

#define STRCPY(a, b) strcpy(a ## _p, #b) int main() {

char var1_p[20]; char var2_p[30];

strcpy(var1_p, \strcpy(var2_p, \STRCPY(var1, var2); STRCPY(var2, var1);

printf(\printf(\return 0; )

6.(10分)不用除法(即不能使用\号)和库函数,计算285 / 16的商和余数, 要求效率尽量高.

7.(15分)编写字符串反转函数:strrev.要求时间和空间效率都尽量高.

(测试用例:输入\输出应为\函数原型为:

char* strrev(char* dest, char* src); 提示:dest和src可能是同一个字符串.

8.(15分)编写将字符串转换成整数的函数atoi,只处理输入字符串仅由0-9,'-','+',和空格的情况. 函数原型为:

int atoi(char* str);

提示:如果字符串不能构成一整数,可以直接返回0.但是对于以空格开始的字符串应特殊处理.

9.(30分)编写一组单链表操作函数.链表的结构如下: struct ListNode

3

{

long id;

struct ListNode* next; };

链表节点以id的从小到大排列,当id相等时,节点顺序没有约定,谁在前都可以. 要求编写的函数有:

遍历函数:返回cur下一个节点, 如果没有更多的节点, 返回0;

struct ListNode* ListNext(struct ListNode* cur);

插入函数:将newNode插入到head链表中,然后返回newNode的前一节点,

如果newNode已经在链表中,返回0.

struct ListNode* ListInsert(struct ListNode* head, struct ListNode* newNode);

删除函数:从head中删除theNode节点.返回theNode的前一节点.

如果theNode不在链表中,返回0;

struct ListNode* ListRemove(struct ListNode* head, struct ListNode* theNode); 为什么要使用宏呢?

因为函数的调用必须要将程序执行的顺序转移到函数所存放在内存中的某个地址,将函数的程序内容执行完后, 再返回到转去执行该函数前的地方。这种转移操作要求在转去执行前要保存现场并记忆执行的地址,转回后要恢复现场,并按原来保存地址继续执行。因此,函数调 用要有一定的时间和空间方面的开销,于是将影响其效率。

而宏只是在预处理的地方把代码展开,不需要额外的空间和时间方面的开销,所以调用一个宏比调用一个函数更有效率。

但是宏也有很多的不尽人意的地方。 1、宏不能访问对象的私有成员。 2、宏的定义很容易产生二意性。 我们举个例子:

#define square(x) (x*x)

我 们用一个数字去调用它,square(5),这样看上去没有什么错误,结果返回25,是正确的,但是如果我们用squre (5+5)去调用的话,我们期望的结果是100,而宏的调用结果是(5+5*5+5),结果是35,这显然不是我们要得到的结果。避免这些错误的方法,一 是给宏的参数都加上括号。

#define square(x) ((x)*(x)) 第二部分:内联函数

从上面的阐述,可以看到宏有一些难以避免的问题,怎么解决呢?

内联函数是代码被插入到调用者代码处的函数。如同 #define 宏,内联函数通过避免被调用的开销来提高执行效率,尤其是它能够通过调用(“过程化集成”)被编译器优化。

内 联函数和宏很类似,而区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内 联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。 声明内联函数看上去和普通函数非常相似: void f(int i, char c);

当你定义一个内联函数时,在函数定义前加上 inline

关键字,并且将定义放入头文件: inline void f(int i, char c) {

// ... }

内联函数必须是和函数体申明在一起,才有效。

像这样的申明inline function(int i)是没有效果的,编译器只是把函数作为普通的函数申明,我们必须定义函数体。

inline int function(int i) {return i*i;}

这样我们才算定义了一个内联函数。我们可以把它作为一般的函数一样调用。但是执行速度确比一般函数的执行速度要快。

当然,内联函数也有一定的局限性。就是函数中的执行代码不能太多了,如果,内联函数的函数体过大,一般的编译器会放弃内联方式,而采用普通的方式调用函数。这样,内联函数就和普通函数执行效率一样了。 有上面的两者的特性,我们可以用内联函数完全取代预处理宏。

4. static有什么用途?(请至少说明两种) 1.限制变量的作用域 2.设置变量的存储域

7. 引用与指针有什么区别?

1) 引用必须被初始化,指针不必。

2) 引用初始化以后不能被改变,指针可以改变所指的对象。 2) 不存在指向空值的引用,但是存在指向空值的指针。 8. 描述实时系统的基本特性

在特定时间内完成特定的任务,实时性与可靠性

9. 全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

全局变量储存在静态数据库,局部变量在堆栈 10. 什么是平衡二叉树?

左右子树都是平衡二叉树 且左右子树的深度差值的绝对值不大于1

11. 堆栈溢出一般是由什么原因导致的? 没有回收垃圾资源

12. 什么函数不能声明为虚函数? constructor

13. 冒泡排序算法的时间复杂度是什么? O(n^2)

14. 写出float x 与“零值”比较的if语句。 if(x>0.000001&&x<-0.000001)

16. Internet采用哪种网络协议?该协议的主要层次结构? tcp/ip 应用层/传输层/网络层/数据链路层/物理层 17. Internet物理地址和IP地址转换采用什么协议? ARP (Address Resolution Protocol)(地址解析協議) 18.IP地址的编码分为哪俩部分?

IP地址由两部分组成,网络号和主机号。不过是要和“子网掩码”按位与上之后才能区分哪些是网络位哪些是主机位。 2.用户输入M,N值,从1至N开始顺序循环数数,每数到M输出该数值,直至全部输出。写出C程序。 循环链表,用取余操作做

3.不能做switch()的参数类型是: switch的参数不能为实型。

1、局部变量能否和全局变量重名?

答:能,局部会屏蔽全局。要用全局变量,需要使用\局部变量可以与全局变量同名,在函数内引用这个变量时,会用到同名的局部变量,而不会用到全局变量。对于有些编译器而言,在同一个函数内可以定义多个同名的局部变量,比如在两个循环体内都定义一个同名的局部变量,而那个局

4

部变量的作用域就在那个循环体内

2、如何引用一个已经定义过的全局变量? 答:extern

可以用引用头文件的方式,也可以用extern关键字,如果用引用头文件方式来引用某个在头文件中声明的全局变理,假定你将那个变写错了,那么在编译期间会报错,如果你用extern方式引用时,假定你犯了同样的错误,那么在编译期间不会报错,而在连接期间报错

3、全局变量可不可以定义在可被多个.C文件包含的头文件中?为什么?

答:可以,在不同的C文件中以static形式来声明同名全局变量。

可以在不同的C文件中声明同名的全局变量,前提是其中只能有一个C文件中对此变量赋初值,此时连接不会出错 4、语句for( ;1 ;)有什么问题?它是什么意思? 答:和while(1)相同。

5、do??while和while??do有什么区别?

答:前一个循环一遍再判断,后一个判断以后再循环 6、请写出下列代码的输出内容 #include main() {

int a,b,c,d; a=10; b=a++; c=++a; d=10*a++;

printf(\,c,d:%d,%d,%d\,b,c,d); return 0; }

答:10,12,120

1、static全局变量与普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数与普通函数有什么区别?

全 局变量(外部变量)的说明之前再冠以static 就构成了静态的全局变量。全局变量本身就是静态存储方式, 静态全局变量当然也是静态存储方式。 这两者在存储方式上并无不同。这两者的区别虽在于非静态全局变量的作用域是整个源程序, 当一个源程序由多个源文件组成时,非静态的全局变量在各个源文件中都是有效的。 而静态全局变量则限制了其作用域, 即只在定义该变量的源文件内有效, 在同一源程序的其它源文件中不能使用它。由于静态全局变量的作用域局限于一个源文件内,只能为该源文件内的函数公用, 因此可以避免在其它源文件中引起错误。

从以上分析可以看出, 把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存期。把全局变量改变为静态变量后是改变了它的作用域, 限制了它的使用范围。 static函数与普通函数作用域不同。仅在本文件。只在当前源文件中使用的函数应该说明为内部函数(static),内部函数应该在当前源文件中说明和定义。对于可在当前源文件以外使用的函数,应该在一个头文件中说明,要使用这些函数的源文件要包含这个头文件

static全局变量与普通的全局变量有什么区别:static全局变量只初使化一次,防止在其他文件单元中被引用;

static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次结果值;

static函数与普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝

2、程序的局部变量存在于(堆栈)中,全局变量存在于(静态区 )中,动态申请数据存在于( 堆)中。

3、设有以下说明和定义:

typedef union {long i; int k[5]; char c;} DATE; struct data { int cat; DATE cow; double dog;} too; DATE max;

则语句 printf(\的执行结果是:___52____

答:DATE是一个union, 变量公用空间. 里面最大的变量类型是int[5], 占用20个字节. 所以它的大小是20

data是一个struct, 每个变量分开占用空间. 依次为int4 + DATE20 + double8 = 32. 所以结果是 20 + 32 = 52.

当然...在某些16位编辑器下, int可能是2字节,那么结果int main(){

char* src = \int len = strlen(src);

char* dest = (char*)malloc(len+1);//要为\\0分配一个空间

char* d = dest;

char* s = &src[len-1];//指向最后一个字符 while( len-- != 0 ) *d++=*s--;

*d = 0;//尾部要加\\0 printf(\

free(dest);// 使用完,应当释放空间,以免造成内存汇泄是 int2 + DATE10 + double8 = 20

4、队列和栈有什么区别? 队列先进先出,栈后进先出 5、写出下列代码的输出内容 #include

int inc(int a) {

return(++a); }

int multi(int*a,int*b,int*c) {

return(*c=*a**b); }

typedef int(FUNC1)(int in);

typedef int(FUNC2) (int*,int*,int*);

void show(FUNC2 fun,int arg1, int*arg2) {

INCp=&inc;

int temp =p(arg1);

fun(&temp,&arg1, arg2); printf(\}

main() {

int a;

show(multi,10,&a); return 0; }

答:110

7、请找出下面代码中的所以错误

说明:以下代码是把一个字符串倒序,如“abcd”倒序后变为“dcba”

1、#include\2、main() 3、{

4、 char*src=\5、 char* dest=NULL; 6、 int len=strlen(src); 7、 dest=(char*)malloc(len); 8、 char* d=dest; 9、 char* s=src[len]; 10、 while(len--!=0) 11、 d++=s--;

12、 printf(\13、 return 0; 14、} 方法1:

return 0; }

方法2: #include #include main() {

char str[]=\int len=strlen(str); char t;

for(int i=0; i {

t=str[i];

str[i]=str[len-i-1]; str[len-i-1]=t; }

printf(\return 0; }

1.-1,2,7,28,,126请问28和126中间那个数是什么?为什么?

第一题的答案应该是4^3-1=63

规律是n^3-1(当n为偶数0,2,4) n^3+1(当n为奇数1,3,5) 答案:63

2.用两个栈实现一个队列的功能?要求给出算法和思路! 设2个栈为A,B, 一开始均为空. 入队:

将新元素push入栈A; 出队:

(1)判断栈B是否为空;

(2)如果不为空,则将栈A中所有元素依次pop出并push到栈B;

(3)将栈B的栈顶元素pop出;

这样实现的队列入队和出队的平摊复杂度都还是O(1), 比上面的几种方法要好。3.在c语言库函数中将一个字符转换成整型的函数是atool()吗,这个函数的原型是什么? 函数名: atol

功 能: 把字符串转换成长整型数

用 法: long atol(const char *nptr); 程序例: #include #include

int main(void) {

long l;

char *str = \

5


vc面试题.doc 将本文的Word文档下载到电脑 下载失败或者文档不完整,请联系客服人员解决!

下一篇:2018人教版三年级数学下册全套试卷

相关阅读
本类排行
× 注册会员免费下载(下载后可以自由复制和排版)

马上注册会员

注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信: QQ: