C/C++面试笔试详细总结—程序代码评价或者找错

前言:

本文覆盖了C/C++工程师笔试的90%以上的程序代码评价或者找错,能够在不参考答案的前提下解决以下问题,那么代表你已经具有了扎实的C/C++语法功底和一定的代码分析能力。通过对代码段的评估找错和修改能够更加真实地反映应聘者的分析代码和解决问题的能力整理总结不易,不点赞收藏一波再走嘛?

在这里插入图片描述

☆☆☆强烈推荐☆☆☆干货:C/C++笔试面试详细总结—基本概念及其它问答题 o(*≧д≦)o!!

文章目录

第1题:“有符号整型和无符号整型”

1、下面的代码输出是什么,为什么?

void foo(void)
{
    unsigned int a = 6; int b = -20;
    (a+b > 6) ? puts(> 6) : puts(<= 6");
                                   }

这个问题测试你是否懂得 C 语言中的整数自动转换原则,我发现有些开发者懂得极少这些东西。不管如何,这无符号整型问题的答案是输出是 ">6″。原因是==当表达式中存在有符号类型和无符号类型时所有的操作数都自动转换为无符号类型。因此-20变成了一个非常大的正整数,所以该表达式计算出的结果大于6。==这一点对于应当频繁用到无符号数据类型的嵌入式系统来说是丰常重要的。如果你答错了这个问题,你也就到了得不到这份工作的边缘。

第2题:“处理器存储字节长度”

2、评价下面的代码片断:

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

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

unsigned int compzero = ~0;

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

在这里插入图片描述

int8_t:表示八位的int,int16_t:表示是十六位的int,int32_t:表示是三十二位的int,int64_t:表示是六十四位的int。这样写的好处是提高了代码的可移植性。==这样的写法不用考虑你是多少位的机子,不管你是十六位的还是32位的,此时都以你定义的int的位数为准。==如果你在一个十六位的机子上定义了一个int32_t 的一个整型数,那么此时你的整型数就是32位的。

第3题:“++和+的优先级”

3、 C 语言同意一些令人震惊的结构,下面的结构是合法的吗,如果是它做些什么?

int a = 5, b = 7, c; c = a+++b;

答:

这个问题将做为这个测验的一个愉快的结尾。不管你相不相信,上面的例子是完全合乎语法的。问题是编译器如何处理它?水平不高的编译作者实际上会争论这个问题,根据最处理原则,编译器应当能处理尽可能所有合法的用法。因此,上面的代码被处理成:

c = a++ + b;

因此, 这段代码执行后 a = 6, b = 7, c = 12。

如果你知道答案,或猜出正确答案,做得好。我发现这个问题的最大好处是这是一个关于代码编写风格,代码的可读性,代码的可修改性的好的话题。

第4题:“union和struct的存储占用字节”

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

typedef union {long i; int k[5]; char c;} DATE; 
struct data { int cat; DATE cow; double dog;} too; 
DATE max;
printf(%d”,sizeof(struct date)+sizeof(max));

则语句 printf(“%d”,sizeof(struct date)+sizeof(max));的执行结果是?

答:

结果是:52。DATE 是一个 union, 变量公用空间. 里面最大的变量类型是 int[5], 占用20个字节, 所以它的大小是20;data 是一个 struct, 每个变量分开占用空间. 依次为 int4 + DATE20 + double8 = 32.所以结果是 20 + 32 = 52.当然…在某些16位编辑器下, int 可能是2字节,那么结果是 int2 + DATE10 + double8 = 20

第5题:“a++和++a的区别”

5、请写出下列代码的输出内容

int main()
{
int a,b,c,d; 
	a=10; b=a++; c=++a; d=10*a++;
	printf(“b,c,d:%d,%d,%d”,b,c,d);
return 0;
}

答:10,12,120

第6题:“一个函数的嵌套调用分析”

6、写出下列代码的输出内容:

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

第7题:“字符串的存储’\0’”

7、找出错误

#include string.h main(void)

{ char *src=”hello,world”; char *dest=NULL;

dest=(char *)malloc(strlen(src)); int len=strlen(str);

char *d=dest; char *s=src[len]; while(len–!=0) d++=s–;

printf(%s”,dest);

}

找出错误!!

#include "string.h"
#include <stdlib.h> //头文件加全
#include <stdio.h>
main(){
    char* src="hello world";
    char* dest=NULL;
    int len=strlen(src);
    dest=(char*)malloc(len+1); // 应该多一个字节存放字符串结束符
    char* d=dest;
    char* s=&src[len-1]; // len个字符索引是0到len-1,最后一个字符'd'的索引是 len-1,而且是赋值给指针,所以需要取地址
    while(len--!=0)
        *(d++) = *(s--); // 应该是交换字符,所以要取指针指向的内容
    *d = '\0'; // 最后记得加上字符串结束符
    printf("%s",dest);
    return 0;
}

第8题:“多维数组的下标位置”

8、请问下面程序有什么错误?

int a[60][250][1000],i,j,k; 
for(k=0;k<=1000;k++) 
    for(j=0;j<250;j++) 
        for(i=0;i<60;i++)
			a[i][j][k]=0;

答:把循环语句内外换一下。

第9题:“ 放在不同位置的*指针”

9、以下3个有什么区别

const char *p;// 声明一个指向字符或字符串常量的指针(p所指向的内容不可修改)

char const *p;// 同上

char * const p;//声明一个指向字符或字符串的指针常量,即不可以修改p的值,也就是地址无法修改。

答:

先看第一种,先看p,根据优先级它和*结合,是指针,指向char类型,但是char前面有一个const修饰,所以p所指向的内容为const类型不可修改。

第二种:由于没有const *的运算,若出现const *的形式,则const实际上是修饰前面的,所以char const *p和const char *p一样。

第三种情况:const修饰的是p,p不能修改。

第10题:“数组变量和指针的空间分配”

10、写出下面的结果

char str1[] = "abc"; 
char str2[] = "abc";
const char str3[] = "abc"; 
const char str4[] = "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是指针,它们指向相同的常量区域。

第11题:“sizeof()的用法”

11、以下代码中的两个 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 有问题。根据语法,sizeof 如用于数组,只能测出静态数组的大小,无法检测动态分配的或外部数组大小。函数外的 str 是一个静态定义的数组,因此其大小为6, 函数内的 str 实际只是一个指向字符串的指针,没有任何额外的与数组相关的信息,因此sizeof 作用于上只将其当指针看,一个指针为4个字节,因此返回4

第12题:“&a+1数组偏移和ptr-1地址偏移”

12、写出输出结果

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[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],但是 ptr与(&a+1)类型是不一样的(这点很重要) 所以 ptr-1只会减去 sizeof(int *)a。

&a 的地址是一样的,但意思不一样,a 是数组首地址,也就是 a[0]的地址,&a 是对象(数组)首地址,a+1是数组下一元素的地址,即a[1],&a+1是下一个对象的地址,即 a[5].

第13题:“指针变量的内存分配”

13、请问以下代码有什么问题:

int main()
{
    char a;
    char *str=&a; 
    strcpy(str,"hello"); 
    printf(str);
    return 0;
}

答:没有为 str 分配内存空间,将会发生异常。

问题出在将一个字符串复制进一个字符变量指针所指地址虽然可以正确输出结果,但因为越界进行内在读写而导致程序崩溃。

第14题:“编译器禁止的表达式”

14、有以下表达式:

int a=248; b=4;
int const c=21;
const int *d=&a;
int *const e=&b;
int const *f const =&a;

请问下列表达式哪些会被编译器禁止?为什么?

答:

1.第一句中的第一个分号应该改成逗号;因为这样的语句被编译器解析为b无类型!
2.const int *d=&a;也不能通过编译,因为通过a变量能够更改d的值,这是不允许的!
3.同2,int const *f const =&a;这句也不能通过!

第15题:“交换两个变量的值,不使用第三个变量”

15、交换两个变量的值,不使用第三个变量。即 a=3,b=5,交换之后 a=5,b=3;

答:

有两种解法, 一种用算术算法, 一种用^(异或)

a = a + b;
b = a - b;
a = a - b; 

或者

a = a^b;// 只能对 int,char..
b = a^b; a = a^b; 

或者

a ^= b ^= a;

第16题:“malloc的内存申请和函数调用返回”

16、请指出下列程序中的错误并且修复:

void GetMemory(char *p)
{
	p=(char *)malloc(100);
}
void Test(void)
{
	char *str=NULL;
	GetMemory=(str);
	strcpy(str,"hello world");
	printf(str);
}

答:

错误。参数的值改变后,不会传回
GetMemory并不能传递动态内存,Test函数中的 str一直都是 NULL。
strcpy(str, “hello world”);将使程序崩溃。
修改如下:

char *GetMemory()
{
	char *p=(char *)malloc(100);
	return p;
}
void Test(void)
{
	char *str=NULL;
	str=GetMemory(){
	strcpy(str,"hello world");
	printf(str);
}

方法二:void GetMemory2(char **p)变为二级指针

void GetMemory2(char **p, int num)
{
	*p = (char *)malloc(sizeof(char) * num);
}

第17题:“strcpy的目标字符串空间不够”

17、下面的语句会出现什么结果?

char szstr[10]; strcpy(szstr,0123456789);

答:长度不一样,会造成非法的OS,应该改为 char szstr[11];

第18题:“(void * )ptr 和 ( * (void**))ptr 是否相同”

18、(void * )ptr 和 ( * (void**))ptr 的结果是否相同?

答:其中 ptr 为同一个指针,(void * )ptr 和 (*(void**))ptr 值是相同的。

原因如下:

这是强制类型转化:第一个没问题,就是把ptr强制转化为指向空类型的指针;第二个(* (void* * ))ptr先看里面的 ,这是转化成指向void指针的指针 ,也就是二级指针,再在前面加上*就是取内容 ,那么内容也是个指针,即相当于一级指针,且这个指针是指向空类型的 ,所以强制转化后和第一个是一样的。

第19题:“main函数返回0和返回1”

19、问函数既然不会被其它函数调用,为什么要返回1?

int main()
{
    int x=3; 
    printf(%d”,x); 
    return 1;
}

答:main 中,c 标准认为0表示成功,非0表示错误。具体的值是其中具体出错信息。

第20题:“绝对地址0x100000的跳转执行”

20、要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234; 那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?

答:

*((void (*)( ))0x100000 ) ( );

首先要将0x100000强制转换成函数指针,即: (void (*)())0x100000。然后再调用它: ((void ()())0x100000)(); 用typedef可以看得更直观些:

typedef void(*)() voidFuncPtr;
*((voidFuncPtr)0x100000)();

第21题:“二进制向 int 或uint 转换时的最高位处理”

21、输出多少?并分析过程

unsigned short A = 10; 
printf(~A = %u\n”, ~A); 
char c=128; 
printf(“c=%d\n”,c);

答:

第一题,~A =0xfffffff5,int 值 为-11,但输出的是 uint。所以输出4294967285

第二题,c=0×10, 输出的是 int,最高位为1,是负数,所以它的值就是0×00 的补码就是128, 所以输出-128。

这两道题都是在考察二进制向 int 或uint 转换时的最高位处理。

A=10,为无符号型,转换为二进制为0000 0000 0000 0000 0000 0000 0000 1010

所以~A的二进制位1111 1111 1111 1111 1111 1111 1111 0101即0xFFFFFFF5,如果转换为符号整型的话则为-11,因为输出的是无符号整型,无符号整型的范围为0~4294967295(232 -1),而0xFFFFFFF5转换为无符号十进制整型为4294967285。

第二题,发生溢出,因为有符号字符型其范围为-128~127

127用二进制表示为:0111 1111,128表示为1000 0000,这里发生溢出,因为第一位为1,为符号位,表示负数,即-128。

在这里插入图片描述

第22题:“库函数的内存请求和free释放”

22、分析下面的程序:

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

问输出结果是什么?

答:

free 只是释放的 str 指向的内存空间,它本身的值还是存在的. 所以 free 之后,有一个好的习惯就是将 str=NULL.此时 str 指向空间的内存已被回收,如果输出语句之前还存在分配空间的操作的话,这段存储空间是可能被重新分配给其他变量的,尽管这段程序确实是存在大大的问题,但是通常会打印出world 来。

这是因为,进程中的内存管理一般不是由操作系统完成的,而是由库函数自己完成的

当你 malloc一块内存的时候,管理库向操作系统申请一块空间(可能会比你申请的大一些),然后在这块空间中记录一些管理信息(一般是在你申请的内存前面一点),并将可用内存的地址返回。但是释放内存的时候,管理库通常都不会将内存还给操作系统,因此你是可以继续访问这块地址的

第23题:“sizeof和strlen统计字符个数的区别”

23、char a[10], strlen(a)为什么等于15?运行的结果

#include “stdio.h”
#include “string.h” 
void main()
{
    char aa[10];
    printf(%d”,strlen(aa));
}

答:

sizeof()和初不初始化没有关系; strlen()和初始化有关。

strlen(char*)函数求的是字符串的实际长度,它求得方法是从开始到遇到第一个’\0’,如果你只定义没有给它赋初值,这个结果是不定的,它会从aa首地址一直找下去,知道遇到’\0’停止。

char aa[10];cout<<strlen(aa)<<endl; //结果是不定的
char aa[10]={'\0'}; cout<<strlen(aa)<<endl; //结果为0
char aa[10]="jun"; cout<<strlen(aa)<<endl; //结果为3

sizeof()函数返回的是变量声明后所占的内存数,不是实际长度

sizeof(aa); //返回10
int a[10]; sizeof(a);// 返回40

第24题:“二进制转换十进制”

24、long a=0×801010;a+5=?

答:0×801010 用二进制表示为:“1000 0000 0001 0000 0001 0000”,十进制的值为8392720, 再加上5就是8392725。

第25题:“位域的定义和使用”

25、给定结构 :

struct A
{
char t:4; 
char k:4; 
unsigned short i:8; 
unsigned long m; 
}; 

问 sizeof(A) = ?

答:

给定结构:

struct A
{
char t:4; //4位
char k:4; //4位
unsigned short i:8; //8位
unsigned long m; // 偏移2字节保证4字节对齐
}; // 共8字节

原因如下:

有些信息在存储时,并不需要占用一个完整的字节, 而只需占几个或一个二进制位。例如在存放一个开关量时,只有0和1两种状态, 用一位二进位即可。为了节省存储空间,并使处理简便,C语言又提供了一种数据结构,称为“位域”或“位段”。

所谓“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

位域的定义和位域变量的说明位域定义与结构定义相仿,其形式为:

struct 位域结构名 { 位域列表 }; 其中位域列表的形式为: 类型说明符 位域名:位域长度 例如: struct bs { int a:8; int b:2; int c:6; }; 位域变量的说明与结构变量说明的方式相同。

可采用先定义后说明,同时定义说明或者直接说明这三种方式。例如: struct bs { int a:8; int b:2; int c:6; }data; 说明data为bs变量,共占两个字节。其中位域a占8位,位域b占2位,位域c占6位。

对于位域的定义尚有以下几点说明:

  1. 一个位域必须存储在同一个字节中,不能跨两个字节。如一个字节所剩空间不够存放另一位域时,应从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如: struct bs { unsigned a:4 unsigned :0 unsigned b:4 unsigned c:4 } 这个位域定义中,a占第一字节的4位,后4位填0表示不使用,b从第二字节开始,占用4位,c占用4位。

  2. 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如: struct k { int a:1 int :2 int b:3 int c:2 }; 从以上分析可以看出,位域在本质上就是一种结构类型, 不过其成员是按二进位分配的。

简而言之,这是位域操作的表示方法,也就是说后面加上“:1”的意思是这个成员的大小占所定义类型的1 bit,“:2”占2 bit,依次类推。当然大小不能超过所定义类型包含的总bit数。 一个bytes(字节)是8 bit(bit)。例如你的结构中定义的类型是u_char,一个字节,共8bit,最大就不能超过8。 32位机下, short是2字节,共16bit,最大就不能超过16. int是4字节,共32bit,最大就不能超过32.

依次类推,这样定义比较省空间。例如你上面的结构,定义的变量类型是u_char,是一字节类型,即8bit。 fc_subtype占了4bit,fc_type占2bit,fc_protocol_version占2bit,共8bit,正好是一个字节。 其他八个成员,各占1bit,共8bit,正好也是一个字节。 因此你的结构的大小如果sizeof(struct frame_control)计算,就是2bytes.

第26题:“static的值保留(1)”

26、下面的函数实现在一个数上加一个数,有什么错误?请改正。

int add_n ( int n )
{
    static int i = 100; 
    i += n;
    return i;
}

答:static使得i的值会保留上次的值,去掉static即可。

第27题:“static的值保留(2)”

27、写出程序运行结果

int sum(int a)
{
auto int c=0; static int b=3;
c+=1; b+=2;
return(a+b+c);
}
void main()
{
int I; int a=2;
for(I=0;I<5;I++)
{
printf("%d,", sum(a));
}
}

答:

static 会保存上次结果,记住这一点,剩下的自己写输出:8,10,12,14,16,

第28题:“统计9999的二进制数值中1的个数”

28、求函数返回值,输入x=9999; 结果是什么?

int func ( x )
{
	int countx = 0; 
	while ( x )
	{
		countx  ++; 
		x = x&(x-1);
	}
	return countx;
}

答:

这是统计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的个数是很效率很高的。

不必去一个一个地移位。循环次数最少。

注意: 每执行一次x = x&(x-1),会将x用二进制表示时最右边的一个1变为0,因为x-1将会将该位(x用二进制表示时最右边的一个1)变为0。

第29题:“原码和补码的转换”

29、分析:

struct bit
{ int a:3; int b:2; int c:3;
};
int main()
{
    bit s;
    char *c=(char*)&s; cout<<<endl;
    *c=0x99;
    cout << s.a <<<s.b<<endl<<s.c<<endl; int a=-1;
    printf("%x",a); return 0;
}

答:

输出为什么是4,1,-1,-4,ffffffff ?

因为0x99在内存中表示为 100 11 001 , a = 001, b = 11, c = 100;当 c 为有符合数时, c = 100, 最高1为表示 c 为负数,负数在计算机用补码表示,所以 c = -4;同理b = -1;当 c 为有符合数时, c = 100,即 c = 4,同理 b = 3。

第30题:“类型数值范围和越界访问”

30、下面这个程序执行后会有什么错误或者效果:

#define MAX 255 
int main() {
    unsigned char a[MAX],i; 
    for(i=0;i<=MAX;i++)
        a[i]=i;
    for(i=0;i<=MAX;i++) 
        printf("%d ",a[i]); 
} 

答:

一:越界访问。下标越界可以说是个问题,但不是问题的本质; 当i=MAX;即i=MAX是,对数组来说,255是越界了, 数组越界访问(C/C++不进行数组越界检查)MAX=255,数组A 的下标范为:0…MAX-1

二:死循环。当 i 循环到255时,循环内执行:A[255]=255;

这句本身没有问题…但是返回 for (i=0;i<=MAX;i++)语句时,由于 unsigned char 的取值范围在(0…255),因为 i本身是unsigned char类型,占8位,所以此时i+1变成了100000000 9位,又因为i只能占8位,所以舍掉了最高位,此时的i值又变成00000000了,即就是0,无限循环下去

第31题:“位域的字节对齐(1)”

31、写出 sizeof(struct name1)=,sizeof(struct name2)=的结果:

struct name1{ char str;
short x; int num;
}
struct name2{ char str;
int num; short x;
}

答:

sizeof(struct name1)=8,sizeof(struct name2)=12

在第二个结构中,为保证num 按四个字节对齐,char 后必须留出3字节的空间;同时为保证整个结构的自然对齐(这里是4字节对齐),在 x 后还要补齐2个字节,这样就是12字节。

第32题:“位域的字节对齐(2)”

32、

struct s1
{
int i: 8;
int j: 4;
int a: 3; double b;
};
struct s2
{
int i: 8;
int j: 4; double b; int a:3;
};
printf("sizeof(s1)= %d\n", sizeof(s1)); 
printf("sizeof(s2)= %d\n", sizeof(s2));

答: 16, 24

第一个struct s1:

struct s1
{
    int i: 8;
    int j: 4;
    int a: 3; 
    double b;
};

理论上是这样的,首先是 i 在相对0的位置,占8位一个字节,然后,j 就在相对一个字节的位置,由于一个位置的字节数是4位的倍数,因此不用对齐,就放在那里了,然后是 a,要在3位的倍数关系的位置上,因此要移一位,在15位的位置上放下,目前总共是18位,折算过来是2字节2位的样子,由于 double 是8字节的,因此要在相对0要是8个字节的位置上放下, 因此从18位开始到8个字节之间的位置被忽略,直接放在8字节的位置了,因此,总共是16 字节。

第二个最后会对照是不是结构体内最大数据的倍数,不是的话,会补成是最大数据的倍数。

第33题:“位域的字节对齐(3)”

33、在对齐为4的情况下,分析下列程序的结果:

#include <stdio.h>  
struct BBB  
{  
    long num;  
    char *name;  
    short int data;  
    char ha;  
    short ba[5];  
}*p;  

int main()  
{  
    p = 0x1000000;  
    printf(" p+0x200=0x%08x\n", p+0x200);  
    printf(" (Ulong)p+0x200=0x%08x\n", (unsigned long)p+0x200);  
    printf("(char*)p+0x200=0x%08x\n", (char*)p+0x200);  
    printf("(short*)p+0x200=0x%08x\n", (unsigned short*)p+0x200);  
    printf("(int*)p+0x200=0x%08x\n", (unsigned int*)p+0x200);  
    return 0;  
}  

答:

运行结果为:

p+0x200=0x01003000

(long)p+0x200=0x01000200

(char * )p+0x200=0x01000200

(short * )p+0x200=0x01000400

(int*)p+0x200=0x01000800

假设在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 + 2 + 1 + 1/ * 补齐
/ + 2 * 5 + 2/ * 补齐*/ = 24 bytes (经Dev-C++验证)

p=0x1000000;
p+0x200=__;
=0x1000000 + 0x200*24

(char*)p+0x200 = 0x1000000 + 0x200 * 1 = 0x01000200
(short*)p+0x200 = 0x1000000 + 0x200 * 2 = 0x01000400
(int*)p+0x200 = 0x1000000 + 0x200 * 4 = 0x01000800

结论:
某一类型的指针 + 一个常数 = 这个常数 * 指针所指区域的大小

第34题:“声明和字符串溢出”

34、找错

Void test1()
{
    char string[10];
    char* str1="0123456789";
    strcpy(string, str1);// 溢出,应该包括一个存放'\0'的字符 string[11]
}
Void test2()
{
    char string[10], str1[10]; for(I=0; I<10;I++)
    {
        str1[i] ='a';
    }
    strcpy(string, str1);// I,i 没有声明。
}
Void test3(char* str1)
{
    char string[10];
    if(strlen(str1)<=10)// 改成<10,字符溢出,将 strlen 改为 sizeof 也可以
    {
        strcpy(string, str1);
    }
}

第35题:“*p和**p的应用”

35、写出输出结果:

void g(int**);
int main()
{
    int line[10],i;
    int *p=line; //p 是地址的地址
    for (i=0;i<10;i++)
    {
        *p=i;
        g(&p);//数组对应的值加1
    }
    for(i=0;i<10;i++) printf("%d\n",line[i]); return 0;
}
void g(int**p)
{
    (**p)++; (*p)++;// 无效
}

答:

1,2,3,4,5,6,7,8,9,10

第36题:“ASCII码的常量值存储”

36、给出下面程序的答案

#include
typedef struct AA
{
int b1:5; int b2:2;
}AA;
void main()
{
AA aa;
char cc[100]; 
    strcpy(cc,0123456789abcdefghijklmnopqrstuvwxyz”); 
    memcpy(&aa,cc,sizeof(AA));
cout << aa.b1 < cout << aa.b2 <
}

答: -16和1

首先 sizeof(AA)的大小为4,b1和 b2分别占5bit 和2bit. 经过 strcpy 和 memcpy 后,aa 的4个字节所存放的值是:0,1,2,3的ASCII 码,即00110000,00110001,00110010,00110011

所以,最后一步:显示的是这4个字节的前5位,和之后的2位分别为:10000,和01

因为 int 是有正负之分 所以:答案是-16和1。

第37题:“switch(){case:}语法”

37、分析代码:

int func(int a)
{
    int b; switch(a)
    {
        case 1: 30;
        case 2: 20;
        case 3: 16;
        default: 0
    }
    return b;
}

则 func(1)=?

答:如果case之后没有break;就会顺序执行一直到最后的default,最后返回0。

第38题:“函数调用内存的回收”

38、请问一下程序将输出什么结果?

char *RetMenory(void)
{
    char p[] = “hellow world”; return p;
}
void Test(void)
{
    char *str = NULL; str = RetMemory(); printf(str);
}

答:

RetMenory 执行完毕,p 资源被回收,指向未知地址。返回地址,str 的内容应是不可预测的, 打印的应该是str 的地址。

第39题:“unsigned : 0和unsigned : 2”

39、写出输出结果

typedef struct
{
    int a:2; 
    int b:2; 
    int c:1;
}test; 

test t;
t.a = 1;
t.b = 3;
t.c = 1; 

printf("%d",t.a); 
printf("%d",t.b); 
printf("%d",t.c);

答:

t.a 为01,输出就是1

t.b 为11,输出就是-1

t.c 为1,输出也是-1

位域定义(bit field),不是普通的结构体,a 2 bits,b 2 bits,c 1bit 共5个bit,而一个int有32位。故整个位域结构就一个int。由于b和c都是有符号数,而b的2位都是1,因此就是有符号数字的-1,c同理。

原因如下:

unsigned : 0; 长度为0的位段,作用是使得下一位段从下一个存储单元(存储单元可能是一个字节或者两个字节,因编译系统而异)开始存放。

unsigned : 2; 可定义无名位段,这两位空间不用。

注意位段允许的最大范围,若超出位段a表示的范围,此情况下, 自动取赋予它的数的低位 。

以unsigned char 和 char为例:

typedef struct{
    unsigned char a : 3;
    char b : 3;   

}  test;
int main()
{
    test t;
    t.a = -10;
    t.b = -10;
    printf("%d  %d\n", t.a, t.b);
    return 0;   
} 

先做个实验:

char d = -10;
int e = -10;
printf("%x \n%x\n", d, e);

结果是:
fffffff6
fffffff6
这里我的编译环境sizeof(int) == 4, sizeof(char) == 1
也就是说,对于-10,编译器会给他分配整型大小的空间来表示, 而不管a,b是char还是int
以8位二进制位表示,而不用原本的32位表示了,方便说明位段允许的范围。
[-10]原 = 1000 1010
[-10]反 = 1111 0101
[-10]补 = 1111 0110
因为结构体成员 a,b 分别是unsigned char a : 3;char b : 3;位段(3位)。 此时自动取-10的低3位赋予a,b。
注意,数据在计算机中是以补码形似存在(带符号的整数才这样,浮点数另有其存储方式)的,所以 机器中,a = (110),b = (110),这里110,仍然是补码(其实,无符号数没有原、反、补码的说法),分析输出printf("%d %d\n", t.a, t.b);
因为a是unsigned char,所以会将(110)解释成6;b 是 char,(110) 的最高位1是符号位, 表示负数,即补码(110)表示的值为- 2, 这里参照下面的 补码的几条规定得出:

8位有符号数的补码表示范围:范围是 -128至127. 根据如下:
补码的几条规定:

  1. 若二进制每位全为0,则表示数0
  2. 若最高位(即符号位)为0,表示正数
  3. 若最高位为1, 表示是负数,而该负数的绝对值是多少呢?将每个二进制位(包括符号位)取反加1,得到一个二进制数,将该数看成无符号数,其值就是上述负数的绝对值。

例如,二进制的 10000000 的最高位为1, 所以它表示的是负数。是负的多少呢?我们将其八位全部取反,得到01111111, 然后加1,得到10000000. 将该数看作无符号数,值为128, 故计算机中的10000000表示的是-128。
最高位(即符号位)为1的8位有符号数有128个,故可表示128个负数;最高位为0的8位有符号数有128个,但全0的那个表示数0,所以总共只能表示127个正整数。

第40题:“strcpy()的工作原理”

40、对下面程序进行分析:

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

答: 如果面试者指出字符数组 str1不能在数组内结束可以给3分;如果面试者指出strcpy(string, str1)调用使得从str1内存起复制到string 内存起所复制的字节数具有不确定性可以给7分,在此基础上指出库函数 strcpy 工作方式的给10分。

str1不能在数组内结束:因为 str1的存储为:{a,a,a,a,a,a,a,a,a,a},没有’\0’(字符串结束符),所以不能结束。

strcpy( char *s1,char *s2)他的工作原理是,扫描 s2指向的内存,逐个字符付到 s1所指向的内存,直到碰到’\0’,因为 str1结尾没有’\0’,所以具有不确定性,不知道他后面还会赋值到什么。修改的程序如下:

void test2()
{
    char string[10], str1[10]; int i;
    for(i=0; i<9; i++)
    {
        str1[i] = 'a'+i; //把 abcdefghi 赋值给字符数组
    }
    str[i]='\0';//加上结束符
    strcpy( string, str1 );
}

第41题:“ * (ptr++)+=和 *(++ptr))的应用”

41、分析:

int arr[] = {6,7,8,9,10};
int *ptr = arr;
*(ptr++)+=123;
printf(%d %d ”, *ptr, *(++ptr));

答:

输出:8 8

过程:对于*(ptr++)+=123;先做加法6+123,然后++,指针指向7;对于 printf(“ %d %d ”, *ptr,

*(++ptr));从后往前执行,指针先++,指向8,然后输出8,紧接着再输出8

第42题:“指向相同常量字符串的指针的地址”

42、分析下面的代码:

char *a = "hello"; char *b = "hello"; if(a= =b) printf("YES"); else printf("NO");

答:输出NO。常量字符串hello,位于静态存储区,它在程序生命期内恒定不变。如果编译器优化的话,会有可能 a 和 b 同时指向同一个 hello 的。则地址相同。如果编译器没有优化,那么就是两个不同的地址,则不同。

第43题:“函数调用的参数先后入栈”

43、写出输出结果

#include
void foo(int m, int n)
{
    printf(“m=%d, n=%d\n”, m, n);
}
int main()
{
    int b = 3; foo(b+=3, ++b);
    printf(“b=%d\n”, b); return 0;
}

答:

输出:m=7,n=4,b=7(VC6.0)

这种方式和编译器中得函数调用关系相关即先后入栈顺序。不过不同 编译器得处理不同。也是因为C 标准中对这种方式说明为未定义,所以各个编译器厂商都有自己得理解,所以最后产生得结果完全不同。

因为这样,所以遇见这种函数,我们首先要考虑我们得编译器会如何处理这样得函数,其次看函数得调用方式,不同得调用方式,可能产生不同得结果,最后是看编译器优化。

第44题:“常量字符串只具有可读性”

44、指出下列程序的错误并修改:

char* s="AAA"; 
printf("%s",s);
s[0]='B';
printf("%s",s);

答:

"AAA"是字符串常量,只具有可读性。修改程序如下:

char str[]="AAA";
char *s=str;

这样“AAA”就定义在堆栈里了。

第45题:“类对象的默认构造函数”

45、下列程序是否正确:

struct Test
{
	Test( int ) {}
	Test() {}
	void fun() {}
};
void main( void )
{
	Test a(1);
	a.fun();
	Test b();
	b.fun();
}

答:变量b定义出错。按默认构造函数定义对象,不需要加括号。

第46题:“三元表达式的类型要求”

46、以下代码有什么问题?

cout << (true?1:1) << endl;

答:三元表达式“?:”问号后面的两个操作数必须为同一类型。

第47题:“定义数组的常量要求”

47、 以下代码能够编译通过吗,为什么?

unsigned int const size1 = 2;
char str1[ size1 ];
unsigned int temp = 0;
cin >> temp;
unsigned int const size2 = temp;
char str2[ size2 ];

答:str2定义出错,size2非编译器期间常量,而数组定义要求长度必须为编译期常量。

第48题:“vector定义变量和无符号整型变量的循环使用”

48、 以下反向遍历array数组的方法有什么错误?

vector array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 3 );
for( vector::size_type i=array.size()-1; i>=0; --i ) // 反向遍历array数组
{
cout << array[i] << endl;
}

