4.1 基本使用方法
4.1.1 以函数返回值之外的方式来返回值
大型程序中,经常需要通过返回值返回程序处理的状态(比如是否成功,如果失败,还需要返回失败的原因)。
如果将指针作为参数传递给函数,此后在函数内部对指针指向的对象填充内容,就可以从函数返回多个值。
#include<stdio.h>
void func(int *a,double *b) {
*a=5;
*b=3.5;
}
int main() {
int a;
double b;
func(&a,&b);
printf("a..%d b..%f\n",a,b);
return 0;
}
将指向int和double的指针传递给函数,此后在函数内部对这两个指针指向的变量设定值。
要点:如果需要通过函数返回值以外的方式返回值,将”指向T的指针“作为参数传递给函数。
4.1.2 将数组作为函数的参数传递
C语言中,数组是不能作为参数进行传递的。但可以通过传递指向数组初始元素的指针,使得在函数内部操作数组称为可能。
#include<stdio.h>
void func(int *array,int size) {
int i;
for(i=0;i<size;i++) {
printf("array[%d]..%d\n",i,array[i]);
}
}
int main() {
int array[]={1,2,3,4,5};
func(array,sizeof(array)/sizeof(int));
return 0;
}
要点:想要将类型T的数组作为参数进行传递,可以考虑传递”指向T的指针“。可是,作为被调用方是不知道数组的元素个数的,所以在必要条件下,需要使用其他方式进行参数传递。
4.1.3 可变长数组
通常C语言在编译时必须知道数组的元素个数,但也可以使用malloc()在运行时再为数组申请必要的内存区域。
这种数组称之为”可变长数组“。
#include<stdio.h>
#include<stdlib.h>
int main() {
char buf[256];
int size;
int *variable_array;
int i;
printf("Input array size");
fgets(buf,256,stdin);
sscanf(buf,"%d",&size);
variable_array=malloc(sizeof(int)*size);
for(i=0;i<size;i++) {
variable_array[i]=i;
}
for(i=0;i<size;i++) {
printf("variable_array[%d]..%d\n",i,variable_array[i]);
}
return 0;
}
必须注意,在使用malloc()实现可变长数组的时候,程序员必须自己来管理数组的元素个数。
要点:在需要获得类型T的可变长数组时,可以使用malloc()来动态地给”指向T的指针“分配内存区域。但此时需要程序员自己对数组的元素个数进行管理。
补充:Java中的数组只能使用内存堆区域。Java的数组和C中使用的malloc()分配的数组,有决定性的不同,Java的数组是知道自身的长度的。此外,尽管Java的数组是保存在堆中,但却不能改变长度,没有类似于realloc()的函数。
4.2 组合使用
4.2.1 可变长数组的数组 P166~P172
4.2.2 可变长数组的可变长数组 P172~P174
4.2.3 命令行参数
对于main()函数,标准中指出必须要写出以下两种方式之一:
int main(void);、int main(int argc,char *argv[]);
正常使用第一种形式,如果使用第二种形式,可以取得命令行参数。如在UNIX中的cat命令-用于输出文件的内容。 cat hoge.txt (命令名后是想要输出的文件名)
如果像cat hoge.txt piyo.txt 输出就是将hoge.txt 和piyo.txt两个文件的内容连接后所得的结果。
对于第二种形式,其实可以和下面这种形式是完全一样的。
int main(int argc,char **argv);
argv[0]保存了命令名自身。在程序输出错误提示信息时,或者需要通过命令名称改变程序的行为时,会经常使用arg[0]。
argc保存了参数的个数(包含了arg[0])。实际上,从ANSI C之后,会保证argv[argc]肯定为NULL,所以完全可以没有argc。
4.2.4 通过参数返回指针
要点:异常处理中使用goto,反而让程序更加简洁。
C++/java中具备异常处理机制的语言,可以不用goto、在C中也有setjmp()/longjmp(),使用它们也能达到相似的效果。
4.2.5 将多维数组作为函数的参数传递
#include<stdio.h>
#include<stdlib.h>
void func(int (*hoge)[3]) {
int i,j;
for(i=0;i<4;i++) {
for(j=0;j<3;j++) {
printf("%d ",hoge[i][j]);
}
putchar('\n');
}
}
int main(void) {
int hoge[][3]={
{1,2,3},
{4,5,6},
{4,5,6},
{7,8,9},
{10,11,12},
};
func(hoge);
return 0;
}
4.2.6 数组的可变长数组
4.2.7 考虑使用结构体
补充:Java和C一样不存在多维数组,也是使用数组的数组来实现多维数组。但是Java和C不同的是,Java的数组都是指针,所以所谓的"数组的数组"其实是”指向数组的指针的数组“。
Java中的类(相当于C中的结构体)也是保存在堆中的,并且只能利用指针进行操作。因此Java中没有类似C中"结构体数组"这样对象。
阻碍Java程序快速执行的最大原因是Java过多使用了堆操作。
C++的对象可以不通过指针,而是通过实体来操作。为实现这个特性,C++使用了带参数的构造方法以及继承的概念,导致C++编程变得非常复杂。
4.3 违反标准的技巧
4.3.1 可变长结构体
4.3.2 从1开始的数组
C语言中,对指向超出数组范围以外的指针,除非它指向”最后元素的下一个元素“,其他情形都属于未定义。
补充:指针可以指到数组的最后元素的下一个元素。
for(p=&array[0];p<&array[SPAN];p++)
这里没有使用循环计数器,而是使用指针遍历数组的各元素。当循环到达终点时,p指向&array[SPAN],也就是array的最后元素的下一个元素。
只是为了照顾以前写的代码,标准才允许指针可以指到数组的”最后元素的下一个元素“。