C语言面试题(五)--------------------网上题目
1.下面的代码输出是什么,为什么?(考查有符号类型与无符号类型之间的转换)
void foo(void) {
unsigned int a = 6;
int b = -20;
(a+b > 6) ? puts("> 6") : puts("<= 6");
}
这个问题测试你是否懂得C语言中的整数自动转换原则;
这无符号整型问题的答案是输出是“>6”。
原因是当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。 因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。
2.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 在编译时处理,不是简单的字符串替换;
3.要对绝对地址0x100000赋值,我们可以用
(unsigned int*)0x100000 = 1234;
那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
*((void (*)( ))0x100000 ) ( );
首先要将0x100000强制转换成函数指针,即:
(void (*)())0x100000
然后再调用它:
*((void (*)())0x100000)();
用typedef可以看得更直观些:
typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();
4.【题目】有一个整数数组,现要求实现这个整数数组的循环右移。如:1,2,3,4,5 则循环右移两位后结果是:4,5,1,2,3。
方法一:(最最容易想到的办法)
void RightCircleShift_00(int buffer[],int shift)
{
int i,j,tt;
for(i=0;i<shift;i++)
{
tt = buffer[ARRSIZE-1];
for(j=ARRSIZE-1;j>0;j--)
buffer[j] = buffer[j-1];
buffer[0] = tt;
}
}
这个办法是用两个循环来控制,内循环每次向右移动一位,外循环则用来限制移动的位数。算法需要执行 ARRSIZE * ShiftValue次,时间复杂度是O( N2 )。
5.运算符优先级问题
给出下面程序的运行结果:
int main()
{
if( 0 & 1 == 0)
printf("0 & 1 == 0/n");
else
printf("0 & 1 != 0/n");
if( 0 & 1 != 0)
printf("0 & 1 != 0/n");
else
printf("0 & 1 == 0/n");
system("pause");
return 0;
}
答案是:
0 & 1 != 0
0 & 1 == 0
而不是我们想象中的
0 & 1 == 0
0 & 1 == 0
== 和 != 运算符优先级要高于 &、^、|、&&、|| 运算符,所以,
if( 0 & 1 == 0) 相当于 if( 0 & (1 == 0) ) 执行else。
if( 0 & 1 != 0) 相当于 if( 0 & (1 != 0) ) 执行else。
这个面试题不是要求我们强记住运算符的优先级,而是因为这个问题让很多程序员想当然是这样,结果最后debug后,才发觉是自己吃了亏,程序运行结果并不是自己想要的结果。这仅仅是告诉C/C++程序员一个很容易犯错误的陷阱。
6.去除数组中重复数字问题
有一个大小为100的数组,里面的数字均介于1到99之间,但是里面的数字有重复,请写个函数去除数组中的重复数字。
#define INIT_NUM -1
方法一:(最最容易想到的办法)
void RemoveBufferRepNum_00(int buffer[])
{
int i,j;
for(i=0;i<BUFFERSIZE;i++)
{
for(j = i+1;j<BUFFERSIZE;j++)
{
if(buffer[i] == buffer[j])
{
buffer[i] = INIT_NUM;
break;
}
}
}
for(i=0,j=0;i<BUFFERSIZE;i++)
{
if(buffer[i] == INIT_NUM)
continue;
buffer[j++] = buffer[i];
}
while(j < BUFFERSIZE)
buffer[j++] = INIT_NUM;
}
这个算法最简单,时间复杂度是O(N2)
7.c语言面试题及答案
编写一个程序,其功能为:计算并输出:sum=1!+2!+...+n!
输入
一个在0到10之间(含0和10)的整数n。
输出
sum=1!+2!+...+n!
样例输入
2
样例输出
sum=3
c语言面试题解答:
int sum=0,i,n,n1=1;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
n1*=i;
sum+=n1;
}
printf("sum=%d\n",sum);
8.一个指向有10个整形数组的指针: int (*a)[10].
9.预处理标识#error的目的: 停止编译并显示错误信息,其目的就是保证程序是按照你所设想的那样进行编译的。
10.static的作用?
1.在函数体内,一个被申明为静态的变量,在这一函数被调用的过程中,维持其值不变
2.在函数体外(在模块内),一个被声明为静态的变量可以被模块内其他函数调用,但不能被模块外的其他函数调用,即作为一个本地的全局变量
3.在模块内,一个函数被声明为静态的,只能被模块内的其他函数调用,即被限制在声明它的模块的本地范围使用
11.const的好处?(const的作用参见之前的博文)
1.告诉用户这个参数的应用目的
2.通过给优化器一些附加的信息,使用const也能产生很紧凑的代码
3.合理使用const可以使编译器很自然的使用那些不希望被改变的参数,以防止其被无意的改变
12.什么是typedef?
typedef用以声明一个已经存在的数据类型的同义字
注意以下的例子:
typedef (struct *) tps
#define dps (struct *)
声明下面的变量:
dps p1,p2; //p1为指向结构体的指针, p2为一个结构体
tps p3, p4; //p3,p4 都是结构体
13.说说内存的分配方式
1.静态存储区分配。内存在程序编译的时候就已经分配好了内存,这块内存在程序的整个运行期间都是存在的
2.在栈上创建。 在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束的时候,存储单元被自动释放
3.从堆上分配,即动态存储方式。程序运行的时候用fmalloc 和 new ()来分配内存,程序员自己使用free()和delete()来释放内存。
14.const和#define 的区别
const的作用: 定义常量、 修饰函数参数、 修饰函数的返回值。 被const修饰的东西都受到强制保护,可以预防意外的变动,提高程序的健壮性
1)const常量有数据类型,#define 没有数据类型。编译器可以对前者进行类型安全检查,而#define只是简单的进行字符替换;并且在字符替换的时候会产生意想不到的错误。
2) 有些集成化的调试工具可以对const常量进行调试,但不能对#define进行调试
15.引用和指针的区别?
1)引用必须被初始化,指针不必
2)引用初始化后就不能改变,但指针可以
3)不存在指向空值的引用,但是指针可以执行控制 void *a;
16.结构体和联合体的区别
1)联合体和结构体都可以存放不同的数据类型,联合体所有成员共用一段存储空间,在任何同一时刻,存储空间只存放被选中的成员
2)结构体所有成员都有独立的存储空间。
3) 对于联合体的不同成员赋值,将会对其他成员重写,原来的成员值就不存在了。结构体中不同成员赋值,不受影响
17.如何判断一段程序是由c编译还是由C++编译?
#ifdef _cplusplus
cout<<"c++"
#else
cout<<"c"
#endif
18.#inclue<a.h> 和 #inclue"a.h"的区别
前者是从标准库函数的路径寻找a.h, 后者是自定义的文件,或者说是从当前路径中寻找
19.实时系统的基本特性
在特定的时间完成特定任务,实时性与可靠性
20.堆栈溢出一般是由什么导致的?
1)没有回收垃圾资源
2)层次太深的递归调用
21.