前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >C语言进阶(八) - 指针进阶

C语言进阶(八) - 指针进阶

作者头像
怠惰的未禾
发布于 2023-04-27 13:12:12
发布于 2023-04-27 13:12:12
64900
代码可运行
举报
文章被收录于专栏:Linux之越战越勇Linux之越战越勇
运行总次数:0
代码可运行

前言

指针是一个变量,用来存放地址。而地址是内存单元的编号。 指针的大小是4字节或8字节。 指针具有多种类型,不同类型的指针看待内存单元的视角不同,管理的内存单元的大小也有差异。 指针可以进行加减运算、比较大小等运算。

接下来是更加详细的的指针专题!

1. 字符指针

1.1 用法

是指向字符的指针,用来存放字符的地址。 重点在后头。

用法1:存放单个字符的地址并解引用。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    char ch = 'a';
    char* p = &ch;
    *p = 'b';
    printf("%c", ch);
    return 0;
}

用法2:存放字符串的首元素的地址,可以找到该字符串。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <string.h>

int main(){
    const char *p = "abcdef";//这是字符串常量,不能被改变,所以用const修饰人为防止修改。
    int len = strlen(p);
    printf("%s", p);
    return 0;
}

注意:一个字符指针只能存放一个地址,存不下整个字符串。

1.2 一个例子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    char* p1 = "abcdef";
    char* p2 = "abcdef";
    if(p1 == p2){
        printf("p1 == p2\n");   
    }
    else{
        printf("p1 != p2\n");
    }
    
    char str1[] = "abcdef";
    char str2[] = "abcdef";
    if(str1 == str2){
        printf("str1 == str2\n");
    }
    else{
        printf("str1 != str2\n");
    }
    return 0;
}

运行结果:

结果分析:

"abcdef"是字符串常量,创建之后就不能够改变,其在内存中是唯一的一份,字符指针p1与p2均指向了字符串常量的首元素,二者相等。 字符数组则有些不同,str1[[]与str2[]是两个不同的数组,二者在内存空间中占据着不同的位置,数组名是数组首元素的地址,str1与str2是不同的数组名,二者不同。


2. 指针数组

是存放指针的数组。 重点在后头。

2.1 用法

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    //(一级)整型指针数组
    int* arr1[10] = {0};
    //二级整型指针数组
    int** arr2[10] = {0};
    //一级字符指针数组
    char* ch1[10] = {0};
    //二级字符指针数组
    char** ch2[10] = {0};
    
    return 0;
}

2.2 一个例子 - 关联几个一维数组

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main() {
    int arr1[5] = { 1,2,3,4,5 };
    int arr2[5] = { 2,3,4,5,6 };
    int arr3[5] = { 3,4,5,6,7 };
    int* parr[3] = { arr1, arr2, arr3 };
    int i = 0;
    for (i = 0; i < 3; i++) {
        int j = 0;
        for (j = 0; j < 5; j++) {
            printf("%d ", parr[i][j]);//*(parr[i]+j)
        }
        printf("\n");
    }
    return 0;
}

运行结果:

arr1、arr2、arr3这三个数组并不一定连续,与二维数组是连续的有着本质不同。


3. 数组指针

是指向数组的指针,用来存放数组的地址。 重点在后头。

3.1 形式

例如:int (*p)[5]

解释:看见小括号,p1小于*结合,故p1首先是一个指针,指向一个数组,该数组有5个元素,每个元素的类型都是int。 p的类型是去掉p之后的部分 ,即int (*)[5]

注意:[]的优先级高于*

3.2 再探数组名

数组名一般情况下指的是数组首元素的地址。 除了两种特殊情况下指的是整个数组。

  1. sizeof(数组名),此时数组名单独出现在sizeof运算符内部,表示整个数组,计算的是整个数组的大小。
  2. &数组名,此时数组名表示整个数组,取出的是整个数组的地址,只是数组上等于数组首元素的地址。

