使用冒泡排序讲解函数指针

指针不光可以指向数组,整型变量等data objects, 在C里面,指针也可以指向函数,指向函数的

指针有很多种用处,下面举例说明。

 

思考下面的问题,你想实现一个函数可以对任意数据类型进行排序,比如说,字符串,整数,浮点

数,甚至是结构体。具体的算法都用一样的,可以是冒泡排序,当然也可以是更复杂一些的算法,

比如 shell 或者快速排序。 为了演示,我们在这里使用简单的冒泡排序。

 

Sedgewick 有描述过怎样用C语言来实现冒泡排序,假设我们把函数名叫bubble(), 它可以是这个样

子的。 bubble1.c,

#include <stdio.h>

int arr[10] = { 3, 6, 1, 2, 3, 8, 4, 1, 7, 2};

void bubble(int a[], int N);

int main(void)
{
    int i;
    putchar('\n');
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    bubble(arr, 10);
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    system("pause");
    return 0;
}
void bubble(int a[], int N)
{
     int i, j, t;
     for (i = N-1; i >= 0; i--)
     {
         for (j = 1; j <= i; j++)
         {
             if (a[j-1] > a[j])
             {
                        t = a[j -1];
                        a[j-1] = a[j];
                        a[j] = t;
             }
         }
     }
}

 

冒泡排序是一种比较简单的排序,这种算法扫描从第二到到最后一个,使用每一个元素和它之前的

那个元素比,如果前一个比当前元素要大,那么交换它们,这样,比较大的那一个就越来越靠近

数组的末尾,第一轮循环结束,最大的那个数将会移动到数组的末尾,下一轮循环将把第二大的

数列在最大的那个数前面,这样重复(元素个数减1)次,就会得到一个有序数组。

 

这里我们的函数有很大的局限性,只能比较整型数组里面的数据,为了能够比较任意类型,我们

先把比较这个过程从bubble()中拿出来,程序是这个样子的。

/* Separating the comparison function */
#include <stdio.h>
int arr[10] = { 3, 6, 1, 2, 3, 8, 4, 1, 7, 2};

void bubble(int a[], int N);
int compare(int m, int n);

int main(void)
{
    int i;
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    bubble(arr,10);
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    system("pause");
    return 0;
}

void bubble(int a[], int N)
{
     int i, j, t;
     for (i = N-1; i >= 0; i--)
     {
         for (j = 1; j <= i; j++)
         {
             if (compare(a[j-1], a[j]))
             {
                   t = a[j-1];
                   a[j-1] = a[j];
                   a[j] = t;
             }
         }
         
     }
}  

int compare(int m, int n)
{
    return (m > n);
}  


 

如果我们的目的是可以排任意类型的数据,有一种搞法是这样的,使用void 指针,而不使用整型

数据类型,作为这个方向的开始,我们对上面函数作一些简答的修改,这里我们将指针指整型数据。

#include <stdio.h>
int arr[10] = { 3, 6, 1, 2, 3, 8, 4, 1, 7, 2 };

void bubble(int *p, int N);
int compare(int *m, int *n);

int main(void)
{
    int i;
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    bubble(arr, 10);
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    system("pause");
    return 0;
}

void bubble(int *p, int N)
{
     int i, j, t;
     for (i = N-1; i >= 0; i--)
     {
         for (j = 1; j <= i; j++)
         {
             if (compare(&p[j-1], &p[j]))
             {
                 t = p[j-1];
                 p[j-1] = p[j];
                 p[j] = t;
             }
         }
         
     }
}

int compare(int *m, int *n)
{
    return (*m > *n);
}


注意我们修改到我地方,这里我们向bubble() 传递了一个指向integer的指针,下一步里,我们

将使用指向void 类型的指针,这样函数对数据类型变得更加的不敏感。

#include <stdio.h>
#include <stdio.h>

int arr[10] = {3, 6, 1, 2, 4, 8, 4, 1, 7, 2};

void bubble(int *p, int N);
int compare(void *m, void *n);

int main(void)
{
    int i;
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    bubble(arr, 10);
    putchar('\n');
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    system("pause");
    return 0;
}

void bubble(int *p, int N)
{
     int i, j, t;
     for (i = N-1; i >= 0; i--)
     {
         for (j = 1; j <= i; j++)
         {
             if (compare((void *)&p[j-1], (void *) &p[j]))
             {
                  t = p[j - 1];
                  p[j-1] = p[j];
                  p[j] = t;
             }
         }
     }
     
}

