面试题1:什么是引用?引用有什么作用?
答案:引用就是一个目标变量的别名,对引用的一切操作和对变量的直接操作是一样的。主要用作函数的参数、
函数返回值和常引用。
面试题2:简述为什么引入常引用,常引用有什么作用?
答案:常引用的引入主要是为了避免使用变量的引用时,在不知情的情况下改变变量的值。常引用主要用于定义
一个普通变量的只读属性的别名、作为函数的传入形参,避免实参在调用函数中被以外地改变。
面试题3:流操作符重载为什么返回引用?
答案:在程序中,流操作符<<和>>经常连续使用。因此这两个操作符的返回值应该是一个仍旧支持这两个操
作符的流引用。其他的数据类型都无法做到这一点。
面试题4:说明以下声明的含义
A: int(**p)[10];
B: char*(*p)[10];
C: float(*p[10])();
D: double*((*p)[10]);
E: short(*p)(char);
F: long(*(*p)(int,int)(int)
答案:
A: p为数组指针的指针,数组有10个元素,元素为int型数据。
B: p为数组的指针,数组有10个元素,元素为char*型数据。
C: p为数组,数组有10个元素,元素为函数指针类型,这个函数没有参数,返回值为float型数据。
D: p为数组的指针,数组有10个元素,元素为double*型数据。
E: p为函数指针,它所指向的函数有一个char型的参数,返回值为short型数据。
F: p为函数指针,它所指向的函数有两个int型的参数,返回值为函数指针型数据,这个返回的函数指针指向
的函数有一个int型的参数,返回值为long型数据。
面试题5:简述指针常量与常量指针的区别
什么是指针常量?什么是常量指针?两者有什么区别?
答案:指针常量是定义了一个指针,这个指针的值只能在定义时初始化,在其他地方不能改变。常量指针是指定
义了一个指针,这个指针指向一个只读的对象,不能通过常量指针来改变这个对象的值。
指针常量强调的是指针的不可改变性,而常量指针强调的是指针对其所指对象的不可改变性。
面试题6:写出以下代码的输出结果:
#include <iostream.h>
void main(void)
{
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
运行结果:
1
1
面试题7:找出代码的错误并改正。
#include <stdio.h>
void swapxy(char *a,char *b)
{
char x = *a, y = *b;
x = x + y;
y = x - y;
x = x - y;
*a = x, *b = y;
return;
}
void main(void)
{
char a = 'a', b = 'b';
char &x = a; &y = b;
printf("a = %c and b = %c before swap.\n",a,b);
swapxy(x,y);
printf("a = %c and b = %c after swap.\n",a,b);
return;
}
答案:
#include <stdio.h>
void swapxy(char &a, char &b)
{
char x = a, y = b;
x = x + y;
y = x - y;
x = x - y;
a = x, b = y;
return;
}
void main(void)
{
char a = 'a', b = 'b';
char &x = a, &y = b;
printf("a = %c and b = %c before swap.\n",a,b);
swapxy(x,y);
printf("a = %c and b = %c after swap.\n",a,b);
return;
}
运行结果:
a = a and b = b before swap.
a = b and b = a after swap.
面试题8:写出代码的输出结果
#include <iostream.h>
void main(void)
{
int b = 0;
int a[5] = {0,1,2,3,4};
for(int i = 0, i < 5)
{
i = a[i+1];
cout<<a[i]<<endl;
}
return;
}
答案:输出结果为:1 2 3 4 0 1 2 3 4 0 1 2 3 4……
(注意结果是一个死循环,重复输出1 2 3 4 0)
面试题9:下面这段程序有问题吗?如果没有,请写出打印结果。
#define MAX 255
# include <stdio.h>
void main(void)
{
unsigned char str[MAX],i;
for( i = 0; i <= MAX; i++)
{
str[i] = i;
printf("%d\n",str[i]);
}
}
答案:这个程序是死循环加数组越界访问(C/C++不进行数组越界检查)。
面试题10:a和&a有什么区别
请写出以下代码的打印结果。
# include <stdio.h>
void main(void)
{
int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a+1);
printf("%d,%d\n",*(a+1),*(ptr-1));
return;
}
答案:2,5
运行结果:
2,5
解析:*(a+1)就是a[1],但是*(ptr-1)并不是a[0],因为&a+1并不是数组a的首地址加1,
系统会认为是数组a的首地址加上一个数组a的偏移,即偏移了一个数组的大小,(本例
中是5个int的地址)。原式int*ptr(int*)(&a+1);的结果是,ptr指向了数组的第六个元
素a[5]。可参考如下程序:
# include <stdio.h>
void main(void)
{
int a[5] = {1,2,3,4,5};
int *ptr = (int*)(&a+1);
printf("&a :%d\nptr:%d\n",&a,ptr);
return;
}
运行结果:
&a :1703724
ptr:1703744
从结果可以看出,ptr比数组a的首地址偏移了20个字节的地址,即偏移了5个int型数据,原因是&a是
数组a的指针,其类型为int(*)[5],而指针加1要根据指针类型加上一定的值,不同类型的指针加1后
的结果不同。可参考如下代码:
# include <stdio.h>
void main(void)
{
int i = 1;
char c = 2;
double f = 3.0;
char A[3] = {0,1,2};
int *pi = &i;
char *pc = &c;
double *pf = &f;
char (*pA)[3] = &A;
printf("&i:%d pi:%d pi+1:%d\n",&i,pi,pi + 1);
printf("&c:%d pc:%d pc+1:%d\n",&c,pc,pc + 1);
printf("&f:%d pf:%d pf+1:%d\n",&f,pf,pf + 1);
printf("&A:%d pA:%d pA+1:%d\n",&A,pA,pA + 1);
return;
}
运行结果:
&i:1703740 pi:1703740 pi+1:1703744
&c:1703736 pc:1703736 pc+1:1703737
&f:1703728 pf:1703728 pf+1:1703736
&A:1703724 pA:1703724 pA+1:1703727
面试题11:以下代码有什么问题?
#include <iostream.h>
void main(void)
{
char *p1,*p2;
char ch[12];
char **p;
p1 = ch;
pp = &ch;
p2 = *pp;
cout<<p1<<endl;
cout<<pp<<endl;
cout<<p2<<endl;
return;
}
答案:(1)数组ch没有初始化。
(2)把数组指针赋值给字符的二级指针。
修改方案:
#include <stdio.h>
void main(void)
{
char *p1;
char *p2;
char **pp;
char ch[] = "abcd";
printf("sizeof p1:%d,p2:%d,pp:%d,ch:%d\n",
sizeof(p1),sizeof(p2),sizeof(pp),sizeof(ch));
p1 = ch;
pp = &p1;
p2 = *pp;
if(p1 == p2)
{
printf("p1 equals p2\n");
}
else
{
printf("p1 doesn't equal p2\n");
}
return;
}
运行结果:
sizeof p1:4,p2:4,pp:4,ch:5
p1 equals p2
面试题12:数组名和指针的区别 请写出以下代码的打印结果:
#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
面试题13:请解析(*(void(*)())0)()的含义
请解析下面这条语句:
(*(void(*)())0)();
答案:从头到尾解析结果如下
(1)void(*0)():是一个返回值为void,参数为空的函数指针0。
(2)(void(*)())0:把0转变成一个返回值为void,参数为空的函数指针。
(3)*(void(*)())0:在上句的基础上加*表示整个是一个返回值为void,无参数,并且起始
地址为0的函数的名字。
(4)(*(void(*)()0)(): 这就是上句的函数名所对应的函数的调用。
面试题14:指出程序的错误
指出下面这段程序的错误并改正:
#include<stdio.h>
void main()
{
int max(x,y);
int *p = max;
int a,b,c,d;
scanf("%d,%d,%d",a,b,c);
d = p(p(a,b),c);
printf("d:%d\n",d);
return;
}
int max(int x,int y)
{
return(x > y ? x:y);
}
答案:
(1)int max(x,y); 函数声明错误,改为:int max(int,int);
(2)int *p=max; 指针定义错误改为:int(*p)(int,int)=max;
(3)scanf("%d%d%d",a,b,c); 库函数使用错误,改为scanf("%d%d%d",&a,&b,&c);
(4)d=p(p(a,b),c); 函数指针调用函数错误,改为d=(*p)((*p)(a,b),c);
修改后:
#include<stdio.h>
void main()
{
int max(int,int);
int (*p)(int,int) = max;
int a,b,c,d;
scanf("%d %d %d",&a,&b,&c);
d = (*p)((*p)(a,b),c);
printf("d:%d\n",d);
return;
}
int max(int x,int y)
{
return(x > y ? x:y);
}
运行结果:
45
78
23
d:78
面试题15:如何避免“野指针”
“野指针”是如何产生的?应该如何避免?
答案:“野指针”产生原因及解决办法如下:
(1)指针变量声明时没有被初始化。解决办法:指针声明时初始化,可以使具体的地址值,也可以让它指向NULL。
(2)指针p被free或delete之后,没有置为NULL。解决办法:指针指向的内存空间被释放后指针应该指向NULL。
(3)指针操作超越了变量的作用范围。解决办法:在变量的作用域结束前释放掉变量的地址空间,并且让指针指向NULL。
面试题16:程序是否正确
下面这段程序是否正确?如果正确,请写出打印结果:
#include <iostream.h>
typedef int* pInt;
void swap(int *p1,int *p2)
{
int *p;
*p = *p1;
*p1 = *p2;
*p2 = *p;
}
void main(void)
{
int a = 1, b = 3;
pInt p1 = &a, p2 = &b;
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
swap(p1,p2);
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
return;
}
答案:不正确,指针p没有被初始化就直接使用,其为“野指针”会导致不可预知的错误。
修改后:
#include <iostream.h>
typedef int* pInt;
void swap(int *p1,int *p2)
{
int p;
p = *p1;
*p1 = *p2;
*p2 = p;
}
void main(void)
{
int a = 1, b = 3;
pInt p1 = &a, p2 = &b;
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
swap(p1,p2);
cout<<"a:"<<*p1<<" b:"<<*p2<<endl;
return;
}
运行结果:
a:1 b:3
a:3 b:1
面试题17:指出程序的错误
#include <stdio.h>
class A
{
public:
A()
{
number = 0;
};
void Fun1()
{
printf("In Fun1 number is:%d\n",number);
};
void Fun2()
{
printf("In Fun2\n");
}
public:
int number;
};
void f(A *p, int i);
void main(void)
{
A *p;
f(p,5);
if(p != NULL)
{
p->Fun1();
p->Fun2();
}
}
void f(A *p,int i)
{
if(p == NULL)
{
A a;
a.number = i;
p = &a;
}
}
答案:在主函数中调用函数f后,p是“野指针”,用p调用函数Fun1时,会出现内存错误,因为在Fun1
中用到了类A的成员变量number,而类的成员变量属于类的成员对象,在类成员对象作用域外对其的所
有访问都是非法的。
面试题18:简述C/C++程序编译的内存分配情况
在C/C++中不同性质的数据在编译时存放在不同的位置,请简述C/C++程序编译时的内存分配情况。
答案:
(1)栈区(stack):由编译器自动分配释放,存放为运行函数而分配的局部变量、函数参数、返回数据、
返回地址等。其操作方式类似于数据结构中的栈。
(2)堆区(heap): 一般有程序员分配释放,若程序员不释放,程序结束时可能由系统回收。分配方式
类似于链表。
(3)全局区(静态区)(static): 存放全局变量、静态数据、常量。程序结束后由系统释放。
(4)文字常量区: 常量字符串就是放在这里的。程序结束后由系统释放。
(5)程序代码区:存放函数体(类成员函数和全局函数)的二进制代码。
面试题19:以下四段代码中哪段没有错误?
请看这四段代码:
代码一:
void GetMemory(char *p)
{
p = (char *)malloc(100);
}
void Test(void)
{
char *str = NULL;
GetMemory(str);
strcpy(str,"hello world");
printf(str);
}
代码二:
char* GetMemory(void)
{
char p[] = "hello world";
return p;
}
void Test(void)
{
char *str = NULL;
str = GetMemory()();
printf(str);
}
代码三:
void GetMemory(char **p,int num)
{
*p = (char *)malloc(num);
}
void Test(void);
{
char *str = NULL;
GetMemory(&str,100);
strcpy(str,"hello");
printf(str);
}
代码四:
void Test(void)
{
char **str = (char *)malloc(100);
strcpy(str,"hello");
free(str);
}
答案:四段代码全有错误。
*代码一:GetMemory(char *p)的参数为字符型指针,在这个函数修改参数p的值时并不能
真正修改实参的值。
%代码二:其中的p[]数组是函数GetMemory中的局部变量,函数返回后,p就被释放掉,
str便指向了一段无用的内存区域。
*代码三:没有判断动态内存申请是否成功而直接使用,没有释放动态申请的内存,造成
内存泄漏。
*代码四:没有判断动态内存申请是否成功而直接使用,动态内存释放后没有将指针置空。