C开发工程师常见面试题

1:什么预编译?
预编译又称预处理,是整个编译过程最先做的事情,即程序执行前的一些预处指令理工作。主要处理#开头的指令。如拷贝#include包含的文件代码、替换#define定义的宏、条件编译#if等。
2:写一个“标准”宏,这个宏输入两个参数并返回较小的一个。
#define Min(x,y) ((x)<(y)?(x):(y))
3:如何避免头文件被重复包含?
使用条件编译:

#ifndef HEAD_H
#define HEAD_H
/**/
#endif

4:static关键字的作用?
1). 在函数体,一个被声明为静态的变量在这一函数被调用过程中维持其值不变。
2). 在模块内(但在函数体外),一个被声明为静态的变量可以被模块内所用函数访问,
但不能被模块外其它函数访问。它是一个本地的全局变量。
3). 在模块内,一个被声明为静态的函数只可被这一模块内的其它函数调用。那就是,这个函数被限制在声明它的模块的本地范围内使用。

5:const关键字的作用?
声明常变量:
使得指定的变量不能被修改。
const int a = 5; //a的值一直为5,不能被改变
const int b; b=10; //b的值被赋值为10后,不能被改变
const int* ptr; //ptr为指向整型常量的指针,ptr的值可以修改,但不能修改其所指向的值
int* const ptr; //ptr为指向整型的常量指针,ptr的值不能被修改,但可以修改其指向的值
修饰函数形参:
使得形参在函数内不能被修改。
int fun(const int a)或者int fun (const char* str)
修饰函数返回值:
使得函数的返回值不能被修改。
const char* getstr(void); 使用:const *str = getstr();
6:volatile关键字的作用?
volatile指定的关键字可能被系统、硬件、进程/线程改变,强制编译器每次从内存中取得该变量的值,而不是从优化后的寄存器中读取。例子:硬件时钟,多线程被多个任务共享的变量。
7:extern关键字的作用?
用于修饰变量或函数,表明改变量或函数都是在别的文件中定义的,提示编译器在其他文件中寻找定义:
extern int a;
extern int *p;
extern int array[];
extern void fun(void);
用于extern “c”
extern "c"的作用就是为了能够正确实现C++代码调用其他C语言代码。加上extern "c"后。会指示编译器这部分代码按照C语言的编译方式进行编译,而不是C++的。
8:size of关键字的作用?
size of的结果等于对象或者类型所占的内存字节数。sizeof的返回值类型为size_t
变量:int a; sizeof(a)=4;
指针:int *p; sizeof§=4;
数组:int b[10]; sizeof(b)=40; 为数组的大小
结构体: struct {int a; char ch;}s1; sizeof(s1)为8,与结构体字节对齐有关
9: 函数参数入栈的顺序?
C语言函数参数入栈顺序是从右向左的,这是由编译器决定的,更具体的说是函数调用约定决定了参数的入栈顺序。C语言采用的函数调用约定是_cdecl的,所以对于函数的声明,完整的形式是:int_cdecl func(int a,int b);
10: inline内联函数?
inline关键字仅仅是建议编译器做内联展开处理,即是将函数直接嵌入调用程序的主体,省去了调用/返回指令。
11: malloc/free和new/delete的区别?
malloc/free是C/C++语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用malloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不再编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。
12: C++的类和C里面的struct有什么区别?
C++中的类具有成员保护功能,并且有继承,多态等特点。而C里的struct没有,C里面的struct没有成员函数,不能继承,派生。
13: 找错题
试题1:

void test1()
{
	char string[10];
	char* str1="0123456789";
	strcpy(string,str1);
}

解析:字符串str1有11个字节(包括末尾的结束符\0),而数组string大小只有1讴歌字节,故而使用strcpy会导致数组string越界。
试题2:

void test2()
{
	char string[10],str1[10];
	int i;
	for(i=0;i<10;i++)
	{
		str1 = 'a';
	}
	strcpy(string,str1);
}

解析:因为str1没有结束符’\0’,故而strcpy复制的字符数不确定。
试题3:

void test3(char* str1)
{
	char string[10];
	if(strlen(str1)<=10)
	{
		strcpy(string,str1);
	}
}

解析:应修改为if(strlen(str1)<10),因为strlen的计算结果没有包含最后的结束符\0
试题4:

void GetMemory(char* p)
{
	p=(char* )malloc(100);
}
int main()
{
	char *str = NULL;
	GetMemory(str);
	strcpy(str,"hello world");
	printf("%s",str);
	return 0;
}