答:首先数组定义有误,应加上类型参数:vector array。其次vector::size_type被定义为unsigned int,即无符号数,这样做为循环变量的i为0时再减1就会变成最大的整数,导致循环失去控制。修改如下:

int main(void)  
{  
    vector<int> array;  
    array.push_back(1);  
    array.push_back(2);  
    array.push_back(3);  
    for(int i = array.size() - 1 ; i >= 0 ; --i)  
        cout<<array[i]<<endl;  
    return 0;  
}  

第49题:“默认构造函数和带参自定义构造函数的调用”

49、以下代码中的输出语句输出0吗,为什么?

struct CLS
{
	int m_i;
	CLS( int i ) : m_i(i) {}
	CLS()
}
CLS(0);
{
};
CLS obj;
cout << obj.m_i << endl; //没有带参数,调用的是默认构造函数CLS()

答:不能。在默认构造函数内部再调用带参的构造函数属用户行为而非编译器行为,亦即仅执行函数调用,而不会执行其后的初始化表达式。只有在生成对象时,初始化表达式才会随相应的构造函数一起调用。

第50题:“vector的类型声明和erase()的使用”

50、以下代码有什么问题?

typedef vector IntArray;
IntArray array;
array.push_back( 1 );
array.push_back( 2 );
array.push_back( 2 );
array.push_back( 3 );
// 删除array数组中所有的2
for( IntArray::iterator itor=array.begin(); itor!=array.end(); ++itor )
{
    if( 2 == *itor ) array.erase( itor );
}

