技术控

    今日:59| 主题:49369
收藏本版 (1)
最新软件应用技术尽在掌握

[其他] 在 C 的世界之外

[复制链接]
時光已泛黃 发表于 2016-11-30 12:27:24
13 4

立即注册CoLaBug.com会员,免费获得投稿人的专业资料,享用更多功能,玩转个人品牌!

您需要 登录 才可以下载或查看,没有帐号?立即注册

x
本文为「C 的容器、环境与算法」的续篇。  
  A:「密集型」的运算过程,还可以用    void *进行抽象吗?  
  B:不可以,除非不在意自己的 C 程序在性能上输给 C++ 模板程序。如果这种密集型的运算过程仅仅是对某些数据类型有所依赖,此时可以用宏进行抽象。
  A:「宏」,似乎我看到了一幅可怕的景象。
  B:也不是很难,可以从简单的一点一点写起。有了感觉之后再去写复杂一些的。如果上面的代码,都能忍受,那么看到宏代码,立刻会觉得小清新无限……
  A:小星星都不见了?
  B:建立一个 array.h 文件,其内容为:
  1. #ifndef ARRAY_H
  2. #define ARRAY_H
  3. typedef struct { \
  4.         size_t n; \
  5.         T *data; \
  6. } Array;
  7. Array * array_alloc(size_t n)
  8. {
  9.         Array *v = malloc(sizeof(Array));
  10.         v->n = n;
  11.         v->data = malloc(n * sizeof(T));
  12.         return v;
  13. }
  14. void array_free(Array *x)
  15. {
  16.         free(x->data);
  17.         free(x);
  18. }
  19. Array * point_sub(Array *a, Array *b)
  20. {
  21.         if (a->n != b->n) fprintf(stderr, "两个不同维度的点无法构成向量!");
  22.         Array *v = array_alloc(a->n);
  23.         for (size_t i = 0; i < a->n; i++) v->data[i] = a->data[i] - b->data[i];
  24.         return v;
  25. }
  26. #endif
复制代码
然后,再建立一份 main.c 文件,其内容为:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define T float
  4. #include "array.h"
  5. int main(void)
  6. {
  7.         size_t n = 3;
  8.         Array *a = array_alloc(n);
  9.         a->data[0] = 1.0f; a->data[1] = 2.0f; a->data[2] = 3.0f;
  10.         Array *b = array_alloc(n);
  11.         b->data[0] = -1.0f; b->data[1] = -2.0f; b->data[2] = -3.0f;
  12.         
  13.         Array *v = point_sub(a, b);
  14.         for (size_t i = 0; i < v->n; i++) {
  15.                 if (i < v->n - 1) printf("%f ", v->data[i]);
  16.                 else printf("%f\n", v->data[i]);
  17.         }
  18.         
  19.         array_free(v);
  20.         array_free(b);
  21.         array_free(a);
  22.         
  23.         return 0;
  24. }
