当前位置: 首页 > news >正文

【持续更新】c++指针相关试题

问题1:

int * pn = new int;
int higgens;
int * pt = &higgens;
  1. 请分别说明在这两种情况下,如何访问所分配的int变量。
  2. 为什么在第一种情况下只能通过指针访问该int,而在第二种情况下既可以通过变量名也可以通过指针访问?
  3. 这两种方式在内存管理上有什么区别?请简要说明各自的优缺点。
  4. 如果在这两种情况下,忘记释放内存(第一种情况)或超出作用域(第二种情况),分别会导致什么问题?如何避免这些问题?
  5. 请解释以下代码的输出结果,并说明原因:
   int * p1 = new int;int * p2 = p1;delete p1;// 此时访问 p2 会导致什么结果?为什么?

答案:

1.       

  • 第一种:只能通过指针pn访问该int变量。
  • 第二种:既可以通过变量名higgens访问,也可以通过指针pt访问。

2. 

  • 第一种情况使用了new在堆区动态分配内存,没有变量名与之直接关联,只能通过指针访问。
  • 第二种情况变量higgens在栈区分配,有变量名,可以直接访问,同时也可以通过指针间接访问。

3.

  • 第一种方式需要手动释放内存(delete pn;),否则会造成内存泄漏;适合需要动态管理生命周期的场景。
  • 第二种方式由编译器自动管理内存,作用域结束自动释放,使用更安全,但生命周期受限于作用域。

4. 

  • 第一种情况忘记释放内存会导致内存泄漏,长期运行可能导致程序崩溃。
  • 第二种情况超出作用域后,变量自动释放,但指针可能成为悬空指针,访问会导致未定义行为。
  • 避免方法:第一种情况使用智能指针(如std::unique_ptr或std::shared_ptr),第二种情况确保指针在变量作用域内使用。

5. 

  • 输出结果:访问p2会导致未定义行为(如崩溃或垃圾值)。
  • 原因:p1和p2指向同一块内存,delete p1后,该内存已释放,p2成为悬空指针,访问它会导致未定义行为。

问题2:

int * ps = new int; 
delete ps; 
delete ps; 
int jugs = 5; 
int * pi = &jugs; 
delete pi; int * pq = ps; 
delete pq; 
  1. 请解释代码中每一行的作用,并说明哪些操作是合法的,哪些是不合法的,以及原因。
  2. 为什么delete ps;执行两次会导致问题?请详细说明可能发生的后果。
  3. 为什么delete pi;是不允许的?请从内存管理的角度解释。
  4. 在代码中,int * pq = ps;和delete pq;的作用是什么?这种操作是否安全?请说明原因。
  5. 如何避免上述代码中可能出现的问题?请给出至少两种解决方案,并说明各自的优缺点。

答案:

1.

  • int * ps = new int;:在堆区动态分配一个int变量,合法。
  • delete ps;:释放ps指向的内存,合法。
  • delete ps;:重复释放同一块内存,不合法,会导致未定义行为(如崩溃)。
  • int jugs = 5;:在栈区分配一个int变量,合法。
  • int * pi = &jugs;:将指针pi指向栈区变量jugs,合法。
  • delete pi;:尝试释放栈区内存,不合法,因为栈区内存由编译器管理,不能手动释放。
  • int * pq = ps;:将指针pq指向ps指向的内存,合法。
  • delete pq;:释放pq指向的内存,合法,但需注意ps和pq指向同一块内存,重复释放会导致问题。

2.

  • 重复释放同一块内存会导致未定义行为,可能引发程序崩溃、内存损坏或不可预测的结果。

3.

  • delete pi;不合法,因为pi指向的是栈区内存,栈区内存由编译器自动管理,不能手动释放。

4.

  • int * pq = ps;将pq指向ps指向的内存,delete pq;释放该内存。
  • 这种操作不安全,因为ps和pq指向同一块内存,重复释放会导致未定义行为。

