指针使用详解

目录

一、什么是指针

1.内存

2.地址的生成

3.数据的储存

4、指针变量

5.解引用操作符*

二、指针变量类型

1.指针加减整数

2.指针的解引用

三、野指针

1.野指针的成因

2.避免野指针的方法

四、指针运算

1.指针加减整数

2.指针减指针

3.指针的关系运算

五、指针和数组

六、二级指针

七、字符指针

1.字符指针的使用

2.笔试题

八、指针数组

九、数组指针

1.数组指针的定义和创建

2.数组名和&数组名

3.数组指针的使用

十、数组与指针传参

1.一维数组传参

2.二维数组传参

3.一级指针传参

4.二级指针传参

十一、函数指针

1.函数指针

2.函数指针实现计算器

十二、函数指针数组

1.函数指针数组的定义

2.函数指针数组简化代码

十三、指向函数指针数组的指针

十四、回调函数

1.qsort函数

 2.利用冒泡排序思想模拟实现qsort函数


一、什么是指针

1.内存

程序的运行需要储存信息,而信息储存在内存中,我们为了有效地使用内存,就需要将内存划分为一个个小的内存单元,每一个单元的大小是一个字节。(一个字节比较合理,这个内存单元太小也不好,太大也不好)为了能够有效地使用每个内存单元,我们给每一个单元都定了一个编号,这个编号就叫做这个内存单元的地址。假如内存是一幢楼房,就像楼中的门牌号,通过门牌号我们可以找到对应的房间。同样在计算机中使用这样的方式,我们可以轻松地找到对应的字节位置,而不需要一个一个去比对。

内存

编号(取十六进制数)

1byte

0x00000001(十进制:1)

1byte

0x00000002(十进制:2)

1byte

0x00000003(十进制:3)

1byte

0x00000004(十进制:4)

1byte

0x00000005(十进制:5)

1byte

0x00000006(十进制:6)

1byte

......

2.地址的生成

我们的电脑中都有硬件电路,用于生成地址的电线叫地址线。当电路中有电路通过时,会产生正负脉冲,从而表示0与1.此处我们以三十二位电脑为例,它在生成地址时32根地址线同时产生电信号表示1或0,当每一个地址线组合起来时就有了许许多多的不同的排列组合方式

我可以三十二个全为0:00000000000000000000000000000000——对应0

我也可以前三十一个全为0:00000000000000000000000000000001——对应1

......

这样的排序方式一共有2的32次方种,也就是说它有是4294967296种排序,内存中就一共有这么多个字节的空间。

但是这个数字不是很直观,我们先对它除以1024得到4194304个KB,再除1024得到4096个MB,再除以1024得到4GB,也就是说在早期的三十二位电脑内存中一共有4GB的内存空间。

3.数据的储存

这里我们打开VS2022,输入以下代码,可以通过调试找到a的地址

#include <stdio.h>
int main()
{
    int a = 10;
    &a;//取出num的地址,&为取地址符号
    //这里的num共有4个字节,每个字节都有地址,但我们取出的是第一个字节的地址(较小的地址)
    printf("%pn", a);//打印地址,%p是以地址的形式打印
    return 0; 
}

具象化的话,就用以下内容表示:

内存

编号

1byte

0xFFFFFFFF

1byte

0xFFFFFFFE

1byte

......

a

0x0012FF47

0x0012FF46

0x0012FF45

0x0012FF44

1byte

......

1byte

0x0000002

1byte

0x0000001

我们实际上取出的只有0x0012FF47这个地址

我们在VS上的内存窗口上可以看到三行,包含以下内容:

地址

内存中的数据(补码)

内存数据的文本解析

0x0012FF47

a0 00 00 00(小端存储)

????(内容不定,没什么用处)

我们a的值为10,用二进制表示即为:0000 0000 0000 0000 0000 0000 0000 1010(二进制的数字表达最后一位表示2的0次方,倒数第二位就表示2的1次方,以此类推,十就是2的3次方加2的一次方也就是1010),在这个时候我们以每个四位为一组,就可以得到数据的表示方法:00 00 00 0a(在16进制数中,a表示10,b表示11,c表示12,d表示13,e表示14,f表示15)

0000 0000 0000 0000 0000 0000 0000 1010

