【c/c++】C语言笔试题总结2

总结2

C语言笔试题总结1,参见【c/c++】C语言笔试题总结1

1. 请编写一个c函数,该函数将一个字符串逆序。

答:具体的代码如下,这里主要使用的就是一个数组元素的位置交换:

#include <stdio.h>

/*
函数说明:将原始字符串逆序
函数参数:src:需要逆序的字符串
返回值  :逆序后的字符串
*/
char * ReverseStr(char *src) {
	int index;
	int length;	//src的长度
	char tmp;	//临时存放字符
	int count;	//存放循环次数

	length = strlen(src);
	count = length / 2;

	for (index = 0; index < count; index++) {
		tmp = src[index];
		src[index] = src[length - 1 - index];
		src[length - 1 - index] = tmp;
	}

	return src;
}

int main(void)
{
	char s[] = "abcdefg";
	printf("%s\n", s);		//打印abcdefg
	printf("%s\n", ReverseStr(s));	//打印gfedcba
	return 0;
}

上面使用的是数组交换,还可以使用递归的方式:

/*
函数说明:将原始字符串逆序
函数参数:src:需要逆序的字符串
          dest:存放临时的排序数据
返回值  :逆序后的字符串
*/
char * ReverseStr2(char *src, char *dest) {
	int src_length;
	int des_length;
	char tmp;

	src_length = strlen(src);
	des_length = strlen(dest);

	if (src_length == 0) {
		return dest;
	}
	tmp = src[src_length - 1];
	src[src_length - 1] = 0;
	dest[des_length] = tmp;
	dest[des_length + 1] = '\0';
	return ReverseStr2(src, dest);
}

int main(void)
{
	char s[] = "abcdefg";
	char tmp[100] = "";//需要足够大,初始字符串长度为0
	printf("%s\n", s);//打印abcdefg
	printf("%s\n", ReverseStr2(s, tmp));//打印gfedcba
	return 0;
}

2. Consider the following code:

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
	int i = 1;
	char buf[4];
	strcpy(buf, "AAAA");
	printf("%d\n", i);
	return 0;
}

a) When compiled and executed on x86, why does this program usually not output what the programmer intended? 
b) Name several ways in which the security problem that causes this program not to output what the programmer intended can be prevented WITHOUT changing the code.

答:这个题目的考点是栈数据的覆盖。因为"AAAA"实际上占据了5个字节(最后有个'\0'),所以根据变量压栈的方式,以及x86下的小端模式,会导致"/0"覆盖1,得到的结果就变成了0。

但是,实际在windows10下vs2015中,结果并不是像上面说的那样,在debug模式下,程序会报错;在release模式下,程序正常运行。且无论是哪种模式,i的值都还是1。

所以说这个问题还是根据编译器不同来决定的,至于vs2015的编译器到底做了什么,可以通过将代码反汇编来确定。

不过作为一个笔试题,能够回答上面就OK了吧,具体先就不追究了。

3. 打印结果是多少?

答:参见具体的代码。(这个题目作为笔试题目是不是太简单了......有什么玄机?)

#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]) {
	int w = 1, x = 2, y = 3, z = 4;
	int m;
	m = (w < x) ? w : x;	//m = 1
	m = (m < y) ? m : y;	//m = 1
	m = (m < 2) ? m : z;	//m = 1
	printf("m = %d\n", m);  //m = 1   
	return 0;
}

4. 说出结果:

答:参见具体代码。这题考察的是对文件的基本操作:

#include <stdio.h>
int main()
{
	FILE *fp;
	int i, a[4] = { 1,2,3,4 }, b;
	fp = fopen("data.dat", "wb");			//只写打开或新建一个二进制文件;只允许写数据
	for (i = 0; i < 4; i++) {
		fwrite(&a[i], sizeof(int), 1, fp);	//写入1,2,3,4
	}
	fclose(fp);
	fp = fopen("data.dat", "rb");			//读写打开一个二进制文件,允许读写数据
	fseek(fp, -2L * sizeof(int), SEEK_END);         //从尾部开始找数据,这里指向的是3
	fread(&b, sizeof(int), 1, fp);			//读取指定的位置,就是前面你说的3
	fclose(fp);
	printf("b = %d\n", b);				//所以结果是打印3

	return 0;
}