5.

  • 使用智能指针(如std::unique_ptr或std::shared_ptr)自动管理内存,避免手动释放。
  • 在释放指针后将其设置为nullptr,避免悬空指针和重复释放。

问题3:

#include <memory>
#include <iostream>void testSmartPointers() {std::unique_ptr<int> up1(new int(10));std::unique_ptr<int> up2 = std::move(up1); std::shared_ptr<int> sp1(new int(20));std::shared_ptr<int> sp2 = sp1; std::weak_ptr<int> wp = sp1; std::cout << "up2: " << *up2 << std::endl;std::cout << "sp1: " << *sp1 << std::endl;std::cout << "sp2: " << *sp2 << std::endl;std::cout << "wp expired: " << wp.expired() << std::endl;
}
  1. 请解释代码中std::unique_ptr、std::shared_ptr和std::weak_ptr的作用和区别。
  2. 为什么在std::unique_ptr中,up2 = std::move(up1);是合法的,而直接赋值up2 = up1;是不合法的?请详细说明。
  3. 在std::shared_ptr中,sp1和sp2共享所有权,请解释引用计数的概念,并说明如何查看当前引用计数。
  4. std::weak_ptr的作用是什么?为什么需要weak_ptr?请结合代码中的wp.expired()说明其用途。
  5. 智能指针相比原始指针有哪些优势?请结合代码中的内存管理方式,说明智能指针如何避免内存泄漏和悬空指针问题。
  6. 请解释以下代码的输出结果,并说明原因:
   std::shared_ptr<int> sp1(new int(30));std::shared_ptr<int> sp2 = sp1;sp1.reset();std::cout << "sp2: " << *sp2 << std::endl;

参考答案要点:

1.

  • std::unique_ptr:独占所有权,同一时间只能有一个指针指向该内存,不能复制,只能移动。
  • std::shared_ptr:共享所有权,多个指针可以指向同一块内存,通过引用计数管理内存释放。
  • std::weak_ptr:弱引用,不增加引用计数,用于解决shared_ptr的循环引用问题。

2.

  • std::unique_ptr不能复制,只能移动,因为复制会导致两个指针指向同一块内存,违反独占原则。
  • std::move(up1)将up1的所有权转移给up2,up1变为空指针。

3.

  • 引用计数:shared_ptr内部维护一个计数器,记录有多少个指针指向同一块内存。
  • 查看引用计数:使用sp1.use_count()或sp2.use_count()。

4.

  • std::weak_ptr用于解决shared_ptr的循环引用问题,不增加引用计数。
  • wp.expired()检查weak_ptr指向的内存是否已被释放,避免访问已释放的内存。

5.

  • 智能指针自动管理内存,避免手动释放,防止内存泄漏。
  • 智能指针在作用域结束时自动释放内存,避免悬空指针问题。

6.

  • 输出结果:sp2: 30。
  • 原因:sp1.reset()将sp1置为空,但sp2仍然指向该内存,引用计数为1,内存未被释放。

问题4:

#include <memory>
#include <iostream>class B; class A {
public:std::shared_ptr<B> b_ptr;~A() { std::cout << "A destructor called" << std::endl; }
};class B {
public:std::shared_ptr<A> a_ptr;~B() { std::cout << "B destructor called" << std::endl; }
};void testCircularReference() {std::shared_ptr<A> a = std::make_shared<A>();std::shared_ptr<B> b = std::make_shared<B>();a->b_ptr = b;b->a_ptr = a;
}
  1. 请解释代码中shared_ptr的循环引用问题是如何产生的,并说明为什么会导致内存泄漏。
  2. 在testCircularReference函数中,a和b的引用计数是多少?为什么在函数结束时,A和B的析构函数没有被调用?
  3. 如何解决shared_ptr的循环引用问题?请给出至少两种解决方案,并说明各自的优缺点。
  4. 请修改代码,使用weak_ptr解决循环引用问题,并解释修改后的代码如何避免内存泄漏。
  5. 除了使用weak_ptr,还有哪些方法可以避免循环引用问题?请结合代码示例说明。

