c/c++常见面试题(一)

0.static、const、volatile的作用和区别

static:

https://www.cnblogs.com/Manual-Linux/p/8870038.html

第一、在修饰变量的时候,static修饰的静态局部变量只执行一次,之后再初始化无效。而且延长了局部变量的生命周期,直到程序运行结束以后才释放。 
第二、static修饰全局变量的时候,这个全局变量只能在本文件中访问,不能在其它文件中访问,即便是extern外部声明也不可以。 
第三、static修饰一个函数,则这个函数的只能在本文件中调用,不能被其他文件调用。Static修饰的局部变量存放在全局数据区的静态变量区。初始化的时候自动初始化为0

const:

1.修饰的变量只读,不可改变

2.防止意外修改,减少bug

3.可以节省空间,避免不必要的内存分配

volatile:

告诉编译器这个变量随时可变,不需要优化

 

1.请问全局变量和局部变量能否重名

能,局部会屏蔽全局。要用全局变量,需要使用"::"

2. 用三目运算符(X)>(Y)?(X):(Y)宏定义实现比较两个数的大小

#define MAX(X, Y) ((X)>(Y)?(X):(Y))

3.malloc的了解

malloc是动态内存分配,是用户动态申请系统分配指定字节的内存块的函数。返回类型是 void* 类型

对应free, 释放ptr指向的存储空间

4.new和malloc的区别

参看:https://blog.csdn.net/weixin_40535588/article/details/90110493

5.模拟实现strcpy,strcat,strcmp,strstr,memcpy

https://blog.csdn.net/studyhardi/article/details/84062485

6.用宏来计算偏移量,判断大小端(联合体法,指针法)

https://blog.csdn.net/studyhardi/article/details/83957869

7.i++是否为原子操作?

   不是。操作系统原子操作是不可分割的,在执行完毕不会被任何其它任务或事件中断,分为两种情况(两种都应该满足)

 (1) 在单线程中, 能够在单条指令中完成的操作都可以认为是" 原子操作",因为中断只能发生于指令之间。

 (2) 在多线程中,不能被其它进程(线程)打断的操作就叫原子操作。

i++分为三个阶段:内存到寄存器     寄存器自增     写回内存

这三个阶段中间都可以被中断分离开.

8. C++中类与结构体的区别?

最本质的一个区别就是默认的访问控制: struct作为数据结构的实现体,它默认的数据访问控制是public的,而class作为对象的实现体,它默认的成员变量访问控制是private的。

9.析构函数的作用?

用来释放所定义的对象消亡时,自动被调用,用来释放对象占用的空间,避免内存泄漏对象中使用的指针

10.虚函数的作用?

      虚函数可以让成员函数操作一般化,用基类的指针指向不同的派生类的对象时,基类指针调用其虚成员函数,则会调用其真正指向对象的成员函数,而不是基类中定义的成员函数(只要派生类改写了该成员函数)。若不是虚函数,则不管基类指针指向的哪个派生类对象,调用时都会调用基类中定义的那个函数。虚函数是C++多态的一种表现,可以进行灵活的动态绑定。

11.什么是纯虚函数

纯虚函数就是没有函数体,同时在定义的时候,其函数名后面要加上“= 0”。

虚函数可以被直接使用,也可以被子类(sub class)重载以后以多态的形式调用,而纯虚函数必须在子类(sub class)中实现该函数才可以使用,因为纯虚函数在基类(base class)只有声明而没有定义。

12. 类模板

类模板,可以定义相同的操作,拥有不同数据类型的成员属性。

通常使用template来声明。告诉编译器,碰到T不要报错,表示一种泛型.

https://blog.csdn.net/zhuzhaoming1994/article/details/80346250

13.什么是C++的多态性

1.多态性可以简单地概括为“一个接口,多种方法”,

2.C++支持两种多态性:编译时多态性,运行时多态性。 

  a、编译时多态性:通过重载函数实现    b、运行时多态性:通过虚函数实现。

3.C++多态性是通过虚函数来实现的,虚函数允许子类重新定义成员函数,而子类重新定义父类的做法称为覆盖(override),或者称为重写。

14..什么情况下需要将析构函数定义为虚函数?

当基类指针指向派生类的对象(多态性)时。如果定义为虚函数,则就会先调用该指针指向的派生类析构函数,然后派生类的析构函数再又自动调用基类的析构函数,这样整个派生类的对象完全被释放。如果析构函数不被声明成虚函数,则编译器实施静态绑定,在删除基类指针时,只会调用基类的析构函数而不调用派生类析构函数,这样就会造成派生类对象析构不完全。所以,将析构函数声明为虚函数是十分必要的。