0 0 0 0 0 0 0 a

4、指针变量

请看以下代码:

#include<stdio.h>
int main()
{
    int a = 10;
    int* p = &a;
    //我们把a这个变量的地址储存在这个变量p中,这个p就叫做指针变量,类型为int*
    return 0;
}

编号代表地址,而地址也可以成为指针,有一定的指向作用,所以叫做指针变量。简单地说指针就是地址。

那我们如何理解这个int* p = &a;呢?

(1)中间的*表示p是个指针变量,注意指针变量是p,而不是*p

(2)int表示指针指向的对象是整形

(3)p为指针变量,接受&a的内容,也就是变量的首地址

在了解这些后,我们也可以创建其它类型的指针变量,比如:

#include <stdio.h>
int main()
{
    char ch = 'w';
    char* pc = &ch;//字符型变量的指针
    *pc = 'q';
    printf("%cn", ch);
    return 0; 
}
//输出:q

5.解引用操作符*

int main()
{
	int a = 10;
	int* p = &a;
	printf("%d", *p);//这里的*p表示对指针变量p解引用,找到其对应的内容
	return 0;
}
//输出:10

注意:int*p = &a;中的*不表示解引用,通过解引用符号,我们可以轻易地找到指针地址的对应值

6.指针变量的大小

#include <stdio.h>
int main()
{
    printf("%dn", sizeof(char *));
    printf("%dn", sizeof(short *));
    printf("%dn", sizeof(int *));
    printf("%dn", sizeof(double *));
    return 0; 
}

你可能认为输出结果是:1 2 4 8

但实际上是:48 48 48 48(4或8)

原因:指针变量储存的是地址,也就是说指针变量的大小取决于存放一个地址需要的空间的大小,32位平台下地址是32个bit位(即4个字节),而64位平台下地址是64个bit位(即8个字节),所以指针变量的大小就是4或8.

结论:指针大小在32位平台是4个字节,64位平台是8个字节。

二、指针变量类型

1.指针加减整数

#include <stdio.h>
int main()
{
    int n = 10;
    char *pc = (char*)&n;
    int *pi = &n;
    printf("%pn", &n);
    printf("%pn", pc);
    printf("%pn", pc+1);
    printf("%pn", pi);
    printf("%pn", pi+1);
    return  0; 
}
//结果:
//007FFC20
//007FFC20
//007FFC21
//007FFC20
//007FFC24

在这里我们可以看到,不管打印int*还是char*指针对应的地址,它们的结果都是一样的。

那么我们为什么不能将所有的指针统一为一个数据类型呢?

然而,当我们观察两种类型的指针加一后的地址时,不难发现,int*类型的地址向后移动了4字节,char*类型的地址向后移动了1字节.

(1)总结:指针的类型决定了指针向前或者向后走一步有多大

2.指针的解引用

#include <stdio.h>
int main()
{
    int n = 0x11223344;
    char* pc = (char*)&n;
    int* pi = &n;
    *pc = 0;
    printf("%xn", n);
    *pi = 0; 
    printf("%xn", n);
    return 0;
}
//结果:
//11223300
//0

在内存中,0x11223344这个数字以小端字节序存储(44 33 22 11),先用char* 的指针解引用只能访问一个字节,所以会把第一个字节改为0,也就会打印11223300;而 int* 的指针解引用能访问四个字节,所以会把第四个字节都改为0,也就会打印0

(2)总结:指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。

三、野指针

野指针就是指针指向的位置是不可知的,就像一条没有拴住的恶犬,接近它是会受伤的。

1.野指针的成因

(1)指针没有初始化

#include<stdio.h>
int main()
{
    int* p;//没有初始化
    *p = 20;//不清楚地址在哪里,为野指针
    return 0;
}

(2)指针的越界访问

#include<stdio.h>
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9,10};
    int i = 0;
    int* p = arr;
    for(i=0; i<=10; i++)
    {
        printf("%d ",*(p+i));    
        //可以读取到arr[10],越界访问,为野指针
    }
    return 0;
}

(3)指针指向的空间被释放