int compare(void *m, void *n)
{
    int *m1, *n1;
    m1 = (int *)m;
    n1 = (int *)n;
    return (*m1 > *n1);
}
我们知道,数据的名字是一个指向其第一个元素(在数据段)地址的指针,同样的道理,函数名将指向函数代码
段的开始的地址。
 
指向函数的指针必须和函数参数的类型,个数以及返回值匹配,在这里我们这样定义
我们的函数指针:
int (*fptr)(const void *p1, const void *p2);


注意不能写成这样:

int *fptr(const void *p1, const void *p2);

如果写成这样,它的意思是给出了一个函数原型,它将返回一个指向int 的指针。这是因为

在C语言中括号() 比  * 的优先级要高。在* fptr  打个括号表示我们声明了一个函数指针。

这里我们修改一下bubble()的声明,增加第四个参数,一个指向合适类型的函数指针。

它的函数原型将变成这个样子:

void bubble(void *p, int width, int N, int (*fptr)(const void *, const void *));


当我们调用bubble的时候,将我们希望使用的比较函数名插进去。下面程序将会阐明这样方法的实现。

#include <stdio.h>
#include <string.h>

#define MAX_BUF 256

long arr[10] = {3, 6, 1, 2, 3, 8, 4, 1, 7, 2};
char arr2[5][20] = { " Mickey Mouse",
                     " Donald Duck",
                     " Minnie Mouse",
                     " Goofy",
                     " Ted Jensen" };
                     
void bubble(void *p, int width, int n,
            int (*fptr)(const void *, const void *));
int compare_string(const void *m, const void *n);
int  compare_long(const void *m, const void *n);                     
int main(void)
{
    int i;
    puts("\nBefore Sorting:\n");
    
    for (i = 0; i < 10; i++)     /* show the long ints */
    {
        printf("%ld ", arr[i]);
    }
    puts("\n");
    
    for (i = 0; i < 5; i++)
    {
        printf("%s\n", arr2[i]);      /* show the strings */
    }
    bubble(arr, 4, 10, compare_long);   /* sort the longs */
    bubble(arr2, 20, 5, compare_string);   /* sort the strings */
    puts("\n\nAfter Sorting:\n");
    
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);
    }
    puts("\n");
    
    for (i = 0; i < 5; i++)     /* show the sorted strings */
    {
        printf("%s\n", arr2[i]);
    }
    system("pause");
    
    return 0;
}

void bubble(void *p, int width, int N, 
            int (*fptr)(const void *, const void *))
{
            int i, j, k;
            unsigned char buf[MAX_BUF];
            unsigned char *bp = p;
            
            for (i = N - 1; i >= 0; i--)
            {
                for (j = 1; j <= i; j++)
                {
                    k =fptr((void *)(bp + width*(j-1)), (void *)(bp + j *width));
                }
                if (k > 0)
                {
                      memcpy(buf, bp + width*(j-1), width);
                      memcpy(bp + width*(j-1), bp + j*width, width);
                      memcpy(bp + j*width, buf, width);
                }
            }
}

int compare_string(const void *m, const void *n)
{
    char *m1 = (char *)m;
    char *n1 = (char *)n;
    return (strcmp(m1,n1));
}

int compare_long(const void *m, const void *n)
{
    long *m1, *n1;
    m1 = (long *)m;
    n1 = (long *) n;
    return (*m1 > *n1);    
}



  