答:同样有缺少类型参数的问题。另外,每次调用“array.erase( itor );”,被删除元素之后的内容会自动往前移,导致迭代漏项,应在删除一项后使itor–,使之从已经前移的下一个元素起继续遍历。

第51题:“malloc内存的申请和泄漏”

51、下面的代码有什么问题?

void DoSomeThing(...)
{
   char* p;
...
   p = malloc(1024); // 分配1K的空间
   if (NULL == p)
   	return;
...
   p = realloc(p, 2048); // 空间不够,重新分配到2K
   if (NULL == p)
   return;
...
}

答:

一:p = malloc(1024); 应该写成: p = (char *) malloc(1024)。

二:没有释放p的空间,造成内存泄漏。

第52题:“指针的释放原理”

52、下面的代码有什么问题?

class A 
{ 
public: 
   A() { p=this; } 
   ~A() { if(p!=NULL) { delete p; p=NULL; } } 
   A*p; 
};

答:会引起无限递归。原因分析,参考第22题。

第53题:“virtual在继承中的用法”

53、程序改错

class mml
{
private:
	static unsigned int x;
public:
	mml(){ x++; }
	mml(static unsigned int &) {x++;}
	~mml{x--;}
	pulic:
	virtual mon() {} = 0;
	static unsigned int mmc(){return x;}
	...... 
};