#include<stdio.h>
int* add(int x,int y)
{
    int d = x+y;
    int* p = &d;
    return p;//返回和的地址
}
int main()
{
    int a = 10;
    int b = 20;
    int* c = add(a,b);
    //因为退出了函数,原来p指针这个地址也就不在程序的作用域内了,c就成为了野指针
    printf("%d ",*c);
    //这个解引用虽然数值是正确的,但由于这个空间不在程序的作用域内,
    //我们无法确定是否会有其他的操作会改变它的值
    return 0;
}

2.避免野指针的方法

(1)指针初始化

(2)小心指针越界

(3)指针指向空间释放即使置NULL

(4)避免返回局部变量的地址

(5)指针使用之前检查有效性

可以在使用指针时加上下面的代码:

#include <stdio.h>
int main()
{
    int a = 10;
    p = &a;
    //使用的指针一定要有准确的地址
    int *p = NULL;
    //不用的指针记得置空
    if(p != NULL)
    {
        *p = 20;
    }
    //空指针不能解引用,加上判断
    return 0; 
}

四、指针运算

1.指针加减整数

#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
//这里虽然下标为五的元素属于越界访问,但是并没有读取它的内容,不算越界访问
{
     *vp++ = 0; 
     //vp先解引用,然后再++
}

指针加减整数可以让地址向后或向前移动对应的字节数。

2.指针减指针

int my_strlen(char *s) 
{
    char *p = s;
    while(*p != '' )
        p++;
    return p-s; 
}

指针减指针可以求出中间所差的类型对应字节数的个数

3.指针的关系运算

for(vp = &values[N_VALUES]; vp > &values[0];)
{
    *--vp = 0; 
}

指针的比较实际上就是十六进制数字的比较

五、指针和数组

#include <stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    printf("%pn", arr);
    printf("%pn", &arr[0]);
    int i = 0;
    for(i=0; i<10; i++)
    {
        printf("&arr[%d] = 0x%p",i,&arr[i])    
    }
    return 0; 
}
//结果:
//00DBFD88
//00DBFD88
//&arr[0] = 0x00DBFD88 = p+0
//&arr[1] = 0x00DBFD8C = p+1
//&arr[2] = 0x00DBFD90 = p+2
//&arr[3] = 0x00DBFD94 = p+3
//&arr[4] = 0x00DBFD98 = p+4
//&arr[5] = 0x00DBFD9C = p+5
//&arr[6] = 0x00DBFDA0 = p+6
//&arr[7] = 0x00DBFDA4 = p+7
//&arr[8] = 0x00DBFDA8 = p+8
//&arr[9] = 0x00DBFDAC = p+9

数组名为数组首元素地址:arr = 00DBFD88,arr[0] = 00DBFD88

&arr[0] = 0x00DBFD88 = p+0

&arr[1] = 0x00DBFD8C = p+1

&arr[2] = 0x00DBFD90 = p+2

&arr[3] = 0x00DBFD94 = p+3

&arr[4] = 0x00DBFD98 = p+4

&arr[5] = 0x00DBFD9C = p+5

&arr[6] = 0x00DBFDA0 = p+6

&arr[7] = 0x00DBFDA4 = p+7

&arr[8] = 0x00DBFDA8 = p+8

&arr[9] = 0x00DBFDAC = p+9

从这些结果我们得到arr[i]等同于*(p+i),这也就解释了数组中元素的访问其实使用了指针的思想,也让我们了解到数组元素的访问可以使用指针。

六、二级指针

1.指针变量也是变量,是变量就有地址,那指针变量的地址就可以储存在二级指针内 。

2.二级指针解引用需要两次才能找到变量

#include<stdio.h>
int main()
{
    int a = 0;
    int* p1 = &a;
    int** p2 = &p;
    //二级指针,存放指针变量的地址
    //可以看作int* *p2,前面的int*表示指向的对象为int*类型,后面的*表示p2为指针变量
    printf("%d",**p2);//p2解引用一次得到指针变量p1,再解引用得到a
    return 0;
}

七、字符指针

1.字符指针的使用

#include<stdio.h>
int main()
{
    char a = 'w';
    char* p = &a;
    printf("%c",*p);
    //这是最简单的使用方法
    const char* pstr = "hello bit.";
    //这里指针保存了这个字符串的首字符地址而不是整个字符串
    printf("%sn", pstr);
    //按字符串打印内存中的数据会打印到终止
    return 0;
}

