|
腾讯一面
windows和linux如何创建线程在Windows和Linux上创建线程的方法如下:
Windows:使用CreateThread函数创建线程。
#include <windows.h>
DWORD WINAPI ThreadFunc(LPVOID param) {
// 线程函数
return 0;
}
int main() {
HANDLE thread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
// 等待线程结束
WaitForSingleObject(thread, INFINITE);
CloseHandle(thread);
}
Linux:使用pthread_create函数创建线程。
#include <pthread.h>
void* ThreadFunc(void* arg) {
// 线程函数
return NULL;
}
int main() {
pthread_t thread;
pthread_create(&thread, NULL, ThreadFunc, NULL);
// 等待线程结束
pthread_join(thread, NULL);
}
在Windows中,使用Windows API(如CreateThread)创建线程;在Linux中,通常使用POSIX线程库(pthread)来创建线程。
线程和进程的区别,线程占用空间吗线程和进程的主要区别在于资源管理和执行环境:
- 进程:是操作系统进行资源分配和调度的一个独立单位,拥有独立的地址空间和系统资源(如文件句柄和设备)。
- 线程:是进程内的执行流,是CPU调度和分派的基本单位,线程共享其所属进程的地址空间和资源,但拥有自己的执行堆栈、程序计数器和一系列寄存器。
线程占用空间:线程确实占用空间,主要包括线程栈(用于存储局部变量和调用历史)、线程本地存储区域和一些必要的管理信息,但相比进程,线程所需的资源和空间通常较少。
线程栈实现原理,线程栈是怎么存储数据的线程栈的实现原理基于以下机制:
- 栈的分配:操作系统为每个线程分配一块连续的内存区域作为栈,用于存储执行流程中的数据。这块内存的大小通常是预设的,可以通过系统设置或程序指定调整。
- 数据存储:线程栈按照后进先出(LIFO)的原则存储数据。每当函数被调用时,函数的局部变量、函数参数和返回地址被推入栈顶;当函数返回时,这些信息被从栈中弹出,回到调用者环境。
- 栈帧(Stack Frame):每个函数调用在栈中占据一个栈帧。栈帧包含函数的局部变量、函数参数、返回地址和某些书keeping信息,如基指针(EBP)等。
- 指针管理:栈指针(SP)用于追踪栈顶的位置,确保数据正确地入栈和出栈。在函数调用过程中,基指针(BP)用于稳定地指向当前栈帧的开始,便于访问函数参数和局部变量。
malloc分配内存是在栈还是在堆
malloc分配的内存位于堆上。
进程结束不释放堆会产生泄露?
是的,如果进程结束时没有释放分配在堆上的内存,这些内存不会自动回收,从而会产生内存泄露。然而,在现代操作系统中,进程结束后,操作系统会回收该进程所使用的所有资源,包括堆内存。
new和malloc区别
new 和 malloc 的区别主要在于:
- 用途:new是C++中用于分配内存的运算符,同时调用构造函数初始化对象;malloc是C中的库函数,仅分配内存。
- 返回类型:new返回具体类型的指针,无需类型转换;malloc返回void*类型,需要显式转换为目标类型的指针。
- 内存初始化:new分配内存后会自动调用构造函数初始化对象;malloc仅分配内存,不进行初始化。
- 配对操作:new与delete配对使用,malloc与free配对使用。
- 错误处理:new在无法分配内存时会抛出异常,而malloc则返回NULL。
- 重载:new和delete可以被重载;malloc和free不能被重载。
C++int占几个字节,指针呢
在C++中,int的大小通常是4个字节。指针的大小通常是4个字节(32位架构)或8个字节(64位架构)。
一般vs程序崩溃是什么原因造成的
Visual Studio程序崩溃通常由以下原因造成:
- 内存访问违规:试图访问无效内存地址。
- 资源泄露:大量消耗系统资源而未释放。
- 指针误用:空指针解引用或野指针操作。
- 异常未捕获:抛出异常且未被捕获处理。
- 堆栈溢出:无限递归或大量局部变量占用。
- 并发错误:多线程访问共享资源未同步。
- 第三方库错误:依赖库中的错误或不兼容。
- 硬件故障:如内存损坏等硬件问题。
- 调试器问题:VS本身的bug或插件造成的问题。
c++生成子类需要虚析构吗
是的,如果你的C++类设计为基类,并且你打算通过基类指针来删除派生类的对象,那么你应该为基类提供一个虚析构函数。这样做可以确保在删除基类指针时,派生类的析构函数会被正确调用,从而避免资源泄露或其他析构相关的问题。
C++虚函数底层是如何实现的
C++中虚函数的底层实现通常是通过虚函数表(虚表,vtable)来实现的。每个包含虚函数的类都有一个虚表,此表是一个包含指向类的虚函数的函数指针的数组。每个对象都包含一个指针(虚指针,vptr),指向其类的虚表。当调用虚函数时,实际上是通过对象的虚指针查找虚表,然后通过虚表调用对应的函数实现的,这允许在运行时进行多态行为。
虚表是存放在哪里的虚表
(vtable)通常存放在程序的只读数据段(.rodata section)中,这是因为虚表包含的函数地址不会在运行时改变。每个对象的虚指针(vptr)指向其类的虚表,确保正确调用对应类的虚函数。
什么是结构体对齐,结构体对齐规则,怎么判断结构体大小
结构体对齐是指在结构体中,每个成员的起始地址相对于结构体起始地址的偏移量是该成员大小的整数倍,这样做是为了满足某些硬件平台对数据存取对齐的要求,提高内存访问效率。
结构体对齐规则通常遵循以下原则:
- 结构体的起始地址能够被其最宽基本类型成员的大小所整除。
- 结构体每个成员相对于结构体起始地址的偏移量应是该成员大小的整数倍,不是的话会进行填充(padding)。
- 结构体的总大小为其最宽基本类型成员大小的整数倍。
判断结构体大小的方法是:
- 确定每个成员的大小和对齐要求。
- 根据成员声明顺序,为每个成员分配地址空间,必要时进行填充。
- 按最大成员对齐要求在结构体末尾可能添加填充。
- 结构体总大小就是最后一个成员末尾地址加上末尾填充(如果有的话)。
使用sizeof(结构体类型名)表达式可以直接得到结构体的大小。
C++里面左值引用和右值引用有什么区别
在C++中,左值和右值是取决于它们可以出现在赋值表达式的哪一边。左值通常出现在赋值表达式的左边,它是一个持久的对象,而右值通常出现在赋值表达式的右边,他们是临时的。
为什么提出右值引用,是为了解决什么
解决两个问题:
- 实现移动语义(Move Semantics):在C++11之前,对象间的操作主要是拷贝,这会引发额外的性能开销。有了右值引用,可以直接转移资源,而不仅仅是复制,这显著提高了性能。
- 支持完美转发(Perfect Forwarding):使用模板和右值引用,函数可以将其参数完美地转发给其他函数,保持原始参数的所有属性(如是左值还是右值,CV限定符等)。这在编写泛型代码(如STL容器)时十分有用,因为它减少了冗余和提高了性能。
线程间怎么同步,同步是解决什么问题
主要的目的是为了防止多个线程同时操作同一片内存空间,导致数据的不一致。
线程间的同步可以通过多种机制实现,包括但不限于以下几种:
- 互斥锁(Mutex):当一个线程使用互斥锁保护的资源时,其他需要这些资源的线程将被阻塞,直到该线程释放资源。
- 信号量(Semaphore):它是一个计数器,用来保护一个或者多个相同的资源。当没有可用资源时,需要这些资源的线程将被阻塞。
- 条件变量(Condition Variables):它可以用来让一个线程等待某个条件成立,而不是忙等。
- Event/Message Queue:线程通过发送和接收事件或消息进行通信。接收线程将阻塞,直到有事件或消息可接收。
如何避免死锁,死锁的条件是什么
避免死锁的一些常见策略包括:
- 破坏互斥条件:尽可能地减少对资源的互斥访问。
- 破坏持有和等待条件:一次性申请所有资源,而不是分步申请。
- 破坏不可抢占条件:使资源可以被抢占,当某个线程需要资源时,已经分配的资源可以被回收。
- 破坏循环等待条件:对资源请求进行排序,按照一定顺序获取资源。
死锁发生的条件有四个,通常被称为死锁的必要条件:
- 互斥:资源不能被共享,只能由一个线程同时使用。
- 持有和等待:线程已经持有至少一个资源,但又提出了新的资源请求,而该资源被其他线程持有。
- 不可抢占:线程持有的资源在未完成其任务前,不能被其他线程抢占。
- 循环等待:存在一种线程资源的循环等待关系。
vector底层是什么数据结构
vector底层是使用连续内存空间来实现的动态数组。
vector空间不够了底层会怎么做
当vector空间不够时,它的底层实现会分配一个更大的连续内存块,将现有元素复制到新的内存块中,然后释放原来的内存块,并更新内部状态以反映新的容量。
vector resize是怎么做的
vector的resize操作主要完成以下步骤:
- 判断新的大小是否大于当前的容量,如果是,则需要额外分配内存空间。
- 如果新的大小比当前元素数量少,将会销毁多余的元素。
- 如果新的大小比当前元素数量多,将会在容器尾部创建新的元素。
- 更新内部的元素数量为新的大小。
C++里面的成员函数和普通函数有什么区别
在C++中,成员函数和普通函数的主要区别如下:
- 成员函数是类的组成部分,它们可以访问类的私有、保护和公共成员。而普通函数不是类的一部分,不可以直接访问类的私有和保护成员。
- 成员函数在调用时需要使用对象或者指向对象的指针或引用来调用,而普通函数则不需要。
- 成员函数可以被声明为虚函数,形成多态性。而普通函数不能。
- 成员函数可以被重载,但是不能被重定义。普通函数可以被重载,不能被重定义。
C++成员函数是怎么访问到成员变量的
C++中,成员函数可以直接访问属于相同类的成员变量,因为它们在调用时隐含地接收一个指向调用对象的指针(通常命名为this)。this指针提供了对调用对象成员变量的直接访问方式。
unity脚本如何与引擎绑定的
在Unity中,脚本与具体的游戏对象(引擎实体)绑定的方式一般如下:
- 首先,创建一个脚本。这可以通过Unity编辑器的"Assets"菜单,选择"Create" -> “C# Script”。
- 接着,将这个脚本拖放到游戏对象上,或者在游戏对象被选中的情况下,直接在检查器窗口点击"Add Component",然后选择你的脚本。
这样,你的脚本就与指定的游戏对象绑定了。然后脚本内的函数就可以通过this.gameObject来访问和控制绑定的游戏对象。
|
|
|