小言_互联网的博客

C++面试题目_语法基础

409人阅读  评论(0)

指针和引用的概念?

  1. 指针是一个变量,引用是一个别名。
  2. 可以有const指针,但是没有const引用;
  3. 指针的值可以为空,但是引用的值不能为NULL,并且引用在定义的时候必须初始化;
  4. 指针的值在初始化后可以改变,即指向其它的存储单元,而引用在进行初始化后就不会再改变了。
  5. "sizeof引用"得到的是所指向的变量(对象)的大小,而"sizeof指针"得到的是指针本身的大小;
  6. 指针和引用的自增(++)运算意义不一样;

程序编译过程

  1. 预处理
    把所有的define展开,注释删除,头文件展开加进去
  2. 编译、优化阶段
    经过预处理以后,代码就是一些常量和关键字 然后编译器对代码进行了优化
  3. 汇编过程实际上指把汇编语言代码翻译成**目标机器指令*的过程。
  4. 链接过程 符号表汇总 静态链接 动态链接

预处理(Preproceessing),预处理的过程主要处理包括以下过程:

  1. 将所有的#define删除,并且展开所有的宏定义
  2. 处理所有的条件预编译指令,比如#if #ifdef #elif #else #endif等。
  3. 处理#include预编译指令,将被包含的文件插入到该预编译指令的位置。
  4. 删除所有注释 “//”和”/* */”。
  5. 添加行号和文件标识,以便编译时产生调试用的行号及编译错误警告行号。
  6. 保留所有的#pragma编译器指令,因为编译器需要使用它们。

编译、优化阶段

  1. 经过预编译得到的输出文件中,只有常量;如数字、字符串、变量的定义,以及C语言的关键字,如main, if , else , for , while , { , } , + , - , * , \ 等等。
  2. 编译程序所要作得工作就是通过词法分析和语法分析,在确认所有的指令都符合语法规则之后,将其翻译成等价的中间代码表示或汇编代码。
  3. 优化处理是编译系统中一项比较艰深的技术。它涉及到的问题不仅同编译技术本身有关,而且同机器的硬件环境也有很大的关系。优化一部分是对中间代码的优化。这种优化不依赖于具体的计算机。另一种优化则主要针对目标代码的生成而进行的。
  4. 对于前一种优化,主要的工作是删除公共表达式、循环优化(代码外提、强度削弱、变换循环控制条件、已知量的合并等)、复写传播,以及无用赋值的删除,等等。
    后一种类型的优化同机器的硬件结构密切相关,最主要的是考虑是如何充分利用机器的各个硬件寄存器存放有关变量的值,以减少对于内存的访问次数。另外,如何根据机器硬件执行指令的特点(如流水线、RISC、CISC、VLIW等)而对指令进行一些调整使目标代码比较短,执行的效率比较高,也是一个重要的研究课题。
  5. 经过优化得到的汇编代码必须经过汇编程序的汇编转换成相应的机器指令,方可能被机器执行。

汇编

汇编过程实际上指把汇编语言代码翻译成目标机器指令的过程。对于被翻译系统处理的每一个C语言源程序,都将最终经过这一处理而得到相应的目标文件。目标文件中所存放的也就是与源程序等效的目标的机器语言代码。

目标文件由段组成。通常一个目标文件中至少有两个段:

  1. 代码段:该段中所包含的主要是程序的指令。该段一般是可读和可执行的,但一般却不可写。
  2. 数据段:主要存放程序中要用到的各种全局变量或静态的数据。一般数据段都是可读,可写,可执行的。

UNIX环境下主要有三种类型的目标文件:

  1. 可重定位文件
    其中包含有适合于其它目标文件链接来创建一个可执行的或者共享的目标文件的代码和数据。

  2. 共享的目标文件
    这种文件存放了适合于在两种上下文里链接的代码和数据。
    第一种是链接程序可把它与其它可重定位文件及共享的目标文件一起处理来创建另一个目标文件;
    第二种是动态链接程序将它与另一个可执行文件及其它的共享目标文件结合到一起,创建一个进程映象。

  3. 可执行文件
    它包含了一个可以被操作系统创建一个进程来执行之的文件。
    汇编程序生成的实际上是第一种类型的目标文件。对于后两种还需要其他的一些处理方能得到,这个就是链接程序的工作了。

链接过程

由汇编程序生成的目标文件并不能立即就被执行,其中可能还有许多没有解决的问题。
例如,某个源文件中的函数可能引用了另一个源文件中定义的某个符号(如变量或者函数调用等);在程序中可能调用了某个库文件中的函数,等等。所有的这些问题,都需要经链接程序的处理方能得以解决。
链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。
根据开发人员指定的同库函数的链接方式的不同,链接处理可分为两种:

  1. 静态链接,在这种链接方式下,函数的代码将从其所在的静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

  2. 动态链接
    在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

