5.9 Pointers vs. Multi-dimensional Arrays
Newcomers to C are sometimes confused about the difference between a two-dimensional array and an array of pointers, such as name in the example above. Given the definitions
对于C 语言的初学者来说,很容易混淆二维数组与指针数组之间的区别,比如上面例子中的name。假如有下面两个定义:
int a[10][20];
int *b[10];
then a[3][4] and b[3][4] are both syntactically legal references to a single int. But a is a true two-dimensional array: 200 int-sized locations have been set aside, and the conventional rectangular subscript calculation 20 * row +col is used to find the element a[row,col]. For b, however, the definition only allocates 10 pointers and does not initialize them; initialization must be done explicitly, either statically or with code. Assuming that each element of b does point to a twenty-element array, then there will be 200 ints set aside, plus ten cells for the pointers. The important advantage of the pointer array is that the rows of the array may be of different lengths. That is, each element of b need not point to a twenty-element vector; some may point to two elements, some to fifty, and some to none at all.
那么,从语法角度讲,a[3][4]和b[3][4]都是对一个int 对象的合法引用。但a 是一个真正的二维数组,它分配了200 个int 类型长度的存储空间,并且通过常规的矩阵下标计算公式20×row+col(其中,row表示行,col表示列)计算得到元素a[row][col]的位置。但是,对b来说,该定义仅仅分配了10个指针,并且没有对它们初始化,它们的初始化必须以显式的方式进行,比如静态初始化或通过代码初始化。假定b的每个元素都指向一个具有20 个元素的数组,那么编译器就要为它分配200 个int类型长度的存储空间以及10 个指针的存储空间。指针数组的一个重要优点在于,数组的每一行长度可以不同,也就是说,b的每个元素不必都指向一个具有20个元素的向量,某些元素可以指向具有2个元素的向量,某些元素可以指向具有50个元素的向量,而某些元素可以不指向任何向量。
Although we have phrased this discussion in terms of integers, by far the most frequent use of arrays of pointers is to store character strings of diverse lengths, as in the function month_name. Compare the declaration and picture for an array of pointers:
尽管我们在上面的讨论中都是借助于整型进行讨论,但到目前为止,指针数组最频繁的用处是存放具有不同长度的字符串,比如函数month_name中的情况。结合下面的声明和图形化描述,我们可以做一个比较。下面是指针数组的声明和图形化描述(参见图5-9):
char *name[] = { "Illegal month", "Jan", "Feb", "Mar" };
with those for a two-dimensional array:
下面是二维数组的声明和图形化描述(参见图5-10):
char aname[][15] = { "Illegal month", "Jan", "Feb", "Mar" };
5.10 Command-line Arguments
In environments that support C, there is a way to pass command-line arguments or parameters to a program when it begins executing. When main is called, it is called with two arguments. The first (conventionally called argc, for argument count) is the number of command-line arguments the program was invoked with; the second (argv, for argument vector) is a pointer to an array of character strings that contain the arguments, one per string. We customarily use multiple levels of pointers to manipulate these character strings.
在支持C 语言的环境中,可以在程序开始执行时将命令行参数传递给程序。调用主函数main 时,它带有两个参数。第一个参数(习惯上称为argc,用于参数计数)的值表示运行程序时命令行中参数的数目;第二个参数(称为argv,用于参数向量)是一个指向字符串数组的指针,其中每个字符串对应一个参数。我们通常用多级指针处理这些字符串。
The simplest illustration is the program echo, which echoes its command-line arguments on a single line, separated by blanks. That is, the command
最简单的例子是程序echo,它将命令行参数回显在屏幕上的一行中,其中命令行中各参数之间用空格隔开。也就是说,命令
echo hello, world
prints the output
hello, world
By convention, argv[0] is the name by which the program was invoked, so argc is at least 1. If argc is 1, there are no command-line arguments after the program name. In the example above, argc is 3, and argv[0], argv[1], and argv[2] are "echo", "hello,", and "world" respectively. The first optional argument is argv[1] and the last is argv[argc-1]; additionally, the standard requires that argv[argc] be a null pointer.
按照C 语言的约定,argv[0]的值是启动该程序的程序名,因此argc 的值至少为1。如果argc的值为1,则说明程序名后面没有命令行参数。在上面的例子中,argc的值为3,argv[0]、argv[1]和argv[2]的值分别为“echo”、“hello,”,以及“world”。第一个可选参数为argv[1],而最后一个可选参数为argv[argc-1]。另外,ANSI 标准要求argv[argc]的值必须为一空指针(参见图5-11)。
The first version of echo treats argv as an array of character pointers:
程序 echo的第一个版本将argv看成是一个字符指针数组:
#include <stdio.h>
/* echo command-line arguments; 1st version */
main(int argc, char *argv[])
{
int i;
for (i = 1; i < argc; i++)
printf("%s%s", argv[i], (i < argc-1) ? " " : "");
printf("\n");
return 0;
}
Since argv is a pointer to an array of pointers, we can manipulate the pointer rather than index the array. This next variant is based on incrementing argv, which is a pointer to pointer to char, while argc is counted down:
因为argv是一个指向指针数组的指针,所以,可以通过指针而非数组下标的方式处理命令行参数。echo程序的第二个版本是在对argv进行自增运算、对argc进行自减运算的基础上实现的,其中argv是一个指向char类型的指针的指针:
#include <stdio.h>
/* echo command-line arguments; 2nd version */
main(int argc, char *argv[])
{
while (--argc > 0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");
return 0;
}
Since argv is a pointer to the beginning of the array of argument strings, incrementing it by 1 (++argv) makes it point at the original argv[1] instead of argv[0]. Each successive increment moves it along to the next argument; *argv is then the pointer to that argument. At the same time, argc is decremented; when it becomes zero, there are no arguments left to print.
因为argv是一个指向参数字符串数组起始位置的指针,所以,自增运算(++argv)将使得它在最开始指向argv[1]而非argv[0]。每执行一次自增运算,就使得argv指向下一个参数,*argv就是指向那个参数的指针。与此同时,argc执行自减运算,当它变成0 时,就完成了所有参数的打印。
argv[0]如下图所示
Alternatively, we could write the printf statement as
printf((argc > 1) ? "%s " : "%s", *++argv);
This shows that the format argument of printf can be an expression too.
这就说明,printf的格式化参数也可以是表达式。