引言 - 从实践狗讲起

  理论到实践(有了算法到实现) 中间有很多过程. 算法方面本人啥也不懂, 只能说说实现方面. 例如下面

一个普通的插入排序.

//
// 插入排序默认从大到小
//
extern void sort_insert_int(int a[], int len) {
int i, j;
for (i = ; i < len; ++i) {
int key = a[j = i];
// 从小到大
while (j > && a[j - ] < key) {
a[j] = a[j - ];
--j;
}
a[j] = key;
}
}

这时候有人就想了, 那数组是 double 的, 那怎么弄了. 也有一种解决方案

#define sort_insert(a, len) \
_Generic(a
, int * : sort_insert_int
, double * : sort_insert_double
, default : sort_insert_default) (a, len)

是不是有所启发. 当然了. 对于上面是使用从大到小封装. 那如果需要从小到大呢. 可以这么做

static inline int _compare_2(const int left, const int key) {
return left - key;
} extern void sort_insert_2(int a[], int len,
int compare(const int left, const int key)) {
int i, j;
for (i = ; i < len; ++i) {
int key = a[j = i];
while (j > && compare(a[j - ], key) < ) {
a[j] = a[j - ];
--j;
}
a[j] = key;
}
}

单独把比较的行为抽象出来, 注册进去. 是不是很开心.

前言 - 细致一点封装

  也许到这里会更开心. 既然能通过高科技泛型模拟出来. 那我们不也可以使用旧科技弄弄.

typedef int (* compare_f)(const void * left, const void * key);

static inline int _compare_3(const void * left, const void * key) {
return *(int *)left - *(int *)key;
} extern void sort_insert_3_(void * data, size_t ez, int len, compare_f compare) {
char * a = data;
void * key;
int i, j; if ((key = malloc(ez)) == NULL)
return; for (i = ; i < len; ++i) {
memcpy(key, &a[i * ez], ez);
for (j = i; j > && compare(&a[(j - ) * ez], key) < ; --j)
memcpy(&a[j * ez], &a[(j - ) * ez], ez);
if (j != i)
memcpy(&a[j * ez], key, ez);
} free(key);
} #define sort_insert_3(a, len, compare) \
sort_insert_3_(a, sizeof(*(a)), len, (compare_f)compare)

是不是很巧妙, 一切都编程 void * 了.  当然了如果使用 C99 版本以上, 或者说用高版本的 GCC.

可以写的更好.

extern void sort_insert_4_(void * data, size_t ez, int len, compare_f compare) {
char * a = data;
char key[ez];
int i, j; for (i = ; i < len; ++i) {
memcpy(key, &a[i * ez], ez);
for (j = i; j > && compare(&a[(j - ) * ez], key) < ; --j)
memcpy(&a[j * ez], &a[(j - ) * ez], ez);
if (j != i)
memcpy(&a[j * ez], key, ez);
}
}

这里用了 C99 的 VLA 特性. 不知道细心的同学是否和思考. GCC 是怎么实现 VLA 可变长数组呢.

拨开云雾见青天, 我们不妨来个实验验证一哈. 看下面测试代码

#include <stdio.h>
#include <stdlib.h> /*
* file : vla.c
* make : gcc -g -Wall -O2 -o vla.exe vla.c
*
*/
int main(int argc, char * argv[]) {
char a[];
int b = ;
char c[b];
int * d = malloc(sizeof(int));
if (d == NULL)
exit(EXIT_FAILURE);
*d = ;
char e[*d]; printf("%p : char a[10]\n", a);
printf("%p : int b\n", &b);
printf("%p : char c[b]\n", c);
printf("%p : int * d\n", d);
printf("%p : char e[*d]\n", e); free(d);
return EXIT_SUCCESS;
}

最终输出结果是

通过地址匹配对于 vla 可变数组, GCC是放在栈上的. 所有可以预测, 当可变数组大小太大. 函数栈会直接崩溃.

如果你有想法, 那么就去实现它, 多数简单我们还是能独立捉出来滴~~

正文 - 通用套路

  还有一种套路, 采用宏模板去实现, 简单提一下这个思路. 看下面代码

#if defined(__T)

#define __f(type) sort_insert_##type
#define __F(type) __f(type) static void __F(__T) (__T a[], int len, int compare(const __T left, const __T key)) {
int i, j;
for (i = ; i < (int)len; ++i) {
__T key = a[j = i];
while (j > && compare(a[j - ], key) < ) {
a[j] = a[j - ];
--j;
}
a[j] = key;
}
} #endif

一般而言上面模板函数都会封装在一个局部文件中使用的时候也很方便, 例如下面这样

// 定义部分, 声明和定义分离可以自己搞
#undef __T
#define __T int
#include "sort_insert.c" // 使用部分和普通函数无异
sort_insert_int(a, LEN(a), _compare_2);

当然除了上面一种基于文件的函数模板. 还用一种纯基于函数宏的函数模板实现.

#define sort_insert_definition(T) \
static void sort_insert_##T (T a[], int len, int compare(const T left, const T key)) { \
int i, j; \
for (i = ; i < len; ++i) { \
T key = a[j = i]; \
while (j > && compare(a[j - ], key) < ) { \
a[j] = a[j - ]; \
--j; \
} \
a[j] = key; \
} \
} sort_insert_definition(int)

使用还是一样  sort_insert_int(a, LEN(a), _compare_2); 跑起来. 第一种函数模板, 在嵌入式用的多.

第二种在实战中用的多, 对于处理各种算法相关的代码很普遍. 到这里应该可以理解上面那些