对于可执行文件中的函数调用,可分别采用动态链接或静态链接的方法。使用动态链接能够使最终的可执行文件比较短小,并且当共享对象被多个进程使用时能节约一些内存,因为在内存中只需要保存一份此共享对象的代码。但并不是使用动态链接就一定比使用静态链接要优越。在某些情况下动态链接可能带来一些性能上损害。

static、const、#define的用法和区别

static

static作用:

  1. 修饰函数的局部变量: 特点:有默认值0,只执行一次,运行一开始就开辟了内存,内存放在全局

  2. 修饰全局函数和全局变量 特点:只能在本源文件使用

  3. 修饰类里面的成员变量 特点:和1差不多,定义多个static y,但只有一个y,不进入类的大小计算,不依赖于类对象的存在而存在(可直接调用,要进行外置声明)

  4. 修饰类的成员函数: 特点:f():括号里无this指针,只能调用他的本类静态函数和他的静态变量,即是用static修饰过的不依赖于类对象的存在而存在(可不进行外置声明,直接调用)

const

  1. 防止被修饰的成员的内容被改变。

  2. 修饰类的成员函数时,表示其为一个常函数,意味着成员函数将不能修改类成员变量的值。

  3. 在函数声明时修饰参数,表示在函数访问时参数(包括指针和实参)的值不会发生变化。

  4. 对于指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,const int b = &a;或者int const b = &a;修饰的都是后面的值,分别代表*b和b不能改变 。

  5. const 可以替代c语言中的#define 宏定义,好处是在log中可以打印出BUFFER_SIZE 的值,而宏定义的则是不能
    #define BUFFER_SIZE 512
    const int BUFFER_SIZE = 512;

C和C++区别

  1. C是面向过程的语言,C++是面向对象的语言。
  2. C语言不支持函数重载。

内存模型

  1. 系统内核
  2. 数据区: bbs 数据区
  3. 代码区

代码段(.text),也称文本段(Text Segment),存放着程序的机器码和只读数据,可执行指令就是从这里取得的。如果可能,系统会安排好相同程序的多个运行实体共享这些实例代码。这个段在内存中一般被标记为只读,任何对该区的写操作都会导致段错误(Segmentation Fault)。

数据段,包括已初始化的数据段(.data)和未初始化的数据段(.bss),前者用来存放保存全局的和静态的已初始化变量,后者用来保存全局的和静态的未初始化变量。数据段在编译时分配。

堆栈段分为堆和栈:

  1. 堆(Heap):用来存储程序运行时分配的变量。
    堆的大小并不固定,可动态扩张或缩减。其分配由malloc()、new()等这类实时内存分配函数来实现。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free 等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减) 堆的内存释放由应用程序去控制,通常一个new()就要对应一个delete(),如果程序员没有释放掉,那么在程序结束后操作系统会自动回收。
  1. 栈(Stack)是一种用来存储函数调用时的临时信息的结构,如函数调用所传递的参数、函数的返回地址、函数的局部变量等。 在程序运行时由编译器在需要的时候分配,在不需要的时候自动清除。
    栈的特性: 最后一个放入栈中的物体总是被最先拿出来,这个特性通常称为先进后出(FILO)队列。
    栈的基本操作: PUSH操作:向栈中添加数据,称为压栈,数据将放置在栈顶; POP操作:POP操作相反,在栈顶部移去一个元素,并将栈的大小减一,称为弹栈。

堆和栈的区别:

  1. 分配和管理方式不同 :

    堆是动态分配的,其空间的分配和释放都由程序员控制。
    栈由编译器自动管理。栈有两种分配方式:静态分配和动态分配。
    静态分配由编译器完成,比如局部变量的分配。

    动态分配由alloca()函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,无须手工控制。

  2. 产生碎片不同

    对堆来说,频繁的new/delete或者malloc/free势必会造成内存空间的不连续,造成大量的碎片,使程序效率降低。
    对栈而言,则不存在碎片问题,因为栈是先进后出的队列,永远不可能有一个内存块从栈中间弹出。

  3. 生长方向不同

    堆是向着内存地址增加的方向增长的,从内存的低地址向高地址方向增长。
    栈的生长方向与之相反,是向着内存地址减小的方向增长,由内存的高地址向低地址方向增长。


转载:https://blog.csdn.net/Jonh_/article/details/116140020
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场