解析:C语言中的函数参数为传值参数,在函数内对形参的修改并不能改变对应实参的值。故而调用GetMemory后,str仍然为NULL。
试题5:

char *GetMemory(void)
{
	char p[] = "hello world";
	return p;
}
void main()
{
	char *str = NULL;
	str = GetMemory();
	printf("%s",str);
}

解析:GetMemory中,p为局部变量,在函数返回后,该局部变量被回收,故而str仍为NULL。
试题6:

void main()
{
	char *str = (char* )malloc(100);
	strcpy(str,"hello");
	free(str);
}

解析:在free(str)后,str未设置为NULL,后面对str的操作可能使它变成一个野指针。
试题7:

swap(int* p1,int* p2)
{
	int* p;
	*p = *p1;
	*p1 = *p2;
	*p2 = *p;
}

解析:在swap函数中,p是个野指针,可能指向系统区,导致程序运行崩溃。故而,应改为

swap(int* p1,int* p2)
{
	int p;
	p = *p1;
	*p1 = *p2;
	*p2 = p;
}

14:编程题
判断字符串str2 是否在字符串str1里面。

#include<stdio.h>
#define OK 1
#define ERROR 0
int str_str(const char *str1,const char *str2)
{
	if(str1 ==NULL)
	{
		return (str2 ==NULL)?OK:ERROR;
	}
	if (str2 ==NULL)
	{
		return OK;
	}
	for(; *str1!= '\0' ; str1++)
	{
		if(*str1 ==*str2)
		{
			for(s1= str1, s2 = str2 ; ;)
			{
				if( *++s2 == '\0')
				{
						return OK;
				}else if(*++s1 != *s2)
				{
						break;			
				}
			}
		}
	}
	return ERROR;
}

15:用和指针的区别是什么?
解析:1)引用必须被初始化,指针不必;
2)引用初始化以后不能被改变,指针可以改变所指的对象;
3)不存在指向空值的引用,但存在指向空值的指针。
指针通过某个变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;
而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
16:.h文件中的ifnedef/define/endif的作用?
解析:防止给头文件被重复引用
17:#include<file.h> 与 #include"file.h"的区别?
解析:前者是从标准库里面查找文件,后者是从当前工作路径查找文件
18:全局变量和局部变量在内存中是否有区别?
解析:全局变量储存在静态数据区,局部变量在堆栈中。
19:如何引用一个已经定义过的全局变量?
解析:可以通过引用头文件的方式,也可以用extern关键字
20:语句for( ; 1 ; )有什么问题?它是什么意思?
解析:和while(1)相同,无限循环。
21:static全局变量和普通的全局变量有什么区别?static局部变量和普通局部变量有什么区别?static函数和普通函数有什么区别?
解析:1)全局变量的前面加上了static就构成了静态的全局变量。全局变量本身就是静态存储方式,静态全局变量也是静态存储方式。这两者在存储方式上并无不同。这两者的区别在于静态全局变量限制了其作用域,即只在定义该变量的源文件内有效,在其他源文件中不能使用它。
2)把局部变量改变为静态变量后是改变了它的存储方式即改变了它的生存周期。把全局变量改为静态全局变量是改变了它的作用域,限制了它的使用范围。
3)普通函数和static函数作用域不同。只在当前源文件中使用的函数应该说明为内部函数(static)
4)static全局变量与普通的全局变量有什么区别:static全局变量只初始化一次,防止在其他文件中被引用
static局部变量和普通局部变量有什么区别:static局部变量只被初始化一次,下一次依据上一次的结果值
static函数和普通函数有什么区别:static函数在内存中只有一份,普通函数在每个被调用中维持一份拷贝。

22:程序的内存分配?
解析:一个由C/C++编译的程序占用的内存分为以下几个部分
1)栈区(stack) 一般由编译器自动分配释放,存放函数的参数值,局部变量的值等等。其操作方式类似于数据结构中的栈
2)堆区(heap) 一般由程序员分配释放。
3)全局区(static) 全局变量和静态变量的存储是在同一块区域的。初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。程序结束后由系统释放。
4)常量区

int a = 0;    //全局初始化区
char* p1;	   //全局未初始化区
void main()
{
	int b;   //栈
	char s[] = "abc";   //栈
	char *p2;  //栈
	char *p3 = "123456"   //字符串在常量区,p3在栈区
	static int c = 0 ;   //全局(静态)初始化区
	p1= (char* ) malloc(10);
	p2 = (char* )malloc(20);         //通过malloc分配得来的区域在堆区
}