15.重载与重写的区别?

从定义上来说:重载:是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)。重写:是指子类重新定义父类虚函数的方法。

16.volatile的作用是作为指令关键字,确保本条指令不会因编译器的优化(编译器自己假设)而省略,且要求每次直接读值

17.volatile和const可以同时使用吗?
可以, 如果一个变量不会被本程序改变,通常可能给它加上const,但如果该变量可能被其他程序改变而本程序又在检测这个变量的值,就需要给它加上volatile

18.联合体union的基本特性——和struct的同与不同

结构体(struct)中所有变量是“共存”的——全面;缺点是struct内存空间是全分配。

而联合体(union)中是各变量是“互斥”的——缺点就是变量共用首地址,一个值变,另外变量会跟着变;但优点是内存使用更为精细灵活,也节省了内存空间。

19.为什么要结构体内存对齐

平台原因(移植原因):
不是所有的硬件平台都能访问任意地址上的任意数据的;某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。

性能原因:
数据结构(尤其是栈)应该尽可能地在自然边界上对齐。
原因在于,为了访问未对齐的内存,处理器需要做两次内存访问;而对齐的内存访问仅需一次访问。(对齐的地址的数据容易访问)

如int要在4的倍数地址分配,char分配后要空闲地址到4的倍数的地址

20.位域是什么

所谓“位域”是把一个字节中的二进位划分为几 个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。 这样就可以把几个不同的对象用一个字节的二进制位域来表示。位段成员必须声明为int、unsigned int或signed int类型(short char long)。

21.什么是可重入函数

可重入函数主要用于多任务环境中,一个可重入的函数简单来说就是可以被中断的函数,也就是说,可以在这个函数执行的任何时刻中断它,转入OS调度下去执行另外一段代码,而返回控制时不会出现什么错误

可重入的函数必须满足以下三个条件:

(1)可以在执行的过程中可以被打断;

(2)被打断之后,在该函数一次调用执行完之前,可以再次被调用(或进入,reentered)。

(3)再次调用执行完之后,被打断的上次调用可以继续恢复执行,并正确执行

22.防止H文件被重复包含的处理方法

#ifndef _H文件名_H_

#define _H文件名_H_

//H文件内容

#endif

23.内存溢出,内存泄漏,数组越界的原因?

溢出 out of memory,分为栈溢出和内存溢出,栈溢出是指函数中的局部变量超过栈最大值造成的溢出,内存溢出是指程序在申请内存时,没有足够的内存空间供其使用,出现out of memory;比如申请了一个integer,但给它存了long才能存下的数,那就是内存溢出。

内存泄露 memory leak,是指程序在申请内存后,无法释放已申请的内存空间,一次内存泄露危害可以忽略,但内存泄露堆积后果很严重,无论多少内存,迟早会被占光。

内存泄漏比内存溢出危害性更大

内存溢出的解决方案:

第一步,修改JVM启动参数,直接增加内存。(-Xms,-Xmx参数一定不要忘记加。)

第二步,检查错误日志,查看“OutOfMemory”错误前是否有其它异常或错误。

第三步,对代码进行走查和分析,找出可能发生内存溢出的位置。

 

数组越界:

char a[9]={0};

cout << a[9] << endl;

24.const和define

const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会产生意料不到的错误。

25.sizeof 和 strlen 的区别

   char str[20]="0123456789";
   int a=strlen(str); //a=10;
   int b=sizeof(str); //而b=20;

26.C、C++中内存分配方式有哪些

C、C++中内存分配方式可以分为三种:

(1)从静态存储区域分配:内存在程序编译时就已经分配好,这块内存在程序的整个运行期间都存在。速度快、不容易出错,因为有系统会善后。例如全局变量,static变量等。

(2)在栈上分配:在执行函数时,函数内局部变量的存储单元都在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。

(3)从堆上分配:即动态内存分配。程序在运行的时候用 malloc 或 new 申请任意大小的内存,程序员自己负责在何时用 free 或 delete 释放内存。动态内存的生存期由程序员决定,使用非常灵活。如果在堆上分配了空间,就有责任回收它,否则运行的程序会出现内存泄漏,另外频繁地分配和释放不同大小的堆空间将会产生堆内碎块。

27.面向对象的三大特征

① 封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

② 继承,是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。通过继承创建的新类称为“子类”或“派生类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”和“组合”来实现。

③ 多态,简单的说,就是一句话:允许将指向子类类型的指针赋值给父类类型的指针。实现多态,有二种方式,覆盖,重载。

覆盖,是指子类重新定义父类的虚函数的做法。