举例说明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    int arr[10] = {0};
    printf("sizeof(arr[0]) == %d\n", sizeof(arr[0]));
    printf("sizeof(arr) == %d\n", sizeof(arr));
    //-------------------------------------------------
    printf("arr == %p\n", arr);
    printf("arr + 1 %p\n", arr + 1);
    printf("arr[0] == %p\n", arr[0]);
    printf("arr[0] + 1%p\n", arr[0] + 1);
    printf("&arr == %p\n", &arr);
    printf("&arr + 1%p\n", &arr + 1);
    
    return 0;
}

运行结果:

3.3 使用数组指针

  1. 指向一维数组:
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    int (*p)[10] = &arr;
    int i = 0;
    for(i=0; i<10; i++){
        printf("%d ", *(*p + i));//
    }
    return 0;
}

运行结果:

p是整个数组的地址,*p就是整个数组,相当于数组名arr,也就是数组首元素的地址,**p就是数组首元素arr[0]。

  1. 指向二维数组

正常二维数组使用:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

void prints(int arr[3][4], int row, int col) {
    int i = 0;
    for (i = 0; i < row; i++) {
        int j = 0;
        for (j = 0; j < col; j++) {
            printf("%d ", arr[i][j]);
        }
        printf("\n");
    }
}
int main() {
    int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
    prints(arr, 3, 4);

    return 0;
}

使用数组指针:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

