面试题累积-指针与引用(二)
面试题1:写出函数指针、函数返回指针、const指针、指向const的指针、指向const的const指针
void (*f)(){}:这里的f就是返回值为空的函数的指针。
int * f(){return}
const int* p = &a:这里p是const指针,意味着不能修改p的值,但能修改p所指向的值,如p++是错的,*p=10是对的。
int* const *p=&a,不修改p所指向的值,但能修改p的值,如p++可以,但是*p=11是错的。
Const int* const p = &a:综合以上。
面试题2:找错
int max(int x, int y){ return x > y ? x : y; //1 } int _tmain(int argc, _TCHAR* argv[]) { int max(x, y); //2 int *p= &max; //3 int a = 3, b = 2, c = 4, d; d = (*p)((*p)(a, b), c); cout << d << endl; return 0; }
解析:该题存在函数指针使用错误。第2步错,改为int max(int,int),第3步错,改为int *p(int,int)=&max;
扩展知识:函数声明和函数定义
C语言编译系统是由上往下编译的.一般被调函数放在主调函数后面的话,前面就该有声明.不然C由上往下的编译系统将无法识别。
函数的定义是指对函数功能的确立,包括指定函数名,函数值类型、形参及其类型以及函数体等,它是一个完整的、独立的函数单位。
函数的声明的作用则是把函数的名字,函数类型以及形参的类型、个数和顺序通知编译系统,以便在调用该函数时进行对照检查(例如,函数名是否正确,实参与形参的类型和个数是否一致),它不包括函数体。
函数原型则特指包括说明参数类型的函数声明。
面试题3:程序结果为多少
void arrayPoint(){ int v[2][3] = { { 1, 2, 3 }, { 4, 5, 6 } }; int(*a)[3] = v; cout << **a << endl; cout << **(a + 1) << endl; cout << *(*a + 1) << endl; cout << *(a[0] + 1) << endl; cout << *(a[1]) << endl; }
解析:答案为1,4,2,2,4。该题定义一个指针指向一个3个int元素的数组,a+1表明a指针向后移动1*sizeof(数组大小),a+1后共移动12个字节,*a+1仅针对一行向后移动4个字节。
面试题4:一个指向整型数组的指针的定义为
A.int(*ptr)[] B.int *ptr[] C.int *(ptr[]) D.int ptr[]
解析:int(*ptr)[]是一个指向整型数组的指针;int *ptr[]是指针数组,ptr[]里存放的是地址,它指向位置值为*ptr[0],*ptr[1],*ptr[2],不要存*ptr[0]=5,*ptr[1]=6,因为这里没有相应地址。Int *(ptr[])与B相同。
扩展知识:指针数组和数组指针
指针数组:定义为int*p[n],[]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。执行p+1和p=a是错误的;可*p=a,这里*p表示指针数组第一个元素的值。如要将二维数组赋给一指针数组:
int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
数组指针:定义为int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
int a[3][4]; int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][]
面试题5:用变量a给出下面的定义:
a)一个整型数 //int a
b)一个指向整型数的指针 //int *a
c)一个指向指针的的指针,它指向的指针是指向一个整型数 //int *(*int) //int **a
d)一个有10个整型数的数组 //int a[10]
e)一个有10个指针的数组,该指针是指向一个整型数的 //int *a[10]
f)一个指向有10个整型数数组的指针 //int (*a)[10]
g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数int (*f)(int)
h)一个有10个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数 //int (*a[10])(int)
面试题6:以下代码哪会有错
void lostPoint(){ SHO *pin = new SHO; *pin = 5; cout << "*pin" << *pin << endl; delete pin; pin = 0; long *plong = new long; *plong = 1000; cout << "*plong" << *plong << endl; *pin = 20; cout << "*pin" << *pin << endl; cout << "*plong" << *plong << endl; delete plong; }
解析:该题考察迷途指针。
迷途指针:当所指向的对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称迷途指针。若操作系统将这部分已经释放的内存重新分配给另外一个进程,而原来的程序重新引用现在的迷途指针,则将产生无法预料的后果。因为此时迷途指针所指向的内存现在包含的已经完全是不同的数据。通常来说,若原来的程序继续往迷途指针所指向的内存地址写入数据,这些和原来程序不相关的数据将被损坏,进而导致不可预料的程序错误。在*pin=20步中给迷途指针赋值,报错。
扩展知识:空指针、迷途指针和野指针的区别
空指针:使用ptr=0,可将迷途指针改为空指针;
野指针:还没有初始化的指针。每个指针在初始化前都是野指针。
面试题7:C++中有了malloc/free,为什么还需要new/delte?
解析:
相同点:都可用于申请动态内存和释放内存。
不同点:
(1)操作对象有所不同
malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。对于非内部数据类的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加malloc/free。
即new将调用constructor,而malloc不能;delete将调用destructor,而free不能。
(2)使用方式不同
malloc/free使用:
函数malloc的原型如下:void * malloc(size_t size);用malloc申请一块长度为length的整数类型的内存:int *p = (int *) malloc(sizeof(int) * length);涉及“类型转换”和“sizeof”。
1、malloc返回值的类型是void *,所以在调用malloc时要显式地进行类型转换,将void*转换成所需要的指针类型。
2、 malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。
函数free的原型如下:void free( void * memblock );如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。
New/delete使用:
int *p2 = new int[length];new内置了sizeof、类型转换和类型安全检查功能。
在用delete释放对象数组时,留意不要丢了符号‘[]’。例如
delete []objects; //正确的用法
delete objects; //错误的用法
后者相当于delete objects[0],漏掉了另外99个对象。