C 语言 “神秘魔杖”—— 指针初相识,解锁编程魔法大门(一)
文章目录
- 一、概念
- 1、取地址操作符(&)
- 2、解引用操作符(*)
- 3、指针变量
- 1、 声明和初始化
- 2、 用途
- 二、内存和地址
- 三、指针变量类型的意义
- 1、 指针变量类型的基本含义
- 2、 举例说明不同类型指针变量的意义
- 四、const修饰指针
- 1、const修饰指针的三种情况
- 五、指针运算
- 1、指针的算术运算
- 2、指针的关系运算
- 六、野指针
- 1、野指针的定义
- 2、野指针的示例
- 3、造成野指针的原因
- 4、如何避免野指针
- 七、assert断言
- 1、assert断言的基本概念
- 八、指针的使用和传址调用
- 1、指针的使用
- 2、传址调用
一、概念
- 指针是C语言中的一个重要概念,它是一个变量,其值为另一个变量的地址。简单来说,指针“指向”了内存中的某个位置,这个位置存储着其他变量的值。就好像你有一个房间号(指针),通过这个房间号可以找到房间里存放的东西(变量的值);也就是可以通过告诉我们房间号(指针),快速的找到房间里的内容(变量的值)。
1、取地址操作符(&)
- 含义:
- 取地址操作符
&
用于获取变量在内存中的地址。在C语言中,每个变量都被存储在内存中的某个位置,这个位置有一个唯一的地址。通过&
操作符,我们可以得到这个地址的值。
- 取地址操作符
- 示例:
- 以下是一个简单的示例代码,展示如何使用取地址操作符。
#include <stdio.h>
int main() {int num = 10;// 使用取地址操作符获取num的地址int *ptr = #printf("The address of num is: %p\n", (void*)&num);return 0;
}
- 在这个例子中,定义了一个整型变量`num`并初始化为`10`。- 然后通过`&num`获取`num`的地址,并将这个地址赋值给一个指针变量`ptr`。- `%p`是用于打印指针(地址)的格式化说明符,由于`printf`函数的参数要求,将`&num`强制转换为`(void*)`类型。
2、解引用操作符(*)
- 含义:
- 解引用操作符
*
用于访问指针所指向的内存位置中的值。当我们有一个指针变量,它存储了一个变量的地址,通过解引用操作符可以获取该地址所对应的实际值。
- 解引用操作符
- 示例:
- 以下是一个示例,演示了解引用操作符的使用。
#include <stdio.h>
int main() {int num = 10;int *ptr = #// 使用解引用操作符获取ptr指向的内存中的值printf("The value pointed to by ptr is: %d\n", *ptr);return 0;
}
- 在这里,首先定义了一个整型变量`num`和一个指向`int`类型的指针`ptr`,并将`num`的地址赋值给`ptr`。- 然后通过`*ptr`来解引用指针,获取`ptr`所指向的内存位置(也就是`num`所在的内存位置)中的值,- 并将其打印出来。这个值就是`10`,因为`ptr`指向`num`,而`num`的值为`10`。
总的来说,取地址操作符&
和解引用操作符*
是C语言中操作指针的重要工具,它们经常一起配合使用,使得程序员能够灵活地处理内存中的数据。
3、指针变量
- 指针变量是一种特殊的变量,它存储的是另一个变量的内存地址。在C语言中,变量在内存中有自己的存储位置,这个位置可以通过地址来表示。指针变量就像是一个指向其他变量的“指示器”,它本身占用一定的内存空间,用于存放目标变量的地址。
1、 声明和初始化
- 声明格式:
- 数据类型 *指针变量名;
- 例如,
int *p;
声明了一个名为p
的指针变量,它可以用来存储int
类型变量的地址。这里的*
是一个说明符,表示p
是一个指针变量,它指向的数据类型是int
。
- 初始化:
- 指针变量在使用前最好进行初始化。可以通过取地址操作符
&
来获取变量的地址并赋值给指针变量。例如:
- 指针变量在使用前最好进行初始化。可以通过取地址操作符
int num = 10;
int *p = #
- 在这个例子中,先定义了一个整型变量`num`并赋值为`10`,然后定义了一个指针变量`p`,- 并通过`&num`将`num`的地址赋值给`p`,这样`p`就指向了`num`。
2、 用途
- 访问变量的值:
- 通过解引用指针变量(使用
*
操作符)可以访问它所指向变量的值。例如:
- 通过解引用指针变量(使用
#include <stdio.h>
int main() {int num = 10;int *p = #// 解引用指针p来获取num的值printf("The value of num through pointer is %d\n", *p);return 0;
}
- 这里,`*p`获取了`p`所指向的变量(即`num`)的值,然后将其打印出来。
- 函数参数传递:
- 指针变量在函数参数传递中有重要作用。当我们想要在函数内部修改外部变量的值时,可以将变量的地址传递给函数,函数通过指针来访问和修改该变量。例如:
#include <stdio.h>
// 函数用于交换两个整数的值
void swap(int *a, int *b) {int temp;temp = *a;*a = *b;*b = temp;
}
int main() {int x = 5, y = 10;printf("Before swap: x = %d, y = %d\n", x, y);swap(&x, &y);printf("After swap: x = %d, y = %d\n", x, y);return 0;
}
- 在这个例子中,`swap`函数接受两个指针变量`a`和`b`,通过解引用这些指针来交换它们所指向的变量的值。- 在`main`函数中,通过`&x`和`&y`将`x`和`y`的地址传递给`swap`函数,从而实现了`x`和`y`值的交换。
- 了解指针中的操作符,我们来举一个例子:假设有一个整型变量
int num = 10;
,可以定义一个指针来指向这个变量。int *p;
这里p
就是一个指针,它可以用来存储num
的地址。通过p=#
操作,就使得p
指向了num
。
二、内存和地址
-
在计算机中,内存就像是一个个小格子(字节),每个格子都有一个唯一的编号,这个编号就是地址。变量存储在内存中,指针存储的就是这些变量的内存地址。也就是说,指针就是这个内存单元的地址。
-
例如,对于上面的
num
变量,它在内存中有一个地址。如果num
的地址是0x100
(这是假设的十六进制地址),那么p
的值在执行p = #
后就会变为0x100
。可以通过printf("%p\n", p);
来打印p
所指向的地址。
三、指针变量类型的意义
1、 指针变量类型的基本含义
- 在C语言中,指针变量的类型非常重要。指针变量的类型决定了它所指向的数据类型。例如,
int *
类型的指针是用来指向int
类型的数据,char *
类型的指针是用来指向char
类型的数据。 - 这种类型规定了指针在进行解引用操作时,编译器会按照该类型所对应的字节数来访问内存。不同数据类型在内存中占用的字节数不同,比如在大多数32位系统中,
int
类型通常占用4个字节,char
类型占用1个字节。
2、 举例说明不同类型指针变量的意义
- 指向整数的指针(
int *
)- 示例代码:
#include <stdio.h>
int main() {int num = 10;int *ptr = #// 解引用指针,获取整数的值printf("The value of num through pointer is %d\n", *ptr);// 指针加法运算ptr++;// 此时ptr指向的地址向后偏移了一个int类型的大小(通常为4字节)// 这里只是展示指针类型对偏移量的影响,实际这样使用可能会导致未定义行为// 因为ptr已经指向了num之后的内存位置,而这个位置可能是未分配给程序使用的return 0;
}
- 在这个例子中,`int *ptr`声明了一个指向`int`类型的指针`ptr`。当`ptr`被初始化为`&num`后,通过`*ptr`解引用可以获取`num`的值。- 当执行`ptr++`时,指针`ptr`会在内存中向后移动`sizeof(int)`个字节(在常见系统中是4字节),这是因为`ptr`是`int *`类型,- 编译器知道`int`类型数据的大小,所以会按照这个大小来移动指针。
- 指向字符的指针(
char *
)- 示例代码:
#include <stdio.h>
int main() {char ch = 'A';char *cptr = &ch;printf("The character through pointer is %c\n", *cptr);cptr++;// 此时cptr指向的地址向后偏移了一个char类型的大小(1字节)return 0;
}
- 对于`char *cptr`,它指向`char`类型的数据。当`cptr`被初始化为`&ch`后,通过`*cptr`可以获取`ch`的值。执行`cptr++`时,- 指针会在内存中向后移动`sizeof(char)`个字节(1字节),这是因为`char`类型数据在内存中占用1字节,- 指针的移动是根据其指向的数据类型来确定的。
- 指向结构体的指针(
struct xxx *
)- 假设我们有一个简单的结构体:
#include <stdio.h>
struct Student {char name[20];int age;
};
int main() {struct Student stu = {"John", 20};struct Student *sptr = &stu;// 解引用结构体指针,访问结构体成员printf("The student's name is %s and age is %d\n", sptr->name, sptr->age);return 0;
}
- 在这里,`struct Student *sptr`是一个指向`struct Student`类型结构体的指针。- 当它被初始化为`&stu`后,通过`->`操作符(这是一种用于结构体指针访问成员的便捷操作符,- 等价于`(*sptr).name`和`(*sptr).age`)可以访问结构体`stu`中的成员。如果要移动这个指针,- 编译器会按照`struct Student`类型结构体的大小(这个大小是`name`成员数组的大小加上`age`成员占用的大小)来移动指针。
四、const修饰指针
1、const修饰指针的三种情况
- 指向常量的指针(
const
修饰所指对象)- 含义:
- 这种情况下,指针所指向的数据被视为常量,不能通过该指针来修改所指向的数据,但指针本身可以重新指向其他内存单元。
- 语法形式:
const 数据类型 *指针变量名;
,例如const int *p;
。
- 示例:
- 含义:
#include <stdio.h>
int main() {int num1 = 10;int num2 = 20;const int *p = &num1;// 下面这行代码是错误的,因为不能通过p修改它所指向的数据// *p = 30;p = &num2; // 这是可以的,指针p可以重新指向num2printf("The value pointed to by p is %d\n", *p);return 0;
}
- 在这个例子中,`p`是一个指向`const int`类型的指针,也就是`p`所指向的`int`数据被当作常量。- 当试图通过`*p = 30`修改`p`所指向的数据时,编译器会报错。但可以将`p`重新指向`num2`,因为只是改变了指针的指向,- 而没有违反`const`的限制。
- 常量指针(
const
修饰指针本身)- 含义:
- 指针本身是常量,它不能再重新指向其他内存单元,但可以通过该指针修改它所指向的数据。
- 语法形式:
数据类型 *const指针变量名;
,例如int *const p;
。
- 示例:
- 含义:
#include <stdio.h>
int main() {int num1 = 10;int *const p = &num1;*p = 30; // 这是可以的,能够修改p所指向的数据// 下面这行代码是错误的,因为p是常量指针,不能重新指向其他内存单元// p = &num2;printf("The value of num1 after modification is %d\n", num1);return 0;
}
- 这里,`p`是一个常量指针,它被初始化为指向`num1`后,就不能再指向其他变量了。- 不过,可以通过`*p = 30`修改`p`所指向的`num1`的值,因为`const`修饰的是指针本身,而不是它所指向的数据。
- 指向常量的常量指针(
const
同时修饰指针和所指对象)- 含义:
- 指针本身不能重新指向其他内存单元,并且不能通过该指针修改所指向的数据。
- 语法形式:
const 数据类型 *const指针变量名;
,例如const int *const p;
。
- 示例:
- 含义:
#include <stdio.h>
int main() {int num1 = 10;const int *const p = &num1;// 下面这行代码是错误的,不能通过p修改所指向的数据// *p = 30;// 下面这行代码也是错误的,p不能重新指向其他内存单元// p = &num2;return 0;
}
- 在这个例子中,`p`是一个指向常量的常量指针。既不能通过`*p`修改`p`所指向的数据,也不能让`p`重新指向其他内存单元,- `p`的指向和它所指向的数据都被`const`修饰固定了。
- 指针可以进行算术运算,如加法、减法等。当指针加上或减去一个整数时,它实际移动的字节数是该整数乘以指针所指向数据类型的大小。
- 例如,对于一个
int *p
,如果p
的值是0x100
,执行p++;
后,p
的值会增加sizeof(int)
字节。在32位系统中,int
占4字节,所以p
的值会变为0x100 + 4 = 0x104
。 - 指针还可以进行减法运算,用来计算两个指针之间的距离。例如,有两个指针
p1
和p2
指向同一个数组中的元素,int *p1, *p2;
,p1 - p2
的结果是两个指针之间元素的个数。
五、指针运算
1、指针的算术运算
- 指针与整数相加/相减
- 含义:
- 当指针与一个整数进行加法或减法运算时,指针移动的字节数是该指针所指向的数据类型大小乘以这个整数。例如,对于一个
int *
类型的指针(假设int
类型占4字节),如果与整数n
相加,那么指针在内存中实际向前移动4 * n
字节。
- 当指针与一个整数进行加法或减法运算时,指针移动的字节数是该指针所指向的数据类型大小乘以这个整数。例如,对于一个
- 示例:
- 含义:
#include <stdio.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p = &arr[0];// 指针p向后移动2个int类型的长度p = p + 2;printf("The value pointed to by p after addition is %d\n", *p);// 指针p向前移动1个int类型的长度p = p - 1;printf("The value pointed to by p after subtraction is %d\n", *p);return 0;
}
- 在这个例子中,首先定义了一个整型数组`arr`,并让指针`p`指向数组的第一个元素`arr[0]`。- 当执行`p = p + 2`时,由于`p`是`int *`类型,`int`类型通常占4字节,- 所以`p`在内存中向后移动了`2 * 4 = 8`字节,此时`p`指向`arr[2]`,输出为`3`。- 接着执行`p = p - 1`,`p`向前移动了4字节,此时`p`指向`arr[1]`,输出为`2`。
- 两个指针相减
- 含义:
- 两个相同类型的指针相减,结果是两个指针之间相隔的元素个数,而不是字节数。这个运算的结果类型是
ptrdiff_t
(在<stddef.h>
头文件中定义的有符号整数类型)。这个运算通常用于计算数组中两个元素之间的距离。
- 两个相同类型的指针相减,结果是两个指针之间相隔的元素个数,而不是字节数。这个运算的结果类型是
- 示例:
- 含义:
#include <stdio.h>
#include <stddef.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p1 = &arr[1];int *p2 = &arr[3];ptrdiff_t diff = p2 - p1;printf("The difference between p2 and p1 is %td\n", diff);return 0;
}
- 在这里,定义了数组`arr`,`p1`指向`arr[1]`,`p2`指向`arr[3]`。- 当计算`p2 - p1`时,结果是`2`,因为`p2`和`p1`之间相隔2个`int`类型的元素。
2、指针的关系运算
- 含义:
- 指针可以进行关系运算(如
>
、<
、>=
、<=
、==
、!=
),用于比较两个指针在内存中的位置关系。通常用于判断指针是否指向同一个数组中的元素,或者判断一个指针是否超出了某个数组的范围。
- 指针可以进行关系运算(如
- 示例:
#include <stdio.h>
int main() {int arr[] = {1, 2, 3, 4, 5};int *p1 = &arr[0];int *p2 = &arr[3];if (p2 > p1) {printf("p2 points to a later element in the array than p1\n");}return 0;
}
- 在这个例子中,定义了数组`arr`,`p1`指向`arr[0]`,`p2`指向`arr[3]`。- 通过比较`p2 > p1`,因为`p2`在内存中指向的位置在`p1`之后,所以条件成立,会输出相应的信息。
需要注意的是,指针运算应该在合理的范围内进行,特别是对于非数组元素的指针进行算术运算可能会导致未定义行为。
六、野指针
1、野指针的定义
- 野指针是指指针变量指向的内存位置是不确定的(随机的、不可预知的)。野指针不是NULL指针,NULL指针明确地指向地址为0的内存空间,而野指针指向的位置是未定义的,可能是已经被释放的内存区域、未分配的内存区域或者其他非法的内存地址。使用野指针可能会导致程序崩溃、数据损坏或者产生不可预测的行为。
2、野指针的示例
- 下面是一个产生野指针的简单示例:
#include <stdio.h>
int main() {int *p;// 此时p是一个未初始化的指针,它的值是随机的,是野指针*p = 10;return 0;
}
- 在这个例子中,定义了一个指针变量
p
,但是没有对它进行初始化。然后就试图通过*p = 10
来修改p
所指向的内存位置的值。由于p
没有被初始化,它指向的是一个随机的内存地址,这个操作可能会导致程序崩溃或者出现其他错误。
3、造成野指针的原因
- 指针变量未初始化:
- 这是最常见的原因之一。当定义一个指针变量时,如果没有给它赋初值,它的值是随机的,就会成为野指针。例如上面的例子中,
int *p;
定义后没有初始化p
就直接使用。
- 这是最常见的原因之一。当定义一个指针变量时,如果没有给它赋初值,它的值是随机的,就会成为野指针。例如上面的例子中,
- 指针所指向的内存被释放后,指针没有置为NULL:
- 当使用
free()
函数(在动态内存分配中)释放了指针所指向的内存后,指针本身的值(即内存地址)并没有改变,它仍然指向原来的内存位置。而这个内存位置已经被释放,再通过这个指针访问内存就会出现问题。例如:
- 当使用
#include <stdio.h>
#include <stdlib.h>
int main() {int *p = (int *)malloc(sizeof(int));if (p!= NULL) {*p = 10;// 释放内存free(p);// 此时p成为野指针,因为它仍然指向刚刚被释放的内存位置*p = 20; }return 0;
}
- 在这个例子中,通过
malloc
函数分配了一块内存给p
,然后给这块内存赋值为10
。接着使用free
函数释放了这块内存,但是p
仍然指向这块被释放的内存。当试图再次通过*p = 20
修改这个内存位置的值时,就会出现错误,因为这块内存已经被释放,此时p
是野指针。
4、如何避免野指针
- 初始化指针变量:
- 尽量在定义指针变量时就对它进行初始化。如果暂时不知道指针应该指向哪里,可以将它初始化为
NULL
。例如int *p = NULL;
,这样在使用p
之前就可以通过判断p == NULL
来确定是否可以安全地进行解引用操作。
- 尽量在定义指针变量时就对它进行初始化。如果暂时不知道指针应该指向哪里,可以将它初始化为
- 及时将释放内存后的指针置为NULL:
- 在使用
free()
函数释放指针所指向的内存后,应该立即将指针赋值为NULL
。例如:
- 在使用
#include <stdio.h>
#include <stdlib.h>
int main() {int *p = (int *)malloc(sizeof(int));if (p!= NULL) {*p = 10;free(p);p = NULL;// 此时p为NULL,后续代码如果不小心使用p,就可以通过判断p == NULL避免错误操作}return 0;
}
- 注意指针的作用域和生命周期:
- 确保指针在其指向的对象的生命周期内有效。例如,在一个函数内部定义的局部指针,在函数返回后,如果这个指针指向的是函数内部的局部变量,那么这个指针就不应该再被使用,因为函数返回后,局部变量的内存已经被释放。# 七、assert断言
assert
是一个宏,用于在程序调试阶段检查一个条件是否为真。当条件为假时,程序会终止并输出错误信息。在涉及指针时,可以使用assert
来检查指针是否为NULL
等情况。- 例如:
int *p = NULL; assert(p!= NULL); // 程序会在这里终止,因为p为NULL
- 通常在程序开发过程中,使用
assert
可以帮助发现一些潜在的指针错误,提高程序的健壮性。不过在发布版本中,可能会因为性能等原因关闭assert
检查。
七、assert断言
1、assert断言的基本概念
- 定义:
assert
是一个在C语言标准库<assert.h>
中定义的宏。它用于在程序开发过程中进行调试检查,帮助程序员验证程序中的假设是否成立。其主要功能是检查一个表达式的值是否为真,如果表达式的值为假(即为0),则程序会以一种“标准错误”的方式终止,并输出相关的错误信息,包括断言失败的表达式、文件名和行号等内容。
- 目的:
- 用于捕捉程序中的逻辑错误,特别是在程序开发阶段,可以帮助定位那些不应该出现的错误情况。例如,在使用指针时,可以通过
assert
来检查指针是否为NULL
,以避免因为空指针解引用而导致的程序崩溃。
- 用于捕捉程序中的逻辑错误,特别是在程序开发阶段,可以帮助定位那些不应该出现的错误情况。例如,在使用指针时,可以通过
- 使用assert断言的示例
- 检查指针是否为
NULL
- 示例代码如下:
- 检查指针是否为
#include <stdio.h>
#include <assert.h>
int main() {int *p = NULL;// 使用assert检查指针p是否为NULLassert(p!= NULL);// 如果p为NULL,程序会在这里终止,并输出错误信息// 因为p是NULL,所以下面这行代码不会执行*p = 10;return 0;
}
- 在这个例子中,首先定义了一个指针`p`并将其初始化为`NULL`。- 然后使用`assert(p!= NULL)`来检查`p`是否不为`NULL`。由于`p`实际上是`NULL`,所以这个断言会失败。- 当断言失败时,程序会终止,并输出类似于以下的错误信息(具体格式可能因编译器和环境而异):- `Assertion failed: p!= NULL, file main.c, line 7`- 这表明在`main.c`文件的第7行,`p!= NULL`这个断言不成立。- 这样就可以很容易地定位到错误发生的位置,并且知道是由于指针为`NULL`导致的问题。
- 检查数组索引是否越界(结合指针运算)
- 假设我们有一个函数,用于遍历一个整数数组并打印元素,代码如下:
#include <stdio.h>
#include <assert.h>
void print_array(int *arr, int size) {int i;// 对于每个合法的索引,打印数组元素for (i = 0; i < size; i++) {assert((arr + i) >= arr);// 检查指针是否还在数组范围内printf("%d ", *(arr + i));}
}
int main() {int arr[] = {1, 2, 3, 4, 5};int size = sizeof(arr) / sizeof(arr[0]);print_array(arr, size);return 0;
}
- 在`print_array`函数中,`for`循环用于遍历数组。- 在每次循环中,使用`assert((arr + i) >= arr)`来检查`arr + i`这个指针是否还在数组范围内。- `arr + i`表示数组`arr`中第`i`个元素的地址,它应该总是大于或等于数组的起始地址`arr`。- 如果在循环过程中由于某种原因(例如数组索引计算错误)导致`arr + i`小于`arr`,- 断言就会失败,程序会终止并输出错误信息,帮助我们发现数组索引越界的问题。
八、指针的使用和传址调用
1、指针的使用
- 访问变量
- 基本原理:
- 通过指针可以间接访问它所指向的变量。首先要获取变量的地址,然后将这个地址存储在指针变量中,最后通过解引用指针来访问变量的值。
- 示例:
- 基本原理:
#include <stdio.h>
int main() {int num = 10;int *p = #// 解引用指针p获取num的值printf("The value of num through pointer is %d\n", *p);return 0;
}
- 在这个例子中,`int *p = #`声明了一个指针`p`并使其指向变量`num`。`*p`用于解引用指针,从而获取`p`所指向变量(即`num`)的值。
- 动态内存分配
- 基本原理:
- C语言中的
malloc
、calloc
和realloc
函数用于动态分配内存,返回的是指向所分配内存块的指针。malloc
函数分配指定字节数的内存,calloc
函数在分配内存的同时将内存块初始化为0,realloc
函数用于重新分配已经分配过的内存块的大小。
- C语言中的
- 示例(使用
malloc
):
- 基本原理:
#include <stdio.h>
#include <stdlib.h>
int main() {int *arr;int n = 5;// 分配能容纳5个整数的内存空间arr = (int *)malloc(n * sizeof(int));if (arr == NULL) {// 检查内存分配是否成功printf("Memory allocation failed!\n");return -1;}// 使用分配的内存for (int i = 0; i < n; i++) {arr[i] = i + 1;}for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}// 释放内存free(arr);return 0;
}
- 这里,`arr = (int *)malloc(n * sizeof(int));`分配了足够容纳`n`个`int`类型数据的内存空间。- 如果`arr`不为`NULL`,则表示内存分配成功,可以像使用普通数组一样使用`arr`。- 最后,使用`free(arr)`释放分配的内存。
2、传址调用
- 基本原理:
- 在C语言中,函数参数传递默认是值传递,即函数会复制一份实参的值作为形参。传址调用则是将变量的地址作为参数传递给函数。这样,函数内部可以通过指针来访问和修改外部变量的值。
- 示例(交换两个数):
#include <stdio.h>
// 交换两个整数的函数,使用传址调用
void swap(int *a, int *b) {int temp;temp = *a;*a = *b;*b = temp;
}
int main() {int x = 5, y = 10;printf("Before swap: x = %d, y = %d\n", x, y);swap(&x, &y);printf("After swap: x = %d, y = %d\n", x, y);return 0;
}
- 在这个例子中,`swap`函数接受两个指针参数`a`和`b`。在`main`函数中,通过`swap(&x, &y)`将`x`和`y`的地址传递给`swap`函数。在`swap`函数内部,通过解引用指针`*a`和`*b`来访问和交换`x`和`y`的值。这样,就实现了在函数内部修改外部变量的值。
- 通过传址调用返回多个值
- 示例(计算一个数的平方和立方):
#include <stdio.h>
// 计算一个数的平方和立方,通过指针返回结果
void square_and_cube(int num, int *square, int *cube) {*square = num * num;*cube = num * num * num;
}
int main() {int num = 3;int square, cube;square_and_cube(num, &square, &cube);printf("The square of %d is %d and the cube is %d\n", num, square, cube);return 0;
}
- 这里,`square_and_cube`函数接受一个整数`num`和两个指针`square`和`cube`。- 函数内部通过解引用指针来将计算结果(`num`的平方和立方)- 存储在`main`函数中定义的`square`和`cube`变量中,从而实现了通过一个函数返回多个值的功能。