void prints(int (*p)[4], int row, int col) {
    int i = 0;
    for (i = 0; i < row; i++) {
        int j = 0;
        for (j = 0; j < col; j++) {
            printf("%d ", *(*(p+i)+j));//p[i][j]
        }
        printf("\n");
    }
}
int main() {
    int arr[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
    prints(arr, 3, 4);

    return 0;
}

运行结果:

3.4 二维数组的首元素探讨

二维数组的首元素是一维数组。 二维数组在内存中实际上是以一维数组的方式进行储存的。

3.5 数组指针数组

是存放数组指针的数组。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int main(){
    int arr1[] = {1,2,3,4,5};
    int arr2[] = {2,3,4,5,6};
    int arr3[] = {3,4,5,6,7};
    int (*arr[3])[5] = {&arr1, &arr2, &arr3};
    
    return 0;
}

4. 数组参数与指针参数

4.1 一维数组传参

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void test_1(int arr[10]){
//正确
}
void test_2(int arr[]){
//正确
}
void test_3(int* arr){
//正确
}
int main(){
    int arr[10] = {0};
    test_1(arr);
    test_2(arr);
    test_3(arr);
    return 0;
}
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void test_1(int* arr[10]){
//正确
}
void test_2(int** arr){
//正确
}

int main(){
    int* arr[10] = {0};
    test_1(arr);
    test_2(arr);
    return 0;
}

4.2 二维数组传参

多维数组作为参数时,只能够省略第一维的数,也就是以一个方框的数。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void test_1(int arr[3][4]){
//正确
}
void test_2(int arr[][4]){
//正确
}
void test_3(int arr[][]){
//错误!
}
void test_4(int* arr){
//错误!
}
void test_5(int* arr[4]){
//错误!
}
void test_6(int (*arr)[4]){
//正确
}
void test_7(int** arr){
//错误!
}
int main(){
    int arr[3][4];
    test_1(arr);
    test_2(arr);
    test_3(arr);
    test_4(arr);
    test_5(arr);
    test_6(arr);
    test_7(arr);
    return 0;
}

4.3 一级指针传参

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

void test(int *p){
   
}

int main(){
    int arr[10] = {1,2,3,4,5,6,7,8,9,0};
    int* p = arr;
    test(p);
    return 0;
}

当函数参数是一级指针时,传递的数据可以有哪些情况?

void test(int *p){} 可以是一级指针、一维数组的数组名、&变量名、二级指针解引用。

4.4 二级指针传参

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void test(int** p){

}
int main(){
    int a = 10;
    int* pa = &a;
    int** ppa = &pa;
    test(ppa);
    test(&pa)
    return 0;
}

当函数参数是二级指针时,传递的数据可以有那些情况?

void test(int** p){} 可以是二级指针int **p、 一级指针的地址、指针数组的数组名int* arr[5]


5. 函数指针

是指向函数的指针,用来存放函数的地址。 一个函数在编译期间就分配了地址。

5.1 形式

int (*p)(int,int); 首先是一个指针p,指向一个函数,这个函数的参数有两个,均是int类型,这个函数的返回类型是int类型。

5.2 类比数组指针

函数名就是函数的地址,&函数名也是函数的地址,故二者等价。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int Add(int x, int y){
    return x + y;
}

int main(){
    printf("%p", &Add);
    printf("%p", Add);
    
    return 0;
}

运行结果:

&Arr - 取出了数组的地址 &Add - 取出了函数的地址

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int Add(int x, int y){
    return x + y;
}
int main(){
    int arr[10] = {0};
    int (*p1)[10] = &arr;
    //
    int (*p)(int,int) = &Add;//int (*p)(int, int) = Add
    return 0;
}

5.3 函数指针的使用方式分析

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>

int Add(int x, int y) {
    return x + y;
}

int main() {
    int (*p)(int, int) = &Add;//int (*P)(int, int) = Add
    int x = 3;
    int y = 4;

    //直接函数调用
    int ret1 = Add(x, y);
    //通过函数指针的函数调用
    int ret2 = (*p)(x, y);
    int ret3 = p(x, y);//由函数直接调用知,其直接使用的是函数名-也就是函数的地址,而p中存放的就是函数的地址,故p与函数名等价
    //由此,*在此处函数调用下不起作用,可以没有,也可以有多个。
    int ret4 = (*****p)(x, y);

    int ret5 = (*Add)(x, y);

    printf("ret1 == %d\n", ret1);
    printf("ret2 == %d\n", ret2);
    printf("ret3 == %d\n", ret3);
    printf("ret4 == %d\n", ret4);
    printf("ret5 == %d\n", ret5);
    return 0;
}

运行结果:

5.4 函数指针拓展分析

(* ( void (*)() ) 0)(); 这是一次函数调用,调用了0地址处的函数。 整数0被一个函数指针void (*)()强制类型转换为该函数指针类型,之后解引用间接对0地址处的函数进行调用。 其中这个函数的没有形参,返回类型为void。

void (* signal( int , void (*)(int) ) )(int); 这是一个函数声明。 函数名是signal,形参一个是int;另一个是函数指针void (*)(int),该函数指针指向一个形参为int,返回类型为void的函数。 返回类型又是一个函数指针void (*)(int),该函数指针指向一个形参为int,返回类型为void的函数。

使用typedef对函数指针void (*)(int)类型进行重命名,简化上面的函数声明:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//对void (*)(int)类型重命名为pf
typedef void (* pf)(int);
//对void (* signal( int , void (*)(int) ) )(int)进行化简
pf signal(int, pf);

5.5 函数指针的用途举例:

实现一个简单的计算器

  1. 未使用函数指针版本
代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void menu() {
    printf("****************************\n");
    printf("****   1.Add    2.Sub   ****\n");
    printf("****   3.Mul    4.Div   ****\n");
    printf("*******   0.Exit   *********\n");
    printf("****************************\n");
}
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 input = 0;
    int x, y;
    int ret = 0;
    do {
        menu();
        printf("请输入你的选择>");
        scanf("%d", &input);
        switch (input) {
        case 1:
            printf("请输入两个操作数>");
            scanf("%d %d", &x, &y);
            ret = Add(x, y);
            printf("%d\n", ret);
            break;
        case 2:
            printf("请输入两个操作数>");
            scanf("%d %d", &x, &y);
            ret = Sub(x, y);
            printf("%d\n", ret);
            break;
        case 3:
            printf("请输入两个操作数>");
            scanf("%d %d", &x, &y);
            ret = Mul(x, y);
            printf("%d\n", ret);
            break;
        case 4:
            printf("请输入两个操作数>");
            scanf("%d %d", &x, &y);
            ret = Div(x, y);
            printf("%d\n", ret);
            break;
        case 0:
            printf("程序退出\n");
            break;
        default:
            printf("输入错误,请重新输入!\n");
                break;
        }
    } while (input);

    return 0;
}

注意到switch语句内部case1到case4的代码太相似,显得代码有些臃肿。