<think>嗯,用户让我用C语言实现冒泡排序,并且要通过函数传参数来完成。首先我得回忆一下冒泡排序的基本原理。冒泡排序就是重复地遍历数组,比较相邻元素的大小,如果顺序不对就交换它们,这样每一轮遍历都能把最大的元素“冒泡”到末尾。对吧? 接下来要考虑如何用函数来实现这个排序。用户提到要利用函数传参数,所以应该把数组作为参数传递给排序函数。不过C语言中数组作为参数传递时,实际上传递的是指针,所以函数内部对数组的修改会影响到原数组。这点需要注意,不过可能用户已经知道,或者需要我说明一下。 然后,函数的参数应该包括数组本身和数组的长度。因为数组作为指针传递时,无法直接获取其长度,所以需要在调用函数时传入长度。比如函数原型可能是void bubbleSort(int arr[], int n),其中n是元素个数。 接下来,我需要实现冒泡排序的具体逻辑。外层循环控制遍历的次数,内层循环负责比较和交换相邻元素。比如,对于长度为n的数组,外层循环从0到n-1,内层循环从0到n-i-1,这样每一轮都能把最大的元素放到正确的位置。不过要注意优化,比如如果某一轮没有发生交换,说明数组已经有序,可以提前结束。 然后要考虑如何测试这个函数。主函数中需要定义一个数组,调用bubbleSort函数,然后打印排序后的结果。这部分用户可能也需要示例代码,所以得写清楚。 另外,可能需要解释一下为什么指针传递数组,以及函数参数中的int arr[]和int* arr其实是等价的。不过用户的问题可能更关注实现步骤,所以这部分可能放在注意事项里。 可能用户是刚开始学C语言,所以需要详细解释每一步,比如循环的作用,交换变量的方法,还有函数调用的方式。同时要确保代码没有语法错误,比如正确使用花括号,分号,以及正确的循环变量。 还需要注意示例代码的正确性,比如在冒泡排序中,内层循环的条件是否正确,避免越界访问。比如内层循环应该是j从0到n-i-1,因为每一轮之后,最后的i个元素已经排好序了,不需要再比较。 可能还需要提到时间复杂度,虽然用户没问,但作为补充说明会更好,说明冒泡排序是O(n²)的时间复杂度,适合小规模数据。 最后检查一下是否符合用户的要求:使用函数传参数实现冒泡排序,结构清晰,步骤正确,代码正确无误,用中文回答。确保所有数学表达式用$...$,但在这个问题中可能用不上太多数学公式,不过如果有提到比较次数之类的,可能需要用。例如,比较次数是$\frac{n(n-1)}{2}$次,但可能不需要,视情况而定。</think>以下是用C语言通过函数传参实现冒泡排序的逐步讲解: --- ### 一、实现思路 1. **冒泡排序原理**:通过相邻元素的比较和交换,将较大的元素逐步“冒泡”到数组末尾 2. **函数参数设计**: - 需要传递待排序数组的**首地址** - 需要传递数组的**元素个数** 3. **关键操作**: - 使用双重循环控制比较轮次 - 通过指针操作直接修改原始数组 --- ### 二、完整代码实现 ```c #include <stdio.h> // 冒泡排序函数 void bubbleSort(int arr[], int n) { for (int i = 0; i < n - 1; i++) { // 控制比较轮次 for (int j = 0; j < n - i - 1; j++) { // 控制每轮比较次数 if (arr[j] > arr[j + 1]) { // 相邻元素比较 // 交换元素 int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; } } } } int main() { int arr[] = {64, 34, 25, 12, 22, 11, 90}; int n = sizeof(arr) / sizeof(arr[0]); bubbleSort(arr, n); // 调用排序函数 printf("排序结果:"); for (int i = 0; i < n; i++) { printf("%d ", arr[i]); } return 0; } ``` --- ### 三、关键代码解析 1. **函数参数分析**: ```c void bubbleSort(int arr[], int n) ``` - `int arr[]`:实际接收的是数组首地址(等效于`int* arr`) - `int n`:明确传递数组长度,因为函数内无法直接获取数组长度 2. **双重循环逻辑**: - **外层循环**:共进行`n-1`轮比较(第`i`轮确定第`n-i`大的元素) - **内层循环**:每轮比较`n-i-1`次(已排序部分无需再比较) 3. **元素交换**: ```c int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; ``` 使用临时变量`temp`完成值交换 --- ### 四、执行流程示例 以初始数组`[5, 3, 8, 6]`为例: 1. 第1轮:确定最大值8的位置 ```text 3,5,6,8 // 经过3次比较交换 ``` 2. 第2轮:确定次大值6的位置 ```text 3,5,6,8 // 经过2次比较(无需交换) ``` 3. 第3轮:确定5的位置 ```text 3,5,6,8 // 经过1次比较(无需交换) ``` --- ### 五、注意事项 1. **时间复杂度**:$O(n^2)$,不适合大规模数据排序 2. **优化方向**: - 添加`flag`标记提前结束已排序的序列 - 记录最后交换位置减少无效比较 3. **参数传递本质**:函数内操作直接影响原始数组(传地址) --- ### 六、延伸思考 若需要降序排序,只需修改比较条件: ```c if (arr[j] < arr[j + 1]) { // 改为小于号 // 交换操作保持不变 } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值