class nnl:public mml
{
private:
	static unsigned int y;
public:
	nnl(){ x++; }
	nnl(static unsigned int &) {x++;}
	~nnl{x--;}
	public:
	virtual mon() {};
	static unsigned int nnc(){return y;}
	...... 
};
//代码片断:
mml* pp = new nnl;
..........
delete pp;

答:基类的析构函数应该为虚函数virtual ~mml{x–;}

第54题:“访问基类的私有虚函数”

访问基类的私有虚函数,写出以下程序的输出结果:

#include <iostream.h> class A 
{ 
 	virtual void g() 
 	{ 
 	 	cout << "A::g" << endl; 
 	} 
private: 
 	virtual void f() 
 	{ 
 	 	cout << "A::f" << endl; 
 	} 
}; 
class B : public A 
{ 
 	void g() 
 	{ 
 	 	cout << "B::g" << endl; 
 	} 
 	virtual void h() 
 	{ 
 	 	cout << "B::h" << endl; 
 	} 
}; 
typedef void( *Fun )( void ); void main() 
{ 
 	B b; 
 	Fun pFun; 
 	for(int i = 0 ; i < 3; i++) 
 	{ 
 	 	pFun = ( Fun )*( ( int* ) * ( int* )( &b ) + i ); 
 	 	pFun(); 
 	} 	 
} 

