Merge pull request #15 from LCTT/master

update
This commit is contained in:
Morisun029 2020-02-15 19:50:01 +08:00 committed by GitHub
commit 41ac3ff859
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 1563 additions and 197 deletions

View File

@ -1,197 +0,0 @@
[#]: collector: (lujun9972)
[#]: translator: (mengxinayan)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to structure a multi-file C program: Part 1)
[#]: via: (https://opensource.com/article/19/7/structure-multi-file-c-part-1)
[#]: author: (Erik O'Shaughnessy https://opensource.com/users/jnyjnyhttps://opensource.com/users/jnyjnyhttps://opensource.com/users/jim-salterhttps://opensource.com/users/cldxsolutions)
How to structure a multi-file C program: Part 1
======
Grab your favorite beverage, editor, and compiler, crank up some tunes,
and start structuring a C program composed of multiple files.
![Programming keyboard.][1]
It has often been said that the art of computer programming is part managing complexity and part naming things. I contend that this is largely true with the addition of "and sometimes it requires drawing boxes."
In this article, I'll name some things and manage some complexity while writing a small C program that is loosely based on the program structure I discussed in "[How to write a good C main function][2]"—but different. This one will do something. Grab your favorite beverage, editor, and compiler, crank up some tunes, and let's write a mildly interesting C program together.
### Philosophy of a good Unix program
The first thing to know about this C program is that it's a [Unix][3] command-line tool. This means that it runs on (or can be ported to) operating systems that provide a Unix C runtime environment. When Unix was invented at Bell Labs, it was imbued from the beginning with a [design philosophy][4]. In my own words: _programs do one thing, do it well, and act on files_. While it makes sense to do one thing and do it well, the part about "acting on files" seems a little out of place.
It turns out that the Unix abstraction of a "file" is very powerful. A Unix file is a stream of bytes that ends with an end-of-file (EOF) marker. That's it. Any other structure in a file is imposed by the application and not the operating system. The operating system provides system calls that allow a program to perform a set of standard operations on files: open, read, write, seek, and close (there are others, but those are the biggies). Standardizing access to files allows different programs to share a common abstraction and work together even when different people implement them in different programming languages.
Having a shared file interface makes it possible to build programs that are _composable_. The output of one program can be the input of another program. The Unix family of operating systems provides three files by default whenever a program is executed: standard in (**stdin**), standard out (**stdout**), and standard error (**stderr**). Two of these files are opened in write-only mode: **stdout** and **stderr**, while **stdin** is opened read-only. We see this in action whenever we use file redirection in a command shell like Bash:
```
`$ ls | grep foo | sed -e 's/bar/baz/g' > ack`
```
This construction can be described briefly as: the output of **ls** is written to stdout, which is redirected to the stdin of **grep**, whose stdout is redirected to **sed**, whose stdout is redirected to write to a file called **ack** in the current directory.
We want our program to play well in this ecosystem of equally flexible and awesome programs, so let's write a program that reads and writes files.
### MeowMeow: A stream encoder/decoder concept
When I was a dewy-eyed kid studying computer science in the <mumbles>s, there were a plethora of encoding schemes. Some of them were for compressing files, some were for packaging files together, and others had no purpose but to be excruciatingly silly. An example of the last is the [MooMoo encoding scheme][5].
To give our program a purpose, I'll update this concept for the [2000s][6] and implement a concept called MeowMeow encoding (since the internet loves cats). The basic idea here is to take files and encode each nibble (half of a byte) with the text "meow." A lower-case letter indicates a zero, and an upper-case indicates a one. Yes, it will balloon the size of a file since we are trading 4 bits for 32 bits. Yes, it's pointless. But imagine the surprise on someone's face when this happens:
```
$ cat /home/your_sibling/.super_secret_journal_of_my_innermost_thoughts
MeOWmeOWmeowMEoW...
```
This is going to be awesome.
### Implementation, finally
The full source for this can be found on [GitHub][7], but I'll talk through my thought process while writing it. The object is to illustrate how to structure a C program composed of multiple files.
Having already established that I want to write a program that encodes and decodes files in MeowMeow format, I fired up a shell and issued the following commands:
```
$ mkdir meowmeow
$ cd meowmeow
$ git init
$ touch Makefile     # recipes for compiling the program
$ touch main.c       # handles command-line options
$ touch main.h       # "global" constants and definitions
$ touch mmencode.c   # implements encoding a MeowMeow file
$ touch mmencode.h   # describes the encoding API
$ touch mmdecode.c   # implements decoding a MeowMeow file
$ touch mmdecode.h   # describes the decoding API
$ touch table.h      # defines encoding lookup table values
$ touch .gitignore   # names in this file are ignored by git
$ git add .
$ git commit -m "initial commit of empty files"
```
In short, I created a directory full of empty files and committed them to git.
Even though the files are empty, you can infer the purpose of each from its name. Just in case you can't, I annotated each **touch** with a brief description.
Usually, a program starts as a single, simple **main.c** file, with only two or three functions that solve the problem. And then the programmer rashly shows that program to a friend or her boss, and suddenly the number of functions in the file balloons to support all the new "features" and "requirements" that pop up. The first rule of "Program Club" is don't talk about "Program Club." The second rule is to minimize the number of functions in one file.
To be honest, the C compiler does not care one little bit if every function in your program is in one file. But we don't write programs for computers or compilers; we write them for other people (who are sometimes us). I know that is probably a surprise, but it's true. A program embodies a set of algorithms that solve a problem with a computer, and it's important that people understand it when the parameters of the problem change in unanticipated ways. People will have to modify the program, and they will curse your name if you have all 2,049 functions in one file.
So we good and true programmers break functions out, grouping similar functions into separate files. Here I've got files **main.c**, **mmencode.c**, and **mmdecode.c**. For small programs like this, it may seem like overkill. But small programs rarely stay small, so planning for expansion is a "Good Idea."
But what about those **.h** files? I'll explain them in general terms later, but in brief, those are called _header_ files, and they can contain C language type definitions and C preprocessor directives. Header files should _not_ have any functions in them. You can think of headers as a definition of the application programming interface (API) offered by the **.c** flavored file that is used by other **.c** files.
### But what the heck is a Makefile?
I know all you cool kids are using the "Ultra CodeShredder 3000" integrated development environment to write the next blockbuster app, and building your project consists of mashing on Ctrl-Meta-Shift-Alt-Super-B. But back in my day (and also today), lots of useful work got done by C programs built with Makefiles. A Makefile is a text file that contains recipes for working with files, and programmers use it to automate building their program binaries from source (and other stuff too!).
Take, for instance, this little gem:
```
00 # Makefile
01 TARGET= my_sweet_program
02 $(TARGET): main.c
03    cc -o my_sweet_program main.c
```
Text after an octothorpe/pound/hash is a comment, like in line 00.
Line 01 is a variable assignment where the variable **TARGET** takes on the string value **my_sweet_program**. By convention, OK, my preference, all Makefile variables are capitalized and use underscores to separate words.
Line 02 consists of the name of the file that the recipe creates and the files it depends on. In this case, the target is **my_sweet_program**, ****and the dependency is **main.c**.
The final line, 03, is indented with a tab and not four spaces. This is the command that will be executed to create the target. In this case, we call **cc** the C compiler frontend to compile and link **my_sweet_program**.
Using a Makefile is simple:
```
$ make
cc -o my_sweet_program main.c
$ ls
Makefile  main.c  my_sweet_program
```
The [Makefile][8] that will build our MeowMeow encoder/decoder is considerably more sophisticated than this example, but the basic structure is the same. I'll break it down Barney-style in another article.
### Form follows function
My idea here is to write a program that reads a file, transforms it, and writes the transformed data to another file. The following fabricated command-line interaction is how I imagine using the program:
```
        $ meow < clear.txt > clear.meow
        $ unmeow < clear.meow > meow.tx
        $ diff clear.txt meow.tx
        $
```
We need to write code to handle command-line parsing and managing the input and output streams. We need a function to encode a stream and write it to another stream. And finally, we need a function to decode a stream and write it to another stream. Wait a second, I've only been talking about writing one program, but in the example above, I invoke two commands: **meow** and **unmeow**? I know you are probably thinking that this is getting complex as heck.
### Minor sidetrack: argv[0] and the ln command
If you recall, the signature of a C main function is:
```
`int main(int argc, char *argv[])`
```
where **argc** is the number of command-line arguments, and **argv** is a list of character pointers (strings). The value of **argv[0]** is the path of the file containing the program being executed. Many Unix utility programs with complementary functions (e.g., compress and uncompress) look like two programs, but in fact, they are one program with two names in the filesystem. The two-name trick is accomplished by creating a filesystem "link" using the **ln** command.
An example from **/usr/bin** on my laptop is:
```
   $ ls -li /usr/bin/git*
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git-receive-pack
...
```
Here **git** and **git-receive-pack** are the same file with different names. We can tell it's the same file because they have the same inode number (the first column). An inode is a feature of the Unix filesystem and is super outside the scope of this article.
Good and/or lazy programmers can use this feature of the Unix filesystem to write less code but double the number of programs they deliver. First, we write a program that changes its behavior based on the value of **argv[0]**, then we make sure to create links with the names that cause the behavior.
In our Makefile, the **unmeow** link is created using this recipe:
```
 # Makefile
 ...
 $(DECODER): $(ENCODER)
         $(LN) -f $< $@
        ...
```
I tend to parameterize everything in my Makefiles, rarely using a "bare" string. I group all the definitions at the top of the Makefile, which makes it easy to find and change them. This makes a big difference when you are trying to port software to a new platform and you need to change all your rules to use **xcc** instead of **cc**.
The recipe should appear relatively straightforward except for the two built-in variables **$@** and **$<**. The first is a shortcut for the target of the recipe; in this case, **$(DECODER)**. (I remember this because the at-sign looks like a target to me.) The second, **$<** is the rule dependency; in this case, it resolves to **$(ENCODER)**.
Things are getting complex for sure, but it's managed.
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/7/structure-multi-file-c-part-1
作者:[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/jnyjnyhttps://opensource.com/users/jnyjnyhttps://opensource.com/users/jim-salterhttps://opensource.com/users/cldxsolutions
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming_keyboard_coding.png?itok=E0Vvam7A (Programming keyboard.)
[2]: https://opensource.com/article/19/5/how-write-good-c-main-function
[3]: https://en.wikipedia.org/wiki/Unix
[4]: http://harmful.cat-v.org/cat-v/
[5]: http://www.jabberwocky.com/software/moomooencode.html
[6]: https://giphy.com/gifs/nyan-cat-sIIhZliB2McAo
[7]: https://github.com/JnyJny/meowmeow
[8]: https://github.com/JnyJny/meowmeow/blob/master/Makefile

View File

@ -0,0 +1,95 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (Why developers like to code at night)
[#]: via: (https://opensource.com/article/20/2/why-developers-code-night)
[#]: author: (Matt Shealy https://opensource.com/users/mshealy)
Why developers like to code at night
======
A nocturnal work schedule is the key to creativity and productivity for
many open source programmers.
![Person programming on a laptop on a building][1]
If you ask most developers when they prefer to work, many will say their most productive hours are at night. This may be especially true for open source contributors who are contributing to projects outside of their day job (though hopefully within healthy limits to [avoid burnout][2]).
Some like to start in the evening and work till the early hours while others get up super early—say, 4 a.m.—to get most of the programming work done before the daily grind kicks in.
This work habit may make many developers seem like oddballs and misfits. However, there are quite a few reasons why so many programmers prefer to work during the odd hours:
### The maker's schedule
According to [Paul Graham][3], people who "produce stuff" tend to adhere to the maker's schedule—they prefer to use time in units of a half-day or longer. In fact, most [developers have the same preference][4].
For one thing, developers work on large abstract systems and need the mental space to process a model in its entirety. Having their schedules sliced into 15- or 30-minute chunks for emails, meetings, phone calls, and interruptions from co-workers is simply counterproductive.
For another, it's often not possible to program effectively in units of an hour; that's barely enough time to wrap your head around the task at hand and get started.
Programming is also adversely affected by context-switching. By working at night, developers can avoid as many distractions as possible. Without the constant barrage of interruptions, they can spend a few solid hours focusing on the task at hand and be as productive as possible.
### The peaceful quiet
With the background noise of various activities (e.g., office chatter, traffic on the street) mostly absent at night or in the early hours, many programmers experience a sense of relaxation. This allows them to be more creative and productive—especially when tackling mentally stimulating tasks such as coding.
The solitude and peacefulness, plus knowing that they'll have a few uninterrupted hours, often take the stress and time pressure associated with a daytime work schedule off their shoulders, allowing them to produce higher quality work.
Not to mention, there's nothing like indulging in your favorite midnight snacks when you have solved a thorny problem!
### Communication
Developers working on open source projects can have a different communication cadence than a programmer working in-house at a company. Most open source communication is done asynchronously through channels like mailing lists or GitHub comments. A lot of times, other programmers are in different countries and time zones, so communicating in real-time often requires developers to be night owls.
### The sleepy brain
This may sound counterintuitive, but as the day wears on, the brain gets tired enough so it can only focus on a single task. This essentially eliminates multitasking, which is a major hurdle to staying focused and productive. But with a sleepy brain, you can't afford not to stay focused!
Also, many developers often make the most significant progress when they go to sleep thinking about the problem they're trying to solve. The subconscious mind goes to work, and the answers often come to them in the early hours when they're still half asleep.
This is not surprising since [sleep boosts brain functions][5], helping us make sense of new information and think more creatively. When the solutions present themselves in the wee hours, these developers just get up and hit the ground running without missing a beat.
### Flexible and creative thinking
Many programmers experience an upswing in creativity at night. The prefrontal cortex, the part of the brain associated with the ability to concentrate, gets tired at the end of the day. This seems to clear the way for more flexible and creative thinking for some people.
According to [Brant Hasler][6], assistant professor of psychiatry at the University of Pittsburgh School of Medicine, "with less of that top-down control and 'cognitive inhibition,' the brain might be freed up for more divergent thinking, allowing one to make new associations between different concepts more easily." Combined with the positive mood made possible by a more relaxed environment, developers can come up with innovative ideas more easily.
Also, without distractions and having the space to concentrate for several hours, you can "get in the zone." This helps you better focus on a project and get in the flow without worrying about things happening around you.
### Bright computer screens
The sleep cycle of many programmers is delayed because they look at bright screens all day. The blue light from computer screens [disrupts our circadian rhythm][7] by delaying the release of sleep-inducing melatonin, increasing alertness, and resetting the body's internal clock to a later schedule. As a result, developers tend to go to bed later and later.
### Influence from the past
In the past, most developers worked at night out of necessity because shared servers didn't have the computing power to support programming work while everyone else in the company is using the servers during the day. Developers needed to wait until late at night to perform tasks that weren't feasible during the day, such as testing projects, running extensive code-compile-run-debug cycles, and deploying new codes. Even though servers are more powerful now and most can support the demand, the trend to work at night continues as part of the culture.
### Final thoughts
While there are many reasons why developers prefer to work at night, keep in mind that being a night owl doesn't mean you should skimp on sleep. Lack of sleep leads to stress and anxiety and, ultimately, burnout.
Getting enough quality sleep is the key to maintaining good physical health and brain functions. For example, it helps you integrate new information, cement memories, think creatively, remove accumulated toxins, regulate your appetite, and prevent premature aging.
No matter what your schedule is, make sure to give your brain the rest it needs so you can be on your game and as productive as possible—all day, every day!
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/2/why-developers-code-night
作者:[Matt Shealy][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/mshealy
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/computer_code_programming_laptop.jpg?itok=ormv35tV (Person programming on a laptop on a building)
[2]: https://opensource.com/article/19/11/burnout-open-source-communities
[3]: http://www.paulgraham.com/makersschedule.html
[4]: https://www.chamberofcommerce.com/business-advice/software-development-trends-overtaking-the-market
[5]: https://amerisleep.com/blog/sleep-impacts-brain-health/
[6]: https://www.vice.com/en_us/article/mb58a8/late-night-creativity-spike
[7]: https://www.sleepfoundation.org/articles/how-blue-light-affects-kids-sleep

View File

@ -0,0 +1,110 @@
[#]: collector: (lujun9972)
[#]: translator: ( )
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to set up your own fast, private open source mesh network)
[#]: via: (https://opensource.com/article/20/2/mesh-network-freemesh)
[#]: author: (Spencer Thomason https://opensource.com/users/spencerthomason)
How to set up your own fast, private open source mesh network
======
FreeMesh is an affordable, performant, privacy-respecting mesh system
that installs in less than 10 minutes.
![people on top of a connected globe][1]
The [FreeMesh][2] system promises to bring fully open source mesh networking to the masses. I recently had a chance to test it; it installed quickly, and the performance was great—especially for the price.
### Why mesh and open source?
The reason to use open source is simple: privacy. With FreeMesh, your data is your own. It doesn't track or collect data. Don't trust it? You can easily check—it's open source, after all! With some other popular mesh solutions, say those provided by very large tech conglomerates, would you trust them with your data?
Another important factor: updates. FreeMesh says it is committed to pushing out security and performance updates regularly. What about 10 years from now? With an open source solution, you are free to update the product for as long as you want.
So why mesh? In a mesh network, multiple wireless routers work together to broadcast a single, very large wireless network. Each router in a mesh network intelligently communicates with the other(s) to provide the best "path" for your data. The following images from FreeMesh's website highlight the difference between using a single wireless router and a mesh network. The red network represents a single wireless router, and the green is a mesh network.
![Single-router network][3] | ![Mesh network][4]
---|---
### Get the equipment
To get started with FreeMesh, [order a kit][5]. Two kits are available: standard and 4G LTE.
The 4G LTE kit, as the name suggests, supports cellular data connections. This feature is a rarity in the consumer networking space, and it will be _very_ useful to some folks. You can set up a portable mesh network anywhere with power and cell service with full fast-failover capability.
The FreeMesh kits come with a primary router and two nodes. The router and nodes use 802.11ac, 802.11r, and 802.11s standards. The included firmware runs a customized version of [OpenWrt][6], a Linux distro for embedded devices.
The FreeMesh router has some really good specs:
* **CPU:** Dual-core 880MHz MediaTek MT7621AT (two cores/four threads!)
* **RAM:** DDR3 512MB
* **Interfaces:** 1x GbE WAN, 4x GbE LAN, 1x USB 2.0 ports, 1x microSD card slot, 1x SIM slot
* **Antenna:** 2x 5dBi 2.4GHz, 2x 5dBi 5GHz, 2x 3dBi 3G/4G (built-in)
* **4G LTE modem:** LTE category 4 module, 150Mbps downlink and 50Mbps uplink
### Setup
Setup is easy, and FreeMesh's [README][7] offers simple instructions and diagrams. Start by setting up the primary router first. Then follow these simple steps:
1. Connect the first node (blue WAN port) to the primary router (yellow LAN port).
![FreeMesh setup step 1][8]
2. Wait about 30 to 60 seconds. The node will flash its LEDs when the setup is complete.
![FreeMesh setup step 2][9]
3. Move the node to another location.
That's it! There is no manual setup required for the nodes; you simply plug them into the primary router, and it does the rest. You can add more nodes the same way; just repeat the steps above.
### Features
Out of the box, FreeMesh runs a combination of OpenWRT and LuCI. It has all the features you'd expect from a router. Want to install new features or packages? SSH in and start hacking!
![Real-time load on FreeMesh network][10]
![Overview of FreeMesh network][11]
![OpenWrt status report][12]
### Performance
After setting up the FreeMesh system, I moved the nodes to various places around my house. I used [iPerf][13] to test the bandwidth and was getting around 150Mbps. WiFi can be affected by any number of environmental variables, so your mileage may vary. Distance between the nodes and the primary router also plays a large factor in bandwidth.
However, the real advantage of a mesh network isn't its top-end speed but much better average speed across a space. Even at the far reaches of my home, I was still able to stream videos and work without interruption. I was even able to work in my backyard. I simply repositioned one of the nodes in front of a window before heading outside.
### Conclusion
FreeMesh is really compelling; it offers performance, privacy, and price, all in a simple, open source package.
In my experience, setup is a breeze, and it is more than fast enough. The range is excellent and far exceeds any single-router setup. You are free to hack and customize your FreeMesh setup, but I didn't feel the need to. It has everything I need out of the box.
If you are looking for an affordable, performant, privacy-respecting mesh system that installs in less than 10 minutes, you might want to consider FreeMesh.
--------------------------------------------------------------------------------
via: https://opensource.com/article/20/2/mesh-network-freemesh
作者:[Spencer Thomason][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/spencerthomason
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/bus-networking.png?itok=fHmulI9p (people on top of a connected globe)
[2]: https://freemeshwireless.com/
[3]: https://opensource.com/sites/default/files/uploads/singlerouternetwork.png (Single-router network)
[4]: https://opensource.com/sites/default/files/uploads/meshnetwork.png (Mesh network)
[5]: https://freemeshwireless.com/#pricing
[6]: https://openwrt.org/
[7]: https://gitlab.com/slthomason/freemesh/-/blob/master/README.md
[8]: https://opensource.com/sites/default/files/uploads/connecttorouter.png (FreeMesh setup step 1)
[9]: https://opensource.com/sites/default/files/uploads/setupcomplete.png (FreeMesh setup step 2)
[10]: https://opensource.com/sites/default/files/uploads/freemeshrealtimeload.png (Real-time load on FreeMesh network)
[11]: https://opensource.com/sites/default/files/uploads/freemeshwirelessoverview.png (Overview of FreeMesh network)
[12]: https://opensource.com/sites/default/files/uploads/openwrt.png (OpenWrt status report)
[13]: https://opensource.com/article/20/1/internet-speed-tests

View File

@ -0,0 +1,189 @@
[#]: collector: (lujun9972)
[#]: translator: (mengxinayan)
[#]: reviewer: ( )
[#]: publisher: ( )
[#]: url: ( )
[#]: subject: (How to structure a multi-file C program: Part 1)
[#]: via: (https://opensource.com/article/19/7/structure-multi-file-c-part-1)
[#]: author: (Erik O'Shaughnessy https://opensource.com/users/jnyjnyhttps://opensource.com/users/jnyjnyhttps://opensource.com/users/jim-salterhttps://opensource.com/users/cldxsolutions)
如何组织构建多文件 C 语言程序:第一部分
======
准备好你喜欢的饮料,编辑器和编译器,放一些音乐,然后开始构建一个由多个文件组成的 C 语言程序。
![Programming keyboard.][1]
大家常说计算机编程的艺术是管理复杂性和命名某些事物中的一部分。此外,我认为“有时需要添加绘图”是很正确的。
在这篇文章里,我会在编写一个小型 C 程序时命名一些东西同时管理一些复杂性。程序的结构基于我所在 “[如何写一个好的 C 语言 main 函数][2]” 文中讨论的。但是,这次做一些不同的事。准备好你喜欢的饮料,编辑器和编译器,放一些音乐,让我们一起编写一个有趣的 C 语言程序。
### 好的 Unix 程序中的哲学
首先你要知道这个 C 程序是一个 [Unix][3] 命令行工具。这意味着它运行在那些提供(或者可被移植) Unix C 运行环境的操作系统中。当 Unix 在贝尔实验室被发明后,它从一开始便充满了 [设计哲学][4]。用我的话来说:程序只做一件事,并做好它,然后对文件进行一些操作。虽然只做一件事并做好它是有意义的,但是“对文件进行一些操作”在这儿有一点儿不合适。
“文件” 在 Unix 中的抽象非常强大。一个 Unix 文件是以 end-of-file (EOF) 标志为结尾的字节流。文件中其他结构均由应用添加而非操作系统。操作系统提供了系统调用,使得程序能够对文件执行标准操作:打开,读取,写入,寻找和关闭(还有其他,但那是庞大的额外内容)。对于文件的标准化访问使得不同人用不同语言编写的程序能共用相同的抽象同时一起工作。
具有共享文件接口可以构建 _可组合的_ 的程序。一个程序的输出可以作为另一个程序的输入。Unix 系操作系统默认为每个运行中程序提供了三个文件:标准输入(`stdin`),标准输出(`stdout`),和标准错误(`stderr`)。其中两个文件是只写的:`stdout` 和 `stderr`。而 `stdin` 是只读的。当我们在常见的 Shell 比如 Bash 中使用文件重定向时,可以看到其效果。
```
`$ ls | grep foo | sed -e 's/bar/baz/g' > ack`
```
这条指令可以被简要地描述为:`ls` 的结果被写入标准输出,它重定向到 `grep` 的标准输入,`grep` 的标准输出重定向到 `sed`的标准输入,`sed` 的标准输出重定向到当前目录下文件名为 `ack` 的文件中。
我们希望我们的程序在系统中灵活而又出色,因此让我们编写一个可以读写文件的程序。
### MeowMeow: 流编码器/解码器概念
当我还是一个孩子在 ltmumblesgts 里学习计算机科学时,有许多编码方案。他们中的有些用于压缩文件,有些用于打包文件,另一些毫无用处因此显得十分愚蠢。列举一个最后一种情况例子:[MooMoo 编码方式][5]。
为了给我们程序一个目的,我将在 [2000s][6] 更新该概念并且完成一个名为 “MeowMeow” 的编码方式(因为在互联网上大家都喜欢猫)。这里的基本的想法获取文件并且使用文本 “meow” 对每半个字节进行编码。小写字母代表 0大写字母代表 1。因为它会将 4 比特替换为 32 比特,因此会扩大文件的大小。这毫无实际意义。但想象一下人们看到经过这样编码后的结果。
```
$ cat /home/your_sibling/.super_secret_journal_of_my_innermost_thoughts
MeOWmeOWmeowMEoW...
```
这非常棒。
### 最后完成
完整的源代码可以在 [GitHub][7] 上面找到,但是我会写下我在编写程序时的思考。目的是说明如何组织构建多文件 C 语言程序。
当我已经确定要编写一个 MeowMeow 编码和解码的程序时,我在 Shell 中执行了以下的命令
```
$ mkdir meowmeow
$ cd meowmeow
$ git init
$ touch Makefile     # recipes for compiling the program
$ touch main.c       # handles command-line options
$ touch main.h       # "global" constants and definitions
$ touch mmencode.c   # implements encoding a MeowMeow file
$ touch mmencode.h   # describes the encoding API
$ touch mmdecode.c   # implements decoding a MeowMeow file
$ touch mmdecode.h   # describes the decoding API
$ touch table.h      # defines encoding lookup table values
$ touch .gitignore   # names in this file are ignored by git
$ git add .
$ git commit -m "initial commit of empty files"
```
简单的说,我创建了一个空文件并且使用 git 提交。
In short, I created a directory full of empty files and committed them to git.
即使文件中没有内容,你依旧可以从它的文件名推断功能。为了避免万一你无法理解,我在每条 `touch` 命令后面进行了简单描述。
通常,一个程序从一个简单 `main.c` 文件开始,只需要两三个函数便可以解决问题。然后程序员便可以向自己的朋友或者老板展示该程序,同时突然显示了文件提示框支持所有新的“功能”和“需求”。“程序俱乐部”的第一条规则便是不谈论“程序俱乐部”。第二条规则是最小化单个文件的功能。
坦率地说C 编译器并不关心程序中的所有函数是否都在一个文件中。但是我们并不是为计算机或编译器写程序,我们是为其他人(有时也包括我们)而去写程序的。我知道这有些奇怪,但这就是事实。程序是计算机解决问题所采用的一系列算法,保证人们可以理解它们是非常重要的,即使问题的参数发生了意料之外的变化。当在人们修改程序时,发现一个文件中有 2049 函数时会诅咒你的。
因此,好的程序员会将函数分隔开,将相似的函数分组到不同的文件中。这里我用了三个文件 `main.c``mmencode.c` 和 `mmdecode.c`。对于这样的小程序,也许看起来有些过头了。但是小程序很难保证一直小下去,因此计划拓展是一个好主意。
但是那些 `.h` 文件呢?我会在后面解释一般的术语,简单地说,它们被称为头文件,同时它们可以包含 C 语言类型 和 C 预处理指令。头文件中不应该包含任何函数。你可以认为头文件和对应 `.c` 文件提供了用户编程接口API的定义以便其他 `.c` 文件使用。
### 但是 Makefile 是什么呢?
我知道所有的酷小孩都使用 “Ultra CodeShredder 3000” 集成开发环境来编写下一个轰动一时的应用,同时构建你的项目包括在 Ctrl-Meta-Shift-Alt-Super-B 上进行混搭。但是回到今天,使用 Makefile 文件可以帮助做很多有用的工作在构建 C 程序时。Makefile 是一个包含如何处理文件的方式的文本文件,程序员可以使用其自动地从源代码构建二进制程序(包括其他东西!)
以下面这个小程序为例:
```
00 # Makefile
01 TARGET= my_sweet_program
02 $(TARGET): main.c
03    cc -o my_sweet_program main.c
```
# 符号后面的文本是注释,例如 00 行
01 行是一个变量赋值,将 `TARGET` 变量赋值为字符串 `my_sweet_program`。按照惯例我的习惯是,所有 Makefile 变量均使用大写字母并用下划线分隔单词。
02 行包含要创建的文件名和其依赖的文件。在本例中,构建目标是 `my_sweet_program`,其依赖是 `main.c`
03 行是最后一行使用了一个制表符号tab而不是四个空格。这是将执行创建目标的命令。在本例中我们使用 C 编译器前端 `cc` 以编译链接到 `my_sweet_program`
使用 Makefile 是非常简单的。
```
$ make
cc -o my_sweet_program main.c
$ ls
Makefile  main.c  my_sweet_program
```
将构建我们 MeowMeow 编码和解码器的 [Makefile][8] 比上面的例子要复杂,但其基本结构是相同的。我将在另一篇文章中将其分解为 Barney 风格。
### 形式伴随着功能
我的想法是程序从一个文件中读取,转换它,并将转换后的结果存储到另一个文件中。以下是我想象使用程序命令行交互时的情况:
```
        $ meow < clear.txt > clear.meow
        $ unmeow < clear.meow > meow.tx
        $ diff clear.txt meow.tx
        $
```
我们需要编写命令行解析和处理输入/输出流的代码。我们需要一个函数对流进行编码并将结果写到另一个流中。最后,我们需要一个函数对流进行解码并将结果写到另一个流中。等一下,我们在讨论如何写一个程序,但是在上面的例子中,我调用了两个指令:`meow` 和 `unmeow`?我知道你可能会认为这会导致越变越复杂。
### 次要内容argv[0] 和 ln 指令
回想一下C 语言 main 函数的结构如下:
```
`int main(int argc, char *argv[])`
```
其中 `argc` 是命令行参数数量,`argv` 是字符指针列表(字符串)。`argv[0]` 是正在运行中的文件的路径。在 Unix 系统中许多互补功能的程序(比如:压缩和解压缩)看起来像两个命令,但事实上,它们在文件系统中是拥有两个名称的一个程序。使用 `ln` 命令通过创建文件系统链接来实现两个名称的功能。
一个在我笔记本中 `/usr/bin` 的例子如下:
```
   $ ls -li /usr/bin/git*
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git
3376 -rwxr-xr-x. 113 root root     1.5M Aug 30  2018 /usr/bin/git-receive-pack
...
```
这里 `git``git-receive-pack` 是同一个文件但是拥有不同的名字。我们说它们是相同的文件因为它们具有相同的 inode 值第一列。inode 是一个 Unix 文件系统的特点,其超越了本文的内容。
优秀或懒惰的程序可以通过 Unix 文件系统此特点已达到写更少的代码但是交付双倍的程序。首先,我们编写一个基于其 `argv[0]` 的值而作出相应改变的程序,然后我们确保为该行为的名称创建链接。
在我们的 Makefile 中,`unmeow` 链接通过以下的方式来创建:
```
 # Makefile
 ...
 $(DECODER): $(ENCODER)
         $(LN) -f $< $@
        ...
```
我喜欢在 Makefile 中将所有内容参数化,很少使用 “裸” 字符串。我将所有的定义都放置在 Makefile 文件顶部,以便可以简单地找到并改变它们。当您尝试将程序移植到新的平台上时,需要将 `cc` 改变为 `xcc`时,这会产生很大影响。
除了两个内置变量 `$@``$&lt` 之外,其余的变量显得很简单的。第一个便是创建目标的快捷方式,在本例中,`$(DECODER)` (我记忆它因为它看起来像一个目标)。第二个,`$&lt` 是规则依赖项,在本例中,它解析为 `$(ENCODER)`
事情当然在变得复杂,但是它易于管理。
--------------------------------------------------------------------------------
via: https://opensource.com/article/19/7/structure-multi-file-c-part-1
作者:[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/jnyjnyhttps://opensource.com/users/jnyjnyhttps://opensource.com/users/jim-salterhttps://opensource.com/users/cldxsolutions
[b]: https://github.com/lujun9972
[1]: https://opensource.com/sites/default/files/styles/image-full-size/public/lead-images/programming_keyboard_coding.png?itok=E0Vvam7A (Programming keyboard.)
[2]: https://opensource.com/article/19/5/how-write-good-c-main-function
[3]: https://en.wikipedia.org/wiki/Unix
[4]: http://harmful.cat-v.org/cat-v/
[5]: http://www.jabberwocky.com/software/moomooencode.html
[6]: https://giphy.com/gifs/nyan-cat-sIIhZliB2McAo
[7]: https://github.com/JnyJny/meowmeow
[8]: https://github.com/JnyJny/meowmeow/blob/master/Makefile