参考答案要点:

1.

  • 循环引用问题:A和B类互相持有对方的shared_ptr,导致引用计数无法降为0,内存无法释放。
  • 内存泄漏:在testCircularReference函数结束时,a和b的引用计数仍为2,析构函数未被调用,内存未被释放。

2.

  • 引用计数:a和b的引用计数均为2(a和b各自持有一次,a->b_ptr和b->a_ptr各持有一次)。
  • 析构函数未被调用:因为引用计数未降为0,shared_ptr不会释放内存。

3.

  • 解决方案:
  • 使用weak_ptr:将其中一个shared_ptr替换为weak_ptr,不增加引用计数。
  • 手动打破循环:在适当的地方将其中一个指针置为nullptr,手动减少引用计数。

4. 

    修改代码:

     class A {public:std::shared_ptr<B> b_ptr;~A() { std::cout << "A destructor called" << std::endl; }};class B {public:std::weak_ptr<A> a_ptr; // 使用 weak_ptr~B() { std::cout << "B destructor called" << std::endl; }};
  • 解释:B类使用weak_ptr指向A,不增加引用计数,A的引用计数降为1,函数结束时A和B的析构函数均被调用,内存被释放。

5.

  • 其他方法:
  • 使用原始指针:在不需要共享所有权的地方使用原始指针,但需注意悬空指针问题。
  • 重新设计类结构:将A和B的共同功能提取到基类Base中,A和B继承Base,避免直接循环依赖。通过多态和基类指针,A和B可以互相引用,但不会导致循环引用问题。
  • 在作用域结束前手动释放:
a->b_ptr = nullptr; // 手动减少引用计数
b->a_ptr = nullptr; // 手动减少引用计数

问题5:

#include <iostream>void testNewDelete() {int stackVar = 10;int *p1 = &stackVar;delete p1; int *p2 = new int(20);delete p2;delete p2; int *p3 = new int[5];delete p3; int *p4 = new int(30);delete[] p4; // 情况 5:对空指针应用 deleteint *p5 = nullptr;delete p5; // 安全:对空指针应用 delete 是安全的
}
  1. 请解释代码中每一行注释的作用,并说明哪些操作是合法的,哪些是不合法的,以及原因。
  2. 为什么不能使用delete释放栈区内存?请从内存管理的角度详细解释。
  3. 为什么不能重复释放同一块内存?请说明可能发生的后果。
  4. 为什么使用new[]分配的内存必须使用delete[]释放,而使用new分配的内存必须使用delete释放?请解释原因。
  5. 为什么对空指针应用delete是安全的?请结合代码中的p5说明。
  6. 请给出至少两种避免上述错误的最佳实践,并说明各自的优缺点。

参考答案要点:

1.

  • delete p1;:不合法,p1指向栈区内存,不能手动释放。
  • delete p2;:合法,释放p2指向的堆区内存。
  • delete p2;:不合法,重复释放同一块内存,导致未定义行为。
  • delete p3;:不合法,p3指向new[]分配的内存,应使用delete[]。
  • delete[] p3;:合法,正确释放new[]分配的内存。
  • delete[] p4;:不合法,p4指向new分配的内存,应使用delete。
  • delete p4;:合法,正确释放new分配的内存。
  • delete p5;:合法,对空指针应用delete是安全的。

2.

  • 栈区内存由编译器自动管理,不能手动释放,否则会导致未定义行为。

3.

  • 重复释放同一块内存会导致未定义行为,可能引发程序崩溃、内存损坏或不可预测的结果。

4.

  • new[]分配的内存是数组,delete[]会调用每个元素的析构函数,而delete只会调用第一个元素的析构函数,导致内存泄漏。
  • new分配的内存是单个对象,delete会调用析构函数,而delete[]会导致未定义行为。