2.笔试题

#include <stdio.h>
int main()
{
    char str1[] = "hello world.";
    char str2[] = "hello world.";
    const char *str3 = "hello world.";
    const char *str4 = "hello world.";
    if(str1 ==str2)
        printf("str1 and str2 are the samen");
    else
        printf("str1 and str2 are not the samen");       
    if(str3 ==str4)
        printf("str3 and str4 are the samen");
    else
        printf("str3 and str4 are not the samen");       
    return 0; 
}
//结果:
//str1 and str2 are not same
//str3 and str4 are same

这段代码中的str1与str2是两个不同的数组,元素的内容都是hello world.,而str3与str4是两个指针变量,内容都是hello world.的首字符地址。

数组储存在栈区,每创建一个新的数组都需要占用内存空间存储,所以两个地址不同;字符串常量储存在静态区,不需要储存多个这样的字符串,所以两个指针变量都指向了同一个地址。

八、指针数组

指针数组也是数组,只是存放的元素是指针。

#include<stdio.h>
int main()
{
    int a = 0;
    int b = 0;
    int c = 0;
    int* arr[3]={&a,&b,&c};
    //这就是一个简单的指针数组
    return 0;
}

九、数组指针

1.数组指针的定义和创建

定义:指向数组的指针

#include<stdio.h>
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    //int arr[10]是一个数组,我们首先去掉数组名
    //int [10]在中间写上指针变量名p,再写上*表示p为指针变量
    //最后为了防止被解析为指针数组再加上括号:int (*p)[10],这就是一个指向数组的指针
    //[10]表示指向的数组有是个元素,前面的int表示数组的元素为int类型
    return 0;
}

2.数组名和&数组名

#include <stdio.h>
int main()
{
    int arr[10] = { 0 };
    printf("arr = %pn", arr);
    printf("&arr= %pn", &arr);
    printf("arr+1 = %pn", arr+1);
    printf("&arr+1= %pn", &arr+1);
    return 0; 
}
//结果:
//arr = 00EFFCDC
//&arr= 00EFFCDC
//arr+1 = 00EFFCE0
//&arr+1= 00EFFD04

arr = 00EFFCDC,&arr= 00EFFCDC二者虽然在内容上是一样的,但是arr+1 = 00EFFCE0跳过了4字节,&arr+1= 00EFFD04跳过了整个数组的40字节,二者加一跳过的字节数不同。

3.数组指针的使用

重要思想:一个二维数组可以看作多个一维数组的组合,但二维数组在内存中是连续存放的。

#include<stdio.h>
void print1(int arr[3][5], int r, int c)//二维数组传参可以直接写数组
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0;j < c;j++)
		{
			printf("%d ", arr[i][j]);
		}
		printf("n");
	}
}
void print2(int (*arr)[5], int r, int c)//但是在本质上,用数组指针接收会更好
//由于arr[1]=*(p+1),我们用指针的思想改变代码
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0;j < c;j++)
		{
                        //printf("%d ",arr[i][j]);
                        //printf("%d ",*((arr[i])+j));
			printf("%d ", *(*(arr+i)+j));
                        //上面三行代码效果是一样的                          
		}
		printf("n");
	}
}
int main()
{
	int arr[3][5] = { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };
	printf("print1n");
	print1(arr, 3, 5);//二维数组的数组名也是首元素地址,但是这个首元素是首个一位数组的地址
	printf("print2n");
	print2(arr, 3, 5);
	return 0;
}
//结果:
//print1
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7
//print2
//1 2 3 4 5
//2 3 4 5 6
//3 4 5 6 7

请看下面的代码,注意理解数据的类型

#include<stdio.h>
int main()
{
    //当去掉变量名时剩下的就是数据的类型
    int parr[5];//整形数组,共有五个元素
    int* parr1[10];//整型指针的数组,共有十个元素
    int(*parr2)[10];//数组指针,指向的数组有十个整型元素,指针的类型为int(*)[10]
    int(*parr3[10])[5];//指针数组,包含十个数组指针,指向的是个数组都是有五个整型元素
    return 0;
}

十、数组与指针传参

1.一维数组传参

