c
文章平均质量分 51
lylhw13_
喜欢探究Linux源码,喜欢从本质分析问题
展开
-
c/c++ 输出执行过程中的函数名
为了分析程序,有时候需要打印出执行过程中的函数。一种方案是修改程序源码,在每个函数头部添加打印函数,这样在函数较少的情况下,可以使用。这种方法,对于较大的程序就不实用。-finstrument-functions第二种方案是使用使用gcc 指令 -finstrument-functions, 添加这种指令,可以在进入程序和退出程序前,添加钩子函数。这两个函数的原型如下:void __cyg_profile_func_enter (void *this_fn,原创 2022-06-05 22:27:08 · 1388 阅读 · 0 评论 -
makefile 示例
下面的 Makefile 包含了一些常用写法,可以根据需要选择增减。CC=gccCFLAGS = -WallLIBS = -lssl -lcryptoifdef DEBUGCFLAGS += -g -DDEBUGendifPROG = prognameSRC = ${wildcard *.h *.c}all: $(PROG)$(PROG): $(SRC) $(CC) $(CFLAGS) $^ -o $@ $(LIBS)clean: rm $(PROG)相应的 make原创 2021-11-21 21:26:45 · 450 阅读 · 0 评论 -
一幅图帮助理解C函数的调用栈
调用栈及栈帧对于理解程序的行为非常重要。本文以x86 程序的默认调用方式为例,说明函数调用栈的详细布局。原始程序以如下一段程序为例:int fun(int i, int j);int callee(int i, int j) { int a = i, b = j; return fun(a, b);}int caller(void) { return callee(1, 2);}汇编代码为了得到程序执行过程中的具体细节,需要查看其汇编语言。使用Compi原创 2021-11-14 23:13:04 · 899 阅读 · 0 评论 -
人人都可以写协程:一个基于汇编的协程实现
本文以有栈非对称协程的实现为例。项目源码在 https://github.com/lylhw13/Coroutine-in-C。准备基本概念编写协程前需要明白几个概念:有栈协程和无栈协程有栈协程:通过保存运行时的堆栈及运行时的上下文来保存运行状态,需要改变调用栈。无栈协程:通过闭包或者状态机的方法记录程序的运行状态,不改变调用栈。对称协程和非对称协程对称协程:各协程之间可以进行执行权的切换。非对称协程:有一个中心化的调度器,所有协程都只和调度器进行执行权的切换。已有的实现案例原创 2021-11-13 23:16:37 · 1047 阅读 · 0 评论 -
一段程序说明C语言不同调用约定的区别
在 x86-64 平台编译 32位程序gcc version$ gcc -vgcc version 9.3.0 (Ubuntu 9.3.0-17ubuntu1~20.04)gcc -m32 -S test.ccdecl32位默认的调用方式是 cdeclint callee(int,int,int);int caller(void) { return callee(1,2,3) + 5;}caller:.LFB0: endbr32 pushl %ebp原创 2021-11-04 00:36:12 · 312 阅读 · 0 评论 -
atoi 函数族和 strtol 函数族的区别与联系
atoi 函数族和 strtol 函数族都用来将字符串转换成数字。但是具体的区别和联系是什么呢?通过查看 glibc 的函数源码,可以清楚的看到两者的关系。两个函数族函数返回类型的对应关系。返回类型atoi函数族strol函数族intatoilongatolstrtollong longatollstrtollfloatstrtofdoubleatofstrtod函数原型 long atol(const char *nptr);原创 2021-09-30 18:26:27 · 660 阅读 · 0 评论 -
puts 和 printf 函数的区别
函数原型 int puts(const char *s); int printf(const char * restrict format, ...);区别相同点:都是将字符串输出到标准输出不同点:puts 直接输出字符串,并追加一个换行,不对字符串进行解析。优点是效率高,缺点是需要记住它会在结尾追加换行。printf 对字符串进行解析,然后将字符串格式化输出,puts(str);/* 等价于 */printf("%s\n", str);从防御式编程来说,不要采取下面的格式:原创 2021-09-05 23:16:25 · 510 阅读 · 0 评论 -
C 变长参数的实现原理和使用方法
C语言调用规范以 x86 平台为例,C语言在 x86 平台的函数调用方式为 cdecl (C declaration) ,调用者自右向左将参数压入栈中,通过栈来传递参数。示意图如下:C 语言可变参数的使用形式如下,可变参数只能作为最后一个参数出现:int printf(char *fmt, ...)va_list 等函数的一种宏实现熟悉了参数传递的方式,通过最后一个参数确定可变参数的起始位置,并依次返回每个参数,就可以写出 va_list 等函数的一种实现。这种实现只在 x86 平台,并遵循原创 2021-09-05 17:37:33 · 366 阅读 · 0 评论 -
GNU C 的扩展特性汇总(持续补充)
三元表达式省略中间操作符下面两种表达方式等价x ? : yx ? x : y零长度数组参考:Extensions to the C Language Family原创 2021-08-28 17:31:22 · 597 阅读 · 0 评论 -
一些 shell 命令的C语言简要实现
uname 打印系统信息$ uname Linuxc 语言实现原创 2021-08-08 23:10:05 · 427 阅读 · 0 评论 -
C 语言执行 shell 命令的三种方式总结
Linux 为执行 shell 命令,提供了三个函数接口,分别是 exec, system, popen。他们的区别简单总结如下:exec # 在当前进程中执行命令,其后所有的代码将被清空,不能执行system = fork + exec # 在子进程中执行 shell 指令popen = fork + exec + pipe # 根据执行的模式重定向子进程的标准输入或输出execexec 有一族相关函数,其作用是替换当前执行的程序为新程序(replaces the原创 2021-08-05 23:03:06 · 7672 阅读 · 0 评论 -
阅读 git 最初版源码总结
git clone https://github.com/git/git.gitgit log --reversegit checkout e83c5163316sudo apt install zlib1g-devsudo apt install libssl-devMakefile 中的 LIBS 变量添加 -lz -cyrpto,如下LIBS= -lssl -lz -lcryptoinit-db: Initializes a new Git repository. Equiva原创 2021-08-02 22:16:08 · 2390 阅读 · 0 评论 -
zlib 库 Linux 平台使用示例及总结
库的安装及使用安装库,注意其中名称中是 1g 不是 lg。sudo apt install zlib1g-dev代码中需要引入头文件#include <zlib.h>编译时需要指定库 -lzgcc zlib_test.c -lz程序示例本程序基于官网源程序zpipe.c的修改而来。#include <zlib.h>#include <fcntl.h>#include <stdio.h>#include <string.h&g原创 2021-08-01 11:31:56 · 2332 阅读 · 0 评论 -
sha1 函数 C 语言使用示例及总结
库的安装及使用安装相应库sudo apt install libssl-dev代码中需要引入头文件#include <openssl/sha.h>编译时需要指定库 -lcryptogcc sha1_test.c -lcrypto使用方法如果数据可以一次性获得size_t len = strlen(data);unsigned char sha1[SHA_DIGEST_LENGTH];SHA1(data, len, sha1);如果数据分批获得SHA_CTX c原创 2021-07-30 23:06:13 · 2011 阅读 · 0 评论 -
Linux typecheck 宏解析
/* * 检查编译时类型 * 表达式为结果恒定为1,方便比较 */#define typecheck(type,x) \({ type __dummy; \ typeof(x) __dummy2; \ (void)(&__dummy == &__dummy2); \ 1; \})/* * 编译时检查函数类型或者是该类型的指针 */#define typecheck_fn(type,function) \({ typeof(type) __tmp = functi原创 2021-07-13 00:20:09 · 479 阅读 · 0 评论 -
解析Linux中的 likely 和 unlikely
Linux 中多处出现 likely 和 unlikely 的使用,它们的定义如下:# define likely(x) __builtin_expect(!!(x), 1)# define unlikely(x) __builtin_expect(!!(x), 0)函数原型__builtin_expect 是 GCC 提供的内置函数 (built-in functions),函数原型是long __builtin_expect (long exp, long c)函数的返回值是 exp,它原创 2021-06-21 19:40:42 · 343 阅读 · 0 评论 -
c 语言中对字符串常量修改的错误原因解析
#include <stdio.h>int main(){ char *a = "hello"; a[0] = 'w'; printf("%s\n", a); return 0;} .file "t_str.c" .section .rodata.LC0: .string "hello" .text .globl main .type main, @functionmain:.LFB0:原创 2021-06-12 09:51:48 · 1010 阅读 · 1 评论 -
C/C++ 大小端字节序的判断和转换
大小端原理示意图 int x = 0x01; 地址增长方向 -----> 小端序,低位位于低字节处 +----+----+----+----+ |0x01|0x00|0x00|0x00| +----+----+----+----+ A | &x 大端序,高位处于低字节处 +----+----+----+----+ |0x00|0x00|0x00|0x01| +----+----原创 2021-03-07 18:32:28 · 3068 阅读 · 0 评论 -
C/C++ 中 assert 和 static_assert 的宏实现
assert 用于运行时的断言实现方法如下#define assert(x) \ do { if (!(x)) printf("assertion failed: %s", #x); } while (0)在宏语句中,参数可以通过前置 # ,将其转化为字符串常量。static_assert 用于编译期断言实现方法如下:#define static_assert(a, b) \ do { switch (0) case 0: case (a): ; } while (0)原理:sw原创 2021-03-07 16:51:45 · 559 阅读 · 1 评论 -
C/C++ 中 extern 的用法
extern 主要有两方面作用:分离编译extern 表示一个外部变量:外部变量必须定义在所有函数之外。在一个源程序的所有文件中,一个外部变量只能在某文件中定义一次,而其他文件可以通过extern声明来访问它(定义外部变量的源文件中也可以包含对该外部变量的extern声明)。外部变量的定义中必须指定数组的长度,但extern声明则不一定要指定数组的长度。外部变量的初始化只能出现在其定义中。// file1extern int sp;// file2int sp = 0;对于co原创 2021-02-20 17:41:00 · 843 阅读 · 0 评论 -
C/C++ 中 static 关键字
static 关键字C语言:修饰全局变量或函数:起到隐藏的作用,该变量或函数只在该文件可见。修饰函数的局部变量:起到持久化作用,该变量一直占据存储空间,且只能在该函数使用。C++引入class,在保持和C语言兼容的同时,又添加两种新用法:修饰class的数据成员,即所谓“静态成员”。静态数据成员每个class只有一份。修饰class的成员函数,即所谓“静态成员函数”。这种函数只能访问静态成员和静态成员函数。...原创 2021-02-20 16:29:50 · 120 阅读 · 0 评论 -
c语言 max 和 min 的宏写法
一般写法:#define max(a,b) ((a) >= (b) ? (a) : (b))缺点:这种写法会造成变量_a 或 _b 被重复求值两次,比如max(i++, j++)会被解释为((i++) >= (j++) ? (i++) : (j++))更好的写法:#define max(a, b) \({ \ typeof(a) _a = (a); \ typeof(b) _b = (b); \ _a >= _b ?..原创 2021-01-12 17:34:20 · 4317 阅读 · 0 评论