关于C之按值传递与指针误区解读

本文深入探讨C语言中指针与函数参数传递的机制,解析如何通过指针修改函数外部变量,对比不同参数传递方式的效果,以及展示正确使用指针进行数据交换和结构体操作的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

我们看下面代码:

#include <stdio.h>
typedef struct node {
	int r;
} Node, * List;
void AddItemX(int R, List plist);
void AddItemY(int R, List* plist);
int main(void) {
	List movies = NULL;

	AddItemX(10, movies); ↔movies确实是Node类型指针
	printf("outerX:%s\n", movies == NULL ? "NULL" : "NOT NULL");

	AddItemY(10, &movies);
	printf("outerY:%s\n", movies == NULL ? "NULL" : "NOT NULL");
}
void AddItemX(int R, List plist) {
	Node* pnew = (Node*)malloc(sizeof(Node));
	pnew->r = R;
	plist = pnew;
	printf("innerX:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
void AddItemY(int R, List* plist) {
	Node* pnew = (Node*)malloc(sizeof(Node));
	pnew->r = R;
	*plist = pnew;
	printf("innerY:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
innerX:NOT NULL
outerX:NULL
innerY:NOT NULL
outerY:NOT NULL

List movies;这行说明movies是指向node结构的指针,在AddItemX()函数体中plist即传进的movies指针,plist=pnew,即moives指针指向pnew,但在main()函数中movies为NULL,就向按值传递一样,但这不是指针吗?
我们看一个最简单交换两数值的实现:

#include<stdio.h>
void swap(int* x, int* y);
int main(void) {
    int a = 3, b = 4;
    printf("a地址=%p,b地址=%p\n", &a, &b);
    swap(&a, &b);
    printf("a=%d,b=%d\n", a, b);
    printf("a地址=%p,b地址=%p\n", &a, &b);
    return 0;
}
void swap(int* x, int* y) { // 用int变量来存储进行a和b的地址交换,并且解引用
    int tmp;
    tmp = *y;
    *y = *x;
    *x = tmp;
}

//下面代码完成不了交换,对传入的a、b的地址也没有交换
//void swap(int* x, int* y) {
//    int* tmp;
//    tmp = y;
//    y = x;
//    x = tmp;
//}
a地址=0x7ffeefbff568,b地址=0x7ffeefbff564
a=4,b=3
a地址=0x7ffeefbff568,b地址=0x7ffeefbff564

swap()函数里面形参(int* a, int* b)只是表示传入的a、b是存储对应int型值的地址。*b = *a;表示将地址a中的值赋值到地址b中。a、b的地址没变,就是值交换了。传进来的是地址,操作的就是外面传入的数据了,否则就是形参的操作,对外面传入的值没任何影响。对于注释部分,没有完成a、b数值的交换,它们的地址也没交换,因为指针地址也就是一个整型值,也就相当于形参的交换,对外面传入的值没有任何影响。

再看一个错误的例子:

#include<stdio.h>
#include<stdlib.h>
void swap(int* x, int* y);
int main(void) {
	int* a = (int*)malloc(sizeof(int));
	int* b = (int*)malloc(sizeof(int));
	*a = 3;
	*b = 4;
	swap(a, b);
	printf("a=%d,b=%d", *a, *b);
	return 0;
}
错误的代码:
void swap(int* x, int* y) {
	int* tmp = y;
	y = x;
	x = tmp;
}
正确的代码:
void swap(int* x, int* y) {
	int tmp = *y;
	*y = *x;
	*x = tmp;
}

由于这里a、b都是指针,都存的是地址,所以不用取地址符,其他操作和上一例一致。

所以上面的AddItemX()改成下面即可:

原(错误):
void AddItemX(int R, List plist) {
	Node* pnew = (Node*)malloc(sizeof(Node));
	pnew->r = R;
	plist = pnew;
	printf("innerX:%s\n", plist == NULL ? "NULL" : "NOT NULL");
}
现(正确???):
void AddItemX(int R, List plist) {
	Node* pnew = (Node*)malloc(sizeof(Node));
	pnew->r = R;
	*plist = *pnew;
}

 plist是个空指针,所以当然不能把内容赋值给它!

前提是plist在main()主函数里面要初始化,否则*plist显然出问题,但如果换成AddItemY()函数,plist为NULL,处理&plist没问题,因为&plist=0x0。 

只要从主调函数传入的是地址,就能通过运算符*如期完成任务。特别要注意,形参类型与声明参数类型是不一样的。形参比如(int * x)代表传入的是一个地址,对于声明int * x;代表x是一个指向int型的指针变量。

最后看一个打印地址的最直观的例子,能更加明白函数按值传递:

#include <stdio.h>
#define ArSize 8
int sum_arr(int *arr, int n);
int main(void) {
  int cookies[ArSize] = {1, 2, 4, 8, 16, 32, 64, 128};
  printf("%p = array address, %d = sizeof cookies\n", cookies, sizeof cookies);

  int sum = sum_arr(cookies, ArSize);
  printf("Total cookies eaten: %d\n", sum);
  sum = sum_arr(cookies, 3);
  printf("First three eaters ate %d cookies.\n", sum);
  sum = sum_arr(cookies + 4, 4);
  printf("Last four eaters ate %d cookies.", sum);
  return 0;
}

int sum_arr(int *arr, int n) {
  int total = 0;
  printf("%p = arr, %d = sizeof arr\n", arr, sizeof arr);
  for (int i = 0; i < n; i++) total = total + arr[i];
  return total;
}
00CFFCEC = array address, 32 = sizeof cookies
00CFFCEC = arr, 4 = sizeof arr
Total cookies eaten: 255
00CFFCEC = arr, 4 = sizeof arr
First three eaters ate 7 cookies.
00CFFCFC = arr, 4 = sizeof arr
Last four eaters ate 240 cookies.

通过程序运行结果,我们知道传递进函数的的确是地址00CFFCEC,从4 = sizeof arr也正说明了传递的是一个地址而不是整个数组,一般地址是int型就是占4个字节。否则就像第一条打印结果一样size为32了。而后面调用sum_arr(cookies + 4, 4);从结果可以看到它的地址cookies+4和cookies的地址偏移了4*sizeof(int)=16,即00CFFCEC + 16(0x10) = 00CFFCFC。总之,传递的是地址这个值。

总之,函数是按值传递参数的。函数只能通过传递地址(或指针变量)才能更改主调程序传入的变量值。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

itzyjr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值