C 封装中一个小函数存在的套路.

后记 - 路越来越窄, 越来越清晰

错误是可以纠正的, 欢迎指正 ~ 表示感谢哈哈

<<啥时候成为津门第一呀>> : http://music.163.com/#/mv?id=197148

对不起 ~ 什么都明白的好晚 ~

C基础 算法实现层面套路的更多相关文章

  1. PHP基础算法

    1.首先来画个菱形玩玩,很多人学C时在书上都画过,咱们用PHP画下,画了一半. 思路:多少行for一次,然后在里面空格和星号for一次. <?php for($i=0;$i<=3;$i++ ...

  2. 10个经典的C语言面试基础算法及代码

    10个经典的C语言面试基础算法及代码作者:码农网 – 小峰 原文地址:http://www.codeceo.com/article/10-c-interview-algorithm.html 算法是一 ...

  3. Java基础算法集50题

    最近因为要准备实习,还有一个蓝桥杯的编程比赛,所以准备加强一下算法这块,然后百度了一下java基础算法,看到的都是那50套题,那就花了差不多三个晚自习的时间吧,大体看了一遍,做了其中的27道题,有一些 ...

  4. 贝叶斯公式由浅入深大讲解—AI基础算法入门

    1 贝叶斯方法 长久以来,人们对一件事情发生或不发生的概率,只有固定的0和1,即要么发生,要么不发生,从来不会去考虑某件事情发生的概率有多大,不发生的概率又是多大.而且概率虽然未知,但最起码是一个确定 ...

  5. 贝叶斯公式由浅入深大讲解—AI基础算法入门【转】

    本文转载自:https://www.cnblogs.com/zhoulujun/p/8893393.html 1 贝叶斯方法 长久以来,人们对一件事情发生或不发生的概率,只有固定的0和1,即要么发生, ...

  6. java入门学习(3)—循环,选择,基础算法,API概念

    1.顺序结构:也就是顺着程序的前后关系,依次执行.2.选择分支:利用if..else , / switch(){case [ 这个必须是常量]:}; / if..else if….. ….else.. ...

  7. Java - 冒泡排序的基础算法(尚学堂第七章数组)

    /** * 冒泡排序的基础算法 */ import java.util.Arrays; public class TestBubbleSort1 { public static void main(S ...

  8. c/c++面试总结---c语言基础算法总结2

    c/c++面试总结---c语言基础算法总结2 算法是程序设计的灵魂,好的程序一定是根据合适的算法编程完成的.所有面试过程中重点在考察应聘者基础算法的掌握程度. 上一篇讲解了5中基础的算法,需要在面试之 ...

  9. c/c++面试指导---c语言基础算法总结1

    c语言基础算法总结 1  初学者学习任何一门编程语言都必须要明确,重点是学习编程方法和编程思路,不是学习语法规则,语法规则是为编程实现提供服务和支持.所以只要认真的掌握了c语言编程方法,在学习其它的语 ...

随机推荐

  1. 【题解】APIO2018 Duathlon 铁人两项

    首先对于给出的图建立圆方树,然后我们分类讨论每一个点作为中间的中转站出现的情况有多少种,累积到 \(ans\) 中. 对于圆点:在任意两个子树内分别选出一个节点都是合法的. 对于方点:连接向方点的点均 ...

  2. POJ1474:Video Surveillance——题解

    http://poj.org/problem?id=1474 题目大意:给按照顺时针序的多边形顶点,问其是否有内核. —————————————————————————————— (和上道题目一模一样 ...

  3. BZOJ4137 & 洛谷4585:[FJOI2015]火星商店问题

    https://www.lydsy.com/JudgeOnline/problem.php?id=4137 https://www.luogu.org/problemnew/show/P4585 火星 ...

  4. BZOJ1297:[SCOI2009]迷路——题解

    http://www.lydsy.com/JudgeOnline/problem.php?id=1297 windy在有向图中迷路了. 该有向图有 N 个节点,windy从节点 0 出发,他必须恰好在 ...

  5. [usaco] 2008 Dec Largetst Fence 最大的围栏 2 || dp

    原网站大概已经上不了了-- 题目大意: 求出平面上n个点组成的一个包含顶点数最多的凸多边形.n<=250. 考虑我们每次枚举凸包的左下角为谁(参考Graham求凸包时的左下角),然后像Graha ...

  6. Linux进程间通信简介

    本人仅做简介.转自:http://www.linuxidc.com/Linux/2013-06/85904p2.htm   管道( pipe ):   (Linux进程间通信) 管道是一种半双工的通信 ...

  7. POJ.2142 The Balance (拓展欧几里得)

    POJ.2142 The Balance (拓展欧几里得) 题意分析 现有2种质量为a克与b克的砝码,求最少 分别用多少个(同时总质量也最小)砝码,使得能称出c克的物品. 设两种砝码分别有x个与y个, ...

  8. dubbox小demo

    概述: 我们建立两个web项目,一个是service负责提供服务,另一个是web项目负责调用服务. 两个项目都是 maven Project 项目 生产者项目: 项目中主要就是: pom文件,引入相关 ...

  9. Android网络请求的时候报错 Connection refused 处理

    在用Android测试JSON数据解析的时候,报了这样一个异常: java.net.ConnectException: localhost/ - Connection refused 原来模拟器默认把 ...

  10. JS this的指向

    总结:this指向调用函数的那个对象. 在不同的应用场景this的指向有所不同,但细细思考都符合总结的意思. 场景一:一般的函数调用 这种常见的函数调用方式this指向的是window,因为相当于是w ...