5.

  • 对空指针应用delete是安全的,因为delete会检查指针是否为空,为空则不做任何操作。

6.

  • 使用智能指针(如std::unique_ptr或std::shared_ptr)自动管理内存,避免手动释放。
  • 在释放指针后将其设置为nullptr,避免悬空指针和重复释放。

问题6:

#include <iostream>void testDynamicArray() {int *arr = new int[10];for (int i = 0; i < 10; i++) {arr[i] = i;}std::cout << "Size of arr: " << sizeof(arr) << std::endl; std::cout << "Size of *arr: " << sizeof(*arr) << std::endl; std::cout << "Size of arr[0]: " << sizeof(arr[0]) << std::endl;delete[] arr;
}
  1. 请解释代码中sizeof(arr)、sizeof(*arr)和sizeof(arr[0])的输出结果,并说明为什么sizeof(arr)不能用来确定动态分配数组的字节数。
  2. 为什么sizeof(arr)输出的是指针大小,而不是数组的字节数?请从内存分配的角度详细解释。
  3. 如何正确获取动态分配数组的字节数?请给出至少两种方法,并说明各自的优缺点。
  4. 在C++中,如何避免使用sizeof运算符来获取动态分配数组的字节数?请结合代码示例说明。

参考答案要点:

1.

  • sizeof(arr):输出指针大小(如4或8字节),而不是数组的字节数。
  • sizeof(*arr):输出int的大小(如4字节)。
  • sizeof(arr[0]):输出int的大小(如4字节)。
  • 原因:sizeof(arr)返回的是指针arr的大小,而不是数组的字节数,因为arr是指针,不是数组。

2.

  • 动态分配数组时,arr是指针,指向堆区内存,sizeof(arr)返回指针的大小,而不是数组的字节数。

3.

  • 方法1:手动记录数组大小,如int size = 10;。
  • 方法2:使用std::vector或std::array,自动管理大小。
  • 优缺点:
  • 方法1简单,但需手动管理。
  • 方法2安全,但需引入STL。

4.

  • 避免使用sizeof:使用std::vector或std::array,自动管理大小。

问题7:

#include <iostream>void testArrayAndPointer() {// 定义数组int arr[5] = {1, 2, 3, 4, 5};int *p1 = arr; p1++; std::cout << "p1[0]: " << p1[0] << std::endl; //arr++; // 动态分配数组int *p2 = new int[5];for (int i = 0; i < 5; i++) {p2[i] = i + 1;}p2++;std::cout << "p2[0]: " << p2[0] << std::endl; // 释放内存p2--; delete[] p2; 
}
  1. 请解释代码中p1++和p2++的作用,并说明为什么arr++是不合法的。
  2. 为什么p1[0]和p2[0]的输出结果分别是2?请从指针操作的角度详细解释。
  3. 在释放动态分配的内存时,为什么需要将p2减1?请结合代码中的delete[] p2;说明。
  4. 在C++中,如何避免指针操作导致的内存问题?请给出至少两种最佳实践,并说明各自的优缺点。

参考答案要点:

1.

  • p1++和p2++:将指针向后移动一个元素,指向下一个内存地址。
  • arr++不合法:数组名是常量,不能修改,因为数组名代表数组的首地址。

2.

  • p1[0]和p2[0]输出2:因为p1和p2指向数组的第二个元素(索引为1),p1[0]和p2[0]访问的是第二个元素的值。

3.

  • 释放内存时,p2必须指向动态分配数组的首地址,否则delete[]会释放错误的内存,导致未定义行为。
  • 因此,在释放前需要将p2减1,恢复指向首地址。

4.

  • 使用智能指针(如std::unique_ptr或std::shared_ptr)自动管理内存,避免手动操作指针。
  • 在释放指针前,确保指针指向动态分配内存的首地址,避免释放错误的内存。