重载,是指允许存在多个同名函数,而这些函数的参数表不同(或许参数个数不同,或许参数类型不同,或许两者都不同)

28.引用和指针区别

① 引用必须被初始化,但是不分配存储空间,其实就是同一个数据。指针是存储了指向的对象的地址,在初始化的时候需要分配存储空间

② 引用初始化以后不能被改变,指针可以改变所指的对象

③ 不存在指向空值的引用,但是存在指向空值的指针

30.数组指针和指针数组,函数指针的区别

数组指针 定义 int (*p)[n];指向一个数组对象的指针

指针数组 定义 int *p[n]; 是一个存放多个指针的数组

函数指针 是一个存放函数地址的指针

                int func(int x); /* 声明一个函数 */

                int (*f) (int x)=&fun或 int (*f) (int x)=fun; /* 声明一个函数指针 */

31.表示数组a第i行第j列a[i][j]的方法

3种

*(a[i]+j)               *(*a+i)+j)                 (*(a+i))[j]

地址和指针

32.const int *a  和 int * const a 区别

const int * a:const修饰的是整型数 int,而不是指针,即a是一个指向常整型数的指针。近一步的理解为:整型数 *a是不可以被重新赋值的,而指针却是可以修改的

int * const a:const修饰的是指针a,而不是整型数*a,*a为变量而不是一个常量。即a是指向一个可以修改的整型数的常指针。进一步的理解为:指针指向的整型数是可以被修改的,但是指针不可以被修改。

总结:const离谁近,谁就不可以被修改

参看:https://blog.csdn.net/leikun153/article/details/80147657

33.怎么将程序跳转到指定内存地址

要对绝对地址0x100000赋值,我们可以用(unsigned int*)0x100000 = 1234;那么要是想让程序跳转到绝对地址是0x100000去执行,应该怎么做?
首先要将0x100000强制转换成函数指针,及 (void (*)())0x100000,在调用他*((void (*)())0x100000)();

34. 抽象类和接口的区别

 在C++里面抽象类就是接口

     抽象类:定义了纯虚函数的类是抽象类,不能实例化。
        抽象类包括抽象方法(纯虚方法),也可以包含普通方法。
        抽象类可以派生自一个抽象类,可以覆盖基类的抽象方法也可以不覆盖。
        虽不能定义抽象类的实例,但是可以定义抽象类的指针。

35. 什么时候用常引用

const int &ra = a;   // 不能通过引用对目标变量的值进行修改,从而使引用的目标成为const的,安全。

36.有几种情况用intialization list(初始化列表)而不是assignment(赋值)

1.对于const和reference类型成员变量,它们只能够被初始化而不能做赋值操作,因此只能用初始化列表

2..需要初始化的数据成员是对象的情况

3.C++的引用也一定要初始化,所以必须在初始化列表中完成。

4.调用一个基类的构造函数,而该函数有一组参数

37.main函数之前会执行什么代码?

全局变量的初始化。

38.数组和指针的区别

数组要么在静态存储区创建,要么在栈上创建。指针可以随时指向任意类型的内存块。

39.为什么基类的析构函数是虚函数?

动态绑定,不会造成潜在的内存泄漏

40.友元函数

友元函数访问对象中的成员要通过对象名

41.switch参数类型
  可以是:byte short int long bool
  不能是: float double(这种浮点型的不能精确的比较,所以不能) string
  但是在c++ 11里面, string可以作为switch的条件了。

42.C++ 内联

是代码嵌套,而不是调用

43.C++函数传参数方式

值传递、指针、引用

44.#include<file.h> #include "file.h" 区别
  前者是从标准库路径寻找
  后者是从当前工作路径

45.什么是c++的空类

空类,声明时编译器不会生成任何成员函数
        对于空类,编译器不会生成任何的成员函数,只会生成1个字节的占位符。

        有时可能会以为编译器会为空类生成默认构造函数等,事实上是不会的,编译器只会在需要的时候生成6个成员函数:一个缺省的构造函数、一个拷贝构造函数、一个析构函数、一个赋值运算符、一对取址运算符和一个this指针。

46.extern "C "指使用c语言的方法定义

47. C ++ 在c基础上加了什么?
  A:包含全部的C语言部分。
  B:面向对象部分,封装,继承,多态。
  C:泛型编程部分,模板,方便使用。
  D:STL库。

STL库:https://blog.csdn.net/qq_38880380/article/details/78360799
              
https://blog.csdn.net/weixin_40535588/article/details/89815453
48.空指针和悬挂指针
  空指针是等于null的指针; 悬挂指针是delete后没有置空的野指针。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值