zoukankan      html  css  js  c++  java
  • c++面试题中经常被面试官面试的小问题总结(二)(本篇偏向指针知识)

    原文作者:aircraft

    原文链接:https://www.cnblogs.com/DOMLX/p/10713204.html

    一套面试题的目录在此,还在继续完善中。。。。。。

    c/c++ 2019面试题目录

    1.利用指针交换两个字符串方法?(这题是我当年读大一的时候看到的,好怀念!!!QAQ)

    (一)指针引用

    #include<iostream>
    using namespace std; 
    void swap(char *&a,char *&b)
    {
        char *temp;
        temp = a;
        a = b;
        b = temp;
        
    }
    int main()
    {
        char *ap = "hello";
        char *bp = "word";
        
        swap(ap,bp);
        
        cout<<"ap:"<<ap<<endl;
        cout<<"bp:"<<bp<<endl;
        
        return 0;
    }

    (二)二维指针指向一维

    #include<iostream>
    using namespace std; 
    void swap(char **a,char **b)
    {
        char *temp;
        temp = *a;
        *a = *b;
        *b = temp;
        
    }
    int main()
    {
        char *ap = "hello";
        char *bp = "word";
        
        swap(&ap,&bp);
        
        cout<<"ap:"<<ap<<endl;
        cout<<"bp:"<<bp<<endl;
        
        return 0;
    }

    2.参数引用--查找下面程序错误

    #include<iostream>
    using namespace std; 
    
    const float pi = 3.14f;
    float f;
    
    float f1(float r)
    {
        f = r*r*pi;
        return f;
    }
    
    float &f2(float r)
    {
        f = r*r*pi;
        return f;
    }
    int main()
    {
        float f1(float=5);
        float &f2(float=5);
        float a = f1();
        float &b = f1(); //虽然返回的好像是一个全局变量,但是函数在处理的时候
                         //编译器机制返回的依然是一个临时建立的temp变量里面存放的是f内的值,对其进行引用报错   
        float c = f2();
        float &d = f2();//函数定义返回值的时候加了引用,此时不会生成临时变量
    //直接返回全局变量f,这种定义最节省空间,但是要注意全局变量f生存周期要大于引用d
    //这里是安全的
        
        d += 1.0f;
        
        cout<<"a:"<<a<<endl;    
        cout<<"b:"<<b<<endl;
        cout<<"c:"<<c<<endl;
        cout<<"d:"<<d<<endl;
        cout<<"f:"<<f<<endl;
        
        
        return 0;
    }    

    3.下面输出是什么?

     int a[5]={1,2,3,4,5};
     int *ptr=(int *)(&a+1);
     printf("%d,%d/n",*(a+1),*(ptr-1));

    答案:输出:2,5
      *(a+1)就是a[1],*(ptr-1)就是a[4],执行结果是2,5
      &a+1不是首地址+1,系统会认为加一个a数组的偏移,是偏移了一个数组的大小(本例是5个int)
      &a是数组指针,其类型为 int (*)[5];

    4.复杂的指针声明

    简单级别:

    a-一个整型数

    b-一个指向整型数的指针

    c-一个指向指针的指针,它指向的指针是指向一个整型数的

    d-一个有十个整型数的数组

    e-一个有十个指针的数组,该指针指向一个整型数

    f-一个指向十个整型数数组的指针

    g-一个指向函数的指针,该函数有一个整型参数并返回一个整型数

    答案:

    a: int a;
    b: int *a;
    c: int **a;
    d: int a[10];
    e: int *a[10];
    f: int (*a)[10];
    g: int (*a)(int);

    复杂级别:

    a-一个有十个指针的数组,该指针指向一个函数,该函数有一个整型参数并返回一个整型数

    b-func是一个指向数组的指针,这个数组的元素是函数指针,这些指针指向有int*类型的形参,返回值为Int类型的函数

    c-func是一个函数指针,这类函数具有int*类型的形参,返回值是指向数组的指针,所指向的元素是5个int元素的数组

    答案:

    a: int (*a[10])(int);
    b: int (*(func)[5])(int *p);
    c: int(*(func)(int *p))[5];

    5.指针数组与数组指针(这个大一刚学的时候真的很混乱!!)

    (不管是数组指针还是指针数组,像这像的词前半区都是修饰词,修辞后面,数组指针本质是个指针,指向一个数组。指针数组本质是个数组,数组里存放的是指针)

    ---同理:指针常量和常量指针。函数指针和指针函数都可以这么理解

    数组指针(也称行指针)
    定义 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][]

    所以数组指针也称指向一维数组的指针,亦称行指针。

     

    指针数组
    定义 int *p[n];
    []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
    如要将二维数组赋给一指针数组:
    int *p[3];
    int a[3][4];
    p++; //该语句表示p数组指向下一个数组元素。注:此数组每一个元素都是一个指针
    for(i=0;i<3;i++)
    p[i]=a[i]
    这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]
    所以要分别赋值。

    这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。
    还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。
    比如要表示数组中i行j列一个元素:
    *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j]

    优先级:()>[]>*

    6.下面输出的是什么?

    int main()
    {
        char * str[] = {"Welcome","to","Forteedia","Nanjing"} ;
        char **p = str + 1;
        str[0] = (*p++) +2;
        str[1] = *(p+1);
        str[2] = p[1] + 3 ;
        str[3] = p[0] + (str[2] - str[1]);
        
        printf("%s
    ",str[0]);
        printf("%s
    ",str[1]);
        printf("%s
    ",str[2]);
        printf("%s
    ",str[3]);    
        return 0;
    }
    答案:
    (空)
    Nanjing
    jing
    g
    
    其他应该没有疑问就说一下第一个空 。
    char **p = str + 1; 这句指向“to”
    str[0] = (*p++) +2;这里(*p++),p本身为一个二维指针,*号已经是指向一维。++的优先级高于*,意思就是*(p++)
    但是后置++先执行完此行代码在自加的 先不管
    这里如果是*p+1的话那么str【0】输出的是“o”,
    也就是*p指向“to”的t,*p+1指向o,*p+2指向“to”之后的字符串结尾符“”,为空
    代码逻辑表示为str[1][2];而“to”这里只有str[1][0],str[1][1];
    好了执行完毕 后置++运行 p=p+1;指向"Forteedia"也就是str[2] 这就是后面str[3] = g;的伏笔

    觉得自己完全理解这题的指针知识点了吗,那么p[1]+3这行执行完毕p[0]为什么指向‘j’了呢?
    可以在评论区写出你的回答,也可以关注我,下篇做出解释,hhhhh

    答案就是 前面的伏笔 p的指向str[2],修改str[2] = p[1] + 3 ; 就是修改 p=p[1] + 3 ;
    所以p[0]就是“jing”的首地址 (str[2] - str[1])这个等于3不用说了吧 N到j的地址距离

     

    7.代码改错-函数指针的使用(下面代码有什么问题?打印三个数中最大者)

    #include<iostream>
    using namespace std; 
    
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    
    int main()
    {
        int *p;
        int a,b,c;
        int result;
        int max(x,y);
        
        p=max;
        cout<<"please input three integer"<<endl;
        cin>>a>>b>>c;
        result = (*p)((*p)(a,b),c);
        cout<<"result= "<<relust<<endl;
        
        return 0;
    }
    答案:
    #include<iostream>
    using namespace std; 
    
    int max(int x,int y)
    {
        return x>y?x:y;
    }
    
    int main()
    {
        int (*p)(int,int); //改正 定义一个函数指针,才能指向一个函数
        int a,b,c;
        int result;
        int max(int,int);  //改正  声明函数是写形参的类型
        
        p=&max;            
        cout<<"please input three integer"<<endl; 
        cin>>a>>b>>c;
        result = (*p)((*p)(a,b),c);
        cout<<"result= "<<result<<endl;
        
        return 0;
    }

    8.typedef用于函数指针定义

    下面的定义有什么作用?

    typedef int (*pfun)(int,int);

    这里的pfun是一个使用typedef的自定义数据类型。意思就是:定义了一种pfun的类型,并定义这种类型为指向某种函数的指针,这种函数以两个个int为参数并返回int类型。

    这样的话定义函数指针什么的就很方便了。

    使用方法:

    #include<iostream>
    using namespace std; 
    
    typedef int (*pfun)(int,int);
    int fun(int x,int y)
    {
        return (x+y);
    }
    int main()
    {
        int fun(int,int);
        pfun p = fun; //注意 pfun是类型  类型定义变量 
        int ret = p(2,3);
        cout<<ret<<endl;
        
        return 0;
    }

    9.什么是“野指针”?

    “野指针”不是NULL指针,而是指向”垃圾”内存的指针。其成因主要为:指针变量没有被初始化,或者指针p被free或者delete之后没有置为NULL

    10.有了malloc/free为什么还要new 和delete

      mallocfreeC++/C语言的标准库函数,new/deleteC++的运算符。它们都可用于申请动态内存和释放内存。

    对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free

           因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。

    我们先看一看malloc/freenew/delete如何实现对象的动态内存管理,见下列代码

    class Obj
    {
        public :
        Obj(void){ cout << "Initialization" << endl; }
        ~Obj(void){ cout << "Destroy" << endl; }
        void    Initialize(void){ cout << "Initialization" << endl; }
        void    Destroy(void){ cout << "Destroy" << endl; }
    };
    
    void UseMallocFree(void)
    {
        cout<<"use mallocFree。。"<<endl;
        Obj *a = (obj *)malloc(sizeof(obj));   // 申请动态内存
        free(a);        // 释放内存
    }
    
    void UseNewDelete(void)
    {
        cout<<"use newFree。。"<<endl;
        Obj *a = new Obj; // 申请动态内存并且初始化
    
        delete a;           // 清除并且释放内存
    } 
    int main()
    {
        UseMallocFree();   
        UseNewDelete();
    
        return 0;
    }
        

    打印结果如下:

    use mallocFree。。

    use newFree。。

    Initialization

    Destroy

     

    对于非内部数据类型的对象而言,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器的控制权限之内,不能把执行构造函数和析构函数的任务强加于malloc/free,因此只有使用new/delete运算符

    11.比较分析两个代码段的输出,错误点在哪--(动态内存的传递)

    代码段一

    char * getMemory()
    {
        char p[] = "hello";
        return p;   
    }
    
    void Test(void)
    {
        char *str = NULL;
        str = getMemory();
        printf(str);
    }

    代码段二

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

    代码段一:栈内存分配,函数结束自动销毁,输出乱码。

    代码段二:此时的函数形参只是个复制体,不能传递动态内存给实参。并且函数结束后丢失堆内存地址,不能释放,导致内存泄漏。

    本篇是第二篇面试题总结,后面还有好多篇,想要剑指offer的关注我把!!!

    若有兴趣交流分享技术,可关注本人公众号,里面会不定期的分享各种编程教程,和共享源码,诸如研究分享关于c/c++,python,前端,后端,opencv,halcon,opengl,机器学习深度学习之类有关于基础编程,图像处理和机器视觉开发的知识

  • 相关阅读:
    (Java实现) 洛谷 P1106 删数问题
    (Java实现) 洛谷 P1603 斯诺登的密码
    (Java实现) 洛谷 P1036 选数
    (Java实现) 洛谷 P1012 拼数
    (Java实现) 洛谷 P1028 数的计算
    (Java实现) 洛谷 P1553 数字反转(升级版)
    (Java实现) 洛谷 P1051 谁拿了最多奖学金
    (Java实现) 洛谷 P1051 谁拿了最多奖学金
    (Java实现) 洛谷 P1106 删数问题
    目测ZIP的压缩率
  • 原文地址:https://www.cnblogs.com/DOMLX/p/10713204.html
Copyright © 2011-2022 走看看