复制代码
A:这些代码似曾相识,星星果然少多了!
  B:    T表示    arrao_alloc与    point_sub等运算所需要的数据类型。它是一个宏,要想让 array.h 中的代码能够通过编译,必须在    #include "array.h"之前定义    T。  
  A:既然用宏可以让代码干净许多,那为啥还要用    void *呢?  
  B:array.h 如果被多个 .c 文件    include,那么 array.h 中的代码会被 C 编译器重复编译为目标代码,这样便会导致程序体积膨胀。  
  A:这个世界不完美。
  B:C++ 的模板,其思路与上述的宏代码很相似,结果也很相似——程序的体积会膨胀,不过 C++ 的编译器提供了优化功能,开了    -O2之后,那些重复的目标代码会被清除。  
  A:C++ 更完美……
  B:那是因为 C++ 编译器的开发者们把这些脏活累活替大家做了,导致 C++ 编译器的复杂程度跟 C 编译器不是一个数量级。当我使用 CGAL 中的 kd 树对 400,000 个数据点进行 k 近邻检索,程序的执行时间只需 2.8 秒,但是 g++ 编译这个程序所用的时间需要 18 秒。我用 C 实现的 kd 树,用尽我的洪荒之力,也只能将其 k 近邻检索的运算时间降至 4.7 秒,但是 gcc 编译我的 C 程序只需 0.5 秒。
  A:这个世界不完美!
  B:你要享受现代科技,那就只能多吸点雾霾了。我觉得,许多人说 C++ 的模板比 C 的宏更好,这是正确的观点。但是要说 C++ 模板比宏更好,这是错误的观点。
  A:是 C 的宏太弱了,衬托出了 C++ 的模板更好?
  B:是这样的。如果我们跳出 C 的世界,来看容器与算法所依赖的那些数据类型,它们不过是非常普通的文本而已。既然如此,我们为什么非要在编程语言自身的体系内左右互搏来解决这个问题呢?
  A:我觉得……你在说一些我并不擅长的话题……
  B:我觉得二维世界里的人会觉得一维世界里的人无比可怜,三维世界里的人看到二维世界里的人因为吃饭而导致自己的身体被分成两半也会觉得很可怜。四维世界里的人怎么看待我们,我难以想象。不过,对于 C 程序而言,它仅仅是个一维世界里的事物而已。    void *也好,宏也好,这些努力都是企图在一维世界里去解决二维问题。  
  A:你到底想表达什么?
  B:我们试试 m4。
  A:HA,HA,HA……你暴露了年龄,以前打 CS 时,我最喜欢用的武器!
  B:貌似是你暴露了年龄。我都不知道你说的是什么。我说的 m4 是一种语言。
  A:老夫……
  B:将 array.h 改造为:
  1. #ifndef ARRAY_H
  2. #define ARRAY_H
  3. typedef struct {
  4.         size_t n;
  5.         T *data;
  6. } Array;
  7. Array * array_alloc(size_t n)
  8. {
  9.         Array *v = malloc(sizeof(Array));
  10.         v->n = n;
  11.         v->data = malloc(n * sizeof(T));
  12.         return v;
  13. }
  14. void array_free(Array *x)
  15. {
  16.         free(x->data);
  17.         free(x);
  18. }
  19. Array * point_sub(Array *a, Array *b)
  20. {
  21.         if (a->n != b->n) fprintf(stderr, "两个不同维度的点无法构成向量!");
  22.         Array *v = array_alloc(a->n);
  23.         for (size_t i = 0; i < a->n; i++) v->data[i] = a->data[i] - b->data[i];
  24.         return v;
  25. }
  26. #endif
复制代码
A:恕我老眼昏花,愣是没看出来改了何处。
  B:你没看错,原样照抄,丝毫未变。试试下面这条命令:
  1. $ m4 -D T=float array.h > array_float.h
复制代码
A:请允许我做个吃惊的表情,array_float.h 的内容如下:
  1. #ifndef ARRAY_H
  2. #define ARRAY_H
  3. typedef struct {
  4.         size_t n;
  5.         float *data;
  6. } Array;
  7. Array * array_alloc(size_t n)
  8. {
  9.         Array *v = malloc(sizeof(Array));
  10.         v->n = n;
  11.         v->data = malloc(n * sizeof(float));
  12.         return v;
  13. }
  14. void array_free(Array *x)
  15. {
  16.         free(x->data);
  17.         free(x);
  18. }
  19. Array * point_sub(Array *a, Array *b)
  20. {
  21.         if (a->n != b->n) fprintf(stderr, "两个不同维度的点无法构成向量!");
  22.         Array *v = array_alloc(a->n);
  23.         for (size_t i = 0; i < a->n; i++) v->data[i] = a->data[i] - b->data[i];
  24.         return v;
  25. }
  26. #endif
复制代码
B:现在你明白了我的意思了吧?
  A:似懂非懂。
  B: 现在将之前的代码文件都删除,新建 array.h_T,其内容为:
  1. #ifndef ARRAY_H
  2. #define ARRAY_H
  3. typedef struct {
  4.         size_t n;
  5.         T *data;
  6. } Array;
  7. Array * array_alloc(size_t n);
  8. void array_free(Array *x);
  9. Array * point_sub(Array *a, Array *b);
  10. #endif
