C/C++
文章平均质量分 50
C/C++语言知识点,主要针对Linux平台开发。
ka__ka__
软件工程师
展开
-
基于邻接矩阵的朴素Dijkstra单源最短路C++极简实现
基于邻接矩阵的Dijkstra实现使用C++,可以方便的写出一个通用的朴素dijkstra极简实现,核心逻辑不超过20行。#include <vector>#include <algorithm>#include <cstdio>using namespace std;vector<int> dijkstra(vector<vector<int>> &graph, int start){ const i原创 2020-08-16 01:08:36 · 552 阅读 · 0 评论 -
sys/queue使用说明与TAILQ使用举例
每个写C的同学想必都会羡慕C++方便的STL,但是无奈标准C是没有这些库的,毕竟C语言不支持泛型,不过好在大多数Linux下都可用sys/queue,源自BSD:https://unix.superglobalmegacorp.com/xnu/newsrc/bsd/sys/queue.h.html这个头文件用宏实现了很多基础数据结构,如:单链表、双链表、队列等。用起来也比较方便,只需要引入一个头文件即可。如著名的的libevent项目就大量使用了这个头文件里的TAILQ. 不过C的宏实现的库并没有C++原创 2020-07-31 00:36:53 · 988 阅读 · 0 评论 -
DFS递归回溯求排列组合的C实现
深度优先搜索DFS本身很好理解,即按照一定顺序递归遍历一个图,但是如何把所有的满足要求的遍历方式给求出来,这里就需要结合回溯backtrace了。在LeetCode中,回溯标签下的很多题目都是求排列组合的,这也算是一类经典类型的题目了。C/C++写的多人都会有个特点:写代码的时候脑子里都是内存布局,栈帧结构,以及代码执行时各个对象的生命周期等等,写的多了某种程度可以对这些东西做到可视化了都。理解...原创 2019-10-25 08:28:12 · 744 阅读 · 0 评论 -
HTTP1.1 基础: 用C语言实现200行内的极简http server
对HTTP协议有了基础的了解后,可以看这篇: HTTP 基础: 请求和响应的消息交互细节, 就可以用纯C来实现一个极简的http server了。这个的http server主要完成此功能: 通过URL访问server根目录的html类型文件。代码如下:#include &amp;amp;amp;amp;amp;amp;amp;lt;sys/types.h&amp;amp;amp;amp;amp;amp;amp;gt;#include &amp;amp;原创 2018-09-02 23:38:55 · 6393 阅读 · 4 评论 -
Linux C/C++适配malloc申请按页对齐的内存,方便使用mprotect
之前的一篇文章: Linux C/C++内存越界定位: 利用mprotect使程序在crash在第一现场。里面没有用malloc来申请按页对齐的内存,其实也可以适配malloc,多申请2页,取中间按照页对齐的内存使用。使用malloc,可以使得进程的地址空间布局不会相对原来发生太大变化。代码如下:#include &amp;amp;lt;stdio.h&amp;amp;gt;#include &amp;amp;lt;stdlib.h&am原创 2018-05-23 00:33:07 · 5432 阅读 · 0 评论 -
Linux C/C++内存越界定位: 利用mprotect使程序在crash在第一现场
对于大型Linux C/C++程序,内存越界和野指针类问题往往比较难定位。有的由于内存被非法改写造成了业务功能问题,有的则直接导致了程序crash,而且还经常不是第一现场。针对这种问题,可以采取的解决方法有:利用valgrind工具来排查,会影响程序性能;使用Address Sanitizer工具排查;如果是固定的内存被破坏,可以利用gdb watch来抓取第一现场的调用栈;可以利用G...原创 2018-04-20 22:43:49 · 12030 阅读 · 1 评论 -
Linux C/C++ 使用GDB环境变量和控制语句进行自动化调试
调试Linux C/C++后台程序,常常会gdb attach到对应进程进行调试,然后各种断点,观察点,观察内存等。但是,有时候我们需要能在gdb中做一些基本逻辑操作,主动call一个函数,甚至循环call几次等,这时就可以用gdb的环境变量和一些基本的控制语句。一个例子如下:#include <stdlib.h>#include <stdio.h>int a...原创 2018-03-19 22:41:29 · 1404 阅读 · 0 评论 -
Linux C/C++ 把Git commit SHA1值编译到程序中来方便查看版本
今天使用 Visual Studio Code,点击关于Visual Studio Code会呈现版本的Git commit SHA1值,这个对于查看一个发布的程序对应的源码十分的方便和精确,可以在非常方便的直接checkout出来对应程序对应的源码版本。对于Linux C/C++程序,如果想做到这样的效果,把SHA1值放到构建的产品,也非常简单。有两种办法:如果程序对外发布包含有很多的...原创 2018-03-11 17:14:04 · 3383 阅读 · 0 评论 -
Linux C项目makefile模板
最近比较的系统学习了下makefile,网上查了很多资料,但是很多都是大同小异,没有一个称心的小型 c项目的比较通用的makefile模板。这里个人总结了一个 c项目的 makefile 模板,先放上模板,后面再具体解释几个关键点。c makefile 参考模板下面的模板比较短,源文件在 src 目录,编译输出在 output 目录。这个makefile文件保存在src、output同...原创 2015-10-07 13:48:35 · 1835 阅读 · 0 评论 -
Linux C/C++ 用variadic macro可变参数宏封装带文件行号和时间的日志接口
在实际的项目中,一般日志接口可以打印出对应的时间、行号、函数名、模块名、级别等信息,可以用variadic macro可变参数进行简单的封装。一个例子:#include <stdio.h>#include <sys/time.h>#include <time.h>/* 日志开关, 关闭打印只把宏需改为0即可 */#define LOG_SWIT...原创 2018-02-12 22:49:10 · 727 阅读 · 0 评论 -
Linux C函数入参和返回值是结构体时的汇编分析
在C语言程序中,一般不会直接传一个结构体给一个函数,也不会让函数的返回值直接返回一个结构体,这样会拷贝过多影响效率。但是这样也是合法的,有时候也会使用,并且有时候效率也并不会变得太差。C函数传参:参数少或者传入的结构体小只借助寄存器即可,否则借助栈。C函数返回值:如果返回一个比较小的结构体,借助寄存器即可,否则依旧借助栈。按调用约定,当返回值是较大的结构体时,会在caller栈里产生一个临...原创 2017-08-13 13:45:12 · 3282 阅读 · 0 评论 -
Linux C函数调用栈帧结构
Linux C程序的反汇编,每个函数第一个指令都是push %rbp,即当caller者调callee时,指令callq <fun addr> callee返回后的下一个指令地址压入栈帧,然后push %rbp 保存 rbp寄存器,紧接着mov %rsp,%rbp 更新rbp 寄存器这样层层调用构成函数调用栈,每个栈帧开始就是存的上一个栈帧的rbp, 结尾就是调用callee后的下一个...原创 2017-04-25 23:06:23 · 2550 阅读 · 0 评论 -
C 语言指针与地址
近段复习C语言,感觉C语言相对于C++、Java等高级语言最大的特点就是:简洁紧凑、灵活方便。C语言特征不多,带有面向硬件思维,小巧而精炼。C语言非常贴近底层,要真正理解指针的本质,还需要些微机原理、计算机组成与原理、操作系统和编译原理等背景知识。物理地址是外部连接使用的、唯一的,它是“与地址总线相对应”;逻辑地址是内部和编程使用的、并不唯一。逻辑地址就是逻辑段管理内存而形成的。C语言中使用的地址...原创 2014-04-09 22:49:16 · 3731 阅读 · 0 评论 -
C/C++ 指针基本用法总结
声明:TYPE *p (TYPE * p 和 TPYE* p 和 TYPE*p 都可以),* 号两边有无空格均可以,推荐第一种写法;赋值:既可以在声明的时候赋值,也可以在后期赋值,不要让没有赋值的野指针出现。指针不可以直接赋值常量,如 p=100 , 这时 *p 就会出错。空类型指针和指向空的指针空类型指针可以接受任何类型指针,使用时将其强制转化为对应的数据类型即可。如:void *p;强转int...原创 2014-04-17 18:53:03 · 2118 阅读 · 1 评论 -
Linux C/C++调试:快速找出GCC O2优化编译的汇编与C源码的对应关系
相信很多C/C++程序员都有过线上gdb调试程序的经历,没有或者剥离了调试信息,线上运行的也是GCC -O2编译优化后的release版本,这时就要求快速看懂汇编指令和对应C代码的对应关系。进而,找到自己需要gdb print出来内存信息,或者针对性的下断点或观察点等,这些调试一切的前提都是要求快速看懂反汇编指令和对应源代码的对应关系。一个函数的反汇编短则数百,长则近万行甚至更多,如何一下子找到其...原创 2018-06-21 23:35:02 · 2123 阅读 · 0 评论 -
Linux IPC: UNIX Domain Socket 基本用法
当前Linux IPC最常用的方法就是UNIX Domain Socket和TCP Socket了。使用TCP Socket可以看:Linux Socket TCP通信基本用法。这两种IPC一个很大的区别就是:TCP使用IP + Port来标记进程;UNIX Domain Socket使用文件来标记进程;由于上面差异,在socket API标记地址的结构体也有所不同,Domain S...原创 2018-06-27 00:21:01 · 2504 阅读 · 0 评论 -
Linux C pthread多线程,join和detach
Linux C多线程使用,有一个注意点需要注意,新启动的子线程一定要在主线程join,或者本身被设置为detach,否则会引起资源泄露,如每个线程都有自己的运行stack,内核中的task_stuct数据结够等。正如,父子进程中的子进程退出没有被父进程join就会变成僵尸进程一样。一个例子,最终会导致资源耗尽 pthread_create 返回 11. 这个错误是 EAGAIN 的意思就是操作系...原创 2019-09-18 23:39:47 · 1278 阅读 · 0 评论 -
小练习 - C语言实现一个极简通用vector
C语言库比较少,很大的原因就是没有泛型,也没有class。但是C语言作为一个较为底层的系统级语言,操纵内存还是很灵活的。模仿C++实现一个泛型vector,可以使用void*,使用的时候强转对应类型,或者使用宏来封装一些操作。这里实现一个极简的vector,拥有增删查功能,存储的元素放在连续内存中,扩容时1.5倍增长。没有加锁,非线程安全。这个可以在控制结构体里添加。原理很简单,内存不够了,ma...原创 2019-08-21 22:11:36 · 1023 阅读 · 0 评论 -
Linux C/C++ 共享库so的搜索路径和顺序
共享库的搜索共享库的搜索出了系统默认路径外,还可以通过下面方法指定。首先通过环境变量指定:环境变量LIBRARY_PATH 编译期查找动态链接库路径, 导出给Makefile或者在Makefile开头指定;LD_LIBRARY_PATH 运行期查找动态链接库的路径,会在系统默认路径之前查找;还可以通过GCC参数指定:编写Makefile一个好的习惯是先-L指定路...原创 2019-06-22 19:26:38 · 4827 阅读 · 0 评论 -
C语言实现极简递归下降JSON parser解析器
JSON字符串解析利用递归下降非常容易实现。本文实现了一个不支持数组,数字解析只支持正数long类型,只支持ASCII字符集的极简JSON解析器,未实现的功能后面也比较容易扩展。原创 2019-04-28 23:47:39 · 1231 阅读 · 0 评论 -
Linux C信号编程:接管段错误信号SIGSEGV打印调用栈
有时候,线上环境没有开启coredump,这时把进程复位时的调用栈打印出来就非常有利于问题定位了。怎么做呢?也很简单,我们在程序中接管SGISEGV信号,在信号处理函数打印调用栈信息即可。信号处理可以看:UNIX环境高级编程内核机制可以看:Linux内核源代码情景分析这两本书虽老了点,但写的确实很不错,相关系统API使用可以配合可能Linux man手册。接管段错误信号后,在信号处理函数中...原创 2019-02-20 22:34:33 · 3760 阅读 · 0 评论 -
CMake的常见用法总结
初识CMakeC/C++项目规模大了,Makefile就不太好组织编译工程,尤其是Makefile涉及了要自己推导C/C++的头文件依赖,输出信息也一般都比较混乱,完全依赖对应作者。相比较之下,使用CMake来做编译工程,就会清晰很多,省掉了自动推导头文件依赖,而且输出信息美观,编译.o, .i, .s都一应俱全,非常方便。而且跨平台,能支持输出很多IDE的配置文件,CLion就直接是支持CMa...原创 2019-01-21 22:41:48 · 984 阅读 · 0 评论 -
C++使用Protobuf举例
安装cpp的protobuf按官方指导来:https://github.com/protocolbuffers/protobuf/tree/master/src下载release包,解压后编译安装即可。一个小例子一个proto文件定义如下:syntax = &amp;amp;amp;quot;proto3&amp;amp;amp;quot;;package test;enum Sex { BOY = 0; GIRL = 1;}...原创 2018-12-25 22:35:20 · 1775 阅读 · 0 评论 -
C语言侵入式链表
C语言虽然没有泛型,但是拥有对内存的终极解释权限,这个也带给了C语言强大的表达能力。对于复杂数据结构,侵入式链表绝对是一个好的经典实现,Linux Kernel 在 2.1 之后就使用 list_head 的内嵌来实现双向循环链表。而且,glibc也提供了这种实现的标准库 glibc/include/list.h。对于C语言项目,有时候会需要精细规划内存使用。这时设计数据结构时,通常都是存储和控...原创 2018-10-26 23:38:28 · 2077 阅读 · 1 评论 -
C语言用结构体给另一个同类型结构体赋值: 用等号即可
看到很多C代码的结构体赋值都是用memcpy函数来拷贝,其实根本不需要,用等号直接赋值即可。结构体描述的是的一块连续内存的内存布局,用等号来用一个结构体给另一个结构体赋值,编译不会有任何问题,其效果显然也和人直觉认为的一样,拷贝对应内存。通过反汇编看编译器的行为,效果跟memcpy一样,还省掉了函数调用开销,代码更加的简洁明了。// @file: main.cstruct node { ...原创 2018-10-05 18:07:50 · 39964 阅读 · 3 评论 -
Linux C/C++定时器的实现原理和使用方法
定时器的实现依赖的是CPU时钟中断,时钟中断的精度就决定定时器精度的极限。一个时钟中断源如何实现多个定时器呢?对于内核,简单来说就是用特定的数据结构管理众多的定时器,在时钟中断处理中判断哪些定时器超时,然后执行超时处理动作。用户空间程序不直接感知CPU时钟中断,感知内核的信号、IO事件、调度,间接依赖时钟中断。软件实现动态定时器常用数据结构有:时间轮、最小堆和红黑树。原创 2018-07-16 22:55:49 · 36538 阅读 · 4 评论 -
C/C++ 数组名与指针的区别与用法
数组名不是指针常量,它更像是一个常数,立即数和右值,这个值就是数组的首地址。编译器在做词法分析和语法分析的时候,遇到一个全局数组的定义,会把数组的有关信息汇集在一个叫做“内情向量”或“信息向量”的表格中,其中的信息包括数组的类型,维数,各维的上、下边界,以及数组的首地址。然后将这个“内情向量”相关信息存储在符号表中。数组定义后位置就是固定的,因此其首地址可以在编译阶段得到。对于一个数组a[], a...原创 2014-04-21 12:17:34 · 2752 阅读 · 0 评论 -
Linux 多线程 pthread库初探
Linux 多线程 pthread库用法(一)Linux 多线程编程基介绍Linux 线程有时候也叫 Light Weight Process LWP 轻量级线程,是进程的一个执行流,有自己的执行栈,是操作系统调度的最小单位。 多线程优势在于切换开销小,同进程内通信方便,涉及IO等阻塞性操作时可以单独开一个线程不阻塞主流程,不足之处健壮性不如多进程,一个线程crash,那么所在进程就都...原创 2016-04-19 23:54:10 · 3145 阅读 · 0 评论 -
Linux C/C++编译和使用so共享库
本文给出一个Linux C 编译和使用共享库so的一个小例子。如下: 文件 my_math.c // @file: my_math.c#include "my_math.h"int add(int a, int b){ return a + b;}// @file: my_math.hint add(int a, int b);文件 test.c/...原创 2018-01-22 00:02:39 · 3394 阅读 · 0 评论 -
Linux C使用宏字符拼接技巧打印日志并统计错误
c语言的宏因为本质是字符替换,不是类型安全的行为,但也因为其字符替换的本质使其具有了一些编译期的动态特性,对于一些特殊的场合就非常的有用,比如下面的打日志。 宏的使用这篇文章写的很不错,C语言宏的特殊用法和几个坑.#include <stdio.h>typedef enum{ ERROR_ONE, // 0 ERROR_TWO, ERROR...原创 2016-03-20 21:04:00 · 1612 阅读 · 0 评论 -
C/C++用状态转移表联合函数指针数组实现状态机FSM
状态机在工程中使用非常的频繁,有如下常见的三种实现方法:1. `switch-case` 实现,适合简单的状态机;2. 二维状态表`state-event`实现,逻辑清晰,但是矩阵通常比较稀疏,而且维护麻烦;3. 用状态转移表`stateTransfer Table`实现,数组大小等于状体转移边个数,易扩展;原创 2015-07-26 16:49:39 · 3833 阅读 · 0 评论 -
C/C++ 字符串处理总结
string 和 char* 的互相转换,从 char* 中格式化提取指定类型的变量值,还有sscanf 和 sprintf 的用法。原创 2015-03-23 14:29:06 · 1936 阅读 · 0 评论 -
STL 笔记(五) 算法 algorithm
在 STL 中,算法是一系列的函数模版。STL 提供了大概 70 个算法,由头文件 <algorithm>、<numeric>、<functional>组成。头文件 <algorithm> 是最大的一个,里面常用到的功能有:查找、排序、修改、移除、交换、合并等;头文件 <numeric> 较小,主要包括数序运算的函数模版;头文件 <functional> 中则定义了一些类模版,来声明函数对象;算法的分类:算法按其实现的功能可分为 8 类:查找、排序、数值计算、比较、集合、容器管理、统计和对操原创 2014-10-09 23:18:26 · 2990 阅读 · 0 评论 -
STL 笔记(四) 迭代器 iterator
stl 中迭代器可以理解为面向对象版本的广义指针,提供了对容器中的对象的访问方法,可以遍历容器所有元素,也可以访问任意元素。stl 迭代器有以下五种:Input iterators 只读,输入迭代器,支持如:istreamOutput iterators 只写,输出迭代器,支持如:ostream、inserterForward iterators 读写,前向迭代器,只能前向移动Bidirectional iterators 读写,双向迭代器,能够双向移动,支持如: list、set、mapR原创 2014-10-08 23:47:51 · 2293 阅读 · 0 评论 -
STL 笔记(三) 容器适配器 stack、queue、priority_queue
栈 stack 是一种先进后出的(First In Last Out, FILO)的数据结构。在 STL中,其底层容器默认使用的是 deque, 也可以自己指定用 vector 或 list容器,然后将其接口适配为栈的接口。队列 queue 是一种先进先出(First In First Out, FIFO)的数据结构。在 STL 中,其底层容器默认使用的也是 deque,也可以自己指定用 vector 或 list容器,然后将其接口适配为队列的接口。优先级队列 priority_queue 是一种允许用户以原创 2014-09-29 17:42:55 · 2732 阅读 · 0 评论 -
STL 笔记(二) 关联容器 map、set、multimap 和 multimap
STL 关联容器的底层数据结构是红黑树,故其增删查的时间复杂度都是 O(logn)map 默认按照 key 的升序进行插入,非基本数据类型要求重载 < 运算符map 重载了 [] 运算符,使的插入和查找非常方便map 用 [] 运算符访问元素时,如果不存在这个key,key会自动插入,value为初始化值map 的 key 对象使用之后就不要再修改,如果必须修改,需要删除后重新插入multimap 的 key-value 是一对多,没有重载 [] 运算符原创 2014-09-28 21:03:52 · 3238 阅读 · 0 评论 -
STL 笔记(一) 顺序容器 vector、list、deque
vector 可变数组,内存空间是连续的,容量不会进行缩减。支持高效随机存取,即支持[]和at()操作。尾部插入删除效率高,其他位置插删效率较低;list 双向链表,内存空间可不连续,不支持随机存取。插入和删除的效率很高;deque 双端队列,内存空间是多个连续的内存块,在一个映射结构中保存对这些块以及顺序的跟踪,可利用的内存更大,且内存大小是可以自动缩减的。支持随机存取,但是随机存取性能没有vector 好。首尾插入效率高,其他位置插删效率低;原创 2014-09-26 14:34:44 · 3770 阅读 · 2 评论 -
C++ 编译单元和命名空间
编译单元,指的是代码的物理组织形式。根据C++标准,每一个cpp 文件就是一个编译单元。编译器不会去编译 `.h` 或者 `.hpp` 文件;编译器只会编译 `.c` 或 `.cpp` 文件;原创 2014-05-04 22:50:20 · 4753 阅读 · 0 评论 -
C++ 模版用法和注意事项
面向对象的多态与组合并不能完全满足实际编程中对于代码复用的全部要求,泛型编程应运而生,而且享有和面向对象等同的地位。面向对象将操作绑定到数据,泛型则是将操作应用于不同数据结构和类型。C++中泛型编程的体现就是模版。模板的技术核心体现在编译期的动态机制,模板实例化则是在编译的过程中,编译器通过“类型推导”进行实例化。而运行的时候,系统不知道模板的概念。与之相比,面向对象就是在运行时调用构造函数进行类的对象的实例化。原创 2014-05-13 21:19:14 · 2353 阅读 · 0 评论 -
C++ 左值引用与右值引用
引用本质上是一个隐式指针,为对象的一个别名,通过操作符&来实现。C++11又提出了左值引用与右值引用的概念,一般如没有特殊说明,提到引用都是指左值引用。变量,有两个属性:左值和右值。左值是变量的地址,右值是变量存储的内容。变量即是一块存储区的名称,编译后变为对应地址。一个C++引用声明后必须被初始化,否则编译不过,初始化之后就相当与一个变量(地址为初始化时所引用变量的地址)。由于拥原创 2014-04-17 08:53:50 · 13231 阅读 · 5 评论