#include <stdio.h>
void test(int arr[10])//可以直接写整形数组
{}
void test(int arr[])//数组的元素个数可以省略
{}
void test(int* arr)//本质上数组名是指针
{}
void test2(int* arr[20])//这是个整型指针数组,不符合
{}
void test2(int** arr)//这是个二级指针,也不符合
{}
int main()
{
    int arr[10] = { 0 };
    test(arr);
    test2(arr);
}

2.二维数组传参

void test(int arr[3][5])
{}
void test(int arr[][])
{}
void test(int arr[][5])
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素才方便运算。



//二维数组的首元素地址是第一行一维数组的地址
void test(int* arr)//这是一个整型指针,不符合
{}
void test(int* arr[5])//这是一个整型指针数组,不符合
{}
void test(int(*arr)[5])//这是一个整型数组指针,符合
{}
void test(int** arr)//这是一个二级指针,不符合
{}
int main()
{
    int arr[3][5] = { 0 };
    test(arr);
}

3.一级指针传参

#include <stdio.h>
void print(int *p, int sz) 
{
    int i = 0;
    for(i=0; i<sz; i++)
    {
        printf("%dn", *(p+i));
    }
}
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9};
    int *p = arr;
    int sz = sizeof(arr)/sizeof(arr[0]);
    //一级指针p,参数为对应的指针类型
    print(p, sz);
    return 0; 
}

4.二级指针传参

#include <stdio.h>
void test(int** ptr) 
{
    printf("num = %dn", **ptr);
}
int main()
{
    int n = 10;
    int* p = &n;
    int** pp = &p;
    test(pp);
    test(&p);//二者相同
    return 0;
}

十一、函数指针

1.函数指针

函数指针是指向函数的指针

#include<stdio.h>
void print(int n)
{
}
int main()
{
    //首先函数的定义去掉函数名:void (int n)
    //在中间加上(*)表明它是指针变量:void (*)(int)
    //写上变量名,也可以删去内部参数的变量名:void (*p1)(int)

    void (*p1)(int) = print;//初始化
    void (*p2)(int) = &print;
    printf("0x%pn", p1);
    printf("0x%pn", p2);
    return 0;
}
//结果:
//0x009013F2
//0x009013F2

(1)函数名与&函数名都是函数的地址

(2)这个函数地址储存在函数区里,所以函数的地址与它是否被调用无关

2.函数指针实现计算器

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}
//定义加减乘除
void menu()
{
	printf("**************************n");
	printf("*** 1.Add ****** 2.Sub ***n");
	printf("*** 3.Mul ****** 4.Div ***n");
	printf("********* 0.exit *********n");
	printf("**************************n");
}
//打印初始界面
void calu(int (*f)(int, int))//接收函数
{
	int ret = 0;
	int a = 0;
	int b = 0;
	printf("请输入两个值:");
	scanf("%d %d", &a, &b);
	ret = f(a, b);//调用相应函数
	printf("结果为:%dn", ret);
}
//通过函数指针可以简化代码
int main()
{
	menu();
	int input = 0;
	do
	{
		printf("请输入:");
		scanf("%d", &input);
		switch(input)//根据input的值判断加减乘除
		{
		    case 0:
		    {
				printf("退出程序");
				break;
		    }
		    case 1:
		    {
				calu(Add);//传参相应的函数指针
				break;
		    }
			case 2:
			{
				calu(Sub);
				break;
			}
			case 3:
			{
				calu(Mul);
				break;
			}
			case 4:
			{
				calu(Div);
				break;
			}
			default:
			{
				printf("请重新输入n");
				break;
			}
		}
	}while (input);
	return 0;
}

十二、函数指针数组

1.函数指针数组的定义

函数指针数组是存储函数指针的数组

#include<stdio.h>
int Add(int x, int y)
{
    return x + y;
}
int Sub(int x, int y)
{
    return x - y;
}
int Mul(int x, int y)
{
    return x * y;
}
int Div(int x, int y)
{
    return x / y;
}
int main()
{

    int (*arr[5])(int, int) = { Add,Sub,Mul,Div };
    //函数指针数组,元素类型为int (*)(int, int)
    //内部写上数组名和元素个数:int (*arr[5])(int, int)
    int (**p[5])(int, int)
    return 0;
}

