关于地址,指针,指针变量可以参考我的这篇文章:
地址,指针,指针变量是什么?他们的区别?符号(*)在不同位置的解释?_juechen333的博客-CSDN博客https://blog.csdn.net/qq_57342311/article/details/129225045
目录
一、引入
1.1实例
1.2解释
1.3变量名和数组名作函数参数的比较
二、程序举例
2.1引入
三、总结
3.1归纳
3.2用指针变量作实参、形参举例
3.3用指针变量作实参,数组名作形参举例
一、引入
1.1实例
#include<stdio.h>
int main()
{
void fun(int arr[], int n); //对fun函数进行声明
int array[10]; //定义array数组
... //一系列操作
fun(array, 10); //用数组名作函数参数
... //一系列操作
return 0;
}
void fun(int arr[], int n) //定义fun函数
{
... //一系列操作
}
array 是实参数组名,arr 为形参数组名,当用数组名作参数时,如果形参数组中各元素的值发生变化,实参数组元素的值随之变化。这究竟为什么呢?请继续往下看。
1.2解释
(1)先看数组元素作实参时的情况
如果已定义一个函数
void swap(int x, int y);
假设函数的作用是将两个形参( x , y )的值交换,进行函数调用
swap (a[1],a[2]);
用数组元素 a[1] 和 a[2] 作实参的情况,与用变量作实参时一样,是 “值传递” 方式,将 a[1] 和 a[2] 的值单向传递给 x 和 y 。当 x 和 y 的值改变时,a[1] 和 a[2] 的值并不改变。
(2)再看用数组名作函数参数的情况
实参数组名代表该数组首元素的地址,而形参是用来接收从实参传递过来的数组首元素地址的。因此,形参应该是一个指针变量(只有指针变量才能存放地址)。实际上,C编译都是将形参数组名作为指针变量来处理的。例如:
fun(int arr[],int n);
但在程序编译时是将 arr 按指针变量处理的,相当于将函数 fun 的首部写成
fun(int* arr,int n);
以上两种写法是等价的。在该函数被调用时,系统会在 fun 函数中建立一个指针变量 arr,用来存放从主调函数传递过来的实参数组首元素的地址。如果在fun函数中用运算符 sizeof 测定 arr 所占的字节数,可以发现 sizeof(arr) 的值为 4 (用 VisualC++ 时)。这就证明了系统是把 arr 作为指针变量来处理的(指针变量在 Visual C++ 中占4个字节)。
当 arr 接收了实参数组的首元素地址后,arr 就指向实参数组首元素,也就是指向 array[0]。因此,*arr 就是 array[0]。arr+1 指向 array[1],arr+ 2 指向 array[2],arr+3 指向 array[3]。也就是说,*(arr+1),*(arr+2), *(arr+3) 分别是 array[1],array[2],array[3]。
根据前面介绍过的知识,*(arr+i) 和 arr[i] 是无条件等价的。
因此,在调用函数期间,arr[0] 和 *arr 以及 array[0] 都代表数组 array 序号为 0 的元素,依此类推,arr[3],*(arr+ 3),array[3] 都代表 array 数组序号为 3 的元素。
1.3变量名和数组名作函数参数的比较
实参类型 | 要求的形参类型 | 传递的信息 | 能否改变实参的值 |
变量名 | 变量名 | 变量的值 | 不能 |
数组名 | 数组名或指针变量 | 实参数组首元素的地址 | 能 |
说明: C 语言调用函数时虚实结合的方法都是采用 “值传递” 方式,当用变量名作为函数参数时传递的是变量的值,当用数组名作为函数参数时,由于数组名代表的是数组首元素地址,因此传递的值是地址,所以要求形参为指针变量。
在用数组名作为函数实参时,既然实际上相应的形参是指针变量,为什么还允许使用形参数组的形式呢?这是因为在 C 语言中用下标法和指针法都可以访问一个数组(如果有一个数组 a,则 a[i] 和 *(a+i) 无条件等价),用下标法表示比较直观,便于理解。因此许多人愿意用数组名作形参,以便与实参数组对应。从应用的角度看,用户可以认为有一个形参数组,它从实参数组那里得到起始地址,因此形参数组与实参数组共占同一段内存单元,在调用函数期间,如果改变了形参数组的值,也就是改变了实参数组的值。在主调函数中就可以利用这些已改变的值。
注意:实参数组名代表一个固定的地址,或者说是指针常量,但形参数组名并不是一个固定的地址,而是按指针变量处理。
二、程序举例
2.1引入
将数组 a 中 n 个整数按相反顺序存放,实参用数组名 a,形参可用数组名,也可用指针变量名。
(1)形参用数组名
#include<stdio.h>
int main()
{
void inv(int x[], int n); //函数声明
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i++) //输出未交换时数组各元素的值
{
printf("%d ", a[i]);
}
printf("\n");
inv(a, 10); //调用函数
for (int i = 0; i < 10; i++) //输出交换后数组各元素的值
{
printf("%d ", a[i]);
}
}
void inv(int x[], int n) //形参x是数组名
{
int temp;
for (int i = 0; i < (n - 1) / 2; i++)
{
int j = n - 1 - i;
temp = x[i];
x[i] = x[j];
x[j] = temp;
}
}
运行结果:
分析:
程序分析:在main函数中定义整型数组 a,并赋予初值。函数 inv 的形参数组名为 x。在定义 inv 函数时,可以不指定形参数组 x 的大小(元素的个数)。因为形参数组名实际上是一个指针变量,并不是真正地开辟一个数组空间(定义实参数组时必须指定数组大小,因为要开辟相应的存储空间)。inv 函数的形参 n 用来接收需要处理的元素的个数。在 main 函数中有函数调用语句 “inv(a,10);",表示要求对 a 数组的 10 个元素实行题目要求的颠倒排列。如果改为 “inv(a,5);",则表示要求将 a 数组的前 5 个元素实行颠倒排列,此时,函数 inv 只处理5个数组元素。
(2)形参用指针变量名
#include<stdio.h>
int main()
{
void inv(int* x, int n); //函数声明
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
for (int i = 0; i < 10; i++) //输出未交换时数组各元素的值
{
printf("%d ", a[i]);
}
printf("\n");
inv(a, 10); //调用函数
for (int i = 0; i < 10; i++) //输出交换后数组各元素的值
{
printf("%d ", a[i]);
}
}
void inv(int* x, int n) //形参x是指针变量
{
int temp;
int* i = x;
int* j = x + n - 1;
for (; i < x + (n - 1) / 2; i++, j--)
{
temp = *i;
*i = *j;
*j = temp;
}
}
运行结果:
三、总结
3.1归纳
如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下 4 种情况。
(1)实参和形参都用数组名
int main()
{
void f(int x[], int n);
int a[10];
...
f(a, 10); //实参用数组名
...
}
void f(int x[], int n) //形参用数组名
{
...
}
(2)实参用数组名,形参用指针变量
int main()
{
void f(int* x, int n);
int a[10];
...
f(a, 10); //实参用数组名
...
}
void f(int* x, int n) //形参用指针变量
{
...
}
(3)实参和形参都用指针变量
int main()
{
void f(int* x, int n);
int a[10];
int* p = a;
...
f(p, 10); //实参用指针变量
...
}
void f(int* x, int n) //形参用指针变量
{
...
}
(4)实参用指针变量,形参用数组名
int main()
{
void f(int x[], int n);
int a[10];
int* p = a;
...
f(p, 10); //实参用指针变量
...
}
void f(int x[], int n) //形参用数组名
{
...
}
3.2用指针变量作实参、形参举例
改写(二)中的实例
#include<stdio.h>
int main()
{
void inv(int* x, int n); //函数声明
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = a;
for (int i = 0; i < 10; i++) //输出未交换时数组各元素的值
{
printf("%d ", a[i]);
}
printf("\n");
inv(p, 10); //调用函数
for (int i = 0; i < 10; i++) //输出交换后数组各元素的值
{
printf("%d ", a[i]);
}
}
void inv(int* x, int n) //形参x是指针变量
{
int temp;
int* i = x;
int* j = x + n - 1;
for (; i < x + (n - 1) / 2; i++, j--)
{
temp = *i;
*i = *j;
*j = temp;
}
}
运行结果:
3.3用指针变量作实参,数组名作形参举例
对数组 a[10] 由大到小进行排序,采用选择排序的算法
#include<stdio.h>
int main()
{
void sort(int x[], int n); //函数声明
int a[10] = { 1,3,5,7,9,2,4,6,8,10 };
int* p = a;
for (int i = 0; i < 10; i++) //输出未交换时数组各元素的值
{
printf("%d ", a[i]);
}
printf("\n");
sort(p, 10); //调用函数
for (int i = 0; i < 10; i++) //输出交换后数组各元素的值
{
printf("%d ", a[i]);
}
}
void sort(int x[], int n)
{
for (int i = 0; i < n - 1; i++) //i的取值范围[0,n-1)
{
int max_idx = i; //记录最大数值所在位置
for (int j = i + 1; j < n; j++) //j的取值范围[1,n)
{
if (x[j] > x[max_idx]) { max_idx = j; } //更新最大值所在位置
}
if (max_idx != i) //进行交换
{
int temp;
temp = x[i];
x[i] = x[max_idx];
x[max_idx] = temp;
}
}
}
运行结果:
上面 sort 函数中是用数组名作为形参,也可以用指针变量作为形参
void sort(int* x, int n)
{
for (int i = 0; i < n - 1; i++) //i的取值范围[0,n-1)
{
int max_idx = i; //记录最大数值所在位置
for (int j = i + 1; j < n; j++) //j的取值范围[1,n)
{
if (*(x + j) > *(x + max_idx)) { max_idx = j; } //更新最大值所在位置
}
if (max_idx != i) //进行交换
{
int temp;
temp = *(x + i);
*(x + i) = *(x + max_idx);
*(x + max_idx) = temp;
}
}
}