2 c/c++面试题
注意:栈区和堆区,堆区的存储是按照随意存储,而栈区是先进后出,栈由编译器自动分配释放,存放函数的参数值,局部变量的值等。操作对象不同,strcpy的两个操作对象都为字符串,sprintf操作对象可以是多种数据类型,目的的操作对象是字符串,memcpy的两个对象是任意可以操作的内存地址,并不限于任何数据类型。队列和栈都是先线性存储结构,但是两者的插入与删除数据都是不同的操作,队列是先进先出的数据结构
目录
6.简述strcpy,sprintf与memove的区别?
三者主要有以下不同之处:
操作对象不同,strcpy的两个操作对象都为字符串,sprintf操作对象可以是多种数据类型,目的的操作对象是字符串,memcpy的两个对象是任意可以操作的内存地址,并不限于任何数据类型。
执行效率不同,memcpy最高,strcpy次之,sprintf的效率最低。
实现功能不同,strcpy主要实现字符串变量间的拷贝,sprintf主要实现其他数据类型到字符串的转化,memcpy主要是内存间的拷贝。
说明 strcpy,sprintf,memcpy都可以实现拷贝功能,但是针对的对象不同,根据实际需求,来选择合适的函数实现拷贝功能。
7 链表与数组的区别?
存储形式:数组以一块连续的存储空间存储数组,声明时就要说明长度,链表是一块不连续的动态内存空间,长度可变,每个节点要保存相联节点的信息。
数据查找:数组以线性查找速度非常快,链表需要遍历按顺序查找。
数据的删除与插入:数组删除数据与插入数据时需要不断移动元素,链表可以快速删除与插入。
越界问题:链表不存在数组越界问题,数组有越界问题。
说明:在选择数组与链表的数据结构时,一定要根据实际需要进行选择。数组便于查询,链表便于插入与删除。数组节省空间但是长度固定,链表随让变长,但是占据了更多存储空间。
8.单链表的反转?
ListNode* reverse(ListNode* node){
if(node==NULL||node->next==NULL)return node;
ListNode*p=NULL;
while(node){
Listnode*p1=node;
node=node->next;
p1->next=p;
p=p1;
}
return p;
}
9.简述队列和栈的异同?
队列和栈都是先线性存储结构,但是两者的插入与删除数据都是不同的操作,队列是先进先出的数据结构,栈是先进后出的数据结构。
注意:栈区和堆区,堆区的 存储是按照随意存储,而栈区是先进后出,栈由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构的栈。堆一般有程序员分配释放,若程序员不释放,程序结束时可能由os回收,分配方式类似于链表。
注意堆栈是一种数据结构,堆区与栈区是不同内存的存储区域。
10 C++引用与c指针的区别?
初始化 地址空间 改变
引用(&)本质是 “变量的别名”,指针(*)本质是 “存储变量地址的变量”,这种本质区别决定了二者的核心差异:
| 对比维度 | C 语言指针(Pointer) | C++ 引用(Reference) |
|---|---|---|
| 定义与初始化 | 1. 定义时可不初始化(野指针,危险); 2. 格式: int* p;(*表示指针);3. 初始化需赋值地址: int a=10; int* p=&a; |
1. 必须初始化(否则编译报错,无 “野引用”); 2. 格式: int& ref=a;(&表示引用);3. 初始化必须绑定到已存在的变量(不能绑定字面量 / 表达式) |
| 是否可修改指向 | 可修改指向的变量(重新赋值地址):int a=10, b=20; int* p=&a; p=&b;(此时p指向b) |
不可修改绑定关系(一旦绑定,终身指向该变量):int a=10, b=20; int& ref=a; ref=b;(实际是给a赋值 20,而非绑定b) |
| 是否可空 | 支持空指针(NULL或nullptr):int* p=NULL;(表示指针未指向任何有效内存) |
不支持空引用(必须绑定有效变量,不存在 “null reference”,若强行绑定空地址会触发未定义行为) |
| 间接访问方式 | 需通过*解引用访问目标变量:*p = 20;(修改p指向的变量值) |
无需解引用,直接通过引用名访问(与变量名用法一致):ref = 20;(直接修改绑定变量的值) |
| 多级间接访问 | 支持多级指针(指针的指针):int a=10; int* p=&a; int** pp=&p;(通过**pp访问a) |
不支持多级引用(无 “引用的引用”):int& ref=a; int&& ref2=ref;(&&是右值引用,非多级引用) |
| 作为函数参数 / 返回值 | 1. 传参:需显式传递地址(func(&a));2. 返回值:可返回局部变量地址(危险,局部变量销毁后地址无效) |
1. 传参:直接传递引用(func(ref)),语法更简洁;2. 返回值:可返回局部变量的引用(危险,同指针),但更常用作 “返回对象别名”(如 vector的[]运算符返回元素引用) |
底层实现差异
从编译器角度看,引用的底层实现依赖指针,但语法层面被严格限制,避免了指针的灵活性(同时也减少了风险):
- 指针:编译后会占用独立的内存空间(通常是 4 字节或 8 字节,取决于系统位数),存储的是目标变量的地址。
- 示例:
int a=10; int* p=&a;→p本身占 4/8 字节,存储a的地址。
- 示例:
- 引用:编译后本质是 “常量指针”(
const int*),但语法上隐藏了指针的细节(无需解引用)。- 示例:
int a=10; int& ref=a;→ 底层等价于int* const ref=&a;(指针不可修改指向),但用户层面看不到*和&,直接用ref操作a。
- 示例:
关键区别:指针是 “显式的地址变量”,用户可直接操作地址;引用是 “隐式的常量指针”,用户只能通过别名操作目标变量,无法直接操作地址。
使用场景对比
二者的设计目标不同,适用场景也有明确区分:
1. 适合用指针的场景
- 需要动态修改指向:例如链表节点的遍历(
p = p->next,指针不断指向新节点)。 - 需要表示 “空状态”:例如函数返回值表示 “无结果”(返回
NULL),或指针参数表示 “可选输入”(如void func(int* p),p为NULL时表示无需处理)。 - 多级间接访问:例如二维数组的遍历(
int a[2][3]; int** p=&a[0];),或函数参数需要修改指针本身(如void func(int** p),修改p的指向)。 - C 语言兼容性:若代码需与 C 语言交互,只能使用指针(C 语言无引用)。
2. 适合用引用的场景
- 函数参数传递(避免拷贝):对于大对象(如
struct、class),传递引用可避免对象拷贝(节省内存和时间),且语法比指针简洁。- 示例:
void printString(string& s)→ 传递string引用,无需拷贝整个字符串。
- 示例:
- 函数返回值(返回对象别名):例如容器的
[]运算符(vector<int>& operator[](int idx)),返回容器中元素的引用,支持修改元素(如vec[0] = 10)。 - 运算符重载:为了保证语法自然,运算符重载通常使用引用。例如
operator+返回对象引用,operator=必须返回引用(否则无法链式赋值a=b=c)。 - 避免野指针风险:引用必须初始化,且不能指向空,可减少 “野指针”“空指针解引用” 等常见错误(如
int* p; *p=10;会崩溃,而引用不会有此问题)。
总结:核心区别一句话
- 指针:是 “存储地址的变量”,灵活但危险(可空、可改指向、需解引用),适合需要控制地址的场景。
- 引用:是 “变量的别名”,安全且简洁(非空、不可改绑定、无需解引用),适合 “间接操作变量” 且无需控制地址的场景。
更多推荐


所有评论(0)