复制代码
再建立 array.c_T 文件,其内容为:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. `#'include `"'array_`'T`'.h`"'
  4. Array * array_alloc(size_t n)
  5. {
  6.         Array *v = malloc(sizeof(Array));
  7.         v->n = n;
  8.         v->data = malloc(n * sizeof(T));
  9.         return v;
  10. }
  11. void array_free(Array *x)
  12. {
  13.         free(x->data);
  14.         free(x);
  15. }
  16. Array * point_sub(Array *a, Array *b)
  17. {
  18.         if (a->n != b->n) fprintf(stderr, "两个不同维度的点无法构成向量!");
  19.         Array *v = array_alloc(a->n);
  20.         for (size_t i = 0; i < a->n; i++) v->data[i] = a->data[i] - b->data[i];
  21.         return v;
  22. }
复制代码
注意,上面这两份文件的末尾都要留出一个空行。
  main.c 文件内容为:
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include "array_float.h"
  4. int main(void)
  5. {
  6.         size_t n = 3;
  7.         Array *a = array_alloc(n);
  8.         a->data[0] = 1.0f; a->data[1] = 2.0f; a->data[2] = 3.0f;
  9.         Array *b = array_alloc(n);
  10.         b->data[0] = -1.0f; b->data[1] = -2.0f; b->data[2] = -3.0f;
  11.         
  12.         Array *v = point_sub(a, b);
  13.         for (size_t i = 0; i < v->n; i++) {
  14.                 if (i < v->n - 1) printf("%f ", v->data[i]);
  15.                 else printf("%f\n", v->data[i]);
  16.         }
  17.         
  18.         array_free(v);
  19.         array_free(b);
  20.         array_free(a);
  21.         
  22.         return 0;
  23. }
复制代码
然后在 Bash 中执行以下命令:
  1. $ ls
  2. array.c_T  array.h_T  main.c
  3. $ for i in array.h array.c ; do m4 -D T=float ${i}_T > ${i%%.*}_float.${i##*.}; done
  4. $ ls
  5. array.c_T  array_float.c  array_float.h  array.h_T  main.c
  6. $ gcc -std=c11 -pedantic -Werror array_float.c main.c -o array-test
  7. $ ./array-test
  8. 2.000000 4.000000 6.000000
复制代码
A:我是不是需要对 m4 有所了解方能看懂下面这样的咒语?
  1. #'include `"'array_`'T`'.h`"'
复制代码
B:然。我已经为你写了一份 m4 教程,详见「    让这世界再多一份 GNU m4 教程」
友荐云推荐




上一篇:PJAX,站点加速之翼
下一篇:Python开启尾递归优化!
酷辣虫提示酷辣虫禁止发表任何与中华人民共和国法律有抵触的内容!所有内容由用户发布,并不代表酷辣虫的观点,酷辣虫无法对用户发布内容真实性提供任何的保证,请自行验证并承担风险与后果。如您有版权、违规等问题,请通过"联系我们"或"违规举报"告知我们处理。

抱着孤单睡 发表于 2016-11-30 17:27:29
别和我谈理想,戒了.
回复 支持 反对

使用道具 举报

z123331240 发表于 2016-11-30 19:05:52
纯粹路过,没任何兴趣,仅仅是看在老用户份上回复一下
回复 支持 反对

使用道具 举报

婳祎 发表于 4 天前
这是破亿的节奏
回复 支持 反对

使用道具 举报

戴依婷 发表于 4 天前
楼主,你妈妈喊你回家吃饭!
回复 支持 反对

使用道具 举报

*滑动验证:
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

我要投稿

推荐阅读

扫码访问 @iTTTTT瑞翔 的微博
回页顶回复上一篇下一篇回列表手机版
手机版/CoLaBug.com ( 粤ICP备05003221号 | 文网文[2010]257号 )|网站地图 酷辣虫

© 2001-2016 Comsenz Inc. Design: Dean. DiscuzFans.

返回顶部 返回列表