面试题积累(四)-指针与引用(1)

来源:互联网 发布:fkled编辑软件下载 编辑:程序博客网 时间:2024/06/05 08:30


面试题1:指针与引用的区别。

相同点:都是地址的概念。指针指向一块内存,它的内容就是所指内存的地址;引用是某块内存的别名(指针用操作符’*’’->’,引用使用操作符’.’)。

不同点:(1非空区别。任何情况下都不能使用指向空值的引用。

        2合法性区别。使用引用前不需要测试其合法性,指针应该总被测试,防止为空。

       3可修改区别。引用只能在定义时被初始化一次,以后不可变;指针可变。引用没有constconst的指针不可变。

        4应用区别:以下情况使用指针:一是考虑到存在不指向任何对象的可能,二是需要能够在不同时刻指向不同对象。如果指向一个对象且一旦指向不会改变指向,使用引用。

实战:

void findWrong(){int v1;                //1int v2 = 1024;          //2int v3 = 99;            //3//int &revi;            //4 int &revi2 = v1;         //5 int &revi3 = v1;        //6 int *p;               //7 //*p = 5;              //8 p = &v3;               //9 //const double di;         //10 const double pi = 333.3;      //11const double *pi1;          //12}

解析: 该题中第4步有错,没有初始化,第8步有错,上步中没有指向实际地址,这种情况下赋值不知存放在哪个内存中;第10步有错,未初始化。

扩展知识:声明、定义、初始化和赋值区别。

声明两种情况:一是需要建立存储空间,如int a,在声明时已经分配内存,为定义性声明;二是不需要建立存储空间,如extern int a,其中a在其他文件中定义的。

定义:没有extern就是一个定义,如int a就是一个定义,而且分配内存;一个程序包含不只一个文件,所有文件中同一个变量,必须 总共只能定义一次。如下:

//A.cppint i;//这是变量i的定义 void main() {}//B.cppint i;//这也是变量i的定义
解析: 程序编译正确,但连接时出错。(程序编译过程为预处理-编译-汇编过程-链接程序)

初始化:初始化一定是一个定义语句,但反过来就不对了,如 int aextern int a = 9,因为初始化,所以也是一个定义,不是声明。

赋值:给一个已经定义的变量(不管是否初始化)一个新值,必须定义了,声明是不行的。

面试题2:以下5个函数,哪个能够正确交换?

swap1(int p, int q){int tmp;tmp = q;q = p;p = tmp;}/*swap2(int *p, int *q){int *tmp; *tmp = *q;           //是拷贝不是指向,将*p所指向内存的值拷贝到*tmp*q = *p;             //所指向内存里*p = *tmp;}*/Swap3(int *p, int *q){int *tmp;tmp = q;               //是指向,不是拷贝,tmp指向了*p所指向的q = p;                 //地址p = tmp;}swap4(int *p, int *q){int tmp;tmp = *q;*q = *p;*p = tmp;}Swap5(int &p, int &q){int tmp;tmp = q;q = p;p = tmp;}int main (){    int a=1,b=2;    //swap1(a,b);    //swap2(&a,&b);    //swap3(&a,&b);    //swap4(&a,&b);    //swap5(a,b);    return 0;}

解析:主要考察值传递、引用传递和指针传递。

        swap1传的是值的副本,在函数体内被修改形参pq,但它们是局部变量,不会影响到主函数中的ab,当函数swap1生命结束时,pq在栈中也被删除了。

        swap2传递的是一个地址进去,在函数体内的形参*p*q是指向实际的参数ab地址的两个指针。但是int *temp; *temp=*p;是不符合逻辑的。

        swap3传递的是一个地址,在函数体内的参数*p*q是指向实际参数ab地址的两个指针。这里要注意:int *temp; temp=p;但是int *temp新建了一个指针(但是没分配内存)。temp=p是指向而不是拷贝。temp指向了*p所指向的地址(也就是a)。但是函数swap3不能实现两数的交换,这是因为函数体内只是指针的变化,而对地址中的值却没有变化。

        swap4可以实现两数的交换,因为它修改的是指针所指向地址中的值。

        swap5函数与swap4相似,是一个引用传递,修改的结果直接影响实参。

扩展知识:变量在内存中分布

    变量还有另一个属性,即存储期(即生命期)。存储期可以分为静态存储期和动态存储期。这是由变量的静态存储方式和动态存储方式决定的。

    在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

    栈:就是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量的存储区。里面的变量通常是局部变量、函数参数等。

       堆:就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

       自由存储区:就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

    全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

    常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)

    注:有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量保留上一次函数调用结束时的值。这时就应该指定该局部变量为静态局部变量(static local variable)

扩展知识:指针赋值与复制区别

char *p,*q;   //声明了两个指针变量p=q;         //p、q两指针指向同一内存区域(即指针值复制),指向的地址一样。*p=*q;       //p、q两指针指向的内存的内容是一样的(即指针内容复制),但是指向的地址不一定一样。

面试题3:该程序有什么问题,该如何修改?

char *strA(){    char str[] = "hello world";    return str;}
解析:该函数返回的是局部变量的地址,当调用完后,str释放,返回结果是不确定不安全的,随时可能被收回。

可改成 char *str =“hello world”或者 static char str[]=”hello world”

其中char *str char str[]区别:

char str[] ="hello world";是分配一个局部数组。局部数组是局部变量,它所对应的是内存中的栈。局部变量的生命周期结束后该变量不存在了。

char*str = "hello world";是指向了常量区的字符串,位于静态存储区,它在程序生命期内恒定不变,所以字符串还在。无论什么时候调用 strA,它返回的始终是同一个“只读”的内存块。

实战:

     int a[3];     a[0]=0;a[1]=1;a[2]=2;     int *p,*q;     p=a; q=&a[2];     cout<<a[q-p]<<endl

解析:q实际地址是0x22ff70p的实际地址是0x22ff68,相减为0x08q-p的实际运算是((q的地址-p的地址)/sizeof(int)),即2。故最后结果为2两个指向同一数组的指针相减得到两个指针之间元素的个数。

面试题4

class A {public:    int _a;    A() {        _a = 1;             puts("调用A的构造函数");    }             void print() {        printf("%d\n", _a);    }};  class B : public A {public:    int _a;    B() {        puts("调用B的构造函数");        _a = 2;         }};  int main() {    B b;    b.print();    printf("%d\n", b._a);    return 0;}

解析:B中的_aA中的_a覆盖,创建B类的对象时,先调用A类的构造函数,再调用B类的构造函数。所以创建对象b之后,A类的_a1B类中的_a2。若要实现b.print();输出2,可以在B中重写print函数实现

1 0