问题8:

#include <iostream>int main()
{using namespace std;double wages[3] = {1.0, 2.0, 3.0};short stacks[3] = {3, 2, 1};double * pw = wages;short * ps = &stacks[0];cout << "pw = " << pw << ", *pw = " << *pw << endl;pw = pw + 1;cout << "add 1 to the pw pointer:\n";cout << "pw = " << pw << ", *pw = " << *pw << "\n";cout << "ps = " << ps << ", *ps = " << *ps << endl;ps = ps + 1;cout << "add 1 to the ps pointer:\n";cout << "ps = " << ps << ", *ps = " << *ps << "\n";cout << "access two elements with array notation\n";cout << "stacks[0] = " << stacks[0] << ", stacks[1] = " << stacks[1] << endl;cout << "access two elements with pointer notation\n";cout << "*stacks = " << *stacks << ", *(stacks + 1) = " << *(stacks + 1) << endl;cout << sizeof(wages) << " = size of wages array\n";cout << sizeof(pw) << " = size of pw pointer\n";return 0;}
  1. 请解释代码中pw和ps的作用,并说明为什么pw = wages和ps = &stacks[0]是合法的。
  2. 为什么pw = pw + 1和ps = ps + 1会导致指针指向下一个元素?请从指针操作的角度详细解释。
  3. 为什么sizeof(wages)和sizeof(pw)的输出结果不同?请从内存分配的角度详细解释。
  4. 在C++中,如何避免指针操作导致的内存问题?请给出至少两种最佳实践,并说明各自的优缺点。

参考答案要点:

1.

  • pw和ps是指针,分别指向wages和stacks数组的首元素。
  • pw = wages合法:数组名wages退化为指针,指向数组首元素。
  • ps = &stacks[0]合法:&stacks[0]获取数组首元素的地址。

2.

  • pw = pw + 1和ps = ps + 1:将指针向后移动一个元素,指向下一个内存地址。
  • 指针加1时,实际移动的字节数取决于指针类型(如double为8字节,short为2字节)。

3.

  • sizeof(wages):返回数组wages的字节数(如3 * sizeof(double) = 24)。
  • sizeof(pw):返回指针pw的字节数(如8字节),而不是数组的字节数。

4.

  • 使用智能指针(如std::unique_ptr或std::shared_ptr)自动管理内存,避免手动操作指针。
  • 在释放指针前,确保指针指向动态分配内存的首地址,避免释放错误的内存

相关文章:

  • STM32入门教程——OLED调试工具
  • 核心机制:延时应答,捎带应答,面向字节流
  • nginx.conf配置详解:从(413 Request Entity Too Large)说起
  • RPG22.处理武器碰撞
  • Thumb-2指令集及其与STM32的关系
  • [前端]Promsie常见应用场景——网络请求、定时任务、文件操作和并发控制,并以并发请求为详细进行详解
  • python版若依框架开发:前端开发规范
  • Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
  • 客户体验数据使用的三种视角——场景视角
  • 企业管理中,商业智能BI主要做哪些事情?
  • 【Elasticsearch】 查询优化方式
  • 技术文档写作全攻略
  • 三分算法与DeepSeek辅助证明是单峰函数
  • 鸿蒙开发List滑动每项标题切换悬停
  • RAG:大模型微调的革命性增强——检索增强生成技术深度解析
  • 简易EPOLL模型
  • XTEA与TEA的区别
  • Linux信号捕捉技术深度解析
  • 统信桌面专业版如何使用python开发平台jupyter
  • CUDA安装与多版本管理
  • 做网站资源知乎/百度一下app下载安装
  • 无锡做企业网站的公司/关键词查询工具
  • 如何优化公司网站/广州百度推广开户
  • jsp商务网站建设/湖南网站设计外包费用
  • 动态网站系统是什么/搜索引擎优化的英语简称
  • 手机端网站怎么做/网络宣传推广