- 博客(40)
- 收藏
- 关注
原创 数据结构:哈希表
为了解决该问题,每当遇到哈希冲突时,我们就进行哈希表扩容,直至冲突消失为止。链式地址(separate chaining)将单个元素转换为链表,将键值对作为链表节点,将所有发生冲突的键值对都存储在同一链表中。比如,输入空间为全体整数,输出空间为数组容量大小,则必然有多个整数映射至同一桶索引。我们先考虑最简单的情况,仅用一个数组来实现哈希表。在哈希表中,我们将数组中的每个空位称为桶(bucket),每个桶可存储一个键值对。构成的输入空间映射到数组所有索引构成的输出空间,而输入空间往往远大于输出空间。
2024-09-09 17:30:52
539
1
原创 数据结构:二叉树
每个节点都有两个引用(指针),分别指向左子节点(left-child node)和右子节点(right-child node),该节点被称为这两个子节点的父节点(parent node)。当给定一个二叉树的节点时,我们将该节点的左子节点及其以下节点形成的树称为该节点的左子树(left subtree),同理可得右子树(right subtree)。与链表类似,二叉树的基本单元是节点,每个节点包含值、左子节点引用和右子节点引用。(2)前序,中序,后序遍历(深度优先遍历)(1)完美二叉树(满二叉树)
2024-09-07 17:21:53
295
原创 数据结构:栈与队列
队列是一种特殊的线性表,其特殊性在于它只允许在表的前端(front)进行删除操作,而在表的后端(rear)进行插入操作。检查栈顶指针是否等于数组的最大索引值减1(因为栈顶指针指向的是栈顶元素的下一个位置),如果是,则栈满。分配一个固定大小的数组空间给栈,并设置栈顶指针(或称为栈顶索引)为-1(或数组的起始位置索引,这取决于具体实现,但-1通常用于表示栈为空的情况)。在栈的动态变化过程中,如果需要扩容(虽然这通常不是顺序栈的常规操作,但理论上可以通过复制数组到更大的内存空间来实现),则成本较高。
2024-09-06 20:21:23
629
原创 数据结构:内核链表
在某些实现中,还可能包含指向数据本身的指针,但在Linux内核链表中,节点并不直接包含用户数据,而是将数据保存在包含链表节点的结构体中。内核链表:在Linux内核中,链表节点(list_head)与具体的数据结构是分开的,通过包含list_head的结构体来构建链表。内核链表通过指针将节点相连,使得插入、删除和遍历操作能够在O(1)或O(n)的时间复杂度内完成,具体取决于操作的特定位置和链表的长度。删除链表中的节点时,需要找到该节点的前一个节点和后一个节点,并修改它们的指针以绕。在双向链表中,可以从链表。
2024-09-05 21:18:33
1018
原创 数据结构:双向链表
内存碎片是指系统中所有不可用的空闲内存,这些碎片之所以不能被使用,是因为负责动态分配内存的分配算法使得这些空闲的内存无法使用。产生原因:假设有一块连续空闲内存空间,当某个进程请求的内存大小与空闲内存块不完全匹配时,系统可能会分配一个稍大一点的内存块给该进程,从而产生内部碎片。定义:外部碎片指的是还没有被分配出去(不属于任何进程),但由于太小了无法分配给申请内存空间的新进程的内存空闲区域。产生原因:频繁的内存分配和释放、不同大小的内存分配、内存对齐问题等,都可能导致外碎片的产生。二、内存碎片的产生原因。
2024-09-04 19:51:04
309
原创 数据结构:链表
程序的设计:将现实中大量而复杂的问题以特定的数据类型和特定的存储结构存储在内存中,索引存储:通过关键字构建索引表,通过索引表来来找到数据的存储位置。散列存储(哈希存储):将数据元素的存储位置与关键码之间建立确定对。线性结构:元素之间一对一的关系(表(数组,链表),队列,栈 )图形结构:元素之间多对多的关系(网状结构)一组用来保存一种或者多种特定关系的数据的集合(组织和存储数据)容易造成存储空间碎片。数据的物理结构:数据的逻辑结构在计算机内存中的存储形式。数据的逻辑结构:数据元素与元素之间的关系。
2024-09-03 19:44:08
321
原创 Linux:SQLite 数据库
功能:在使用sqlite3_exec执行select语句时, 每查找到一条数据,则调用一次回调。column_value: 查找到的一行数据的每一列值的地址的集合。column_name:查找到的一行数据的每一列列名的地址的集合。arg : sqlite3_exec传递的第4个参数。column_cnt: 查找到的数据的列数。2. 回调函数必须返回0;注意:1.每找到一行,回调被触发一次。.schema 表名。
2024-08-29 20:15:16
2286
原创 Linux系统编程:网络服务器
单循环服务器适合处理少量的并发连接,但随着连接数的增加,它会导致性能问题,因为单个线程无法高效地处理大量的并发连接。优点:每个进程都拥有独立的内存空间和系统资源,进程间不会互相干扰,提高了系统的稳定性,在一个进程崩溃时,其他进程不会受到影响。优点:线程间共享同一进程的内存空间,使得多线程在并发处理上具有更高的效率,相比于多进程,多线程能够更节省资源,特别是内存资源。缺点:每个进程都需要消耗一定的系统资源,包括内存和CPU时间,进程间的通信和切换比线程间的通信和切换更加耗时,效率较低。
2024-08-27 23:50:46
595
原创 Linux系统编程:TCP,UDP协议特点,粘包,wireshark抓包
应用程序资源调用:在SERVER/CLIENT架构的应用 程序中,当服务器与客户端位于同一台机器且缺乏外部SERVER时,可将SERVER资源部署于本机,并通过将SERVER的IP设置为127.0.0.1来实现资源的本地调用,确保程序顺畅运行。此方法虽复杂,但高度灵活,适用于多种复杂场景。在非阻塞套接字上,send可能返回小于请求发送的字节数的值,表示只有部分数据被发送,需再次调用send发送剩余数据。面向数据报:UDP保留了数据包的边界,每个UDP数据包都是独立的,适合传输少量数据或需要快速响应的应用。
2024-08-23 20:28:14
1731
原创 Linux系统编程:IPC通信和网络通信
网络地址是子网的唯一标识,类似于电话号码的区号;直接使用二进制数,不容易记忆,为了方便记忆,将32位二进制码划分为4个字节,每个字节转化成相应的十进制数,字节之间用“.”来分隔,这种表示方法,称之为“点分十进制表示法”。UDP(User Datagram Protocol)用户数据报协议,是不可靠的无连接的协议。shmaddr 本地可用的地址,如果不确定则用NULL,表示。全局区定义空的handler,接收进程的接收循环中用pause();在数据发送前,因为不需要进行连接,所以可以进行高效率的数据传输。
2024-08-21 18:41:18
926
原创 Linux系统编程:进程间通信 2:有名管道和信号
有名管道可以实现两个非亲缘关系的进程间的通信,通过将两个进程连接在同一个管道上,使用有名管道进行通信:进程可以通过open函数以只写模式打开有名管道,然后通过write函数向管道中写入数据;当某个事件发生时,例如键盘输入、硬件故障等,内核会向目标进程发送一个信号,通知进程进行处理。信号通常用于进程控制,例如在终端中通过Ctrl+C发送SIGINT信号来终止进程;(1)信号发送:可以使用kill等函数向进程发送信号。例如:kill(pid, SIGINT)向进程ID为pid的进程发送SIGINT信号。
2024-08-17 20:14:26
231
原创 Linux系统编程:进程间通信 1:管道
此时,写操作,会导致管道破裂,SIGPIPE //这个信号会使得程序结束。管道如果为空,此时可以一直写,直到写满,此时写操作阻塞。(1)pipefd //用来获取 管道的两端 //读端pipefd[0]写端为pipefd[1],读端为pipefd[0];管道中没有数据,则读操作不阻塞,立即返回。管道中有数据,则读取数据,最终把数据读完。管道中有数据,则读取数据,最终把数据读完。(2)管道操作特点 :数据读走之后,认为就没了。管道中没有数据,则读操作阻塞。4写端不存在,读管道。(6) 信号量集()(同主机)
2024-08-16 18:36:28
417
原创 Linux系统编程:线程 3:信号量
sem_init的第二个参数表示信号量是在线程间使用还是在进程间使用,为0表示在线程间使用,非0表示在进程间使用,第三个参数表示资源,1表示有一个资源可用,0表示没有资源;线程1中sem_wait (&sem_w),sem_post (&sem_r),表示先进行p操作,sem_w资源-1,然后进行v操作,sem_r资源+1;线程2中sem_wait (&sem_r),sem_post (&sem_w),表示先进行p操作,sem_r资源-1,然后进行v操作,sem_w资源+1;2.写线程写完了,资源-1。
2024-08-16 15:36:24
603
原创 Linux系统编程:线程 2 :互斥锁
线程之间会有资源竞争,这个资源是共享资源(临界资源),访问共享资源的那段代码叫临界区,当多个线程同时运行时,会发生资源竞争,可能在一个线程还没有执行完毕另一个线程就去使用资源了,导致在某些场景下程序的结果与预期不同,互斥锁可以解决这类问题。允许死锁的发生,不过操作系统会负责检测出死锁的发生,然后采取某种措施解除死锁。3.请求和保持条件:进程己经保持了至少一个资源,但又提出了新的资源请求,而该资源又被其他进程。4.循环等待条件:存在一种进程资源的循环等待链,链中的每一个进程已获得的资源同时被下一个进。
2024-08-15 19:55:09
807
原创 Linux系统编程:线程 1
参数:(1)thread --- 线程tid (2)retval --- 用来保存,退出状态值,所在空间的地址。线程——轻量级的进程,线程是CPU执行的最小单位,进程是资源分配的基本单位,线程的创建和调度的时空开销都比进程小;本质上是一个函数的名称即可, 线程回调函数 --完成线程任务功能的函数 ,需要调用者自己实现。线程的编程过程类似于进程的编程过程:线程的创建,线程的执行,线程的结束,线程结束后的资源回收;线程退出时,可以带出退出状态值,但是传的是,退出状态值对应空间的地址;
2024-08-14 18:29:18
743
原创 Linux系统编程:进程 3 wait函数
(2)非阻塞调用:父进程会查看子进程状态改变,但如果未发生状态改变,父进程不阻塞,整个程序继续向下,非阻塞调用必须套在循环中处理;一般形式为:pid_t waitpid(pid_t pid, int *wstatus, int options);pid > 0 表示等待指定的子进程状态改变,如pid为50,表示等待pid号为50的子进程状态改变;子进程:exit(退出状态值),退出状态值只有最低8位有效,为0~255;(1)wait和waitpid都是等待子进程状态改变;功能:1.获取子进程的退出状态;
2024-08-14 14:48:17
675
原创 Linux系统编程:进程 2
(2)父子进程做的事不同,名称也不同,如:shell系统——bash,在这里调用了exec函数族,作用是启动(运行)了一个新的程序,本质是在函数调用后用新的镜像程序替代当下的程序。execle和execvpe,“e”表示环境变量environ,可以调用extern char **environ作环境变量,在传参的最后要加环境变量environ;int execl(const char *path, const char *arg, ...) —— execl(“路径”,“程序名”,“参数”,NULL);
2024-08-13 18:57:57
220
原创 Linux系统编程:进程 1
当一个进程暂停后又继续,会从前台进程变成后台进程,用ps查看会发现失去了'+'号,此时进程无法用ctrl + c终止,只能用kill -9 pid号终止。(1)创建进程,为了实现多任务,fork()创建,pid_t fork(void),父进程返回子进程的pid号,子进程返回0;(4)kill:给进程发信号,kill -l查看kill的所有信号,常用信号有kill -9 pid号:终止进程。kill -19 pid号:暂停进程,kill -18 pid号:继续进程,(3)进程结束(僵尸态);
2024-08-12 19:18:17
428
原创 Linux系统编程:目录和文件
DIR * opendir("目录名"),./是当前目录,../是上层目录。根据用户id到/etc/passwd文件下解析获得。根据gid到/etc/group文件中解析组信息。打印errnum出错码对应的出错信息。目录操作流程:打开目录,读取目录,关闭目录。一般写为Makefile或makefile。成功返回保存日历时间结构体的指针。timep:保存秒数空间的地址。timep:保存时间空间的地址。成功返回获得时间字符串的首地址。关闭目录,closedir(dir)8.ll,stat获得文件的详细信息。
2024-08-08 18:39:04
1853
原创 Linux系统编程:文件IO
头文件:#include <sys/types.h> #include <unistd.h>(3)文件描述符:很小的非负的整数 int 0-1023,内核每打开一个文件就会获得一个文件 描述符。如果是字符型数组,用stelen,写入有效字符,如果用sizeof,会把128个字节全部写入,除有效字符外全为空字符。类似于fleek函数,返回值是off_t,是长整型,打印的时候用%ld,偏移后写入默认是覆盖写。一般形式:lseek(fd, 偏移个数,SEEK_SET)
2024-08-06 17:35:21
689
原创 Linux系统编程:标准IO
eg : ====》stdio.h ===>stdio.c==>libc.so ==>/usr/lib so 动态库。一般都是对普通文件操作是一种有缓存的IO 在文件IO和用户程序之间,加入缓冲区,可以有效减少系统调用的效率,节省系统IO调度资源。man xxx == man 1 xxx ===>查看当前xxx命令。I: 键盘是标准输入设备 ====》默认输入就是指键盘 /dev/input。1,标准io的概念 1975 Dennis r IO库,C语言的标准,ANSI c。
2024-08-05 19:00:13
392
原创 Linux系统编程:shell脚本
shell编程:解释型语言,边翻译边执行,擅长文件处理,操作系统管理,开发效率高 cp 1 2,执行效率低,移植性好。-gt是判断>,-eq是判断=,-lt是判断<,-le是判断<=,-ge是判断>=,-ne是判断!使用' * '时,不能直接写,要写成' \* ',两边要空格,因为在命令行中' * '叫通配符。.c:编译型语言,先编译再执行,擅长数据计算和数据处理,开发效率低,执行效率高,移植性差。$#:传入参数的个数,$?命令行使用' + ',' - ',' / '时可以直接写,两边要空格。
2024-08-03 17:54:55
539
原创 C语言:链表,共用体,枚举,位运算
例如,可把一个整型变 量、一个字符型变量、一个实型变量放在同一个地址开始的内存单元中。这种使几个不同的变量共占同一段内存的结 构,称为“共用体”类型的结构。右移运算符‘ >> ’的作用是使数整体向右移,当该数有符号时,正数补1,负数补0(算数右移),无符号时,前面补0(逻辑右移);左移运算符‘ << ’的作用是使数整体向左移,后面补0(逻辑左移);异或‘ ^ ’的作用是指定位置翻转,不同取1,相同取0;位运算要求整型或与整型相兼容的类型,枚举类型也可以;1.链表的尾插,头删和尾删。
2024-08-02 19:47:58
242
原创 C语言:指针3
强转为 int *,此时再 - 5减的是 int 型,4个字节,就会从数组a的外面减到a[ 1 ][ 4 ];————此时p + 1表示指针p所指的地址进入到了二维数组a的第二行;当一个指针数组作为实参传递时,对应的形参应该是指针的指针(指向指针地址的指针):**p;给 **p传参时要穿地址,&s,如果是数组,传:&s[ i ]或s + i;*(*(a + i) + 1)指的是a[ i ][ 1 ]的值;是指针数组,表示有10个指针的数组;*(a + i) + 1指向 a[ i ][ 1 ]的地址;
2024-07-31 19:13:25
450
原创 C语言:指针2
则 p 只能指向 a,但可以通过 *p = 20;,返回一个指向已分配内存的指针,如果分配失败则返回 NULL。释放 p 所指向的内存空间,释放后应将 p 置为 NULL 以避免悬空指针。p 所指向的地址不可改变,但可以通过 p 修改地址处的值。例如,const int * const p = &i;,其中 i 的值和 p 的地址都不可改变。既不能修改 p 所指向的地址,也不能修改该地址处的值。例如,p = realloc(p, 200);例如,char *p = malloc(100);
2024-07-30 19:42:57
455
原创 C语言:指针
指针定义的一般规则:基类型 *指针变量名,指针变量只能装对应的基类型的类型,如:int *p,只能装int型;当用数组时,a[ i ] <=> *(a + i),下标写法改成*(a + i),a[ 0 ] = *a;野指针:定义*p时,没有初始化,此时*p是随机地址,编译不报错,但是运行程序会崩溃;* 和 &两个在一起时互相抵消,取地址,如:*&p和&*p一样都是取p的地址;* 是指针运算符,在int *p时,里面的 * 是类型说明符;*p++和++*p都表示加指针所指的变量;
2024-07-29 19:47:28
518
原创 C语言标识符的作用域与可见性
函数中的局部变量,如果不专门声明为static存储类别,都是动态地分配存储空间的,数据存储在动态存储区中。函数中的形参和在函数中定义的变量(包括在复合语句中定义的变量),都属此类,在调用该函数时系统会给它们分配存储空间,在函数调用结束时就自动释放这些存储空间。有时希望函数中的局部变量的值在函数调用结束后不消失而保留原值,即其占用的存储单元不释放,在下一次该函数调用时,该变量已有值,就是上一次函数调用结束时的值。有不带参数的宏定义和带参数的宏定义,本质上是在预处理阶段将所有的宏名变成定义的字符串;
2024-07-27 17:16:42
847
原创 C语言:数组函数
传递数组名本质上是指针,只传递了首元素地址,无法判断元素的个数,因此传参时还要传递第二个参数len;在被调函数中,sizeof(a)= 8,这是因为sizeof打出的是数组a的首元素的地址长度;由于实参可以是表达式,而数组元素可以是表达式的组成部分,因此数组元素当然可以作为函数的。二维数组在传参时要穿行数(rows),函数定义时,列数(cols)要是一个固定值;数组函数的传参是指针传参,会改变实参,因此可以在被调函数中修改主调函数的参数;实参,与用变量作实参一样,是单向传递,即“值传送”方式。
2024-07-26 19:42:01
229
原创 嵌入式学习第十天:函数
函数的调用本质上是程序的跳转,在函数跳转时,有一个pc(program counter)会将函数要跳转的地方指出,这时,会有栈区将定义的变量保存,栈相当于一个只有一端开口的管子,先进去的值会在最后出来,当要回到程序时,实际上是先把main装入栈区,这样最后main出来就会回到程序中,在Linux系统中,栈区的大小约为8兆,Windows系统中只有大约1兆,栈区相当于是用来保护和恢复现场的,当栈区空间不够用时,会出现堆区,增加栈区的空间,堆区需要手动申请和销毁。返回main函数中调用a函数的位置;
2024-07-25 19:17:53
225
原创 嵌入式学习第九天:二维数组
a[3][4]的初始化:a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};也可以是a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};他们两个的区别是如果中间缺少一个元素,第一种方法是在选定的位置少一个变成0,第二种方法是在数组的最后一位少一个变成0;这个二维数组可以理解为a[0],a[1],a[2]相当于是一个一维数组的数组名,每一个都有一个有4个元素的一维数组,a[0][0]表示数组a[0]中的第一个元素;如int a[3][4];
2024-07-24 20:00:47
278
原创 嵌入式学习第八天:字符数组
字符数组就是用来存放字符数据的数组,相当于是存放字符串的容器,,这个容量要大于要存放的字符串,字符数组中的一个元素存放一个字符,如 char s[10] = "hello";用来比较两个字符串的大小,用%d打印,如果结果大于0,说明s1 > s2,如果结果等于0,说明s1 = s2,如果结果小于0,说明s1 < s2;是将s1复制给s2,不能使用赋值运算符“=”,用%d打印,目标容量要足够;字符数组的输出是puts(s);
2024-07-23 18:43:48
163
原创 嵌入式学习第七天:一维数组的定义与引用
计算数组中的个数可用:int len = sizeof(a) / sizeof(a[0]);一维数组的定义方式为:类型说明符 数组名 [常量表达式],如:int a[10];数组如果不进行初始化,则其内的元素都是随机数,如果至少初始化一个元素,其他的都为0。数组的数组名代表数组首元素地址,数组不能整体赋值,数组本质上是一种数据结构;访问数组外的内存叫越界访问,可能造成其他数据被改写;今天学习了一维数组以及三种排序方式;当数组元素为变量时,不允许初始化;数组具有单一性,有序性,连续性;
2024-07-22 20:24:02
164
原创 嵌入式学习第六天:C语言的循环控制
dowhile语句会先执行一次,再判断是否符合条件,一般可用来检查程序或硬件的可用性;continue语句又称循环短路语句,它会终止本次循环,直接进入下一次循环;语句标号就是标识符,一般给一个想要跳转到的语句命名,命名规则与标识符一样;如果循环跳出,则条件依旧为真,后续可以用来当判断条件,再进行后续操作;goto语句为无条件跳转语句,一般形式为:goto语句标号;break语句用来跳出一个循环,且只能跳出自身所在的循环;while语句的末尾不加分号,有可能一次也不执行;有使循环趋于结束的语句;
2024-07-20 18:28:36
172
原创 嵌入式学习第五天:C语言选择结构程序设计
注意:if,else if,else下不能加空行,else单独出现时编译报错,else根据实际情况可以省略,一般在写关系表达式时,最好是关系运算符的左边写常量,右边写变量。"&&"和"| |"是双目运算符,要求有两个运算量,如:(a > b) && (a > c), (a > b) || (a > c);"| |"的左边的表达式如果为真,则直接结束,表达式截断,不运行右边的表达式;"&&"的左边的表达式如果为假,则直接结束,表达式截断,不运行右边的表达式;(3)if(表达式1)语句1。
2024-07-19 20:39:47
178
原创 嵌入式学习第四天:C语言数据的输入输出
其中int表示该函数的返回值,是scanf成功输入的个数,scanf是该函数的函数名,const char *format是该函数的格式控制,逗号之后的是该函数的输入列表,也是该函数的形参;其中int表示该函数的返回值,printf是该函数的函数名,const char *format是该函数的格式控制,逗号之后的是该函数的输出列表,也是该函数的形参;%d 可写为%+5d,表示在左边空5位,%-5d 表示在右边空5位,该位数会将数据本身的长度算在内,如:_ _1 0 0,1 0 0_ _;
2024-07-18 19:16:26
465
原创 嵌入式学习第三天:C语言的运算符和表达式
int = short,由于int有4个字节,short只有2个字节,所以当short赋值给int时,赋给int的低位字节且int仍有2个字节的空位,此时int的两个空字节将根据short的符号来进行不同的变化,如果short为正,则空字节为00 00,如果short为负,则空字节为FF FF,这种方式称为符号位扩展;例如int型与double型数据进行运算,先将int型的数据转换成double型,然后在两个同类型(double型)数据间进行运算,结果为double型。注意,表达式应该用括号括起来。
2024-07-17 19:11:31
398
原创 嵌入式学习第二天:C语言的基本数据类型
无符号型中又分为无符号整型、无符号短整型和无符号长整型,分别以unsigned int,unsigned short,unsigned long和unsigned long long表示。int数的值范围是 -2,147,483,648 到 2,147,483,647。整型(int):用于存储整数,根据存储大小和符号可以细分为signed int、unsigned int、short int、long int等。long占8个字节,范围是 -2,147,483,648 到 2,147,483,647。
2024-07-16 18:08:01
273
原创 嵌入式学习第一天:Linux系统的基础指令
cd :切换到指定目录,cd .. :切换到当前所在的上一级目录,cd . :切换的当前目录。cp:文件拷贝,cp 源路径 目标路径,拷贝文件夹加-r。mv:文件移动/修改文件名,mv 源路径 目的路径。ls -l:查看当前路径下文件的详细信息。rm :删除普通文件/目录文件(加-r)ls -a:查看当前路径下所有文件。pwd:查看当前所在的绝对路径。ls:查看当前路径下的文件。touch:创建普通文件。mkdir:创建目录文件。
2024-07-15 18:44:24
161
1
空空如也
空空如也
TA创建的收藏夹 TA关注的收藏夹
TA关注的人