笔试题(2)

  1. C语言变量的生命周期和可见性。

变量的生命周期:从创建变量开始到该变量被销毁的这一段时间

  • 全局变量:进程开始时创建,进程结束时销毁,在代码编译链接后,直接将
    其初始值写入到可执行文件中,创建时按照定义时的初始值进
    行赋值
  • 局部变量和参数变量:进入函数时创建,退出函数时销毁
  • 全局静态变量:定义一个全局变量并使用static关键字修饰时,这个变量
    就成了全局静态变量,它的生命周期和全局变量一样,但是
    作用域被限制在定义文件内,无法使用extern来让其他源
    文件中使用它
  • 静态局部变量:在函数内使用static关键字修饰一个变量时,这个变量就
    是静态局部变量,它的生命周期同全局变量一样,作用域被
    限制在函数内

  1.  空指针和野指针的区别。

空指针是指一个指针变量没有被初始化,即没有被赋予具体的内存地址。在程序中,空指针表示指向“空”的内存地址。

野指针是指一个指针变量指向一个无效的内存地址,即指针的值不是有效的内存地址。野指针指向的内存可能已经被释放或者尚未分配

空指针是一个未初始化的指针变量,没有指向具体的内存地址;而野指针是指针变量指向一个无效的内存地址。
空指针可以被赋予有效的内存地址,成为指向有效内存的指针;而野指针无法被赋予有效的内存地址。
空指针可以通过对其赋予有效内存地址来解除为空,使其成为有效指针;而野指针没有办法通过赋值操作变为有效指针,只能通过正确的内存分配和释放操作来避免。

 访问野指针会发生什么状况,访问空指针会发生什么情况。

1. 访问野指针

  • 野指针是指那些指向已被释放或者未经初始化的内存地址的指针。它们可能指向内存中的任意位置。
  • 后果:
    • 如果访问野指针指向的内存区域,可能会导致程序崩溃(例如,访问非法地址或受保护的内存区域)。
    • 可能会导致数据损坏,因为你可能无意中修改了程序的其他部分或系统的内存。
    • 在某些情况下,程序可能看起来仍能正常运行,但会产生不可预测的行为隐藏的 bug,这会使调试变得极为困难。

2. 访问空指针

  • 空指针(即 NULL 指针)是指向值为 0 的指针,通常用来表示指针没有实际指向任何有效的内存地址。
  • 后果:
    • 在大多数系统中,访问空指针通常会导致程序立即崩溃(例如,产生段错误,Segmentation Fault),因为地址 0 通常是一个受保护的内存地址,程序不能访问。
    • 因为空指针是一个明确无效的地址,所以访问它比访问野指针更容易被检测和调试。
  1. . 释放之后的指针再次调用会发生什么状况,如何避免释放之后的指针再次调用。

1. 释放之后的指针再次调用的后果

  • 当你调用 free()delete 释放了指针所指向的内存后,该指针仍然指向原来的内存地址,但该地址已经不再有效。
  • 如果再次使用这个指针进行读取或写入操作,可能会导致以下情况:
    • 崩溃:系统可能检测到你正在访问无效的内存地址,从而终止程序。
    • 数据损坏:如果该内存区域被重新分配给其他用途,可能会导致你无意中修改或读取其他数据,造成程序逻辑错误。
    • 未定义行为:程序可能表现出不可预测的行为,这些行为可能在不同的编译器或不同的环境下有所不同。

2. 避免释放之后的指针再次调用的方法

  • 将指针置为 NULL:释放指针后,立即将其设置为 NULL。这样做可以确保你在不小心再次使用这个指针时,能够更容易检测出错误。例如:

  • free(ptr);
    ptr = NULL;
    

  1.  C++面向对象的三要素,虚函数和多态概念。

C++ 面向对象编程的三要素是封装继承多态

  1. 封装:将数据和操作数据的函数捆绑在一起,使用访问控制符保护对象的内部状态,只通过公开的接口进行访问。

  2. 继承:通过继承基类来创建新类,复用和扩展已有类的属性和行为,形成类的层次结构。

  3. 多态:通过基类指针或引用调用派生类的方法,使同一接口有不同的实现。多态通常通过虚函数实现。

虚函数与多态

  • 虚函数:在基类中使用 virtual 关键字声明的函数,允许派生类重写。
  • 多态:依赖虚函数实现,基类指针或引用调用派生类重写的函数,实现运行时的动态行为。

  1.  进程间通信方式。

  • 管道(Pipe):单向通信,用于父子进程之间传递数据。
  • 命名管道(FIFO):类似管道,但支持无关进程之间的双向通信。
  • 消息队列:通过消息队列传递数据块,支持同步和异步通信。
  • 共享内存:多个进程共享一块内存区域,实现高效通信,需要同步机制(如信号量)来防止数据冲突。
  • 信号量(Semaphore):用于控制进程对共享资源的访问,实现同步和互斥。
  • 信号(Signal):进程间发送通知或中断信息,主要用于控制和管理进程行为。
  • 套接字(Socket):支持网络通信和本地进程间通信,灵活性高,常用于分布式系统。
  1.  const int *p 和int *const p 的区别。

  • const int *p:指针指向的内容不可修改,但指针本身可以指向其他地址。
  • int *const p:指针本身不可修改(即始终指向同一地址),但指向的内容可以修改。

看const的左边是谁。

1)如果const的左边是类型,那么说明,这个类型值是不能动的,即,这是指向一个不能动的值(常量)的指针。所以int const * p1是一个指向整型常量的指针。

2)如果const的左边是*,那么说明,这个指针是不能动的(*意味着指针嘛),即,这是一个不能动的指针(常量指针)。所以int * const p2是一个指向整型的常量指针。

  1. 堆和栈区别。

1. 内存分配方式

  • 栈(Stack)
    • 分配方式:由编译器自动分配和释放,遵循“后进先出”的原则。
    • 用途:用于存储局部变量、函数参数、函数返回地址等。
    • 特点:分配速度快,内存空间有限(通常由操作系统设定的栈大小限制)。
  • 堆(Heap)
    • 分配方式:由程序员手动分配和释放(使用 malloc, freenew, delete)。
    • 用途:用于动态分配大块内存,数据在程序的任意时刻都可以存取。
    • 特点:分配速度较慢,但内存空间相对大(受限于系统的可用内存)。

2. 生命周期

  • 栈(Stack):内存由函数调用时分配,函数返回时自动释放,生命周期与作用域相关。
  • 堆(Heap):内存由程序员分配,直到显式释放或程序结束前都存在,生命周期由程序控制。

3. 管理方式

  • 栈(Stack):内存管理是自动的,系统负责分配和释放,无需手动干预。
  • 堆(Heap):内存管理是手动的,程序员必须小心管理,以避免内存泄漏或重复释放。

4. 性能

  • 栈(Stack):由于栈是由系统自动管理,分配和释放的速度非常快。
  • 堆(Heap):分配和释放速度较慢,且容易产生内存碎片,影响性能。

5. 存储内容

  • 栈(Stack):通常用于存储局部变量和函数调用信息。
  • 堆(Heap):用于存储动态分配的对象或数组,尤其是需要跨函数使用的大块内存。

总结:

  • :自动管理,速度快,空间小,适用于局部变量。
  • :手动管理,灵活性大,速度慢,适用于动态分配的长期存在的数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值