2.函数指针数组简化代码

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int Sub(int x, int y)
{
	return x - y;
}
int Mul(int x, int y)
{
	return x * y;
}
int Div(int x, int y)
{
	return x / y;
}
void menu()
{
	printf("**************************n");
	printf("*** 1.Add ****** 2.Sub ***n");
	printf("*** 3.Mul ****** 4.Div ***n");
	printf("********* 0.exit *********n");
	printf("**************************n");
}
void calu(int (*f)(int, int))
{
	int ret = 0;
	int a = 0;
	int b = 0;
	printf("请输入两个值:");
	scanf("%d %d", &a, &b);
	ret = f(a, b);
	printf("结果为:%dn", ret);
}
int main()
{
	menu();
	int input = 0;
	int (*arr[5])(int, int) = { 0,Add,Sub,Mul,Div };//函数指针数组,对应数字对应函数
	do
	{
		printf("请输入:");
		scanf("%d", &input);
		if(input>=1 && input<=4)
		{
			calu(arr[input]);//对应函数,大量简化代码
		}
		else if (input == 0)
		{
			printf("退出程序");
		}
                else
		{
			printf("请重新输入n");
		}
	} while (input);
	return 0;
}

十三、指向函数指针数组的指针

指向函数指针数组的指针就是指向函数指针数组的指针

禁止套娃~

是不是已经晕了,其实这样可以无限套娃。了解这些定义方法是帮我们认识这些指针与数组的定义方法。

void test(const char* str) 
{
    printf("%sn", str);
}
int main()
{
    //函数指针pfun
    void (*pfun)(const char*) = test;
    //函数指针的数组pfunArr
    void (*pfunArr[5])(const char* str);
    pfunArr[0] = test;
    //指向函数指针数组pfunArr的指针ppfunArr
    void (*(*ppfunArr)[5])(const char*) = &pfunArr;
    return 0; 
}

十四、回调函数

定义:回调函数就是一个被作为参数传递的函数,一个函数作为另一个函数参数。

在下面,我们通过qsort(快速排序)函数来讲解

1.qsort函数

(1)qsort函数定义在stdlib.h的头文件中,注意包含头文件。

(2)qsort一共有四个参数,需要排序元素的首地址(void*),元素的个数(size_t),每个元素的大小(size_t),用于定义判定大小方式的compare函数,也就是回调函数的使用。

(3)compare函数需要满足参数为void*的指针,两个元素相减的结果为正数,前大于后;两个元素相减的结果为负数,后大于前;两个元素相减的结果为零,二者相等。

(4)qsort函数可以排序任何类型的数据且默认排升序。

#include<stdio.h>
#include<stdlib.h>
int compare(const void* e1, const void* e2)
{
	return (*(int*)e1 - *(int*)e2);
}
int main()
{
	int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
	qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(arr[0]), compare);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 2.利用冒泡排序思想模拟实现qsort函数

//使用冒泡排序思想模拟实现qsort函数
#include<stdio.h>
int int_compare(const void* e1, const void* e2)//设计的函数,这只是int类型的比较,还可以编写其他函数排序其他数据
{
	return (*(int*)e1 - *(int*)e2);
}
void exchange(char* p1, char* p2, int sz)//一个字节一个字节交换
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		int temp = 0;
		temp = *(p1+i);
		*(p1+i) = *(p2+i);
		*(p2 + i) = temp;
	}
}
void my_sort(void* p, int n, int sz, int compare(const void*, const void*))
{
	char* arr = (char*)p;
	int i = 0;
	for (i=0; i<n-1; i++)
	{
		int j = 0;
		for (j=0; j<n-i-1; j++)
		{
			if (compare(arr + sz * j, arr + sz * (j + 1)))//以自己设计的函数的返回值确定先后顺序
			{
				exchange(arr + sz * j, arr + sz * (j + 1), sz);
			}
		}
	}
}
int main()
{
	int arr[10] = { 10,9,8,7,6,5,4,3,2,1 };
	my_sort(arr, sizeof(arr)/sizeof(arr[0]), sizeof(arr[0]),int_compare);
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
//结果:1 2 3 4 5 6 7 8 9 10

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>