把这些相似的代码封装成一个函数,用函数指针来接收,达到简化代码的目的。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void menu() {
    printf("****************************\n");
    printf("****   1.Add    2.Sub   ****\n");
    printf("****   3.Mul    4.Div   ****\n");
    printf("*******   0.Exit   *********\n");
    printf("****************************\n");
}
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 calculate(int (*p)(int, int)){
    printf("请输入两个操作数>");
    int x,y;
    scanf("%d %d", &x, &y);
    int ret = p(x, y);
    printf("%d\n", ret);
}
int main() {
    int input = 0;
    do {
        menu();
        printf("请输入你的选择>");
        scanf("%d", &input);
        switch (input) {
        case 1:
            calculate(Add);
            break;
        case 2:
            calculate(Sub);
            break;
        case 3:
            calculate(Mul);
            break;
        case 4:
            calculate(Div);
            break;
        case 0:
            printf("程序退出\n");
            break;
        default:
            printf("输入错误,请重新输入!\n");
                break;
        }
    } while (input);

    return 0;
}

6. 函数指针数组

是存放函数指针的数组。

6.1 形式

int (* arr[10])(int, int);其中arr是数组名。 解释:arr先与[10]结合,是一个数组,这个数组有10个元素,每一个元素都是一个函数指针。 这个函数指针类型是int (*)(int, int),指向一个有两个int类型形参,返回类型是int类型的函数。

6.2 用途 - 再次改进计算器

使用函数指针数组存放Add( )、Sub( )、Mul( )、Div( )函数,不再使用switch语句。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void menu() {
    printf("****************************\n");
    printf("****   1.Add    2.Sub   ****\n");
    printf("****   3.Mul    4.Div   ****\n");
    printf("*******   0.Exit   *********\n");
    printf("****************************\n");
}
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 input = 0;
    //函数指针数组
    int (* arr[10])(int, int) = {0, Add, Sub, Mul, Div};
    int x, y;
    int ret = 0;
    do {
        menu();
        printf("请输入你的选择>");
        scanf("%d", &input);
        
        if(input > 0 && input < 5){
            printf("请输入两个操作数>");
            scanf("%d %d", &x, &y);
            ret = (arr[input])(x, y);
            printf("%d\n", ret);
        }
        else if(input == 0){
            printf("程序退出!\n");
        }
        else{
            printf("输入错误,请重新输入!\n");
        }
    } while (input);

    return 0;
}

7. 指向函数指针数组的指针

是一个指针。 指向一个数组。 数组的元素都是函数指针,也就是函数的地址

7.1 形式

函数指针数组: int (*arr[10])(int, int) = {Add, Sub};其中Add、Sub是函数名。 指向函数指针数组的指针: int (* (*parr)[10])(int, int) = &arr;


8. 回调函数

8.1 是什么

回调函数是一个通过函数指针调用的函数。 当你把函数的指针(或地址)作为参数传递给另一个函数,这个指针被用来调用其所指向的函数时,就说这是回调函数。 回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外一方调用的,对该事件或条件进行反馈。

8.2 qsort函数初识

8.2.1 函数原型

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void qsort(void* base, //待排序的数据的起始地址 
           size_t num, //待排序的数据元素个数
           size_t size, //待排序的单个数据元素的大小(以字节为单位)
           int (*comper)(const void* e1, const void* e2));
           //函数指针,指向一个比较函数,该函数的形参是const void* e1和const void* e1,返回 			  //类型是int。

qsort函数可以用来比较任意相同类型的数据,具体比较哪一种类型的数据由使用者自己决定。 决定的方式便通过比较函数实现,这个比较函数需要由使用者写出。 这个比较函数可以接收两个地址,并返回一个整数。

void*类型的指针作为形参可以接收任意类型的指针,是一种泛型指针,也可以说是垃圾箱-来者不拒。 比较函数内部,对e1与e1所指向的数据进行比较之前需要先进行强制类型转换,否则出错。而强制类型转换为哪一种类型由待比较的数据的类型决定。

qsort函数排序默认结果是升序排列。 默认返回情况如下: e1所指向数据大于e2所指向数据返回1; e1所指向数据等于e2所指向数据返回0; e1所指向数据小于e2所指向数据返回-1。

