C++面试之语言基础(2)

时间:2021-07-30 08:10:32

21.不借助第三个数交换两个数的值。

  • 第一种:a=a+b;b=a-b;a=a-b;
  • 第二种:a=a*b;b=a/b;a=a/b;(b不为0)
  • 第三种:a=a^b;b=a^b;a=a^b;

22.用宏定义写出swap(x,y)

#define swap(x,y) (x)=(x)+(y);(y)=(x)-(y);(x)=(x)-(y);
//宏定义时参数用括号括起来,且表达式之间不要有空格

23.带参数的宏和带参数的函数的区别

  • 宏在编译时处理,会展开,而函数在运行时处理
  • 宏里的参数不需要定义类型,函数中参数必须有类型
  • 宏会使程序变长,而函数不会
  • 宏不占用存储空间,函数占用
  • 宏不占用运行时间,函数调用和返回时占用运行时间
  • 带参宏简单,不灵活

24.定义宏,求出数组元素的个数

#define NTBL(table) (sizeof(table)/sizeof(table[0]))

25.两个栈实现一个队列的功能

入队:将元素压入栈a
出队:

  1. 判断栈b是否为空
  2. 如果不为空,则将栈a中所有元素依次pop出并push到栈b

26.在c++中调用c函数,为什么要加extern c?

  • c++支持函数重载,c语言不支持函数重载。函数被c++编译后在库中的名字和c的不同。
void foo(int x,int y)
  • 在c中编译结果:_foo
  • 在c++中:_foo_int_int

所以要加extern “c”来解决名字匹配问题

27.将给定的字符串转换成整数。

#include <iostream>
#include <string>

using namespace std;

int invert(char *str)
{
    int num=0;
    while(*str!='\0')
    {
        int d=*str-48;
        num=num*10+d;
        str=str+1;
    }
    return num;
}

int main(){
  int b = invert("zhanghui");
  cout<< b <<endl;
}

28.将整数转换成字符串。

void fun(int num, char *pval)
{
    char strval[100];
    int i,j;
    int val0=0;
    int val1=0;
    val0=num;
    while(1)
    {
        val1=val0%10;
        val0=val0/10;
        strval[i]=val1+48;
        if(val0<10)
        {
            i++;
            strval[i]=val0+48;
            break;
        }
    }
    for(j=0;j<=i;j++)
        pval[j]=strval[i-j];
    pval[j]='\0';
}

29.怎么判断链表中是否有环?

追逐方式:

  • 两个指针遍历链表,一个每次走一步,一个每次走两步,若后者能追上前者,则表示有环。

30.双向链表的插入和删除。

  • 插入修改四个指针。
  • 删除只需要修改两个。

31.二维数组转置。

  • 核心操作:b[j][i]=a[i][j]

32.输入一行字符,统计其中有多少个单词。

  • 按空格累加。

33.计算字符串中子串出现的次数

  • 循环比较

34.数组a[n],存放1到n,找出被重复的数字,时间复杂度为O(n)

int do_dup(int a[],int N)
{
    int sum=0;
    for(int i=0;i<N;i++)
        sum+=a[i];
    sum=sum-(N-1)(N)/2;
    return sum;
}

35. 16bit的整数,每4bit为一个数,写函数求他们的和

循环,移位相加即可。

c+=n&15; n=n>>4;

36.什么函数不能声明为虚函数?

  • 内联函数,内联函数在编译时展开,而虚函数是运行时动态绑定,所以两者矛盾。
  • 构造函数,构造函数用来创建一个新的对象,而虚函数运行时建立在对象基础上,在构造函数时对象尚未形成。
  • 静态成员函数,静态成员函数属于一个类而非某一对象,没有this指针,无法进行对象的判别
  • 非成员函数
  • 类的成员函数是模板函数的时候。

37.编写一个函数:作用是把char数组字符串循环右移n位

void LoopMove(char *pstr,int steps)
{
    int n=strlen(pstr)-steps;
    char temp[MAX_LEN];
    strcpy(temp,pstr+n);
    strcpy(temp+steps,pstr);
    *(tmp+strlen(pstr))='\0';
    strcpy(pstr,temp);
}

