系列文章目录
趣味c程序专栏
一.c语言关系操作符练习题(新手必会)
一.c语言常见概念(超全)
一.趣味c程序—关机程序(整蛊同学版)
二.趣味c程序—猜数字游戏(含干货知识点
三.趣味c程序—打印图形(1)(含干货知识点)
文章目录
前言
文章介绍
在编程的世界中,交换两个变量的值是一个看似简单但实则蕴含多种技巧的操作。我的这篇博客深入探讨了交换两个变量的多种方法,旨在为读者提供一份全面的指南,以便他们能够更好地理解这一基础但重要的编程操作。
无论是初学者还是经验丰富的开发者,交换两个变量都是编程中经常遇到的任务。通过这篇博客,读者将了解到从传统的临时变量法到利用异或运算、加减运算乃至指针操作等多种交换方法。每一种方法都有其独特的原理和适用场景,通过对比和实践,读者可以选择最适合自己需求的方法。
在博客中,我详细解释了每种方法的实现步骤和原理,并通过示例代码展示了如何在实际编程中应用这些方法。同时,我也探讨了各种方法的优缺点,以便读者能够根据自己的需求和编程环境做出明智的选择。
1.创建临时变量
一、简单版(创建临时变量直接交换)
要求:
交换两个整数的内容
示例
代码
- 创建中间变量
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int m, n;
printf("请输入两个想要交换的值:>");
scanf("%d %d", &m, &n);
printf("交换前:m=%d,n=%d\n", m, n);
//创建中间变量
int z = 0;
//把m给这个中间变量
z = m;
//把m给n
m = n;
//把z给n
n = z;
printf("交换后:m=%d,n=%d\n", m, n);
}
二、函数版(指针——传址调用)
示例
代码
#include <stdio.h>
// 定义一个交换两个整数指针指向值的函数
// pm 和 pn 是两个整数的指针
void swap1(int* pm, int* pn) {
int z = 0; // 定义一个临时变量z,用于保存pm所指向的值
z = *pm; // 将pm所指向的值赋给z
*pm = *pn; // 将pn所指向的值赋给pm所指向的位置
*pn = z; // 将z(原pm的值)赋给pn所指向的位置
}
int main() {
int m, n;
printf("请输入两个想要交换的值:>"); // 提示用户输入两个整数
scanf("%d %d", &m, &n); // 从标准输入读取两个整数,并保存到m和n中
printf("交换前:m=%d,n=%d\n", m, n); // 打印交换前的m和n的值
// 调用swap1函数,并传入m和n的地址
// 这样swap1函数就可以通过指针修改m和n的值了
swap1(&m, &n);
printf("交换后:m=%d,n=%d\n", m, n); // 打印交换后的m和n的值
return 0;
}
三、函数版(传值调用)——错误示范
示例
代码
// 尝试通过值传递来交换两个整数,但这种方法实际上不能交换外部变量的值
void swap2(int x, int y) {
// 声明一个临时变量z,并初始化为0(在这里初始化为0其实没必要,因为后面会立即覆盖)
int z = 0;
// 将x的值赋给z
z = x;
// 将y的值赋给x(这里的x是局部变量,不是外部变量m)
x = y;
// 将z的值(也就是原来x的值)赋给y(这里的y是局部变量,不是外部变量n)
y = z;
// 注意:这里只是交换了函数内部局部变量x和y的值,对外部变量m和n没有影响
}
int main() {
int m, n;
// 提示用户输入两个整数
printf("请输入两个想要交换的值:>");
// 从标准输入读取两个整数,并保存到m和n中
scanf("%d %d", &m, &n);
// 打印交换前m和n的值
printf("交换前:m=%d,n=%d\n", m, n);
// 调用swap2函数,但是这里传递的是m和n的值,而不是它们的地址
// 因此,swap2函数内部只是交换了局部变量x和y的值,对m和n没有影响
swap2(m, n);
// 打印交换后m和n的值(但实际上m和n的值并未改变)
printf("交换后:m=%d,n=%d\n", m, n);
return 0; // main函数返回0,表示程序正常结束
}
错因
为什么这种方法不能交换?
在C语言中,当你将一个变量作为参数传递给一个函数时,除非使用指针或引用(C++中的特性),否则你实际上是在传递这个变量的值的一个副本。这意味着在函数内部,你对这个参数所做的任何修改都不会影响到原始的外部变量。
在swap2函数中,x和y是m和n的副本。当你交换x和y的值时,你只是在修改这两个副本的值,原始的m和n的值并没有改变。这就是为什么在swap2函数返回后,m和n的值仍然保持不变的原因。
为了交换m和n的值,你需要传递它们的地址(即指针)给函数,然后在函数内部通过指针来修改原始变量的值。这就是swap1函数所做的事情,它使用指针来交换m和n的值。
四、比较传值调用和传址调用不同
swap1 函数
功能:通过指针交换两个整数的值。
参数:接收两个指向整数的指针 pm 和 pn。
实现:在函数内部,它使用了一个临时变量 z 来保存 pm 指向的值,然后将 pn 指向的值赋给 pm 指向的位置,最后将 z(即原来 pm 指向的值)赋给 pn 指向的位置。
结果:由于 swap1 函数接收的是两个整数的地址(指针),因此它可以修改外部变量 m 和 n 的值。
swap2 函数
功能:尝试通过值交换两个整数的值,但实际上只在函数内部交换了局部变量的值。
参数:接收两个整数 x 和 y。
实现:在函数内部,它使用了一个临时变量 z 来保存 x 的值,然后将 y 的值赋给 x,最后将 z(即原来 x 的值)赋给 y。但是,这里的 x 和 y 是局部变量,它们的值在函数返回后不会影响到外部变量 m 和 n。
结果:由于 swap2 函数接收的是整数的值(而不是地址),因此它不能修改外部变量 m 和 n 的值。函数执行完毕后,m 和 n 的值仍然保持不变。
易错点
误解指针与值:在使用指针时,很容易误解为传递的是值而不是地址。在 swap2 函数中,尽管它看起来像是在交换两个整数的值,但实际上它只是在交换局部变量的值,这些局部变量的值在函数返回后就消失了。
忘记取地址:在调用需要指针作为参数的函数时,如 swap1,必须确保传递的是变量的地址(使用 & 运算符)。如果忘记取地址,编译器会报错,因为函数期望的是指针类型,而不是整数类型。
误解函数的作用域:在 swap2 函数中,局部变量 x 和 y 的作用域仅限于该函数内部。任何在函数外部对 m 和 n 的修改都不会影响到 x 和 y,同样,对 x 和 y 的任何修改也不会影响到 m 和 n
总结
(1)实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实
参。
(2)传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。
2.不创建临时变量
一、使用加减运算
这种方法假设两个整数之和不会导致溢出(这在大多数情况下都是安全的,但需要注意整数的大小和范围)。
核心原理
a = a + b; //a=a+b=6+7=13
b = a - b; //b=a-b=13-6=7 b已经交换
a = a - b; //a=a-b=13-7=6 a已经交换
示例
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a, b;
// 提示用户输入两个整数
printf("请输入两个想要交换的值:>");
scanf("%d %d", &a, &b);
// 打印交换前a和b的值
printf("交换前:a=%d,b=%d\n", a, b); //a=7 b=6
a = a + b; //a=a+b=6+7=13
b = a - b; //b=a-b=13-6=7 b已经交换
a = a - b; //a=a-b=13-7=6 a已经交换
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
注意
加减方法可能面临溢出风险,特别是当处理的是大整数或者接近INT_MAX或INT_MIN的整数时。在实际应用中,异或方法通常更受欢迎,因为它既安全又高效。
二、使用异或运算
异或运算是一种二进制运算,它对每一位执行逻辑异或操作:如果两个相应的二进制位有一个为1(但不同时为1),则该位的结果值为1,否则为0。异或运算的一个重要性质是,任何数和0做异或运算,结果仍然是原来的数;任何数和其自身做异或运算,结果是0;异或运算满足交换律和结合律。
核心原理
使用异或(XOR)运算来交换两个变量的值的原理基于异或运算的几个关键特性:
任何数与0异或都等于它本身:a ^ 0 = a
任何数与它自身异或都等于0:a ^ a = 0
异或运算满足交换律: a ^ b = b ^ a
异或运算满足结合律: (a ^ b) ^ c = a ^ (b ^ c)
步骤
第一步:将 a 与 b 进行异或运算,并将结果存回 a。此时,a 包含的是 a 和 b 的异或结果,而 b 的值没有改变。
a = a ^ b; // 此时a的值是a与b异或的结果
第二步:将 b 与现在的 a(它包含 a 和 b 的异或结果)进行异或运算,并将结果存回 b。由于 b 的原始值与第一步中 a 的异或结果再次进行异或运算,根据异或运算的特性,这将恢复出 a 的原始值
b = b ^ a; // 此时b的值是b与(a^b)异或的结果,即a的原始值
这里的关键是,由于第一步中 a 已经变成了 a ^ b,所以这里的 a 实际上是 a ^ b。因此,b 与 a ^ b 进行异或运算,相当于 b ^ (a ^ b)。根据异或的结合律和交换律,这可以简化为 b ^ b ^ a,再进一步简化为 0 ^ a,即 a 的原始值。
第三步:将 a(它现在包含 a 和 b 的异或结果)与现在的 b(它包含 a 的原始值)进行异或运算,并将结果存回 a。此时,由于 a 包含的是 a 和 b 的异或结果,而 b 包含的是 a 的原始值,所以 a 与 b 进行异或运算将恢复出 b 的原始值。
a = a ^ b; // 此时a的值是(a^b)与a异或的结果,即b的原始值
同样地,这里的 a 实际上是 a ^ b,所以这里的异或运算实际上是 (a ^ b) ^ a。根据异或的结合律和交换律,这可以简化为 a ^ a ^ b,进一步简化为 0 ^ b,即 b 的原始值。
经过这三步操作后,a 和 b 的值就成功交换了。这种方法不需要使用任何额
代码
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a, b;
// 提示用户输入两个整数
printf("请输入两个想要交换的值:>");
scanf("%d %d", &a, &b);
// 打印交换前m和n的值
printf("交换前:a=%d,b=%d\n", a, b); //a=7 b=6
a = a ^ a ^ b;
//a^a=0 0^b=b 所以a=b 完成交换
b = a ^ 0;
printf("交换后:a=%d,b=%d\n", a, b);
return 0;
}
小结
异或方法更简单,且不需要考虑溢出问题,但需要注意,如果a和b相等,则它们的内容不会改变(在题目要求中这通常不是问题,因为即使相等,也没有必要交换)。
总结
通过这篇博客,我们深入探讨了交换两个变量的多种方法,包括临时变量法、异或运算、加减运算和指针操作等。每一种方法都有其独特的优势和适用场景,通过对比和实践,我们可以找到最适合自己需求的方法。
学习交换两个变量的不同方法不仅能够帮助我们更好地掌握基本的编程语法和技巧,还能提升我们的逻辑思维和问题解决能力。无论是初学者还是经验丰富的开发者,都可以从这篇博客中受益,并在实际编程中灵活运用这些方法。
希望这篇博客能够成为你学习编程道路上的一盏明灯,引领你走向更广阔的编程世界。让我们一起继续探索编程的奥秘吧!