C语言学习教程(四):本系列教程第12-14章。
函数
是一组一起执行一个任务的语句。每个 C 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数。
你可以把代码划分成单独的函数中。如何在不同的函数之间划分代码由你自己决定,但在逻辑上,划分通常是根据每个函数执行一个特定的任务来进行的。
函数声明
(function declaration)告诉编译器函数的名称
、返回值类型
和参数
。
函数定义
(function definition)提供函数的实际主体。
C 标准库提供了大量的程序可以调用的内置函数。例如,函数strcat()
用来连接两个字符串,函数memcpy()
用来复制一个内存位置到另一个位置。
函数还有很多叫法,比如方法、子例程或程序,等等。
C语言中,函数定义的一般形式如下:
1
2
3
4
|
return_type function_name( parameter list )
{
body of the function;
}
|
在 C 语言中,函数定义由一个函数头
和一个函数主体
组成。下面列出一个函数的所有组成部分:
- 返回值类型(return_type):一个函数可以返回一个值。return_type是函数返回值的数据类型。有些函数执行所需的操作而不返回值,在这种情况下,return_type是关键字void。
- 函数名称(function_name):这是函数的实际名称。函数名和参数列表一起构成了函数签名。
- 参数列表(parameter list):参数就像是占位符。当函数被调用时,直接向参数传递一个值,这个值被称为实际参数。参数列表包括函数参数的类型、顺序、数量。参数是可选的,也就是说,函数可以不包含参数。
- 函数主体(Function Body):函数主体包含一组定义语句,这些语句定义了函数的功能和作用。
函数示例:
以下是max()
数的源代码。该函数有两个参数 num1 和 num2,max()
函数会返回这两个数中较大的那个数:
1
2
3
4
5
6
7
8
9
10
|
int max(int num1, int num2){
int result;
if(num1 > num2){
result = num1;
}else{
result = num2;
}
return result;
}
|
函数声明
会告诉编译器函数名称及如何调用函数。函数的实际主体可以单独定义。
函数声明包括以下几个部分:
1
|
return_type function_name( parameter list );
|
针对上面定义的函数max(),以下是该函数的函数声明:
1
|
int max(int num1, int num2);
|
注意:在函数声明中,参数的名称并不重要,只有参数的类型是必需的,因此下面也是有效的声明:
当在一个源文件中定义函数并在另一个文件中调用该函数时,函数声明是必需的。在这种情况下,应该在调用函数的文件顶部声明该函数。
在创建 C 函数时,会定义该函数要做什么。要使用该函数,就必须调用该函数来执行已定义的任务。
当程序调用函数时,程序控制权会转移给被调用的函数。被调用的函数执行已定义的任务,当函数的返回语句被执行时,或到达函数的结束括号时,会把程序控制权交还给主程序。
要调用一个函数,你只需要传递所需的参数和函数名。如果函数返回一个值,那么你可以存储这个返回值。
示例代码:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <stdio.h>
int max(int num1, int num2); //函数声明
int max(int num1, int num2){ //函数定义
int result;
if(num1 > num2){
result = num1;
}else{
result = num2;
}
return result;
}
int main(){
int a = 100;
int b = 200;
int ret;
ret = max(a, b); //函数调用
printf("Max value is: %d.\n", ret);
return 0;
}
|
运行结果:
1
2
3
|
$ gcc -o test1 test1.c
$ ./test1
Max value is: 200.
|
如果一个函数要使用参数,就必须声明接受参数值的变量。这些变量称为函数的形式参数
(formal parameters)。
形式参数
就像函数内的其他局部变量,在进入函数时被创建,退出函数时被销毁。
当调用函数时,有两种向函数传递参数的方式:按值调用函数
(Function call by value)和按引用调用函数
(Function call by reference)。
按值调用函数(Function call by value)
将参数传递给函数的按值调用方法,它会将参数的实际值复制到函数的形式参数中。在这种情况下,对函数内部参数所做的更改对参数没有任何影响。
默认情况下,C 语言使用按值调用函数
方法来传递参数。一般来说,这意味着函数内的代码不能更改用于调用函数的实际参数。
函数swap()
的定义如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
#include <stdio.h>
void swap(int x, int y); //函数声明
void swap(int x, int y){ //函数定义
int temp;
temp = x; /* save the value of x */
x = y; /* put y into x */
y = temp; /* put x into y */
return;
}
int main(){
int a = 100;
int b = 200;
printf("Before swap, value of a is: %d.\n", a);
printf("Before swap, value of b is: %d.\n\n", b);
swap(a,b); //函数调用
printf("After swap, value of a is: %d.\n", a);
printf("After swap, value of b is: %d.\n", b);
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
|
$ gcc -o test2 test2.c
$ ./test2
Before swap, value of a is: 100.
Before swap, value of b is: 200.
After swap, value of a is: 100.
After swap, value of b is: 200.
|
上面的实例表明了,虽然在函数swap()内改变了 a 和 b 的值,但是实际上 a 和 b 的值没有发生变化。
按引用调用函数(Function call by reference)
将参数传递给函数的按引用调用方法,它会将参数的内存地址复制到形式参数中。在函数内部,内存地址用于访问函数调用中使用的实际参数。这意味着对函数内部参数所做的更改会影响传递的参数。
为了通过引用传递值,参数指针
会被传递给函数,就像任何其他值一样。因此,你需要将函数参数声明为指针类型,如下函数 swap() 中所示,该函数交换其参数指向的两个整数变量的值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
|
#include <stdio.h>
void swap(int *x, int *y);
void swap(int *x, int *y){
int temp;
temp = *x; //保存x处地址所指向的值
printf("x is %p\n", x); //x: 0x7ff7b17ed5d8
printf("*x is %d\n", *x); //*x:100
printf("temp is %d\n\n", temp); //temp: 100
*x = *y; /* put y into x */
*y = temp; /* put x into y */
return;
}
int main(){
int a = 100;
int b = 200;
printf("Before swap, value of a is: %d.\n", a);
printf("Before swap, value of b is: %d.\n\n", b);
swap(&a,&b); //函数调用
// &a 表示指向 a 的指针,即变量 a 的地址
// &b 表示指向 b 的指针,即变量 b 的地址
printf("After swap, value of a is: %d.\n", a);
printf("After swap, value of b is: %d.\n", b);
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
|
$ gcc -o test3 test3.c
$ ./test3
Before swap, value of a is: 100.
Before swap, value of b is: 200.
x is 0x7ff7b836c5d8
*x is 100
temp is 100
After swap, value of a is: 200.
After swap, value of b is: 100.
|
上面的实例表明了,与按值调用函数不同,按引用调用函数,在函数内部改变了 a 和 b 的值,实际上也改变了函数外 a 和 b 的值。
任何一种编程中,作用域
(Scope Rules)都是程序中定义的变量所存在的区域,超过该区域变量就不能被访问。
C 语言中有三个地方可以声明变量:
- 在函数或代码块内部的
局部变量
(local variables)
- 在所有函数之外的
全局变量
(global variables)
- 在函数参数定义中的
形式参数
(formal parameters)
在函数或代码块中声明的变量称为局部变量。它们只能由该函数或代码块内的语句使用。局部变量对于它们自身之外的函数是未知的。以下是使用局部变量的示例。
这里所有的变量 a、b 和 c 都是 main()函数内的局部变量。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#include <stdio.h>
int main(){
int a, b, c;
a = 10;
b = 20;
c = a + b;
printf("Value of a is: %d, b is: %d, c is: %d.\n", a, b, c);
return 0;
}
|
运行结果:
1
2
3
|
$ gcc -o test1 test1.c
$ ./test1
Value of a is: 10, b is: 20, c is: 30.
|
全局变量是在函数之外定义的,通常在程序的顶部。全局变量将在程序的整个生命周期中保持其值,并且可以在为程序定义的任何函数中访问它们。
任何函数都可以访问全局变量。也就是说,全局变量在声明后可在整个程序中使用。
以下是使用全局变量和局部变量的示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <stdio.h>
int g; //全局变量
int main(){
int a, b; //定义局部变量
a = 10; //局部变量实际初始化
b = 20; //局部变量实际初始化
g = a + b;
printf("Value of a is: %d, b is: %d, g is: %d.\n", a, b, g);
return 0;
}
|
运行结果:
1
2
3
|
$ gcc -o test2 test2.c
$ ./test2
Value of a is: 10, b is: 20, g is: 30.
|
程序可以对局部变量和全局变量使用相同的名称,但函数内的局部变量的值会被优先考虑。下面是一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
#include <stdio.h>
int g = 20; //定义全局变量并初始化
int main(){
int a, b; //定义局部变量
a = 10; //局部变量实际初始化
b = 20; //局部变量实际初始化
g = a + b;
printf("Value of a is: %d, b is: %d, g is: %d.\n", a, b, g);
return 0;
}
|
运行结果:
1
2
3
|
$ gcc -o test3 test3.c
$ ./test3
Value of a is: 10, b is: 20, g is: 30.
|
函数参数,即这里所谓的形式参数,在函数中会被视为局部变量,它将优先于全局变量。下面是一个例子:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
#include <stdio.h>
int a = 20;
int sum(int a, int b){
printf ("Value of a in sum() is: %d\n", a);
printf ("Value of b in sum() is: %d\n", b);
return a + b;
}
int main(){
int a = 10;
int b = 20;
int c = 0;
printf ("Value of a in main() is: %d\n", a);
c = sum(a, b);
printf ("Value of c in main() is: %d\n", c);
return 0;
}
|
运行结果:
1
2
3
4
5
6
|
$ gcc -o test4 test4.c
$ ./test4
Value of a in main() is: 10
Value of a in sum() is: 10
Value of b in sum() is: 20
Value of c in main() is: 30
|
全局变量与局部变量在内存中的区别:
- 全局变量保存在内存的全局存储区中,占用静态的存储单元。
- 局部变量保存在栈中,只有在所在函数被调用时才动态地为局部变量分配存储单元。
全局(静态)存储区:包括data段和bss段。
data段(全局初始化区)存放已初始化的全局变量和静态变量。
bss段(全局未初始化区)存放未初始化的全局变量和静态变量。bss段在程序执行之前会被系统自动清0,所以未初始化的全局变量和静态变量在程序执行之前已经为0。
存储在全局(静态)存储区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。
对于全局变量,如果在函数中修改了值,则全局变量的值就会被改变,在其他函数中访问的就是修改后的值。
如果在函数中定义了跟全局变量名称一样的局部变量,则函数中修改的是局部变量,全局变量的值是无法被修改的。
当局部变量被定义时,系统不会对其初始化,你必须自行对其初始化。
定义全局变量时,如果未手动初始化,系统就会自动对其进行初始化,如下所示:
Data Type |
Initial Default Value |
int |
0 |
char |
‘\0’ |
float |
0 |
double |
0 |
pointer |
NULL |
正确地初始化变量是一个良好的编程习惯,否则有时候程序可能会产生意想不到的结果,因为未初始化的变量将占用其内存位置已经可用的一些垃圾值。
C 编程语言提供了一种称为数组
的数据结构,它可以存储相同类型元素的固定大小顺序集合。
数组是用于存储数据的集合,但将数组视为相同类型变量的集合通常更有用。
数组的声明
并不是声明一个个单独的变量,比如number0、number1、 … 、number99。而是声明一个数组变量,例如 numbers。然后使用 numbers[0]、numbers[1]、…、numbers[99]来表示一个个单独的变量。
所有数组都由连续的内存位置组成。最低地址对应于第一个元素,最高地址对应于最后一个元素。
数组中的特定元素(element)可以通过索引(index)访问,第一个索引值为 0。
在 C 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:
1
|
type arrayName[arraySize];
|
这叫做一维数组
。arraySize必须是一个大于零的整数常量,type可以是任意有效的 C 数据类型。
例如,要声明一个数据类型为 double 的,包含 5 个元素的数组balance(余额),声明语句如下:
现在balance是一个可用的数组,它可以容纳 5 个类型为 double 的数字。
在 C 中,你可以逐个初始化数组,也可以使用一个初始化语句,如下所示:
1
|
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0}
|
大括号{ } 之间的值的数目不能大于我们在数组声明时在 方括号[ ] 中指定的元素数目arraySize。
如果初始化数组时省略掉了数组的大小,那么,数组的大小则为初始化时元素的个数。因此,如果:
1
|
double balance[] = {1000.0, 2.0, 3.4, 17.0, 50.0}
|
那么,它将创建一个包含 5 个元素的数组balance,它与前一个实例中所创建的数组是完全相同的。
下面是一个为数组中某个元素赋值的实例:
上述的语句把数组中第五个元素的值赋为50.0。所有的数组都是以0作为它们第一个元素的索引,0也被称为基索引,数组的最后一个索引是数组的总大小(arraySize)减去1。以下是上面所讨论的数组的的图形表示:
数组元素可以通过数组名称
加索引
的形式进行访问。元素的索引是放在方括号内的,跟在数组名称的后边。例如:
1
|
double salary = balance[3];
|
上面的语句将把数组中第 4 个元素的值赋给了 salary(薪水) 变量。
下面的第一个使用了上述的三个概念,即:声明数组、初始化数组、访问数组。
1
2
3
4
5
6
7
8
9
10
11
12
|
#include <stdio.h>
int main(){
int i;
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
//输出数组中每个元素的值
for(i = 0; i < 5; i++){
printf("balance[%d] is: %.1f\n", i, balance[i]);
}
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
|
$ gcc -o test1 test1.c
$ ./test1
balance[0] is: 1000.0
balance[1] is: 2.0
balance[2] is: 3.4
balance[3] is: 17.0
balance[4] is: 50.0
|
下面是第二个示例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
#include <stdio.h>
int main(){
int i, j;
int n[10]; //n是一个包含10个整数元素的数组
//初始化每一个数组元素
for(i = 0; i < 10; i++){
n[i] = 100 + i; //设置元素的值为100+i
}
//输出数组中的每一个元素
for(j = 0; j < 10; j++){
printf("n[%d] is: %d.\n", j, n[j]);
}
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
$ gcc -o test2 test2.c
$ ./test2
n[0] is: 100.
n[1] is: 101.
n[2] is: 102.
n[3] is: 103.
n[4] is: 104.
n[5] is: 105.
n[6] is: 106.
n[7] is: 107.
n[8] is: 108.
n[9] is: 109.
|
C语言支持多维数组(multidimensional arrays),下面是多维数组声明的一般形式:
1
|
type name[size1][size2]...[sizeN];
|
例如,下面声明创建了一个三维整数数组。
1
|
int threedim[5][10][4];
|
二维数组
多维数组最简单的形式是二维数组(two-dimensional array)。二维数组本质上是一维数组的列表。
要声明一个大小为x, y的二维整数数组,可以编写如下内容:
其中,type可以是任意有效的 C 数据类型,arrayName是一个有效的C标识符。一个二维数组可以被认为是一个带有x行和y列的表格。
下面是一个二维数组,包含 3 行和 4 列:
数组a中的每个元素都是由a[i][j]
形式的元素标识的,其中a是数组的名称,i和j是唯一标识a中每个元素的下标。
初始化二维数组
多维数组可以通过在括号内为每行指定值来进行初始化。下面是一个带有 3 行 4 列的数组。
1
2
3
4
5
|
int a[3][4] = {
{0, 1, 2, 3}, //初始化索引号为 0 的行
{4, 5, 6, 7}, //初始化索引号为 1 的行
{8, 9, 10, 11} //初始化索引号为 2 的行
};
|
内部嵌套的括号是可选的,下面的初始化与上面是等同的:
1
|
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
|
访问二维数组元素
二维数组中的元素是通过使用下标(即数组的行索引和列索引)来访问的。例如:
1
2
3
|
int a[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11}; //初始化二维数组a
int val = a[2][3]; //访问数组元素a[2][3]并将其赋值给变量val
|
下面的示例演示了如何通过嵌套循环来处理二维数组:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
#include <stdio.h>
int main(){
/*
int a[5][2] = {
{0, 0},
{1, 2},
{2, 4},
{3, 6},
{4, 8}
}; */
//int a[5][2] = {0, 0, 1, 2, 2, 4, 3, 6, 4, 8};
int a[5][2] = {{0, 0}, {1, 2}, {2, 4}, {3, 6}, {4, 8}};
int i, j;
for(i=0; i<5; i++){
for(j=0; j<2; j++){
printf("a[%d][%d] = %d\n", i, j, a[i][j]);
}
}
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
|
$ gcc -o test3 test3.c
$ ./test3
a[0][0] = 0
a[0][1] = 0
a[1][0] = 1
a[1][1] = 2
a[2][0] = 2
a[2][1] = 4
a[3][0] = 3
a[3][1] = 6
a[4][0] = 4
a[4][1] = 8
|
如上所述,你可以创建任意维度的数组,但是一般情况下,我们创建的数组是一维数组和二维数组。
如果想要在函数中传递一个一维数组作为参数,你必须用以下面三种方式来声明函数形式参数
,这三种声明方式的结果是一样的,因为每种方式都会告诉编译器将要接收一个整型指针。同样地,你也可以传递一个多维数组作为形式参数。
方法一:
形式参数是一个指针(这将在下一章中学习到有关指针的知识)。
1
2
3
4
|
void myFunction(int *param){
...
...
}
|
方法二:
形式参数是一个已定义大小的数组。
1
2
3
4
|
void myFunction(int param[10]){
...
...
}
|
方法三:
形式参数是一个未定义大小的数组。
1
2
3
4
|
void myFunction(int param[]){
...
...
}
|
下面是一个示例,函数getAverage()
,它将一个数组作为参数,同时还传递了另一个参数。根据所传的参数,函数会返回数组中元素的平均值。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
#include <stdio.h>
double getAverage(int arr[], int size);
double getAverage(int arr[], int size){
int i;
double avg;
double sum = 0;
for(i=0; i<size; i++){
sum += arr[i];
}
avg = sum / size;
return avg;
}
int main(){
double avg;
int balance[5] = {1000, 2, 3, 17, 50};
avg = getAverage(balance, 5);
printf("Average value is : %f\n", avg);
return 0;
}
|
运行结果:
1
2
3
|
$ gcc -o test4 test4.c
$ ./test4
Average value is : 214.400000
|
可以看到,就函数而言,数组的长度是无关紧要的,因为 C 不会对形式参数执行边界检查。
C语言不允许将整个数组作为参数返回给函数。但是,你可以通过指定不带索引的数组名称来返回指向数组的指针。你将在下一章学习指针,因此你可以跳过本章,直到你了解 C 中指针的概念。
如果要从函数返回一维数组,则必须声明一个返回指针的函数,如下例所示:
1
2
3
4
|
int * myFunction(){
...
...
}
|
另外,C语言不提倡将局部变量的地址返回到函数外部,因此您必须将局部变量定义为静态变量(static)。
因为局部变量是存储在内存的栈区内,当函数调用结束后,局部变量所占的内存地址便被释放了,因此当其函数执行完毕后,函数内的变量便不再拥有那个内存地址,所以不能返回其指针。
除非将其变量定义为 static 变量,static 变量的值存放在内存中的静态数据区,不会随着函数执行的结束而被清除,故能返回其地址。
现在,让我们来看下面的函数,它会生成 10 个随机数,并使用数组来返回它们,具体如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int * getRandom(){
static int value[10];
int i;
//设置种子(seed)
srand((unsigned)time(NULL));
//time(NULL):返回自格林威治标准时间00:00:00小时以来经过的秒数。
for(i=0; i<10; i++){
value[i] = rand();
printf("value[%d] = %d\n", i, value[i]);
}
return value;
}
int main(){
int *p;
int i;
p = getRandom();
printf("\n");
printf("P+n = 指针P的地址 + n*sizeof(指针的类型).\n");
for(i=0; i<10; i++){
printf("(p+%d) is : %p\n", i, p+i);
}
printf("\n");
for(i=0; i<10; i++){
printf("*(p+%d) is : %d\n", i, *(p+i));
}
return 0;
}
|
需要注意的是:
srand((unsigned)time(NULL))
的作用是是初始化种子(seed)。
①rand()和srand()要一起使用,其中srand()用来初始化随机数种子,rand()用来产生随机数。
②默认情况下随机数种子为1,而相同的随机数种子产生的随机数序列是一样的,这失去了随机性的意义,所以为了使每次得到的随机数不一样,就需要使用函数srand()初始化随机数种子。srand()的参数,一般用time函数值(即当前时间),因为每次调用rand()函数的时间通常是不同的,这样就可以保证随机性了。
③使用srand()函数时,参数可以是unsigned型的任意数据,比如srand(10)。
rand()和srand()的用法可以参考:https://blog.csdn.net/candyliuxj/article/details/4396666#:~:text=rand()%E5%92%8Csrand()%E8%A6%81%E4%B8%80%E8%B5%B7%E4%BD%BF%E7%94%A8%EF%BC%8C%E5%85%B6%E4%B8%ADsrand,()%E5%88%9D%E5%A7%8B%E5%8C%96%E9%9A%8F%E6%9C%BA%E6%95%B0%E7%A7%8D%E5%AD%90%E3%80%82
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
$ gcc -o test5 test5.c
$ ./test5
value[0] = 1251415447
value[1] = 84579011
value[2] = 2032747210
value[3] = 65018347
value[4] = 1841665353
value[5] = 1187783660
value[6] = 71991108
value[7] = 921258895
value[8] = 241153395
value[9] = 763467876
P+n = 指针P的地址 + n*sizeof(指针的类型).
(p+0) is : 0x10a44e030
(p+1) is : 0x10a44e034
(p+2) is : 0x10a44e038
(p+3) is : 0x10a44e03c
(p+4) is : 0x10a44e040
(p+5) is : 0x10a44e044
(p+6) is : 0x10a44e048
(p+7) is : 0x10a44e04c
(p+8) is : 0x10a44e050
(p+9) is : 0x10a44e054
*(p+0) is : 1251415447
*(p+1) is : 84579011
*(p+2) is : 2032747210
*(p+3) is : 65018347
*(p+4) is : 1841665353
*(p+5) is : 1187783660
*(p+6) is : 71991108
*(p+7) is : 921258895
*(p+8) is : 241153395
*(p+9) is : 763467876
|
在读完与 C 中的指针相关的章节之前,您很可能不会理解这一章。
如果你对 C 语言中指针的概念有所了解,那么就可以开始本章的学习。数组名是一个指向数组中第一个元素的常量指针。因此,在下面的声明中:
balance 是一个指向&balance[0]
的指针,即数组balance的第一个元素的地址。因此,下面的代码把 p 赋值为 balance 的第一个元素的地址:
1
2
3
4
|
double *p;
double balance;
p = balance;
|
使用数组名作为常量指针是合法的,反之亦然。因此,*(balance + 4)
是一种访问balance[4]
数据的合法方式。
一旦你把第一个元素的地址存储在 p 中,那么就可以使用p
、(p+1)
、(p+2)
等来访问数组元素。
下面的示例演示了上面讨论到的这些概念:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
#include <stdio.h>
int main(){
int i;
double *p;
double balance[5] = {1000.0, 2.0, 3.4, 17.0, 50.0};
p = balance;
printf("1.Array values using balance.\n");
for(i=0; i<5; i++){
printf("balance[%d] : %f\n", i, balance[i]);
}
printf("\n");
printf("2.Array values using balance as address.\n");
for(i=0; i<5; i++){
printf("*(balance + %d) : %f\n", i, *(balance+i));
}
printf("\n");
printf("3.Array values using pointer.\n");
for(i=0; i<5; i++){
printf("*(p+%d) : %f\n", i, *(p+i));
}
return 0;
}
|
运行结果:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
$ gcc -o test6 test6.c
$ ./test6
1.Array values using balance.
balance[0] : 1000.000000
balance[1] : 2.000000
balance[2] : 3.400000
balance[3] : 17.000000
balance[4] : 50.000000
2.Array values using balance as address.
*(balance + 0) : 1000.000000
*(balance + 1) : 2.000000
*(balance + 2) : 3.400000
*(balance + 3) : 17.000000
*(balance + 4) : 50.000000
3.Array values using pointer.
*(p+0) : 1000.000000
*(p+1) : 2.000000
*(p+2) : 3.400000
*(p+3) : 17.000000
*(p+4) : 50.000000
|
在上面的实例中,p 是一个指向 double 类型的指针,这意味着它可以存储一个 double 类型的变量。一旦我们有了 p 中的地址,*p 将给出存储在 p 中相应地址的值,正如上面实例中所演示的。
printf() 只会看到双精度数,printf 的 %f 格式总是得到 double,所以在 printf() 中使用 %f 跟 %lf 的输出显示效果是一样的。
但是对于变量来说,double 类型比 float 类型的精度要高。double 精度更高,是指它存储的小数位数更多,但是 printf 的输出默认都是 6 位小数,如果你想输出更多小数,可以自己控制,比如 %.10lf 就输出 10 位小数。
一般情况下 double 类型的占位符用 %lf。