38.编写类String的构造函数、析构函数和赋值函数。

一个类包含指针的话,一般会有析构函数,而且要重写拷贝构造和赋值运算符,因为默认是地址的拷贝和赋值,对指针来说无意义。

class String{
public:
    String(const char *str=NULL);
    String(const string &other);//拷贝构造
    ~String();
    String & operator=(const String &other);//赋值运算符
};

39.逗号运算符(优先级最低)

a=3;b=5;
c=a,b;
d=(a,b);

执行之后,c=3,d=5.

40.sizeof运算符。

int i=3;
int j;
j=sizeof(++i+ ++i);
print(“i=%d,j=%d”,i,j);
  • 输出:i=3,j=4
  • 原因:编译器进行优化,发现++对sizeof根本没影响,所以会优化不计算++,发生短路现象。

41.赋值运算符”=”作为循环条件。

  • 赋非零值,无限循环。
  • 赋0,不循环

42.(a+b)+c与(a+c)+b是否恒等

不一定,可能会溢出。假设a+b溢出,但是c是负数,a+c后再加b就不一定溢出了。
a+b+c一定等于b+a+c

43. memset、memcpy和strcpy的根本区别。

  • memset和memcpy需要包含memory.h,strcpy需要string.h
  • memset用来对一段内存空间全部设置为某个字符 memset(a,0,sizeof(a))
  • memcpy用来内存拷贝,拷贝任何数据类型的对象 memcpy(b,a,sizeof(b))
  • strcpy只能拷贝字符串,遇到’\0’就结束,所以不需要指定大小。
  • 要注意内存溢出。

44.析构函数有何特点。

  • 析构函数也是特殊的类成员函数,和构造函数一样没有返回类型
  • 没有参数
  • 不能重载
  • public、private、protected对析构函数无效
  • 析构函数不能手动调用,只有在类对象生命周期结束的时候,由系统自动调用释放在构造函数中分配的资源,回收内存。
  • 构造函数不可以是virtual,但析构函数可以。

45.虚函数有什么用?

  • 虚函数的功能是使子类可以用同名的函数对父类函数进行覆盖,并且在通过父类指针调用时,如果有覆盖则自动调用子类覆盖函数,如果没覆盖调用父类中的函数,从而实现灵活扩展和多态性。
  • 如果是纯虚函数,则纯粹是为了在子类覆盖时有个统一的命名而已,子类必须覆盖纯虚函数,否则子类也是抽象类
  • 含有纯虚函数的类称为抽象类,不能实例化对象,主要用于做接口

46.虚析构函数的作用

  • 虚构函数调用是先子后父。
  • 当一个派生类对象通过使用一个基类指针删除,而这个基类有一个非虚的析构函数,就会导致运行时派生类不会被销毁,然而基类部分已经被销毁,这就导致了部分析构,造成内存泄露。此时就需要给基类一个虚析构函数。

47.分别给出bool、in、float、指针变量与零值比较的if语句

//bool:
if(!var)
//int:
if(var==0)
//float: 
const float var=0.000001;
if(x>-var && x<var)//浮点数不能精确到0,所以需要在一个范围内近似看做0
//指针:
if(var==NULL)

48. 32位下,计算sizeof

(1)

void fun(char str[100])
{
sizeof(str)=?  //4
}

原因:数组做函数形参,会转化成指针
(2)

void p=malloc(100);
sizeof(p) //4

(3)

int a[100]
sizeof(a)//4100

(4)

char *p=”aaa”
sizeof(p)=?//4

49. 写函数返回1+2+3+。。+n的值

  • 解法1:一重循环
  • 解法2:利用高斯公式直接求 (1+n)*n/2

50.内联函数和普通函数、宏的区别

  • 内联函数是将简单函数内嵌到调用它的程序代码中,目的是节约原本函数调用时的时空开销,不能含有循环、条件、选择等复杂的结构。
  • 内联函数和宏的区别,宏是由预处理器对宏进行替代,而内联函数是通过编译器来控制的。内联函数是真正的函数,取消了函数的参数压栈,减少调用开销,不用担心像宏函数的问题。
  • 用inline定义内联函数,任何在类的说明部分定义的函数都会自动认为是内联函数。