5. 下面代码会输出123吗?123创建在堆上还是栈上呢?123的空间是什么时候释放的?

#include <stdio.h>
char * GetStr()
{
	char *tmp;
	tmp = "123";
	return tmp;
}

void main()
{
	printf("%s", GetStr());
}

答:会。这里需要区别一点,“123”并不是像普通变量一样放在栈中的,而是存放在.rdata段中,它在编译阶段就确定了。如果编译出来是一个二进制,你可以在二进制中找到这个字符串。所以说,它并不会因为退出函数而消失,因此就能够打印出来。它的释放需要在程序结束的时候。

6. 字符指针、浮点数指针、以及函数指针这三种类型的变量哪个占用的内存最大?为什么?

答:都是指针,占用的一样大。具体见下面的代码:

#include <stdio.h>
int main(void)
{
	char *p1;
	float *p2;
	void(*fp)(void);
	printf("p1: %d, p2: %d, fp: %d\n", sizeof(p1), sizeof(p2), sizeof(fp));	//p1: 4, p2: 4, fp: 4
	return 0;
}

7. char **p, a[16][8];  问:p=a是否会导致程序在以后出现问题?为什么?

答:本身并不会报错,但是还是会存在问题。因为a的实际类型应该是char (*)[8],而不是p的char **。

8. 应用程序在运行时的内存包括代码区和数据区,其中数据区又包括哪些部分?

答:数据区包含堆栈等动态的数据,和全局变量、静态变量和字符串等静态数据,且字符串数据是只读的。

9. 用<<,>>,|,&实现一个WORD(2个字节)的高低位交换。

答:参见具体代码:

#include <stdio.h>
int main(void)
{
	unsigned short num = 0xABCD;            //unsigned short在本机上是两个字节的
	printf("num: 0x%x\n", num);		//num: 0xabcd
	unsigned char tmp;
	tmp = num >> 8;				//获取高字节
	num = (num << 8) | tmp;
	printf("num: 0x%x\n", num);		//num: 0xcdab

	return 0;
}

10. 写出程序运行结果:

答:参见具体代码,这里主要考察的是static在非初次进出函数时并不会重新赋值:

#include <stdio.h>
int sum(int a)
{
	auto int c = 0;		//值都是0
	static int b = 3;	//值分别是3,5,7,9,11
	c += 1;			//值都是1
	b += 2;			//值分别是5,7,9,11,13
	return (a + b + c);	//值分别是8,10,12,14,16
}
void main()
{
	int i;
	int a = 2;
	for (i = 0; i < 5; i++){
		printf("%d\n", sum(a));	//打印结果分别是分别是8,10,12,14,16
	}
}

11. 写出函数func(1)的返回值:

答:参见具体的代码。这里主要考察的是switch的一种错误写法,忘了break:

#include <stdio.h>
int func(int a)
{
	int b;
	switch (a)
	{
	case 1: 
		b = 30;
	case 2:
		b = 20;
	case 3:
		b = 16;
	default:
		b = 0;
	}
	return b;
}

void main()
{
	printf("%d\n", func(1));		//返回0
}

12. 写出程序运行的结果:

答:参见代码。本题考察的是指针的加减法运算:

#include <stdio.h>

void main()
{
	int a[3];
	a[0] = 0; a[1] = 1; a[2] = 2;
	int *p, *q;
	p = a;			//p指向a[0]
	q = &a[2];		//q指向a[2]

	printf("%d\n", a[q - p]);	//q - p = 2,所以返回的是a[2] = 2;

}