答:
输出结果:
B::g,A::f,B::h
注意:本题主要考察了面试者对虚函数的理解程度。一个对虚函数不了解的人很难正确的做出本题。
在学习面向对象的多态性时一定要深刻理解虚函数表的工作原理。

第55题:“编译时数组长度常量的要求”

55、以下定义对嘛?为什么?

int id[sizeof(unsigned long)];

答:正确,这个 sizeof是编译时运算符,编译时就确定了 ,可以看成和机器有关的常量。

第56题:“sizeof的用法”

写出程序结果:

void Func(char str[100])
{
	printf("%d\n", sizeof(str));
}

答:4(分析:指针长度)

第57题:“数组名和指针的区别”

数组名和指针的区别
请写出以下代码的打印结果:

#include <iostream.h>
#include <string.h>
void main(void)
{
	char str[13]="Hello world!";
	char *pStr="Hello world!";
	cout<<sizeof(str)<<endl;
	cout<<sizeof(pStr)<<endl;
	cout<<strlen(str)<<endl;
	cout<<strlen(pStr)<<endl;
	return;
}

答:
打印结果:
13,4,12,12
注意:一定要记得数组名并不是真正意义上的指针,它的内涵要比指针丰富的多。但是当数组名当做参数传递给函数后,其失去原来的含义,变作普通的指针。另外要注意 sizeof 不是函数,只是操作符。

  • 16
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 19
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Super__Tiger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值