# 练习10:字符串数组和循环
> 原文:[Exercise 10: Arrays Of Strings, Looping](http://c.learncodethehardway.org/book/ex10.html)
> 译者:[飞龙](https://github.com/wizardforcel)
你现在可以创建不同类型的数组,并且也知道了“字符串”和“字节数组”是相同的东西。接下来,我们要更进一步,创建一个包含字符串的数组。我也会介绍第一个循环结构,`for`循环来帮我们打印出这一新的数据结构。
这一章的有趣之处就是你的程序中已经有一个现成的字符串数组,`main`函数参数中的`char *argv[]`。下面这段代码打印出了所有你传入的命令行参数:
```c
#include <stdio.h>
int main(int argc, char *argv[])
{
int i = 0;
// go through each string in argv
// why am I skipping argv[0]?
for(i = 1; i < argc; i++) {
printf("arg %d: %s\n", i, argv[i]);
}
// let's make our own array of strings
char *states[] = {
"California", "Oregon",
"Washington", "Texas"
};
int num_states = 4;
for(i = 0; i < num_states; i++) {
printf("state %d: %s\n", i, states[i]);
}
return 0;
}
```
`for`循环的格式是这样的:
```c
for(INITIALIZER; TEST; INCREMENTER) {
CODE;
}
```
下面是`for`循环的工作机制:
+ `INITIALIZER`中是用来初始化循环的代码,这个例子中它是`i = 0`。
+ 接下来会检查`TEST`布尔表达式,如果为`false`(0)则跳过`CODE`,不做任何事情。
+ 执行`CODE`,做它要做的任何事情。
+ 在`CODE`执行之后会执行`INCREMENTER`部分,通常情况会增加一些东西,比如这个例子是`i++`。
+ 然后跳到第二步继续执行,直到`TEST`为`false`(0)为止。
例子中的`for`循环使用`argc`和`argv`,遍历了命令行参数,像这样:
+ OS将每个命令行参数作为字符串传入`argv`数组,程序名称`./ex10`在下标为0的位置,剩余的参数紧随其后。
+ OS将`argc`置为`argv`数组中参数的数量,所以你可以遍历它们而不会越界。要记住如果你提供了一个参数,程序名称是第一个,参数应该在第二个。
+ 接下来程序使用`i < argc`测试`i`是否使用`argc`,由于最开始`1 < 2`,测试通过。
+ 之后它会执行代码,输出`i`,并且将`i`用做`argv`的下标。
+ 然后使用`i++`来运行自增语句,它是`i = i + 1`的便捷形式。
+ 程序一直重复上面的步骤,直到`i < argc`值为`false`(0),这时退出循环但程序仍然继续执行。
## 你会看到什么
你需要用两种方法运行它来玩转这个程序。第一种方法是向命令行参数传递一些东西来设置`argc`和`argv`。第二种是不传入任何参数,于是你可以看到第一次的`for`循环没有被执行,由于`i < argc`值为`false`。
## 理解字符串数组
你应该可以从这个练习中弄明白,你在C语言中通过混合`char *str = "blah"`和`char str[] = {'b','l','a','h'}`语法构建二维数组来构建字符串数组。第十四行的`char *states[] = {...}`语法就是这样的二维混合结构,其中每个字符串都是数组的一个元素,字符串的每个字符又是字符串的一个元素。
感到困惑吗?多维的概念是很多人从来都不会去想的,所以你应该在纸上构建这一字符串数组:
+ 在纸的左边为每个字符串画一个小方格,带有它们的下标。
+ 然后在方格上方写上每个字符的下标。
+ 接着将字符串中的字符填充到方格内。
+ 画完之后,在纸上模拟代码的执行过程。
理解它的另一种方法是在你熟悉的语言,比如Python或Ruby中构建相同的结构。
## 如何使它崩溃
+ 使用你喜欢的另一种语言,来写这个程序。传入尽可能多的命令行参数,看看是否能通过传入过多参数使其崩溃。
+ 将`i`初始化为0看看会发生什么。是否也需要改动`argc`,不改动的话它能正常工作吗?为什么下标从0开始可以正常工作?
+ 将`num_states`改为错误的值使它变大,来看看会发生什么。
## 附加题
+ 弄清楚在`for`循环的每一部分你都可以放置什么样的代码。
+ 查询如何使用`','`(逗号)字符来在`for`循环的每一部分中,`';'`(分号)之间分隔多条语句。
+ 查询`NULL`是什么东西,尝试将它用做`states`的一个元素,看看它会打印出什么。
+ 看看你是否能在打印之前将`states`的一个元素赋值给`argv`中的元素,再试试相反的操作。
- 笨办法学C 中文版
- 前言
- 导言:C的笛卡尔之梦
- 练习0:准备
- 练习1:启用编译器
- 练习2:用Make来代替Python
- 练习3:格式化输出
- 练习4:Valgrind 介绍
- 练习5:一个C程序的结构
- 练习6:变量类型
- 练习7:更多变量和一些算术
- 练习8:大小和数组
- 练习9:数组和字符串
- 练习10:字符串数组和循环
- 练习11:While循环和布尔表达式
- 练习12:If,Else If,Else
- 练习13:Switch语句
- 练习14:编写并使用函数
- 练习15:指针,可怕的指针
- 练习16:结构体和指向它们的指针
- 练习17:堆和栈的内存分配
- 练习18:函数指针
- 练习19:一个简单的对象系统
- 练习20:Zed的强大的调试宏
- 练习21:高级数据类型和控制结构
- 练习22:栈、作用域和全局
- 练习23:认识达夫设备
- 练习24:输入输出和文件
- 练习25:变参函数
- 练习26:编写第一个真正的程序
- 练习27:创造性和防御性编程
- 练习28:Makefile 进阶
- 练习29:库和链接
- 练习30:自动化测试
- 练习31:代码调试
- 练习32:双向链表
- 练习33:链表算法
- 练习34:动态数组
- 练习35:排序和搜索
- 练习36:更安全的字符串
- 练习37:哈希表
- 练习38:哈希算法
- 练习39:字符串算法
- 练习40:二叉搜索树
- 练习41:将 Cachegrind 和 Callgrind 用于性能调优
- 练习42:栈和队列
- 练习43:一个简单的统计引擎
- 练习44:环形缓冲区
- 练习45:一个简单的TCP/IP客户端
- 练习46:三叉搜索树
- 练习47:一个快速的URL路由
- 后记:“解构 K&R C” 已死
- 捐赠名单