c语言:指针作为参数传递

探究实参与形参它们相互独立

c语言:指针作为参数传递_第1张图片

由于主调函数的变量ab与被调函数的形参xy它们相互独立。函数 swap 可以修改变量xy,但是却无法影响到主调函数中的ab

现在利用取地址运算符,分别打印它们的首地址,让我们从内存的角度,来分析一下它们。

c语言:指针作为参数传递_第2张图片

a在内存中为首地址10484860开始的 sizeof(int) 字节。

b在内存中为首地址10484856开始的 sizeof(int) 字节。

x在内存中为首地址10484832开始的 sizeof(int) 字节。

y在内存中为首地址10484836开始的 sizeof(int) 字节。

c语言:指针作为参数传递_第3张图片

调用 swap 函数时,a的值1,传给xb的值2,传给y。

c语言:指针作为参数传递_第4张图片

图中,红色数值为数据对象首地址,黑框内的为变量名和值。

即使xy已经交换了,但是并未影响ab 


将指针作为参数传递

由于在被调函数内部无法直接修改主调函数的变量。那么我们采用迂回战术,在函数 main 中取得ab 的指针。将两个指针传递到函数 swap 。那么,在函数 swap 内部可以根据这两个信息修改ab

这下,我们就需要用到指针类型作为参数了。

c语言:指针作为参数传递_第5张图片

现在将 x y 改为了 int * 类型的指针。在主调函数中,对 a b 进行取地址获取指针并传入函

swap 。在函数 swap 内部,通过这两个指针交换目标数据对象的值。注意,不是交换指针xy的值, 而是交换目标数据对象ab的值。所以,需要在指针前使用取值运算符*

c语言:指针作为参数传递_第6张图片

图中,红色数值为数据对象首地址,黑框内的为变量名和值。

现在终于能解释为何在使用 scanf 函数时,需要对变量先取地址再传入参数了。

int n; scanf("%d", &n);

scanf 会从读取从键盘的输入,转换后存储到变量n当中。被调函数 scanf 无法直接修改在主调函数中的变量n。因此,我们将变量n的指针传入 scanf 函数。通过指针使得被调函数间接地修改主调函数中的变量。


指针不仅仅是首地址

再次强调,指针内保存的不仅仅是目标数据对象首地址,指针的类型也非常重要。要在内存中找到一个数据对象,需要有以下两个信息。

  1. 数据对象的首地址
  2. 数据对象占用存储空间大小

指针的值保存着数据对象首地址,指针类型对应着目标数据对象的类型,用于标记目标数据对象的空间大小和指针运算时的步长。

char * ,目标数据对象大小为 sizeof(char) 。运算时,步长为sizoef(char)。

short * ,目标数据对象大小为 sizeof(short) 。运算时,步长为sizoef(short)

int * ,目标数据对象大小为 sizeof(int) 。运算时,步长为sizoef(int)

long * ,目标数据对象大小为 sizeof(long) 。运算时,步长为sizoef(long)

long long * ,目标数据对象大小为 sizeof(long long) 。运算时,步长为sizoef(long long)

float * ,目标数据对象大小为 sizeof(float) 。运算时,步长为sizoef(float)

double * ,目标数据对象大小为 sizeof(double) 。运算时,步长为sizoef(double)

若要用函数 swap 交换两个int类型的变量,必须传入指向这两个int类型变量的指针。函数内部可以通过指针知道对象的首地址和类型。但是,这样也使得函数 swap ,只能交换int类型的变量了。

如果,想让函数 swap 函数更加通用一点,可以交换更多类型的变量。应该怎么做呢?


仅有首地址的指针类型void *

由于指针类型定死了指针所指向的数据类型。为了让函数可以交换更多的数据类型,我们仅需要指针类型中保存的首地址,目标数据大小通过额外的参数传入。

void swap(void *x, void *y, int size)

int * 修改为 void * 。类型为 void * 的指针仅保存首地址,不保存目标数据对象的空间大小。所以, 不能对 void * 类型的指针进行取值。同样的,它也没有步长,所以不能对 void * 类型的指针进行加减运算。

int n;

void *p = &n;   // int *赋值给void *,类型信息被丢弃,仅保存首地址。

*p;     // 仅有首地址,未保存目标数据对象大小,无法取值。

p + 1; // 仅有首地址,没有步长,无法进行加减运算。

但是, void * 有一个好处,那就是任意类型的指针都可以直接赋值给它。而其他类型的指针是不能相互赋值的,由于赋值会改变目标数据对象的类型。

char *pc; int *pn;

pc = pn;    // 编译出错,目标数据对象类型不同,无法直接赋值。

void *p;

p = pn;     // 编译通过,任意类型的指针都可以直接赋值给它。

p = pc;     // 编译通过,任意类型的指针都可以直接赋值给它。

规律

  1. 不同指针类型不能相互赋值,相互赋值后会造成目标数据对象类型的改变,无法通过编译。
  2. void * 类型为特例,它可以接受任意指针类型的赋值,也可以赋值给任意类型的指针。

我们将函数定义修改为:

void swap(void *x, void *y, int size)

{

// 指针转为char *,单个字节操作内存

char *pX = (char *)x; char *pY = (char *)y; char temp;

for (int i = 0; i < size; i++)

{

temp = pX[i]; pX[i] = pY[i]; pY[i] = temp;

}

}

由于 void * 不能取值和加减,所以我们将其转换为 char * char * 可以提供单个单个操作内存的能力。

C语言中 void * 类型不但可以接受任意类型的指针,也可以自动转换为任意类型的指针。

但在C++中,规则稍微严格了一点, void * 仅能接受任意类型的指针,不能自动转换为其他类型的指针。为了保证代码的兼容性,我们将 void * 强制转为 char * ,避免在C++中编译出错。

char *pX = (char *)x; char *pY = (char *)y;

你可能感兴趣的:(c语言,开发语言)