23、解释堆和栈的区别?
解析:
(1):申请方式
stack由系统自动分配:例如,在函数中声明一个局部变量int b; 系统自动在栈中为b开辟空间
heap 需要由程序员自己申请:并指明大小,在C中malloc函数,在C++中用new运算符
(2):申请后系统的响应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将异常提示栈溢出
堆:首先应该知道操作系统有一个记录空间内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆节点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一样正好等于申请的大小,系统会自动将多余的那部分重新放入空闲链表中。
(3):申请大小的限制
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统预先规定好的,在Windows下,栈的大小是2M,如果申请的空间超过栈的剩余空间时,将提示overflow,因此,能从栈获得的空间较小。
堆:是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地址。

24:什么是预编译?何时需要预编译?
解析:预编译又称为预处理,是做些代码文本的替换工作。处理#开头的指令,比如拷贝#include包含的文件代码,#define宏定义的替换,条件编译等。
25:关键字const是什么意思?
解析:只读
const int a; //常整型数
const int *a; //指向常整型数的指针
int *const a ; //指向整型数的常指针
26:关键字volatile有什么含意,并给出三个不同的例子。
解析:一个定义为volatile的变量是说这变量可能会被意想不到地改变,这样,编译器就不会去假设这个变量的值了。精准地来说,在用到这个变量时必须每次都小心地重新读取这个变量,而不是使用保存在寄存器里的备份。
(1)并行设备的硬件寄存器,如状态寄存器
(2)一个中断服务子程序中会访问到的非自动变量
(3)多线程应用中被几个任务共享的变量
27:一个参数既可以是const还可以是volatile吗?解释为什么。
解析:可以。
const修饰的变量在程序里面是不可以被改变的。但是可以被程序外的东西修改。最常见的例子是外部端口的值,它的变化可以不用程序内的任何赋值语句就有可能被改变的。这种情况就可以用volatile来修饰。
28:一个指针可以是volatile 吗?解释为什么
解析:volatile关键字修饰的变量意思为值可能会改变,指针的值是可以改变的。
29:结构和联合有什么区别?
解析:结构和联合都是由多个不同的数据类型成员组成,但在任何同一时刻,联合中只存放了一个被选中的成员。
对于联合的不同成员赋值,将会对其它成员重写,原来成员的值就不存在了。而对于结构的不同成员赋值是互不影响的。
30:简述数组和指针的区别?
解析:数组要么在静态存储区(如全局数组)被创建,要么在栈上被创建,指针可以随时指向任意类型的内存块。
(1)修改内容上的差别
char a[] = “hello” ;
a[0] = ‘x’ ;
char *p = “world” ; //指针p指向字符串常量
p[0] = ‘X’ ; //编译器不能发现该错误,运行时错误
(2)用运算符 sizeof 可以计算出数组的大小,sizeof§ ,p为指针得到的是一个指针变量的字节数,而不是p所指的内容容量。
31:
z=x>y? x : y 的含义?

if(x>y){
z=x;
}else{
z=y;
}

32:如何判断一段程序是由C编译还是由C++编译的?
#ifdef _cplusplus
cout << “c++” ;
#else
cout << “c” ;
#endif
33:嵌入式系统中经常要用到无限循环,你怎么样用C编写死循环呢?
while(1)
{
};

34:位操作
嵌入式系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a, 写两段代码,第一个设置a的bit3 , 第二个清除a的bit 3 。在以上两个操作中,要保持其他位不变。

#define bit3 (0x1 << 3)
static int a;
void set_bit3 (void)
{
	a | = bit3;
}
void clear_bit3 (void)
{
	a &= ~bit3;
}

35:访问固定的内存位置
嵌入式系统经常要求程序员去访问某特定的内存位置,在某工程中,要求设置一绝对地址为0x67a9的整型变量的值为0xaa66。
解析:访问一绝对地址把一个整型数强制转换为一指针
int *ptr;
ptr = (int *)0x67a9 ;
*ptr = 0xaa66 ;

36:用变量a给出下面的定义
a) 一个整型数 int a;
b) 一个指向整型数的指针 int *a ;
c) 一个指向指针的指针,它所指向的指针是指向一个整型数 int **a ;
d) 一个有10个整型数的数组 int a[10] ;
e) 一个有10个指针的数组,该指针是指向一个整型数的。 int *a[10] ;
f ) 一个指向有10个整型数数组的指针 int (*a)[10] ;
g) 一个指向函数的指针,该函数有一个整型参数并返回一个整型数。 int (*a)(int) ;
h) 一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数。
int (*a[10])(int) ;

