diff --git a/published/20190731 How to structure a multi-file C program- Part 2.md b/published/20190731 How to structure a multi-file C program- Part 2.md new file mode 100644 index 0000000000..53dc8bb70e --- /dev/null +++ b/published/20190731 How to structure a multi-file C program- Part 2.md @@ -0,0 +1,224 @@ +[#]: collector: (lujun9972) +[#]: translator: (mengxinayan) +[#]: reviewer: (wxy) +[#]: publisher: (wxy) +[#]: url: (https://linux.cn/article-12000-1.html) +[#]: subject: (How to structure a multi-file C program: Part 2) +[#]: via: (https://opensource.com/article/19/7/structure-multi-file-c-part-2) +[#]: author: (Erik O'Shaughnessy https://opensource.com/users/jnyjny) + +如何组织构建多文件 C 语言程序(二) +====== + +> 我将在本系列的第二篇中深入研究由多个文件组成的 C 程序的结构。 + +![](https://img.linux.net.cn/data/attachment/album/202003/16/122928i6qheufnh24jq2qf.jpg) + +在[第一篇][2]中,我设计了一个名为[喵呜喵呜][3]的多文件 C 程序,该程序实现了一个玩具[编解码器][4]。我也提到了程序设计中的 Unix 哲学,即在一开始创建多个空文件,并建立一个好的结构。最后,我创建了一个 `Makefile` 文件夹并阐述了它的作用。在本文中将另一个方向展开:现在我将介绍简单但具有指导性的喵呜喵呜编解码器的实现。 + +当读过我的《[如何写一个好的 C 语言 main 函数][5]》后,你会觉得喵呜喵呜编解码器的 `main.c` 文件的结构很熟悉,其主体结构如下: + +``` +/* main.c - 喵呜喵呜流式编解码器 */ + +/* 00 系统包含文件 */ +/* 01 项目包含文件 */ +/* 02 外部声明 */ +/* 03 定义 */ +/* 04 类型定义 */ +/* 05 全局变量声明(不要用)*/ +/* 06 附加的函数原型 */ +    +int main(int argc, char *argv[]) +{ +  /* 07 变量声明 */ +  /* 08 检查 argv[0] 以查看该程序是被如何调用的 */ +  /* 09 处理来自用户的命令行选项 */ +  /* 10 做点有用的事情 */ +} +    +/* 11 其它辅助函数 */ +``` + +### 包含项目头文件 + +位于第二部分中的 `/* 01 项目包含文件 */` 的源代码如下: + + +``` +/* main.c - 喵呜喵呜流式编解码器 */ +... +/* 01 项目包含文件 */ +#include "main.h" +#include "mmecode.h" +#include "mmdecode.h" +``` + +`#include` 是 C 语言的预处理命令,它会将该文件名的文件内容拷贝到当前文件中。如果程序员在头文件名称周围使用双引号(`""`),编译器将会在当前目录寻找该文件。如果文件被尖括号包围(`<>`),编译器将在一组预定义的目录中查找该文件。 + +[main.h][6] 文件中包含了 [main.c][7] 文件中用到的定义和类型定义。我喜欢尽可能多将声明放在头文件里,以便我在我的程序的其他位置使用这些定义。 + +头文件 [mmencode.h][8] 和 [mmdecode.h][9] 几乎相同,因此我以 `mmencode.h` 为例来分析。 + +``` +/* mmencode.h - 喵呜喵呜流编解码器 */ +   +#ifndef _MMENCODE_H +#define _MMENCODE_H + +#include + +int mm_encode(FILE *src, FILE *dst); + +#endif /* _MMENCODE_H */ +``` + +`#ifdef`、`#define`、`#endif` 指令统称为 “防护” 指令。其可以防止 C 编译器在一个文件中多次包含同一文件。如果编译器在一个文件中发现多个定义/原型/声明,它将会产生警告。因此这些防护措施是必要的。 + +在这些防护内部,只有两个东西:`#include` 指令和函数原型声明。我在这里包含了 `stdio.h` 头文件,以便于能在函数原型中使用 `FILE` 定义。函数原型也可以被包含在其他 C 文件中,以便于在文件的命名空间中创建它。你可以将每个文件视为一个独立的命名空间,其中的变量和函数不能被另一个文件中的函数或者变量使用。 + +编写头文件很复杂,并且在大型项目中很难管理它。不要忘记使用防护。 + +### 喵呜喵呜编码的最终实现 + +该程序的功能是按照字节进行 `MeowMeow` 字符串的编解码,事实上这是该项目中最简单的部分。截止目前我所做的工作便是支持允许在适当的位置调用此函数:解析命令行,确定要使用的操作,并打开将要操作的文件。下面的循环是编码的过程: + +``` +/* mmencode.c - 喵呜喵呜流式编解码器 */ +... + while (!feof(src)) { + + if (!fgets(buf, sizeof(buf), src)) + break; + + for(i=0; i> 4; + fputs(tbl[hi], dst); + fputs(tbl[lo], dst); + } + } +``` + +简单的说,当文件中还有数据块时( `feof(3)` ),该循环读取(`feof(3)` )文件中的一个数据块。然后将读入的内容的每个字节分成两个 `hi` 和 `lo` 的半字节nibble。半字节是半个字节,即 4 个位。这里的奥妙之处在于可以用 4 个位来编码 16 个值。我将 `hi` 和 `lo` 用作 16 个字符串查找表 `tbl` 的索引,表中包含了用半字节编码的 `MeowMeow` 字符串。这些字符串使用 `fputs(3)` 函数写入目标 `FILE` 流,然后我们继续处理缓存区的下一个字节。 + +该表使用 [table.h][14] 中的宏定义进行初始化,在没有特殊原因(比如:要展示包含了另一个项目的本地头文件)时,我喜欢使用宏来进行初始化。我将在未来的文章中进一步探讨原因。 + +### 喵呜喵呜解码的实现 + +我承认在开始工作前花了一些时间。解码的循环与编码类似:读取 `MeowMeow` 字符串到缓冲区,将编码从字符串转换为字节 + +``` + /* mmdecode.c - 喵呜喵呜流式编解码器 */ + ... + int mm_decode(FILE *src, FILE *dst) + { +   if (!src || !dst) { +     errno = EINVAL; +     return -1; +   } +   return stupid_decode(src, dst); + } +``` + +这不符合你的期望吗? + +在这里,我通过外部公开的 `mm_decode()` 函数公开了 `stupid_decode()` 函数细节。我上面所说的“外部”是指在这个文件之外。因为 `stupid_decode()` 函数不在该头文件中,因此无法在其他文件中调用它。 + +当我们想发布一个可靠的公共接口时,有时候会这样做,但是我们还没有完全使用函数解决问题。在本例中,我编写了一个 I/O 密集型函数,该函数每次从源中读取 8 个字节,然后解码获得 1 个字节写入目标流中。较好的实现是一次处理多于 8 个字节的缓冲区。更好的实现还可以通过缓冲区输出字节,进而减少目标流中单字节的写入次数。 + +``` +/* mmdecode.c - 喵呜喵呜流式编解码器 */ +... +int stupid_decode(FILE *src, FILE *dst) +{ + char buf[9]; + decoded_byte_t byte; + int i; + + while (!feof(src)) { + if (!fgets(buf, sizeof(buf), src)) + break; + byte.field.f0 = isupper(buf[0]); + byte.field.f1 = isupper(buf[1]); + byte.field.f2 = isupper(buf[2]); + byte.field.f3 = isupper(buf[3]); + byte.field.f4 = isupper(buf[4]); + byte.field.f5 = isupper(buf[5]); + byte.field.f6 = isupper(buf[6]); + byte.field.f7 = isupper(buf[7]); + + fputc(byte.value, dst); + } + return 0; +} +``` + +我并没有使用编码器中使用的位移方法,而是创建了一个名为 `decoded_byte_t` 的自定义数据结构。 + +``` +/* mmdecode.c - 喵呜喵呜流式编解码器 */ +... + +typedef struct { +  unsigned char f7:1; +  unsigned char f6:1; +  unsigned char f5:1; +  unsigned char f4:1; +  unsigned char f3:1; +  unsigned char f2:1; +  unsigned char f1:1; +  unsigned char f0:1; +} fields_t; +   +typedef union { +  fields_t      field; +  unsigned char value; +} decoded_byte_t; +``` + +初次看到代码时可能会感到有点儿复杂,但不要放弃。`decoded_byte_t` 被定义为 `fields_t` 和 `unsigned char` 的 **联合**。可以将联合中的命名成员看作同一内存区域的别名。在这种情况下,`value` 和 `field` 指向相同的 8 位内存区域。将 `field.f0` 设置为 `1` 也将会设置 `value` 中的最低有效位。 + +虽然 `unsigned char` 并不神秘,但是对 `fields_t` 的类型定义(`typedef`)也许看起来有些陌生。现代 C 编译器允许程序员在结构体中指定单个位字段的值。字段所在的类型是一个无符号整数类型,并在成员标识符后紧跟一个冒号和一个整数,该整数指定了位字段的长度。 + +这种数据结构使得按字段名称访问字节中的每个位变得简单,并可以通过联合中的 `value` 字段访问组合后的值。我们依赖编译器生成正确的移位指令来访问字段,这可以在调试时为你节省不少时间。 + +最后,因为 `stupid_decode()` 函数一次仅从源 `FILE` 流中读取 8 个字节,所以它效率并不高。通常我们尝试最小化读写次数,以提高性能和降低调用系统调用的开销。请记住:少量的读取/写入大的块比大量的读取/写入小的块好得多。 + +### 总结 + +用 C 语言编写一个多文件程序需要程序员要比只是是一个 `main.c` 做更多的规划。但是当你添加功能或者重构时,只需要多花费一点儿努力便可以节省大量时间以及避免让你头痛的问题。 + +回顾一下,我更喜欢这样做:多个文件,每个文件仅有简单功能;通过头文件公开那些文件中的小部分功能;把数字常量和字符串常量保存在头文件中;使用 `Makefile` 而不是 Bash 脚本来自动化处理事务;使用 `main()` 函数来处理命令行参数解析并作为程序主要功能的框架。 + +我知道我只是蜻蜓点水般介绍了这个简单的程序,并且我很高兴知道哪些事情对你有所帮助,以及哪些主题需要详细的解释。请在评论中分享你的想法,让我知道。 + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/19/7/structure-multi-file-c-part-2 + +作者:[Erik O'Shaughnessy][a] +选题:[lujun9972][b] +译者:[萌新阿岩](https://github.com/mengxinayan) +校对:[wxy](https://github.com/wxy) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/jnyjny +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/file_system.jpg?itok=pzCrX1Kc (4 manilla folders, yellow, green, purple, blue) +[2]: https://linux.cn/article-11935-1.html +[3]: https://github.com/jnyjny/MeowMeow.git +[4]: https://en.wikipedia.org/wiki/Codec +[5]: https://linux.cn/article-10949-1.html +[6]: https://github.com/JnyJny/meowmeow/blob/master/main.h +[7]: https://github.com/JnyJny/meowmeow/blob/master/main.c +[8]: https://github.com/JnyJny/meowmeow/blob/master/mmencode.h +[9]: https://github.com/JnyJny/meowmeow/blob/master/mmdecode.h +[10]: http://www.opengroup.org/onlinepubs/009695399/functions/feof.html +[11]: http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html +[12]: http://www.opengroup.org/onlinepubs/009695399/functions/strlen.html +[13]: http://www.opengroup.org/onlinepubs/009695399/functions/fputs.html +[14]: https://github.com/JnyJny/meowmeow/blob/master/table.h +[15]: http://www.opengroup.org/onlinepubs/009695399/functions/isupper.html +[16]: http://www.opengroup.org/onlinepubs/009695399/functions/fputc.html diff --git a/sources/tech/20200310 Run Kubernetes on a Raspberry Pi with k3s.md b/sources/tech/20200310 Run Kubernetes on a Raspberry Pi with k3s.md new file mode 100644 index 0000000000..2079a18983 --- /dev/null +++ b/sources/tech/20200310 Run Kubernetes on a Raspberry Pi with k3s.md @@ -0,0 +1,203 @@ +[#]: collector: (lujun9972) +[#]: translator: ( ) +[#]: reviewer: ( ) +[#]: publisher: ( ) +[#]: url: ( ) +[#]: subject: (Run Kubernetes on a Raspberry Pi with k3s) +[#]: via: (https://opensource.com/article/20/3/kubernetes-raspberry-pi-k3s) +[#]: author: (Lee Carpenter https://opensource.com/users/carpie) + +Run Kubernetes on a Raspberry Pi with k3s +====== +Create your own three-node Kubernetes cluster with these easy-to-follow +instructions. +![A ship wheel with someone steering][1] + +For a long time, I've been interested in building a [Kubernetes][2] cluster out of a stack of inexpensive Raspberry Pis. Following along with various tutorials on the web, I was able to get Kubernetes installed and working in a three Pi cluster. However, the RAM and CPU requirements on the master node overwhelmed my Pi. This caused poor performance when doing various Kubernetes tasks. It also made an in-place upgrade of Kubernetes impossible. + +As a result, I was very excited to see the [k3s project][3]. K3s is billed as a lightweight Kubernetes for use in resource-constrained environments. It is also optimized for ARM processors. This makes running a Raspberry Pi-based Kubernetes cluster much more feasible. In fact, we are going to create one in this article. + +### Materials needed + +To create the Kubernetes cluster described in this article, we are going to need: + + * At least one Raspberry Pi (with SD card and power adapter) + * Ethernet cables + * A switch or router to connect all our Pis together + + + +We will be installing k3s from the internet, so they will need to be able to access the internet through the router. + +### An overview of our cluster + +For this cluster, we are going to use three Raspberry Pis. The first we'll name **kmaster** and assign a static IP of 192.168.0.50 (since our local network is 192.168.0.0/24). The first worker node (the second Pi), we'll name **knode1** and assign an IP of 192.168.0.51. The final worker node we'll name **knode2** and assign an IP of 192.168.0.52. + +Obviously, if you have a different network layout, you may use any network/IPs you have available. Just substitute your own values anywhere IPs are used in this article. + +So that we don't have to keep referring to each node by IP, let's add their host names to our **/etc/hosts** file on our PC. + + +``` +echo -e "192.168.0.50\tkmaster" | sudo tee -a /etc/hosts +echo -e "192.168.0.51\tknode1" | sudo tee -a /etc/hosts +echo -e "192.168.0.52\tknode2" | sudo tee -a /etc/hosts +``` + +### Installing the master node + +Now we're ready to install the master node. The first step is to install the latest Raspbian image. I am not going to explain that here, but I have a [detailed article][4] on how to do this if you need it. So please go install Raspbian, enable the SSH server, set the hostname to **kmaster**, and assign a static IP of 192.168.0.50. + +Now that Raspbian is installed on the master node, let's boot our master Pi and **ssh** into it: + + +``` +`ssh pi@kmaster` +``` + +Now we're ready to install **k3s**. On the master Pi, run: + + +``` +`curl -sfL https://get.k3s.io | sh -` +``` + +When the command finishes, we already have a single node cluster set up and running! Let's check it out. Still on the Pi, run: + + +``` +`sudo kubectl get nodes` +``` + +You should see something similar to: + + +``` +NAME     STATUS   ROLES    AGE    VERSION +kmaster  Ready    master   2m13s  v1.14.3-k3s.1 +``` + +### Extracting the join token + +We want to add a couple of worker nodes. When installing **k3s** on those nodes we will need a join token. The join token exists on the master node's filesystem. Let's copy that and save it somewhere we can get to it later: + + +``` +`sudo cat /var/lib/rancher/k3s/server/node-token` +``` + +### Installing the worker nodes + +Grab some SD cards for the two worker nodes and install Raspbian on each. For one, set the hostname to **knode1** and assign an IP of 192.168.0.51. For the other, set the hostname to **knode2** and assign an IP of 192.168.0.52. Now, let's install **k3s**. + +Boot your first worker node and **ssh** into it: + + +``` +`ssh pi@knode1` +``` + +On the Pi, we'll install **k3s** as before, but we will give the installer extra parameters to let it know that we are installing a worker node and that we'd like to join the existing cluster: + + +``` +curl -sfL | K3S_URL= \ +K3S_TOKEN=join_token_we_copied_earlier sh - +``` + +Replace **join_token_we_copied_earlier** with the token from the "Extracting the join token" section. Repeat these steps for **knode2**. + +### Access the cluster from our PC + +It'd be annoying to have to **ssh** to the master node to run **kubectl** anytime we wanted to inspect or modify our cluster. So, we want to put **kubectl** on our PC. But first, let's get the configuration information we need from our master node. **Ssh** into **kmaster** and run: + + +``` +`sudo cat /etc/rancher/k3s/k3s.yaml` +``` + +Copy this configuration information and return to your PC. Make a directory for the config: + + +``` +`mkdir ~/.kube` +``` + +Save the copied configuration as **~/.kube/config**. Now edit the file and change the line: + + +``` +`server: https://localhost:6443` +``` + +to be: + + +``` +`server: https://kmaster:6443` +``` + +For security purpose, limit the file's read/write permissions to just yourself: + + +``` +`chmod 600 ~/.kube/config` +``` + +Now let's install **kubectl** on our PC (if you don't already have it). The Kubernetes site has [instructions][5] for doing this for various platforms. Since I'm running Linux Mint, an Ubuntu derivative, I'll show the Ubuntu instructions here: + + +``` +sudo apt update && sudo apt install -y apt-transport-https +curl -s | sudo apt-key add - +echo "deb kubernetes-xenial main" | \ +sudo tee -a /etc/apt/sources.list.d/kubernetes.list +sudo apt update && sudo apt install kubectl +``` + +If you're not familiar, the above commands add a Debian repository for Kubernetes, grab its GPG key for security, and then update the list of packages and install **kubectl**. Now, we'll get notifications of any updates for **kubectl** through the standard software update mechanism. + +Now we can check out our cluster from our PC! Run: + + +``` +`kubectl get nodes` +``` + +You should see something like: + + +``` +NAME     STATUS  ROLES   AGE   VERSION +kmaster  Ready   master  12m   v1.14.3-k3s.1 +knode1   Ready   worker  103s  v1.14.3-k3s.1 +knode1   Ready   worker  103s  v1.14.3-k3s.1 +``` + +Congratulations! You have a working 3-node Kubernetes cluster! + +### The k3s bonus + +If you run **kubectl get pods --all-namespaces**, you will see some extra pods for [Traefik][6]. Traefik is a reverse proxy and load balancer that we can use to direct traffic into our cluster from a single entry point. Kubernetes allows for this but doesn't provide such a service directly. Having Traefik installed by default is a nice touch by Rancher Labs. This makes a default **k3s** install fully complete and immediately usable! + +We're going to explore using Traefik through Kubernetes **ingress** rules and deploy all kinds of goodies to our cluster in future articles. Stay tuned! + +-------------------------------------------------------------------------------- + +via: https://opensource.com/article/20/3/kubernetes-raspberry-pi-k3s + +作者:[Lee Carpenter][a] +选题:[lujun9972][b] +译者:[译者ID](https://github.com/译者ID) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/carpie +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/ship_wheel_gear_devops_kubernetes.png?itok=xm4a74Kv (A ship wheel with someone steering) +[2]: https://opensource.com/resources/what-is-kubernetes +[3]: https://k3s.io/ +[4]: https://carpie.net/articles/headless-pi-with-static-ip-wired-edition +[5]: https://kubernetes.io/docs/tasks/tools/install-kubectl/ +[6]: https://traefik.io/ diff --git a/translated/tech/20190731 How to structure a multi-file C program- Part 2.md b/translated/tech/20190731 How to structure a multi-file C program- Part 2.md deleted file mode 100644 index 85499e4362..0000000000 --- a/translated/tech/20190731 How to structure a multi-file C program- Part 2.md +++ /dev/null @@ -1,226 +0,0 @@ -[#]: collector: (lujun9972) -[#]: translator: (mengxinayan) -[#]: reviewer: ( ) -[#]: publisher: ( ) -[#]: url: ( ) -[#]: subject: (How to structure a multi-file C program: Part 2) -[#]: via: (https://opensource.com/article/19/7/structure-multi-file-c-part-2) -[#]: author: (Erik O'Shaughnessy https://opensource.com/users/jnyjny) - -如何组织构建多文件 C 语言程序(二) -====== -我将在本系列的第二篇中深入研究由多个文件组成的C程序的结构。 -![4 个 manilla 文件,黄色,绿色,紫色,蓝色][1] - -在 [(第一篇)][2] 中,我设计了一个名为 [MeowMeow][3] 的多文件 C 程序,该程序实现了一个玩具——[codec][4]。我提到了程序设计中的 Unix 哲学,即在一开始创建多个空文件,并建立一个好的结构。最后,我创建了一个 Makefile 文件夹并阐述了它的作用。在本文中将另一个方向展开:现在我将介绍简单但具有指导性的 MeowMeow 编/解码器的实现。 - -当读过我的 "[如何写一个好的 C 语言 main 函数][5]." 后,你便会知道 `main.c` 文件中 `meow` 和 `unmeow` 的结构,其主体结构如下: - -``` -/* main.c - MeowMeow 流编码器和解码器 */ - -/* 00 system includes */ -/* 01 project includes */ -/* 02 externs */ -/* 03 defines */ -/* 04 typedefs */ -/* 05 globals (but don't)*/ -/* 06 ancillary function prototypes if any */ -    -int main(int argc, char *argv[]) -{ -  /* 07 variable declarations */ -  /* 08 check argv[0] to see how the program was invoked */ -  /* 09 process the command line options from the user */ -  /* 10 do the needful */ -} -    -/* 11 ancillary functions if any */ -``` - -### 包含项目头文件 - -位于第二部分中的 `/* 01 project includes */` 的源代码如下: - - -``` -/* main.c - MeowMeow 流编码器和解码器 */ -... -/* 01 project includes */ -#include "main.h" -#include "mmecode.h" -#include "mmdecode.h" -``` - -`#include` 是 C 语言的预处理命令,它会将其后面的文件内容拷贝到当前文件中。如果程序员在头文件名称周围使用双引号,编译器将会在当前目录寻找该文件。如果文件被尖括号包围,编译器将在一组预定义的目录中查找该文件。 - -[main.h][6] 文件中包含了 [main.c][7] 文件中用到的定义和别名。我喜欢在头文件里尽可能多的声明,以便我想在我的程序的其他位置使用这些定义。 - -头文件 [mmencode.h][8] 和 [mmdecode.h][9] 几乎相同,因此我以 `mmencode.h` 为例来分析。 - - -``` - /* mmencode.h - MeowMeow 流编码器和解码器 */ -    - #ifndef _MMENCODE_H - #define _MMENCODE_H -    - #include <stdio.h> -    - int mm_encode(FILE *src, FILE *dst); -    - #endif /* _MMENCODE_H */ -``` - -`#ifdef`,`#define`,`#endif` 指令统称为 “防护” 指令。其可以防止 C 编译器在一个文件中多次包含同一文件。如果编译器在一个文件中发现多个定义/原型/声明,它将会产生警告。因此这些防护措施是必要的。 - -在防护内部,它只做两件事:`#include` 指令和函数原型声明。我将包含 `stdio.h` 头文件,以便于能在函数原型中使用 `FILE` 流。函数原型也可以被包含在其他 C 文件中,以便于在文件的命名空间中创建它。你可以将每个文件视为一个命名空间,其中的变量和函数不能被另一个文件中的函数或者变量使用。 - -编写头文件很复杂,并且在大型项目中很难管理它。不要忘记使用防护。 - -### MeowMeow 编码的实现 - -该程序的功能是按照字节进行 `MeowMeow` 字符串的编解码,事实上这是该项目中最简单的部分。截止目前我所做的工作便是支持允许在适当的位置调用此函数:解析命令行,确定要使用的操作,并打开将要操作的文件。下面的循环是编码的过程: - - -``` - /* mmencode.c - MeowMeow 流编码器 */ - ... -     while (![feof][10](src)) { -    -       if (![fgets][11](buf, sizeof(buf), src)) -         break; -              -       for(i=0; i<[strlen][12](buf); i++) { -         lo = (buf[i] & 0x000f); -         hi = (buf[i] & 0x00f0) >> 4; -         [fputs][13](tbl[hi], dst); -         [fputs][13](tbl[lo], dst); -       } -            } -``` - -简单的说,上面代码循环读取文件的一部分,剩下的内容通过 `feof(3)` 函数和 `fgets(3)` 函数读取。然后将读入的内容的每个字节分成 `hi` 和 `lo` 半个字节。半个字节是 4 个比特。这里的奥妙之处在于可以用 4 个比特来编码16个值。我将字符串 `hi` 和 `lo` 用作 16 字符串查找表 `tbl` 的索引,表中包含了对每半个字节编码后的 `MeowMeow` 字符串。这些字符串使用 `fputs(3)` 函数写入目标 `FILE` 流,然后移动到缓存区的下一个字节。 - -该表使用 [table.h][14] 中的宏定义进行初始化,在没有特殊原因(比如:要展示包含了另一个项目的本地头文件)时,我喜欢使用宏来进行初始化。我将在未来的文章中进一步探讨原因。 - -### MeowMeow 解码的实现 - -我承认在开始工作前花了一些时间。解码的循环与编码类似:读取 `MeowMeow` 字符串到缓冲区,将编码从字符串转换为字节 - - -``` - /* mmdecode.c - MeowMeow 流解码器 */ - ... - int mm_decode(FILE *src, FILE *dst) - { -   if (!src || !dst) { -     errno = EINVAL; -     return -1; -   } -   return stupid_decode(src, dst); - } -``` - -这不是所期望的吗? - -在这里,我通过外部公开的 `mm_decode()` 函数公开了 `stupid_decode()` 函数细节。我上面所说的“外部”是指在这个文件之外。因为 `stupid_decode()` 函数不在头文件中,因此无法在其他文件中调用它。 - -当我们想发布一个可靠的公共接口时,有时候会这样做,但是我们还没有完全使用函数解决问题。在本例中,我编写了一个 I/O 密集型函数,该函数每次从源中读取 8 个字节,然后解码获得 1 个字节写入目标流中。较好的实现是一次处理多于 8 个字节的缓冲区。更好的实现还可以通过缓冲区输出字节,进而减少目标流中单字节的写入次数。 - - -``` - /* mmdecode.c - MeowMeow 流解码器 */ - ... - int stupid_decode(FILE *src, FILE *dst) - { -   char           buf[9]; -   decoded_byte_t byte; -   int            i; -      -   while (![feof][10](src)) { -     if (![fgets][11](buf, sizeof(buf), src)) -       break; -     byte.field.f0 = [isupper][15](buf[0]); -     byte.field.f1 = [isupper][15](buf[1]); -     byte.field.f2 = [isupper][15](buf[2]); -     byte.field.f3 = [isupper][15](buf[3]); -     byte.field.f4 = [isupper][15](buf[4]); -     byte.field.f5 = [isupper][15](buf[5]); -     byte.field.f6 = [isupper][15](buf[6]); -     byte.field.f7 = [isupper][15](buf[7]); -        -     [fputc][16](byte.value, dst); -   } -   return 0; - } -``` - -我并没有使用编码器中使用的位移方法,而是创建了一个名为 `decoded_byte_t` 的自定义数据结构。 - -``` - /* mmdecode.c - MeowMeow 流解码器 */ - ... - - typedef struct { -   unsigned char f7:1; -   unsigned char f6:1; -   unsigned char f5:1; -   unsigned char f4:1; -   unsigned char f3:1; -   unsigned char f2:1; -   unsigned char f1:1; -   unsigned char f0:1; - } fields_t; -    - typedef union { -   fields_t      field; -   unsigned char value; - } decoded_byte_t; -``` - -初次看到代码时可能会感到有点儿复杂,但不要放弃。`decoded_byte_t` 被定义为 `fields_t` 和 `unsigned char` 的 **联合体** 。可以将联合中的命名成员看作同一内存区域的别名。在这种情况下,`value` 和 `field` 指向相同的 8 比特内存区域。将 `field.f0` 设置为 1 也将设置 `value` 中的最低有效位。 - -虽然 `unsigned char` 并不神秘,但是对 `fields_t` 的 别名(`typedef`) 也许看起来有些陌生。现代 C 编译器允许程序员在结构体中指定单个 bit 的值。`field` 成员是一个无符号整数类型,可以在成员标识符后紧跟一个冒号和一个整数,该整数指定了比特字段的长度。 - -这种数据结构使得按 `field` 名称访问每个比特变得简单。我们依赖编译器生成正确的移位指令来访问 `field`,这可以在调试时为你节省不少时间。 - -最后,因为`stupid_decode()` 函数一次仅从源 `FILE` 流中读取 8 个字节,所以它效率并不高。通常我们尝试最小化读写次数,以提高性能和降低调用系统调用的开销。请记住:少次数的读取/写入大的块比多次数的读取/写入小的块好得多。 - -### 总结 - -用 C 语言编写一个多文件程序需要程序员做更多计划,而不仅仅是一个 `main.c`。但是当你添加功能或者重构时,只需要多花费一点儿努力便可以节省大量时间以及避免让你头痛的问题。 - -回顾一下,我更喜欢这样做:多个文件,每个文件仅有简单功能;通过头文件公开那些文件中的小部分功能;把数字常量和字符串常量保存在头文件中;使用 `Makefile` 而不是 Bash 脚本来自动化处理事务;使用 `main()` 函数来处理和解析命令行参数并作为程序主要功能的框架。 - -我知道我只是涉及了这个简单程序中发生的事情,并且我很高兴知道哪些事情对您有所帮助以及哪些主题需要详细的解释。在评论中分享您的想法,让我知道。 - --------------------------------------------------------------------------------- - -via: https://opensource.com/article/19/7/structure-multi-file-c-part-2 - -作者:[Erik O'Shaughnessy][a] -选题:[lujun9972][b] -译者:[萌新阿岩](https://github.com/mengxinayan) -校对:[校对者ID](https://github.com/校对者ID) - -本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 - -[a]: https://opensource.com/users/jnyjny -[b]: https://github.com/lujun9972 -[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/file_system.jpg?itok=pzCrX1Kc (4 manilla folders, yellow, green, purple, blue) -[2]: https://opensource.com/article/19/7/how-structure-multi-file-c-program-part-1 -[3]: https://github.com/jnyjny/MeowMeow.git -[4]: https://en.wikipedia.org/wiki/Codec -[5]: https://opensource.com/article/19/5/how-write-good-c-main-function -[6]: https://github.com/JnyJny/meowmeow/blob/master/main.h -[7]: https://github.com/JnyJny/meowmeow/blob/master/main.c -[8]: https://github.com/JnyJny/meowmeow/blob/master/mmencode.h -[9]: https://github.com/JnyJny/meowmeow/blob/master/mmdecode.h -[10]: http://www.opengroup.org/onlinepubs/009695399/functions/feof.html -[11]: http://www.opengroup.org/onlinepubs/009695399/functions/fgets.html -[12]: http://www.opengroup.org/onlinepubs/009695399/functions/strlen.html -[13]: http://www.opengroup.org/onlinepubs/009695399/functions/fputs.html -[14]: https://github.com/JnyJny/meowmeow/blob/master/table.h -[15]: http://www.opengroup.org/onlinepubs/009695399/functions/isupper.html -[16]: http://www.opengroup.org/onlinepubs/009695399/functions/fputc.html diff --git a/translated/tech/20200123 6 things you should be doing with Emacs.md b/translated/tech/20200123 6 things you should be doing with Emacs.md index 62ad555cd3..bdd21e4458 100644 --- a/translated/tech/20200123 6 things you should be doing with Emacs.md +++ b/translated/tech/20200123 6 things you should be doing with Emacs.md @@ -100,3 +100,33 @@ Emacs 许多重要功能都不是只能通过 Emacs 来实现的; Org mode 是 E 我们汇聚了最常用的 Emacs 快捷键成为一份 Emacs 备忘录以便你查询。将它挂在屏幕附近或办公室墙上,把它作为鼠标垫也行。让它触手可及经常翻阅一下。每次翻两下可以让你获得十倍的学习效率。而且一旦开始编写自己的函数,你一定不会后悔获取了这个免费的备忘录副本的! [这里下载 Emacs 备忘录 ](https://opensource.com/downloads/emacs-cheat-sheet) + +-------------------------------------------------------------------------------- +via: https://opensource.com/article/20/1/emacs-cheat-sheet + +作者:[Seth Kenlon][a] +选题:[lujun9972][b] +译者:[lujun9972](https://github.com/lujun9972) +校对:[校对者ID](https://github.com/校对者ID) + +本文由 [LCTT](https://github.com/LCTT/TranslateProject) 原创编译,[Linux中国](https://linux.cn/) 荣誉推出 + +[a]: https://opensource.com/users/seth +[b]: https://github.com/lujun9972 +[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/browser_blue_text_editor_web.png +[2]: https://www.gnu.org/software/emacs/ +[3]: https://en.wikipedia.org/wiki/Lisp_(programming_language) +[4]: https://www.gnu.org/software/tramp/ +[5]: https://www.openssh.com/ +[6]: https://orgmode.org/ +[7]: https://f-droid.org/en/packages/com.orgzly/ +[8]: https://opensource.com/sites/default/files/uploads/orgzly-agenda.jpg +[9]: https://opensource.com/article/19/4/calendar-git +[10]: https://opensource.com/article/19/12/favorite-terminal-emulator +[11]: http://racket-lang.org/ +[12]: https://en.wikipedia.org/wiki/Logo_(programming_language)#Turtle_and_graphics +[13]: https://opensource.com/sites/default/files/racket-mode.jpg +[14]: https://www.racket-mode.com/ +[15]: https://docs.racket-lang.org/quick/index.html +[16]: https://www.gnu.org/software/guile/ +[17]: https://en.wikipedia.org/wiki/Scheme_(programming_language)