13. 某文件中定义的静态全局变量(或称静态外部变量)其作用域是 (  )    A.只限某个函数 B.本文件C.跨文件 D.不限制作用域
答:B. 本文件。

14. #include “filename.h”和#include <filename.h>的区别?

答:对于#include <filename.h>编译器从标准库开始搜索filename.h;对于#include “filename.h”编译器从用户工作路径开始搜索filename.h

15. 头文件的作用是什么。

答:1) 提供接口,而不需要提供实现;2)加强类型安全检查。如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

16. 内存的分配方式的分配方式有几种?

答:一、从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。

二、在栈上创建。在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

三、从堆上分配,亦称动态内存分配。程序在运行的时候用malloc或new申请任意多少的内存,程序员自己负责在何时用free或delete释放内存。动态内存的生存期由我们决定,使用非常灵活,但问题也最多。

17. 内联函数在编译时是否做参数类型检查?

答:内联函数要做参数类型检查,   这是内联函数跟宏相比的优势。

18. 三个float:a,b,c,问 (a+b)+c==(b+a)+c,(a+b)+c==(a+c)+b 是否成立。
答:两者都可能不行。在比较float或double时,不能简单地比较。由于计算误差,相等的概率很低。应判断两数之差是否落在区间(-e,e)内。这个e应比浮点数的精度大一个数量级。 

19. int i=(j=4,k=8,l=16,m=32); printf("%d", i); 输出是多少? 

答:本题目考察的是","运算符的使用,i=32。

20. 下面代码中的sizeof的使用是否有问题?

void UpperCase(char str[]) // 将 str 中的小写字母转换成大写字母
{
	for (size_t i = 0; i<sizeof(str) / sizeof(str[0]); ++i)
		if ('a' <= str[i] && str[i] <= 'z')
			str[i] -= ('a' - 'A');
}
char str[] = "aBcDe";
cout << "str字符长度为: " << sizeof(str) / sizeof(str[0]) << endl;
UpperCase(str);
cout << str << endl;

答:函数中的sizeof有问题,因为这里的str是参数,它表示的是char *,而不能表示实际的数组,因此sizeof的值是4,而不是真正数组的大小。

注意,这里代码是c++的,并不是c。,不过不影响题目的意思。

21. 全局变量如int i=5; int*(pf)()=foo; 分别在何时被初始化?

答:全局变量的话,在编译的时候就初始化了。函数指针指向具体的函数,需要在链接的时候确定。

22. 以下代码运行结果是什么?

答:参见具体代码。这里考察的是变量的作用域:

#include <stdio.h>
int main()
{
	int a, b, c, abc = 0;
	a = b = c = 40;
	if (c)
	{
		int abc;
		abc = a*b + c;	//abc只在if作用范围内
	}
	printf("%d,%d\n", abc, c);	//abc还是0,c=40
	return 0;
}

23. 请定义一个宏,比较两个数a、b的大小,不能使用大于、小于、if语句。

答:参见具体的代码:

#define MAX(a,b) (((a)-(b))&(1<<31))?(b):(a) 

24. 如何输出源文件的标题和目前执行行的行数。

答:使用预编译宏:

printf("%s %d\n", __FILE__, __LINE__);  

25. 全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

答:一些变量在整个程序中都是可见的,它们称为全局变量。一些变量只能在一个函数中可知,称为局部变量。这就是他们的区别。在任何函数外面定义的变量就是全局变量,在函数内部定义的变量是局部变量,这是它们在程序中的实现过程。操作系统和编译器是根据程序运行的内存区域知道他们的,程序的全局数据放在所分配内存的全局数据区,程序的局部数据放在栈区。

26. 局部变量能够和全局变量重名?

答:可以,但是有覆盖。

27. 如何引用一个定义过的全局变量?

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

28. 请写出下列代码的输出内容:

答:参见具体代码说明,这里主要考察符号优先级的内容:

#include <stdio.h>
main()
{
	int a, b, c, d;
	a = 10;
	b = a++;
	c = ++a;
	d = 10 * a++;
	printf("b,c,d:%d,%d,%d\n", b, c, d); // b,c,d:10,12,120
	return 0;
}

29. 以下代码的运行结果是:

答:参见具体代码:

#include <stdio.h>
#define product(x) ((x)*(x))
main()
{
	int i = 3, j, k;
	j = product(i++); // i++ * i++ = 9
	k = product(++i); // ++i * ++i = 7 * 7 = 49,原来i的值已经是5了,还要再经过两个++,所以是7
	printf("%d %d\n", j, k); // 9 49
}

注意:这里的结果只在VS编译器上成立,使用GCC会得到不同的结果,参考【c/c++】c语言的自增操作在不同编译器的差别_jiangwei0512的博客-CSDN博客

30. 以下代码的运行结果是:

答:参见具体的代码:

#include <stdio.h>

main()
{
	unsigned char i = 0xA5;
	unsigned char j = ~i >> 4 + 1; // 真正的执行过程应该是((~i) >> (4 + 1))
	printf("j = %d\n", j);	//250
}

首先执行过程已经写了:(~i)>>(4+1),但是还有另外一个需要注意的是,i的类型是unsigned char,它在~操作中会被提升为int,见具体的反汇编代码:

	unsigned char i = 0xA5;
0038178E C6 45 FB A5          mov         byte ptr [i],0A5h  
	unsigned char j = ~i >> 4 + 1;	//真正的执行过程应该是((~i) >> (4 + 1))
00381792 0F B6 45 FB          movzx       eax,byte ptr [i]  
	unsigned char j = ~i >> 4 + 1;	//真正的执行过程应该是((~i) >> (4 + 1))
00381796 F7 D0                not         eax  
00381798 C1 F8 05             sar         eax,5  
0038179B 88 45 EF             mov         byte ptr [j],al  
	printf("j = %d\n", j);

从上述的反汇编代码可以看到,i的值被传到了eax中,得到的结果是0x000000A5,再执行NOT之后变成了0xFFFFFF5A,在右移5位,变成了0xFFFFFFFA,然后再取其中的低8位,得到0xFA,即十进制的250。

31. 以下代码的运行结果:

#include <stdio.h>
int main()
{
	int arr[] = { 1, 2, 3, 4 };
	int *ptr = arr;
	printf("%d %d\n", *ptr, *(++ptr));
}

答:vs2015中的打印是2 2。

之前网上看到的答案说“因为参数在函数的传递时是从右到左的,所以会先运行一遍++ptr”。

但是实际上c标准中并没有什么规定,所以其实并不一定会++ptr先运行,真正的答案应该是“不确定”...

32. 以下代码的运行结果:

#include <stdio.h>
int main()
{
	unsigned int a = 0xFFFFFFF7;
	unsigned char i = (unsigned char)a;
	char *b = (char *)&a;
	printf("%08x, %08x\n", i, *b);
	printf("%d, %d\n", i, *b);
}

答:

000000f7, fffffff7
247, -9

需要注意fffffff7这个结果!

33. 下面代码的打印结果是什么:

#include <stdio.h>

typedef struct A
{
	int num:2;
}A;

int main()
{
	A a;
	a.num = 1;
	a.num++;
	printf("%d\n", a.num);
	return 0;
}

这个题目有点意思,今天诺基亚面试的时候遇到的。它看似考察的是struct,其实考察的是数的取值范围。

对于最基本的类型char,它有8位,取值范围是-128~127,即-2^7~2^7-1。

而对于A.num,它只有2位,所以对应的取值范围应该是-2^1~2^1-1,即-2~1这四个数。

所以a.num=1的时候再自增的话,就变成了-2。

结果打印就是-2。

  • 4
    点赞
  • 25
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值