37:写一个“标准”宏
解析:交换两个参数值的宏定义为:#define swap(a,b)

38:下面的代码输出是什么?为什么?
void fun(void)
{
unsigned int a = 6;
int b = -20 ;
(a+b > 6) ? puts(">6") : puts("<=6") ;
}

39:设有以下说明和定义
typedef union {long i; int k[5]; char c;} DATA;
struct data {int cat; DATA cow; double dog;} too;
DATA max;
则语句printf("%d",sizeof(struct data)+sizeof(max)); 的执行结果是?
解析:结果是:52
DATA是一个union。变量公用空间,里面最大的变量类型是int k[5],占用20个字节,所以联合体的大小是20

40:写出下列代码的输出内容
#include<stdio.h>
int main()
{
int a,b,c,d;
a = 10;
b = a++; //先调用a,再递增a,
c = ++a; //先递增a,在调用a
d = 10*a++ ; //先调用a,再递增a,此时d=120,然后a递增变成13
printf("%d,%d,%d",b,c,d);
return 0;
}
解析:10,12,120

40:写出下面代码的输出内容

#include<stdio.h>
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("%d/n",*arg2);
}
int main()
{
	int a;
	show(multi,10,&a);
	return 0;
}

解析:110

41:请找出下面代码中的所有错误,以下代码是吧一个字符串倒序,如“abcd”倒序后变成“dcba”

#include<string.h>
#include<stdio.h>
int main()
{
	char *src = "hello world";
	char *des = NULL;
	int len = strlen(src);
	des = (char *)malloc(len);
	char *d = des;
	char *s = &src[len];
	while(len-- != 0)
	d++ = s--;
	printf("%s",des);
	return 0;
}
#include<string.h>
#include<stdio.h>
int main()
{
	char *src = "hello world";
	char *des = NULL;
	int len = strlen(src);
	des = (char *)malloc(len+1);    //要为\0 分配一个字节
	char *s = &src[len-1];          //指向最后一个字符
	while(len-- != 0)
	{
		des++ = s--;
	}
	*des = '\0' ;      //尾部要加\0
	printf("%s",des);
	free(des)       //使用完,应当释放空间,以免造成内存泄漏
	return 0;
}

42:请问下面程序会出现什么情况?

#define Max_CB 500
void LmiQueryCSmd(Struct MSgCB * pmsg)
{
	unsigned char ucCmdNum;
     .....  
for(ucCmdNum=0;ucCmdNum<Max_CB;ucCmdNum++)
{
     ......;
}

解析:进入死循环

43:以下3个有什么区别
char *const p //常量指针,指针的值不可修改
char const *p //指向常量的指针,指向的常量值不可以改
const char *p //和char const *p 一样

44:写出下面的结果

char  str1[] = "abc" ;
char  str2[] = "abc" ;
char *str3 = "abc" ;
char *str4 = "abc" ;
cout << (str1 == str2) << endl ;
cout << (str3 == str4) << endl ;

解析:0 1
str1和str2是数组变量,它们有各自的内存空间。
str3和str4是指针,它们指向相同的常量区域。

45:写出输出结果

int main()
{
	int a[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[5]
&a+1 不是首地址+1,系统会默认为加一个a数组的偏移,是偏移了一个数组的大小(本题是5个int的大小)
此时ptr = a[5] ;
原因是:
&a是数组指针,其类型为int (*)[5] ;

46:交换两个变量的值,不适用第三个变量
即a=3,b=5交换之后a=5,b=3
解析:a=a+b ;
b=a-b;
a=a-b;

47:下面的语句会出现什么结果?
char str[10] ;
strcpy(str,“01234567890”);
解析:字符串‘\0’ 占一个字节,应该改为char str[11]

48:main函数既然不会被其他函数调用,为什么要返回1?
解析:main中,C标准认为0表示成功,非0表示错误,具体的值是某中具体出错信息

49:下面的程序输出结果是什么?

void GetMemory(char **p,int num)
{
	*p = (char *) malloc(num) ;
}
int main()
{
	char *str = NULL;
	GetMemory(&str,100) ;
	strcpy(str,"hello") ;
	free(str);
	if(str != NULL)
	{
		strcpy(str,"world") ;
	}
	printf("\n str is %s",str) ;
	getchar();
}

解析:输出str is world ,free只是释放的str指向的内存空间,它本身的值还是存在的。

50:0x801010用二进制表示为:1000 0000 0001 0000 0001 0000 ,十进制的值为8392720

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值