8.2.2 void* 类型的指针

  • void*是无具体类型的指针,可以接收任意类型的地址。
  • 不能对void*类型的指针直接进行解引用操作、等指针运算操作。

8.2.3 具体使用qsort函数

qsort是基于快速排序思想写成的函数,头文件是stdlib.h

对整型数据进行排列:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
int cmp_int(const void* e1, const void* e2) {
    return *(int*)e1 - *(int*)e2;
}
void test_int() {
    int arr[10] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    qsort(arr, sz, sizeof(arr[0]), cmp_int);
    
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
int main() {
    test_int();
    return 0;
}

运行结果:

对结构体类型数据进行排列:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

typedef struct student {
    char name[20];
    int num;
}STU;
//比较结构体中的字符串
int cmp_name(const void* e1, const void* e2) {
    return strcmp(((STU*)e1)->name, ((STU*)e2)->name);
}
void test_struct_name() {
    STU s[] = { {"sunwukong", 2}, {"shasheng", 4}, {"tangsheng",1},{"zhubajie",3} };
    int sz = sizeof(s) / sizeof(s[0]);
    int i = 0;
    printf("前\n");
    for (i = 0; i < sz; i++) {
        printf("%s %d\n", s[i].name, s[i].num);
    }

    qsort(s, sz, sizeof(s[0]), cmp_name);

    printf("后\n");
    for (i = 0; i < sz; i++) {
        printf("%s %d\n", s[i].name, s[i].num);
    }
    return 0;
}

//比较结构体中的整型
int cmp_num(const void* e1, const void* e2) {
    return ((STU*)e1)->num - ((STU*)e2)->num;
}
void test_struct_num() {
    STU s[] = { {"sunwukong", 2}, {"shasheng", 4}, {"tangsheng",1},{"zhubajie",3} };
    int sz = sizeof(s) / sizeof(s[0]);
    int i = 0;
    printf("前\n");
    for (i = 0; i < sz; i++) {
        printf("%s %d\n", s[i].name, s[i].num);
    }

    qsort(s, sz, sizeof(s[0]), cmp_num);

    printf("后\n");
    for (i = 0; i < sz; i++) {
        printf("%s %d\n", s[i].name, s[i].num);
    }
    return 0;
}

int main() {
    test_struct_name();
    printf("----------------\n");
    test_struct_num();
    return 0;
}

运行结果:

8.2.4 采用冒泡排序思想模拟实现qsort函数的功能

冒泡排序思想: 快速图解:

慢速图解:

一个例子:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
#include <stdio.h>
void bubble_sort(int arr[], int sz) {
    int i = 0;
    int flag = 0;//默认数据已经升序排列,不需要交换
    //趟数
    for (i = 0; i < sz - 1; i++) {
        int j = 0;
        //比较次数
        for (j = 0; j < sz - 1 - i; j++) {
            //比较
            if (arr[j] > arr[j + 1]) {
                //交换
                int tmp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = tmp;
                //交换过,flag要改变
                flag = 1;
            }
        }
        //如果第一趟为交换,那么之后的几趟也不会交换,直接退出!
        if(flag == 0){
            break;
        }
    }
}
int main() {
    int arr[] = { 9,8,7,6,5,4,3,2,1,0 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    bubble_sort(arr, sz);
    
    for (i = 0; i < sz; i++) {
        printf("%d ", arr[i]);
    }
    return 0;
}

模拟实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void Swap(char* buf1, char* buf2, int size) {
    int i = 0;
    for (i = 0; i < size; i++) {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}
//
void bubble_sort(void* base,
                 size_t num, 
                 size_t size, 
                 int (*cmp)(const void* e1, const void* e2)) {
    int i = 0;
    //趟数
    for (i = 0; i < num - 1; i++) {
        int j = 0;
        int flag = 0;//默认数据已经升序排列,不需要交换
        //比较次数
        for (j = 0; j < num - 1 - i; j++) {
            //比较,
            if (cmp((char*)base + j * size, (char*)base + (j + 1) * size) > 0) {
                //交换
                Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);
                //交换过,flag要改变
                flag = 1;
            }
        }
        //如果第一趟为交换,那么之后的几趟也不会交换,直接退出!
        if(flag == 0){
            break;
        }
    }
}

注意比较部分cmp((char*)base + j * size, (char*)base + (j + 1) * size)

作为设计者,应该想到作为一个通用的排序函数,可以比较事先未知的数据类型。既然是未知的数据类型那么就不能仅仅使用某一种特定的数据类型直接对指针解引用找到某一个元素,而是把指针base强制类型转换为字符指针,从而借助单个数据元素大小size找到相应的元素。

注意交换部分: Swap((char*)base + j * size, (char*)base + (j + 1) * size, size);

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
void Swap(char* buf1, char* buf2, int size) {
    int i = 0;
    for (i = 0; i < size; i++) {
        char tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

作为设计者,既然实现不能事先知道排序的是哪一种类型,那要实现交换两个元素通过两个字符指针分别指向两个元素,以字节为单位交换这两个元素对应的内容,这样就不再需要考虑是哪一种数据类型,直接完成交换。


9. 结语

本节主要涉及了指针的一些拓展知识,从一般的一级指针开始、逐渐到指针数组、数组指针、函数指针、函数指针数组。另外还了解了回调函数,模拟实现了qsort函数并很好地完成了功能。


END

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2022-07-06,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C语言进阶——指针
str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域, 当几个指针,指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4值相同,地址不同
小李很执着
2024/06/15
1370
C语言进阶——指针
C语言进阶——指针进阶
指针就是地址,而凡是存储在内存中的值都会有属于自己的地址,指针指向地址,这样我们就能通过指针间接操作变量。我们在指针初阶中介绍了指针的基本概念:如指针大小、野指针问题、指针间的关系运算等,在我们的指针进阶中,将会对指针进行进一步剖析,见识更深的指针!🎊🎊
北 海
2023/07/01
1740
C语言进阶——指针进阶
【C语言】指针进阶知识终章
其实这看起来是难以理解的,所以我们可以用typedf(起别名)来简化一下,更加容易认识代码:
平凡的人1
2022/11/15
5090
【C语言】指针进阶知识终章
【C指针进阶】(C精髓)——对指针的更进一步深入剖析(图文近2w详解)
本文承接了上文:初阶指针——全面了解指针,在此基础上对指针进行更深一步的剖析,相信我,也请相信你自己!
诺诺的包包
2023/02/17
5900
【C指针进阶】(C精髓)——对指针的更进一步深入剖析(图文近2w详解)
C语言指针深度解剖
指针是C语言的灵魂,深入理解指针,是学好学会C语言的重要前提。因此,本文将重点讲解C语言指针的深度内容。
二肥是只大懒蓝猫
2023/03/30
4870
C语言指针深度解剖
C语言——指针进阶
指针的主题,我们在初级阶段的《指针》章节已经接触过了,我们知道了指针的概念:
绝活蛋炒饭
2024/12/16
1680
C语言——指针进阶
带你深入了解c语言指针后续
以数组指针为例: 数组指针示例:写一个指向 int arr[10] 数组的数组指针;
初阶牛
2023/03/01
3830
指针进阶(2)
那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?
用户10923087
2024/01/23
960
指针进阶(2)
【C语言篇】深入理解指针4(模拟实现qsort函数)
如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数时,被调⽤的函数就是回调函数。
半截诗
2024/10/09
850
【C语言篇】深入理解指针4(模拟实现qsort函数)
指针进阶详解
理论上字符串第一个字符被改为w,实际上代码无法正常运行,直接挂掉。所以避免被修改,我们const修饰字符指针,避免其被改。
用户10517932
2023/10/07
2120
指针进阶详解
抽丝剥茧C语言(高阶)指针的进阶
数组指针是指针?还是数组? 答案是:指针。 我们已经熟悉: 整形指针: int * pint; 能够指向整形数据的指针。 浮点型指针: float * pf; 能够指向浮点型数据的指针。 那数组指针应该是:能够指向数组的指针。 下面代码哪个是数组指针?
有礼貌的灰绅士
2023/03/28
4250
抽丝剥茧C语言(高阶)指针的进阶
【C语言】C语言数组和指针
---- 友情提醒:本文可能是全csdn最详细的指针内容了,希望你能用心读下去💪💪 前言 接下来的讲解部分是指针的进阶,包含多种指针以及对应的数组,这部分章节对我们来说很重要,也是c语言中的重点模块儿,重要性不言而喻 我们直接进入正题,开始我们今天重要的学习旅程吧😎😎😎 一、指针部分 1.字符指针: 在字符指针使用时,我们通常有两种使用的方式,前者是一般使用方式,后者是我们今天重点所讲部分 int main() { char ch = 'w'; char *pc = &ch; *p
举杯邀明月
2023/04/12
65.3K0
【C语言】C语言数组和指针
指针进阶(C语言)
*pp 通过对 pp 中的地址进行解引用,这样找到的是 p , *pp 其实访问的就是 p .
薄荷冰
2024/01/22
1540
指针进阶(C语言)
我与C语言二周目邂逅vlog——6.指针
上⾯代码的意思是把⼀个常量字符串的⾸字符 h 的地址存放到指针变量 pstr 中。 《剑指offer》中收录了⼀道和字符串相关的笔试题,我们⼀起来学习⼀下:
hope kc
2024/09/23
640
我与C语言二周目邂逅vlog——6.指针
C语言被指针手撕
这里对第二种使用方法简单介绍一下:虽然我们把常量字符串"hi boy "作为初始值赋给字符指针pstr,但是**实际上pstr只是把这个常量字符串的首地址,即’h’的地址给存储起来了。**后续我们可以用%s的方式打印整个字符串。
始终学不会
2023/03/28
3980
C语言被指针手撕
C语言——I /深入理解指针(四)
回调函数就是⼀个通过函数指针调⽤的函数。 如果你把函数的指针(地址)作为参数传递给另⼀个函数,当这个指针被⽤来调⽤其所指向的函数 时,被调⽤的函数就是回调函数。回调函数不是由该函数的实现⽅直接调⽤,⽽是在特定的事件或条件发⽣时由另外的⼀⽅调⽤的,⽤于对该事件或条件进⾏响应。
用户11015888
2024/03/11
1130
【指针合集】全方位理解C语言指针
内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。 所以为了有效的使用内存,就要把内存划分成一个个小的内存单元,每个内存单元的大小都是一个字节。 为了能够有效的访问到内存的每个单元,就要给内存单元进行编号,这些编号被称为内存单元的地址。 在写程序时,创建的变量、数组等都要在内存上开辟空间。 每个内存都有唯一的编号,这个编号也被称为地址 地址 == 编号
Yui_
2024/10/16
2850
【指针合集】全方位理解C语言指针
【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现
本小节,我们将继续学习C语言转移表,什么是回调函数,回调函数又是什么?qsort函数怎么使用,怎么理解处理,要注意的细节,当然qsort使用举例,最后我们进行qsort函数的模拟实现!文章干货满满,走起!
学习起来吧
2024/02/29
4180
【C指针(五)】6种转移表实现整合longjmp()/setjmp()函数和qsort函数详解分析&&模拟实现
C语言高级指针理解及应用(下)
出品|https://blog.csdn.net/ningningmingming/article/details/78688031
C语言中文社区
2022/05/30
7000
C语言高级指针理解及应用(下)
C语言进阶-高阶指针
目录 前言 字符指针 指针数组 数组指针 &数组名VS数组名 数组指针的使用 数组参数、指针参数 一维数组传参 二维数组传参 一级指针传参 二级指针传参 函数指针 函数指针数组 指向函数指针数组的指针 ---- 前言 对于初阶指针内容在我的另一篇文章中已经写到(有想法的可以去看看) 附上链接:C语言初阶指针 回顾: 1. 指针是地址,指针变量是用来存放地址的变量(唯一标识一块内存空间)  2. 指针的大小是固定的4/8个字节(32位平台/64位平台)  3. 指针的类型决定了指针的+-整数的步长,指
用户9645905
2022/11/30
4070
C语言进阶-高阶指针
相关推荐
C语言进阶——指针
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档