C语言可变参数实战
printf 是怎么做到接受任意多个参数的?答案是 stdarg.h 里的三件套:va_list、va_start、va_arg。写了一个按格式符消费可变参数的实验,还能顺便理解”参数是怎么传进去的”。 核心机制123456789101112131415161718192021222324252627282930char *handle(char* format, ...){ va_list args; va_start(args, format); // 从 format 之后开始读参数 while(*format) { if(*format == '%') { switch(*(format + 1)) { case 'c': case 'd': va_arg(args, in...
变参函数及其应用
📌 更新(2026-09-01):整理归档,全部代码用 GCC 15.1.0 重新编译复测,输出与当年一致——另外新编译器对老代码的几处写法有话要说,见正文。 printf 是怎么做到想吃多少参数就吃多少参数的?这个问题的标准答案在 stdarg.h 里:va_list、va_start、va_arg 三件套。机制本身之前在《C 语言可变参数实战》里已经拆解过一次,那篇是机制篇;这一篇是应用篇——同一个文件夹里的三个程序,横跨 2022 年 9 月到 2024 年 4 月,记录了从”会数参数”到”手搓 printf”的完整进化链。 一、先数数,再吃参数:mysum 和 my_max变参函数的第一课:光有 ... 没法干活,因为函数体里根本不知道这次传了几个参数。于是有了最朴素的协议——count-first,第一个参数先报数: 123456789101112131415int mysum(int num, ...){ va_list valist; int i; int sum = 0; //初始化参数列表 va_start(vali...
抽象代码:先有鸡还是先有蛋
人类吵了几千年的问题,我用 qsort 裁决了。完整代码如下: 先有鸡还是先有蛋.c1234567891011121314151617181920212223242526272829303132333435363738394041#include <stdio.h>#include <stdlib.h>#include <string.h>int number[] = {3, 2, 1};char letter[] = {'l', 'e', 'j'};char* object[] = {"🐔", "🥚"};int _intcmp_(const void* a,const void* b){ return *(int*)a > *(int*)b;}int _charcmp_(const void* a,const void* b){ re...
C语言静态库实战:字符串查找运算符函数
把四则运算做成一个静态库,调用方式很优雅——用字符串找运算符函数: 项目共三个文件:static.h(库的接口声明)、static.c(实现)、main.c(调用示例)。 123456789101112131415#include "static.h"int main(){ double result; result = getoptFunction("+")(1.9, 2); printf("%lf\n", result); result = getoptFunction("-")(1.9, 2); printf("%lf\n", result); result = getoptFunction("**")(1.9, 2); // 自定义的幂运算 printf("%lf\n", result); return 0;} getoptFunction 接收运算符字符串,返...
数电帮你算开发记:真值表、卡诺图和 C 的析构函数
数字电路课有个绕不开的体力活:画真值表。一个 4 变量的逻辑函数就是 16 行,5 变量 32 行,手写一遍只为验证表达式对不对。2023 年 7 月我先写了个卡诺图绘制的小程序,2024 年 3 月 15 日晚上把它扩成了一个完整的工具——数电帮你算:给任意 2~5 元逻辑函数自动生成全部输入组合,配上函数指针直接打出整张真值表。当天夜里又迭代了一版 V1.1。两个程序的源码都整理在 GitHub 仓库 Digital-Logic-Helper 里,提交历史就是它们的开发顺序。 📌 更新(2026-08-31):为写这篇重新编译复现了全套流程,并在卡诺图程序里当场抓到并修复了一个 scanf 越界 bug——详见复盘一节。 先说清楚要做什么工具的核心流程三步: 生成测试矢量(test vector):把 n 个变量的 2ⁿ 种取值组合全部枚举出来; 拿逻辑函数逐行代入,打印出 x1 x2 ... | y 的完整真值表; 顺手把内存收拾干净。 加上之前的卡诺图绘制(输入 16 个输出值,反打最小项表达式 + 格雷码排序的卡诺图网格),数电作业的两大苦力活就都自动化了。 ...
C语言100级函数指针挑战
一个整活挑战:定义一个”指向函数的指针,该函数返回同类型的指针”,然后套 100 层。这个类型长这样(节选开头和结尾): 12typedef long long int (* (* (* ... (* (*a)())(void) ... );// 中间是 100 层重复的 (* ... () ) 这是什么鬼类型从变量名 a 出发,用”右左法则”读这个声明: a 先被 * 包住:a 是一个指针 右边跟 ():指针指向一个函数 函数返回值又在 (* ) 里:返回值还是指针 重复第 2、3 步 100 次 也就是:a 是一个函数指针,调用它返回下一个函数指针,再调用又返回下一个……一共 100 层。 实现:自返回的函数1234567891011121314a fun(){ static int i = 0; printf("(%d)", ++i); return (a)fun;}int main(){ fun(); printf("\n"); fun()()()()/* .....
抽象代码:会自杀的代码
函数能不能在干完活之后,把自己从程序里”抹掉”?对象能不能在出生的那一刻自我了断?能。今天这篇讲两个短小精悍的自杀程序——一个 C 的,一个 C++ 的。 📌 更新(2026-08-31):为写这篇重新编译复现了两个程序,并修正了对自杀对象输出的描述——它比想象中死得更干脆,详见运行一节。 自杀函数指针:信使送信的比喻先看代码: 自杀函数指针.c12345678910111213141516171819202122// 一个简单(但可能不那么让人愉快)的比方是,信使P把一封信从A送到B,// 而信的内容的第一句就是"干掉P,然后再看下面的内容"。#include <stdio.h>void init(void);void (*__init__)(void) = init;void init(void){ printf("init programm\n"); __init__ = NULL;}int main(){ __init__(); // __init__(); ...
抽象代码:一个"O(1)"的质数判断器
先看运行效果。一个判断质数的程序,输入 7: 1237 → 7 is a Prime100 → 100 isn't a Prime254 → 254 isn't a Prime 响应快得惊人,因为它一次除法都没做——不是算法好,是它根本不判断。它是一份”背好答案”的程序:把 1 到 255 每个数是不是质数,直接写成 1029 行 if-else。而这个程序的作者,是另一个程序。 生成器一号:把判断展开成 if-else生成器的思路是时间换空间再换时间:生成的时候用普通试除法判断一次质数,然后把结果硬编码进一个新程序: 123456789101112131415161718for (int i = 2; i < num; i++){ char temp[50]; sprintf(temp, " else if(num == %d)\n", i); fprintf(fp, temp); fprintf(fp, " {\n"); if(isPrime(i)...
C语言宏函数的优先级陷阱:一个 MAX 宏的六级进化
最近研究了一下 MAX 宏的进化史:从青铜到王者共 6 个版本(define0.c ~ define5.c),完整记录了一个三目运算符宏是怎么被优先级、副作用、类型问题一步步逼着进化的。 先上本篇的理论基础——C 语言运算符优先级表(16 级,本篇 MAX 宏的悲剧就发生在第 7 级 > 和第 8 级 != 之间): 优先级 运算符 结合律 1 后缀运算符:[] () . -> ++ -- (类型名称){列表} 从左到右 2 一元运算符:++ -- ! ~ + - * & sizeof _Alignof 从右到左 3 类型转换运算符:(类型名称) 从右到左 4 乘除法运算符:* / % 从左到右 5 加减法运算符:+ - 从左到右 6 移位运算符:<< >> 从左到右 7 关系运算符:< <= > >= 从左到右 8 相等运算符:== != 从左到右 9 位运算符 AND:& 从左到右 10 位运算符 XOR:^ 从左到右 11 位运算符 OR:| 从左...
发现了一个 O(N^N!) 的算法
起因是刷到一张表情包:汤姆猫一脸坏笑—— “突然想到一个 O(NN!) 的算法。” O(NN!)——N 的 N! 次方。这个复杂度夸张到什么程度?N=3 时就是 729,N=4 直接冲破四百万,再往上数数都没意义了。猫都能”想到”的算法,那我能不能真的把它写出来?于是有了这个程序:不借助任何数论技巧,用最朴素的递归把这道题画出来。完整代码只有 30 行: N^N!算法.c123456789101112131415161718192021222324252627282930313233343536373839#include <stdio.h>long long total_sum = 0;long long factorial(unsigned n){ if (n == 1) { printf("+1"); total_sum++; return 1; } long long sum = 0; for (unsigned ...
