- 博客(913)
- 资源 (2)
- 收藏
- 关注
原创 C++对象模型:构造、析构、拷贝语意学
Point 类的声明public:private:Point类有三个私有成员变量 _x _y 和 _z,定义一个带有默认参数的构造函数,用于初始化Point对象。默认构造函数的行为当一个Point对象被声明但未显式初始化时,如全局变量Point global;,默认构造函数会被调用,初始化所有成员变量为0.0(默认构造函数是 Point,构造函数是一个带默认参数的构造函数,可以作为默认构造函数使用)。对于全局变量,其初始化会在程序启动时发生。显式初始化列表 vs. 构造函数内联扩展。
2024-11-04 12:54:31
786
原创 C++对象模型:Function 语意学
使用C++时,成员函数和非成员函数在性能上应该是等价的。当设计类时,我们不应该因为担心效率问题而避免使用成员函数。实现:编译器会将成员函数转换为一个带有额外this指针参数的非成员函数,这使得成员函数可以直接访问对象的数据成员。成员函数到非成员函数的转换签名修改:首先,成员函数的签名会被修改,在最前面添加一个隐式的this指针作为参数。如果成员函数是const的,那么this指针也会是const。
2024-10-31 17:49:22
796
原创 C++对象模型:构造函数语意学
在C++中,可以为类定义一个类型转换运算符,允许对象被隐式地转换成另一个类型,如operator int()可以让一个类的对象当作整数使用。但是这种隐式类型转换有时会导致意外。如为了让cin可以用于条件判断(如if (cin) ...),定义了一个operator int(),这样cin就可以被转换成一个整数值。如果不小心写错了代码,比如本该是cout << intVal;误写成了cin << intVal;,那么编译器会尝试将cin转换为整数,并且由于存在operator int(),这个转换是合法的。
2024-10-29 15:15:23
664
原创 C++对象模型:关于对象
如果类是从其他类派生的,基类和派生类的数据成员的布局也是不确定的。这会影响内存布局的一致性。当调用一个成员函数时,程序会通过类对象中的指针找到对应的成员函数表,然后根据函数的位置索引找到正确的函数指针并执行该函数。当程序需要访问某个数据成员时,它会通过类对象中的指针找到对应的数据成员表,然后根据成员的位置索引直接读取或写入数据。多重继承:在多重继承的情况下,特别是当有复杂的转换发生时(比如从派生类向其中一个基类进行类型转换),编译器需要维护一些额外的信息来正确处理这种情况,这也可能导致一定程度的性能损失。
2024-10-24 19:22:26
1034
原创 STL源码剖析:Hashtable
哈希表是一种数据结构,它提供了快速的数据插入、删除和查找功能。它通过使用哈希函数将键(key)映射到表中的一个位置来实现这一点,这个位置称为哈希值或索引。哈希表使得这些操作的平均时间复杂度为常数时间,即O(1)。哈希表使用哈希函数将键映射到一个固定大小的数组上。碰撞两个不同的键通过哈希函数得到了相同的索引。由于哈希表的大小是有限的,而键的数量可能非常多,所以碰撞是不可避免的。(1)线性探测:当发生碰撞时,线性探测会在哈希表中按线性顺序搜索下一个空闲位置。(2)二次探测。
2024-10-18 21:13:04
1018
原创 STL源码剖析:适配器
适配器(adapter)是一种设计模式,将一个 class 的接口转换为另一个 class 的接口,使原本因接口不兼容而不能合作的对象可以一起运作。适配器之所以能够工作,是因为adapter内部持有对象的副本,副本可以是容器、迭代器、流或者一个仿函数。通过对这个对象的封装,适配器可以控制对象的行为,并对其输入和输出进行必要的调整。这样,适配器就能够将原本不兼容的接口或功能转化成一个统一的形式,使其能够在标准库算法中使用。
2024-10-15 20:13:59
1084
原创 STL源码剖析:STL算法
诸如拷贝(copy)、互换(swap)、替换(replace)、填写(fill)、删除(remove)、排列组合(permutation)、分割(partition)、随机重排(random shuffling)、排序(sort)等算法,都属此类。查找(find),匹配(search),计数(count),巡访(for_each),比较,寻找极值(max, min)等算法。lg()计算的是二进制对数的值(即log2),并乘以 2,这是控制分割深度的手段,以防止快速排序出现性能退化的情况。
2024-10-14 20:54:39
1045
原创 STL源码剖析:仿函数
可以当函数使用的对象,一个类或结构体,重载圆括号操作符 "()",可以像函数一样被调用。仿函数对象可以当成参数传入函数中。
2024-10-12 17:30:14
599
原创 嵌入式C语言自我修养:编译链接
重定位条目:在汇编器生成目标模块时,由于它无法预知模块中的数据和代码在内存中的具体加载位置,也无法确定模块所引用的外部函数或全局变量的具体位置,因此每当遇到这类不确定位置的引用时,汇编器会在目标文件中生成相应的重定位条目。当动态库第一次被链接器加载到内存参与动态链接时,动态库映射到了当前进程虚拟空间的mmap区域,动态链接和重定位结束后,程序就开始运行。在命令行中,如果定义一个符号的库在引用这个符号的目标文件之前,引用就不会被解析(因为加到了未解析符号引用的集合),因此链接会失败。局部变量会由栈来管理。
2024-10-04 22:16:29
1315
原创 嵌入式C语言的自我修养:内存泄漏与防范
如fast bins,主要用来保存用户释放的小于80 Bytes(M_MXFAST)的内存,在提高内存分配效率的同时,带来了大量的内存碎片。如果指针在遍历链表时已经指向链表的末尾或头部,指针已经指向NULL了,此时再通过该指针去访问节点的成员,就相当于访问零地址了,也会发生一个段错误,这个指针也就变成了非法指针。内存管理子系统将一个进程的虚拟空间划分为不同的区域,如代码段、数据段、BSS段、堆、栈、mmap映射区域、内核空间等,每个区域都有不同的读、写、执行权限。
2024-09-25 21:48:17
1057
原创 xv6讲解(2) Operating system organization
这一章 讲操作系统是如何实现进程并发、进程隔离和进程交互(multiplexing isolation and interaction)操作系统需要同时支持多个进程。如一个进程可以使用fork来启动新的进程。对计算机的资源进行时间共享,确保所有进程都有机会执行。操作系统需要为进程之间提供隔离,如果一个进程有错误并且出现故障,它不应影响不依赖于有错误进程的其他进程。进程之间应可以进行交互(通信):如管道....。
2024-09-23 22:34:20
821
原创 嵌入式C语言自我修养:GNU C编译器扩展语法精讲
)(void)是一个类型转换,它将后面的表达式转换为void类型,这意味着表达式的结果被丢弃。而是一个比较两个变量地址是否相同的表达式,这个表达式的结果是一个布尔值,但通过将其转换为void类型,这个结果就被忽略了。void这一行代码的目的是让编译器认为_min1和_min2(或_max1和_max2)这两个变量被使用了,从而避免编译器发出未使用变量的警告。长度为0的数组int a[0];零长度数组有一个特点,就是不占用内存存储空间。
2024-09-22 11:57:52
1814
原创 嵌入式C语言自我修养:C语言的模块化的编程思想
extern关键字对外部文件的符号进行声明extern声明的变量或函数在别的文件里定义,在本文件使用,告诉编译器不要报错。int age;int num;int num;j<10;定义的本质就是为对象分配存储空间,声明则将一个标识符与某个C语言对象相关联(函数、变量等)。
2024-09-19 21:08:43
1643
原创 C和指针:指针
只有当两个指针都指向同一个数组中的元素时,才允许从一个指针减去另一个指针,两个指针相减的结果的类型是 ptrdiff_t,它是一种有符号整数类型。减法运算的值是两个指针在内存中的距离(以数组元素的长度为单位,而不是以字节为单位),因为减法运算的结果将除以数组元素类型的长度。要求边界对齐的机器上,整型值存储的起始位置只能是特定的字节,通常是2或4的倍数。c声明为浮点数,但是存放在内存中使用的整数,它们可以被解释为整数,也可以被解释为浮点数,取决于它们被使用的方式。a存放的是一个随机的整数,行为未定义。
2024-09-13 15:14:31
1522
原创 C和指针:数组(一维数组,多维数组,指针数组)
char const *string这样的类型声明时,string是一个指向字符的指针,这个字符是指向的内容是不可修改的。上面两个声明等价,数组当作参数传入函数本质是传入指针,但是它传递的只是数组第一个元素的指针,但是这种方法无法让函数知道数组的确切长度,所以需要把数组长度显示传入函数中。d是一个包含3个元素的数组,每个元素都是包含6个元素的数组,6个元素中的每一个又都是包含10个整型元素的数组。c是个一维数组的一维数组。一维数组名的值是一个指针常量,类型是指向元素类型的指针,指向数组的第1个元素。
2024-09-13 10:16:23
902
原创 C和指针:标准函数库
longjmp 函数:一旦在程序的其他部分遇到了 longjmp 调用,并且传入了之前 setjmp 初始化的 jmp_buf 数组以及一个非零值作为跳转值,那么程序的控制就会立即跳回到与那个 jmp_buf 关联的 setjmp 处。setjmp 函数:一个函数中调用 setjmp 并传递一个类型为 jmp_buf 的数组作为参数时,setjmp 将会保存当前程序执行环境的一个快照(包括处理器寄存器和程序计数器等状态信息)到这个 jmp_buf 数组中。如果在某个低层的函数中检测到一个错误,你可以立。
2024-09-11 09:33:21
715
原创 C和指针:预处理(#include/define/if...)
预处理器把所有name替换成 stuff。在程序中扩展#define定义符号和宏时,需要涉及几个步骤:1.在调用宏时,首先对参数进行检查,看看是否包含了任何由#define 定义的符号。如果是,它们首先被替换。2.替换文本随后被插入到程序中原来文本的位置。对于宏,参数名被它们的值所替代。3.最后,再次对结果文本进行扫描,看看它是否包含了任何由#define 定义的符号。如果是,就重复上述处理过程。这样,宏参数和#define 定义可以包含其他#define定义的符号。但是,宏不可以出现递归。
2024-09-10 15:27:18
796
原创 C和指针:高级指针话题
进一步探讨指向指针的指针这些声明在内存中创建了下列变量。如果它们是自动变量,无法猜测它们的初始值。二级指针指向一级指针因为函数传参使用值传递不会修改原值,传递指针可以修改原变量,如只有传入一个链表节点的指针才可以进行链表的插入或删除操作。
2024-09-09 21:22:18
539
原创 C和指针:结构体(struct)和联合(union)
结构体包含一些数据成员,每个成员可能具有不同的类型。数组的元素长度相同,可以通过下标访问(转换为指针)。但是结构体的成员可能长度不同,所以不能用下标来访问它们。成员有自己的名字,可以通过名字访问成员。
2024-09-08 09:53:33
803
原创 C和指针:字符串
字符串就是一串零个或多个字符,并且以一个位模式为全0的NUL字节结尾。字符串长度就是字符串中字符数。string为指针常量(const修饰string),指向的string是常量不能修改。size_t是无符号数,定义在stddef.h。C语言遍历字符串: while(*string++!='\0'){}上面两句不等价,第二个恒真,无符合数-无符合数还是无符合数,恒大于0。
2024-09-07 11:53:47
1042
原创 WSL2 使用usbipd工具 连接USB设备
usbipd list 可以查看连接到win上的设备。把USB设备从win转移到WSL需要执行下面两个指令。使用开源工具usbipd,可以让usb设备连接WSL。
2024-08-13 23:02:28
631
原创 进程通信(7):互斥锁(mutex)和条件变量
/在条件变量上等待,线程进入阻塞状态,直到有其他线程把它唤醒。// 获取锁,如果获取不到锁,线程进入阻塞状态,释放CPU。条件变量可以让获取互斥锁 的线程在某个条件变量上等待,直到有其他线程把他唤醒。//非阻塞 获取锁,如果获取不到,就返回错误码。//唤醒等待在cond的所有线程。互斥锁(mutex)用于互斥访问临界区,只允许一个线程访问共享变量。//唤醒等待在cond的某个线程。
2024-08-03 22:32:17
477
原创 进程通信(6):POSIX信号量
定义两个线程,一个是生产者,一个是消费者,初始化信号量(这里使用的是有名信号量,根据一个路径字符串初始化信号量的值。也可以使用sem_init和sem_destory创建基于内存的信号量),mutex初始化为1,nempty初始化为NBUFF,nstored为0。初始化多个生产者线程和一个消费者线程,然后初始化3个信号量(这里用到的是基于内存的信号量),同(1)。nitems是要存取的个数,buff表示缓冲区,nput 和 nputval 表示已经存放的个数和下一个存放的值,三个信号量作用和(1)相同。
2024-08-03 18:42:34
452
原创 linux内核:文件系统的组织(超级块,索引节点,目录项,文件对象)
文件1和文件2指向同一个inode,使用ls -i 可以为一个文件建立硬链接后,inode号相同,内容也是相同的,操作的是同一个文件。文件目录树的路径的一个组成部分。方便查找文件设立的,一个路径的各个组成部分不管是文件还是目录都是一个目录项对象。为一个文件建立硬链接的就是为这个文件对应的inode节点建立一个新的目录项指向这个inode。文件对象和物理文件是对应的,表示进程和文件的对应。多个进程可以打开同一个文件,所以同一个文件可以对应多个文件对象。存放具体文件的信息,代表实际一个文件。
2024-08-01 20:09:19
298
原创 Linux内核:哈希表hlist_head和hlist_node
结构体有hlist_head和hlist_node,hlist指向hlist_node。linux内核中哈希表使用链接法实现哈希表,为什么要使用二级指针?
2024-08-01 17:52:02
177
原创 计算机体系结构:缓存一致性&&ESI
不同核访问存储器时间相同。共享存储器按模块分散在各处理器附近,处理器访问本地存储器和远程存储器的延迟不同,共享数据可进入处理器私有高速缓存,并由系统保证同一数据的多个副本的一致性。每个处理器核拥有本地的LLC(最后一级缓存),并通过片上互连访问其他处理器核的LLC。在共享存储的多核处理器中,存在Cache一致性问题,如何使同一数据块在不同Cache以及主存中的多个备份保持数据一致的问题。
2024-07-31 22:37:24
996
原创 进程通信(5):POSIX消息队列
消息赋予一个优先级,Posix读一个消息队列总是返回最高优先级的最早消息,System V消息队列读可以返回任意指定优先级的消息。当往一个空队列放置一个消息时,Posix消息队列允许产生一个信号或者启动一个线程(Posix消息队列不提供这种机制。Posix消息队列,信号量和共享内存至少是随内核持续的,但是如果使用映射文件实现的,那就是随文件系统持续的。mq_send和mq_receive函数向队列中放置一个消息,从队列中取走一个消息。消息队列可以认为是一个消息链表,可以向消息队列发送消息和读取消息。
2024-07-19 22:13:09
298
原创 进程通信(2):命名管道FIFO
在服务器端使用的公共的fifo命名管道(路径名被所有客户端知道),每个客户端创建自己的fifo文件,然后以只写的方式打开服务器的fifo文件,向fifo写自己的fifo路径名和要打开的文件名。(3)写操作的阻塞特性:FIFO的写操作是阻塞的(如果没有设置为非阻塞模式),这意味着如果没有读进程在另一端等待接收数据,写进程将被阻塞,直到有读进程出现。(2)大于PIPE_BUF的写操作:如果写入的数据量超过了PIPE_BUF的大小,那么写操作将可能被分割,也就是说,写操作可能不是原子的。
2024-07-15 23:22:36
993
原创 进程通信(1):无名管道(pipe)
首先父进程使用pipe创建两个管道pipe1和pipe2,然后使用fork创建子进程,在子进程中关闭pipe1的写端和pipe2的读端。因为父进程调用fork函数创建子进程,子进程拷贝父进程的文件表,由于父子进程文件表内容相同,指向的file相同,所有最终父子进程操作的pipe管道相同。(1)创建管道1(fdl[0]和fdl[1])和管道2(fd2[0]和fd2[1]);如果需要双向数据流,需要创建两个管道,然后关闭一个管道的读端和另一个管道的写端。(3)父进程关闭管道1的读出端(fd[0]);
2024-07-14 18:07:05
359
原创 linux内核中创建进程和线程做了什么工作?
但是线程由于共享了内存空间和其他一些资源,所以线程上下文切换的开销省去了复制父进程内存空间和其他一些资源的开销,所以使用线程可以有更高的并发度。从上面可以看到在创建线程时,有一个标志时表示创建的是线程,然后创建的线程和父进程共享虚拟内存空间,文件描述符,但是每个线程都有自己一个栈和TLS块,在线程初始化的时候设置栈指针、栈大小和TLS地址。stack_size=0x7fff00: 这个值通常表示栈大小,但在这个上下文中,它看起来像是一个无效的值(可能是输出解析的问题),因为栈大小应该是字节的数量。
2024-07-12 11:36:05
353
原创 操作系统:进程的状态
终止态:指进程完成任务到达正常结束点,或出现无法克服的错误而异常终止,或被操作系统及有终止权的进程所终止时所处的状态。当进程已分配到除CPU以外的所有必要资源后,只要再获得CPU,便可立即执行,进程这时的状态称为就绪状态。创建一个进程需要通过两个步骤:1.为新进程分配所需要的资源和建立必要的管理信息,设置该进程为就绪态,并等待被调度执行。当系统资源尤其是内存资源已经不能满足进程运行的要求时,必须把某些进程挂起(suspend),对换到磁盘对换区中,释放它占有的某些资源,暂时不参与低级调度。
2024-06-08 12:10:12
299
原创 操作系统笔记(1)进程相关
进程同步:多个相关进程在执行次序上进行协调,使并发执行的进程之间能按照一定的规则共享系统资源,并能很好的合作,从而使进程的执行具有可再现性。进程之间可能存在互斥或者同步的关系。访问临界资源。临界资源:一段时间内只允许一个进程访问的资源。进程合作。:临界资源必须互斥访问,访问临界资源的代码称为临界区。:每个进程在进入临界区钱,应先对临界资源进行检查,看是否正被访问。在临界区后,用于将临界区正被访问的标志恢复为未被访问的标志。(1)空闲让进:临界区无进程访问,让请求进入的进程立即进入。
2024-06-04 09:38:47
686
原创 C++笔记(1)
封装,类可以将内部数据(私有成员)隐藏起来,仅暴露有限的公共接口(公有成员函数)供外部访问,这样可以防止数据被不当修改。多态允许使用一个接口来表示不同类型的实体,从而能够在不修改现有代码的情况下,使程序能够处理多种数据类型或类的对象。这通过虚函数、重载运算符和函数重写等机制实现。继承是一种建立类与类之间关系的方式,子类可以继承父类的属性和方法,并在此基础上添加或修改,实现代码的复用和扩展。这有助于构建一个层次化的类结构,促进模块化和灵活性。6. 预处理指令有什么用?
2024-06-03 23:17:06
577
原创 超标量处理器设计:重排序缓存(ROB)
★超标量处理器的很多地方用到了重排序缓存,但是我对它不是很了解,所以我整理一下重排序缓存的知识点。重排序缓存(ROB)在确保乱序执行的指令能够正确地完成和提交(Commit),也可以用来寄存器重命名。ROB是一个先进先出的表,每个项是ROB表项,可以记录指令执行的信息。ROB表项的字段标志位,用来标记指令是否已经完成执行阶段。当指令的所有操作(包括计算、访存等)都完成,标志就会被置为“是”,指令准备好进入退休阶段。指令在程序代码中指定的目的寄存器(逻辑寄存器)。
2024-05-09 21:43:45
973
eclipse连接hadoop
2022-05-16
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人
RSS订阅