1.采用中间变量
include<stdio.h>
void swap(int *x,int *y);//函数原型声明
int main(){
int a=3;
int b=4;
printf("交换前 a=%d b=%d\n",a,b);
swap(&a,&b);
printf("交换后 a=%d b=%d\n",a,b);
}
void swap(int *x,int *y){
int tmp;
tmp=*x;
*x=*y;
*y=tmp;
}
tips:
这里你将会遇见一个问题,如果定义的swap函数为void swap(x,y) ,而不是指针呢,你会发现数值没有交换,这其实是一种假交换,形式参数实际上只是实际参数的一份临时拷贝,在函数内部交换完毕后又被释放了,并没有改变原来的值。
而使用指针,过程与传值类似,只是传过来的是两个变量的地址,同时在调用swap函数时为swap(&a,&b),确保传入的为地址。
2.不使用中间变量
void swap(int *x,int *y){//假设x为10,y为5
*x=*x+*y;//x现在是15
*y=*x-*y;//y现在是15-5=10
*x=*x-*y;//x现在是15-10=5
}
tips:
这里只是单纯的数值操作,大家试试就懂了
3.支持多类型数值的交换
上面我们对int类型进行交换,那怎么对floatl类型进行交换呢,如果你说直接采用上面的代码,int类型也可以转换为float类型啊,例如采用上面同代码,float a=3.14,float b=4.13,有的编译器可能会交换,但会警告你类型不匹配,当然,大多数的编译器可能无法运行,所以我们用什么方法可以让数值交换不局限于类型呢,这里只列举一种---宏定义
#include <stdio.h>
#include <stdlib.h>
#define SWAP(x,y) x=x+y;y=x-y;x=x-y;
int main()
{
int a=3;
int b=4;
float c=3.14;
float d=4.13;
SWAP(a,b);
SWAP(c,d);
printf("交换后 a=%d b=%d\n",a,b);
printf("交换后 c=%.2f d=%.2f\n",c,d);
}
怎么样,漂亮的宏定义是不是异常的简洁,同时也解决了我们对类型的要求。
我们使用#define(预处理器指令)来定义宏,现在实例一个简单的宏
#define PI 3.14
//#define 预处理器指令
//PI 宏
//3.14 替换体
定义好这条宏之后,后面所有出现的PI,我们都认为是3.14
宏可以分为无参数类型和有参数类型,上述举例即为无参数类型,而我们定义的SWAP(x,y)即为有参数类型,相信这个不难理解,那么宏在代码中最后是什么样子呢,继续使用上方的SWAP(x,y),我们将宏展开
#include <stdio.h>
#include <stdlib.h>
//#define SWAP(x,y) x=x+y;y=x-y;x=x-y;
int main()
{
int a=3;
int b=4;
float c=3.14;
float d=4.13;
//SWAP(x,y)
a=a+b;
b=a-b;
a=a-b;
//SWAP(x,y)
c=c+d;
d=c-d;
c=c-d;
printf("交换后 a=%d b=%d\n",a,b);
printf("交换后 c=%.2f d=%.2f\n",c,d);
}
这就是宏的效果,简单来说就是用替换体的内容代替代码中宏出现的位置,现在是不是感觉和函数的作用差不多,那么我们到底是选择函数好一点还是宏好一点呢,这个其实没有硬性规定,宏和函数的选择其实是时间与空间的权衡。
在调用函数的时候,需要开辟栈空间用来储存返回地址,函数返回同时又要释放堆栈,也就是程序的控制必须跳转至函数内,然后再返回主调程序,这种过程将降低代码的运行效率,同时函数的参数必须被声明为某种特定的类型,而宏定义可以应用于各种类型(上面代码很好地体现了这一点),所以使用宏的好处是不是显而易见呢。
当然使用宏不是全无坏处,使用宏将生成内联代码,也就是在程序中生成语句,每一次调用宏,即在程序中插入代码,上面调用了两次SWAP(x,y),程序中就插入了两次代码,那如果我们调用100次呢,要知道每一行代码都是要占用空间的,而调用函数时,假如调用100次,而程序只有一份函数语句的副本,所以节省了空间。
所以在函数和宏的使用中我们需要考虑首先使用宏和函数是否有较大差异,其次使用宏是否有效,毕竟只调用一次宏显然无法明显的减少程序的运行时间,所以在嵌套循环中是不是很合适呢,嘿嘿。
最后,使用宏需要有几点注意:
1.宏名不能有空格,而替换体可以有
2.采用大写字母表示宏的名称,这是一个惯例,当其他人看见大写的常量时,就能意识到这可能是宏
4.不使用赋值符号“=”(提示:直接对内存进行操作)
假如我们不使用宏,还是选择函数来支持多种类型的交换呢,我们都知道c有void*,作为无类型指针,它可以指向任何数据类型,但是按照ANSI标准,不能对void指针进行算法操作。这个时候就可以考虑采用内存拷贝函数了,常用的内存拷贝函数有很多,例如memcpy()、memmove()、strcpy(),他们的使用与具体参数,大家可以参考其他博客,这里我使用memcpy()
memcpy(void *x,void *y,size_t n),简单理解就是将y指定位置的前n个字节拷贝到x的指定位置,在数组操作中感觉极为有用,这里我们要交换两个值,只要知道值的类型大小,设定好n,是不是就可以对想x,y进行交换呢,以下是最终代码
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
void swap(void *x,void *y,size_t n);//函数原型声明
int main()
{
int a=3;
int b=4;
float c=3.14;
float d=4.13;
swap(&a,&b,sizeof(a));
swap(&c,&d,sizeof(b));
printf("%d %d\n",a,b);
printf("%.2f %.2f\n",c,d);
return 0;
}
void swap(void *x,void *y,size_t n){
void *tmp=(void *)malloc(n);//申请使用一个大小为n字节的内存
if(tmp!=NULL){
memcpy(tmp,x,n);//内存拷贝函数
memcpy(x,y,n);
memcpy(y,tmp,n);
free(tmp);//释放空间
}
}
在刚开始的时候,假如我们将y拷贝给了x,这时候x的值发生了改变,那我们怎么将原来x的值拷贝g给y呢,我的思路是添加一个新的指针,保存原来x的值,于是就有了下面的代码
void swap(void *x,void *y,size_t n){
void *tmp=x//我单纯的添加了一个指针
memcpy(x,y,n);
memcpy(y,tmp,n);
}
这样写其实tmp和x共用了一个内存空间,实际上并没有保存x的值,请原谅一个初学者对指针的单纯理解,所以我们需要对tmp分配一个新的空间,所以用到了malloc()函数,于是代码变成了这样
void swap(void *x,void *y,size_t n){
void *tmp=(void *)malloc(n);
memcpy(tmp,x,n);
memcpy(x,y,n);
memcpy(y,tmp,n);
free(tmp);
}
malloc() 向系统申请分配指定字节的内存空间,返回类型是 void* 类型.在上面代码中由于需要我们将需要的内存空间设定为n,同时由于支持多种类型转换前面的参数依旧为(void *).那这样是不是就大功告成了呢,使用malloc()为动态内存分配,分配与释放时都会占用cpu支援,那我们如何能保障你的内存剩余可以满足程序的需要呢?所以这里我犯了一个错误,因为我们不知道现在的内存是否够用。所幸,malloc()函数的返回值可以解决这个问题
如果分配成功则返回指向被分配内存的指针,否则返回空指针NULL。
所以添加一个判断条件即可,最终代码如下
void swap(void *x,void *y,size_t n){
void *tmp=(void *)malloc(n);
if(tmp!=NULL){
memcpy(tmp,x,n);
memcpy(x,y,n);
memcpy(y,tmp,n);
free(tmp);//使用完记得释放
}
}
以上就是全部内容了,感谢您的观看,新人学习,如有不当,恳请指正