用指针数组实现字符串排序

 什么是指针数组

指针数组是C语言中的一种数据结构,其本质是一个数组,但它的每个元素都是一个指针。以下是详细解释:


1. 基本定义

  • 指针数组:数组中的每个元素都是指针(即存储内存地址的变量)。

  • 声明格式数据类型 *数组名[数组长度];

2. 与数组指针的区别

  • 指针数组:是数组,元素是指针。
    例如:char *arr[5] 表示一个数组,包含5个字符指针。

#include <stdio.h>

int main() {
    // 声明并初始化一个字符指针数组
    char *words[] = {"cat", "dog", "bird"};
    
    // 遍历输出
    for (int i = 0; i < 3; i++) {
        printf("words[%d]: %s\n", i, words[i]);
    }
    
    return 0;
}

//words[0]: cat
//words[1]: dog
//words[2]: bird

  • 数组指针:是指针,指向整个数组。
    例如:char (*arr)[5] 表示一个指针,指向一个包含5个字符的数组。

#include <stdio.h>

int main() {
    int arr[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };

    // 声明数组指针并指向二维数组
    int (*ptr)[4] = arr;

    // 通过指针访问元素
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 4; j++) {
            printf("%2d ", ptr[i][j]); // 等价于arr[i][j]
        }
        printf("\n");
    }

    return 0;
}

 //1  2  3  4 
 //5  6  7  8 
 //9 10 11 12 

 
我会先讲解几个错误写法,再开始介绍正确写法


然后,在写这题的时候,有的人会写成

#include<stdio.h>
#include<string.h>
int main()
{ 
    char str[5][50];
    char *s[5];
    int i=0;
    for(i=0;i<5;i++)
    {
        gets(str[i]);
        s[i]=str[i];
    }

    
    for(i=0;i<5;i++)
    {
        char *min=s[i];
        int j=i+1;
        for(j=i+1;j<5;j++)
        {
            if(strcmp(s[j],min)<0)
            {
                min=s[j];
            }
        }
        char *tmp=s[i];
        s[i] = min;
        min = tmp;
    }
    for(i=0;i<5;i++)
    {
        printf("%s\n",s[i]);
    }
    return 0;
}

 这代码错了,那么,为什么错?
先看下这个代码会输出什么

 

我们注意到,这个代码中创建了一个指针变量min,然后我们把那个s[i]的地址赋给它,刚开始这个赋值只是初始化,没什么目的

想着等下用这个min记录一下最小字符串的地址,然后再与
第一个位置的地址交换

然后,记录一下后面的字符,串中最小字符串地址,在与第二个位置交换

再记录一下,更后面的字符串中最小字符串的地址,在与第三个位置交换
……
以此类推,最终达到排序的目的

乍一看,思路倒是对的,但是呢?仔细看,在交换位置的时候,他是把这个min与第i个位置的元素的地址交换

假如我们第一次内部循环中找到的最小字符串,是第三个

然后我们就利用指针min将这第三个字符串的地址记录下来

内层循环结束以后

我们把第一个s[i]所记录的第一个字符串的地址
与这时候min记录的第三个字符串的地址进行交换
然后,是的,s[i]所记录的第一个字符串地址确实被转换为第三个字符串的地址

但是,你在选择排序的时候应该是也要把第三个字符串的地址变为第一个字符串的地址
是吧,交换应该是相互的

但这个min只是个临时变量,现在只是这个临时变量储存的地址改变了,你第三个字符串的地址还是原来那个

所以这就是这个代码不对的地方,要稍加注意

那么,怎么改?

#include<stdio.h>
#include<string.h>
int main()
{ 
    char str[5][50];      // 存储输入的5个字符串(每个最大长度50)
    char *s[5];           // 指针数组,用于按序指向字符串
    int i=0;

    // 步骤1:输入字符串并绑定指针
    for(i=0;i<5;i++)
    {
        gets(str[i]);     // 输入字符串到二维数组的第i行(存在缓冲区溢出风险)
        s[i]=str[i];      // 指针数组s的每个元素指向str的对应行
    }

    // 步骤2:选择排序(通过指针操作调整顺序)
    for(i=0;i<5;i++)
    {
        char **min=&s[i]; // 初始化最小值为当前指针s[i]的地址
        int j=i+1;
        for(j=i+1;j<5;j++)
        {
            // 比较当前s[j]指向的字符串与当前最小值指向的字符串
            if(strcmp(s[j],*min)<0)
            {
                min=&s[j]; // 更新最小值指针为s[j]的地址
            }
        }
        // 交换s[i]与最小值的指向
        char *tmp=s[i];
        s[i] = *min;
        *min = tmp;
    }

    // 步骤3:输出排序后的字符串
    for(i=0;i<5;i++)
    {
        printf("%s\n",s[i]); // 按指针顺序输出
    }
    return 0;
}

修改完毕!

还有的人会写成

int main()
{
    char *str[5];
    char s[51];
    int i,j;
    for(i=0;i<5;i++)
    {
        scanf("%s",s);
        str[i]=s;
    }
    char *temp;
    for(i=0;i<4;i++)
    {
        for(j=i+1;j<5;j++)
        {
            if(strcmp(str[i],str[j])>0)
            {
                *temp=*str[i];
                *str[i]=*str[j];
                *str[j]=*temp;
            }
        }
    }
    for(i=0;i<5;i++)
    {
        printf("%s\n",str[i]);
    }
    return A;
}

                                        这代码错了,那么,为什么错?
                                           先看下这个代码会输出什么

代码错误分析:

  1. 所有指针指向同一内存地址
    str[i] = s 使得所有指针都指向数组 s 的地址。每次输入新字符串时,s 的内容会被覆盖,最终所有 str[i] 都指向最后一次输入的字符串。
    解决方法:为每个字符串动态分配内存(如 str[i] = strdup(s))。

for (i = 0; i < 5; i++) {
    scanf("%s", s);
    str[i] = strdup(s); // 关键:为每个字符串分配独立内存
}
  1. 错误的指针交换逻辑
    以下代码试图通过解引用指针交换字符内容,而非交换指针本身:

*temp = *str[i];  
*str[i] = *str[j];  
*str[j] = *temp;

                why?

  1. 仅交换第一个字符
    *str[i] 表示指针 str[i] 指向的字符(即字符串的首字符)。
    以下代码:

    *temp = *str[i];  
    *str[i] = *str[j];  
    *str[j] = *temp;

    实际交换的是两个字符串的第一个字符,而不是整个字符串。
    示例

    • 假设 str[i] 指向 "Apple"str[j] 指向 "Banana"

    • 执行后,str[i] 变为 "Bpple"str[j] 变为 "Aanana"
      结果字符串被破坏,无法正确排序。

  2. temp 未初始化
    char *temp 未指向任何有效内存地址,直接解引用 *temp 会导致 未定义行为(如程序崩溃)。

  3. 所有指针指向同一内存
    原代码中,str[i] = s 使得所有指针指向同一数组 s
    每次输入新字符串时,s 的内容被覆盖,最终所有指针指向最后一次输入的值。
    示例

    • 输入 "A""B""C""D""E"

    • str[0] 到 str[4] 全部指向 s,最终 s 的值为 "E"

    • 排序后所有指针仍然指向 "E",输出全部为 "E"

那么,怎么改?

#include<stdio.h>
#include<string.h>
int main()
{
    char *str[5];      // 定义指针数组,用于按序指向字符串
    char s[5][50];     // 二维数组,存储输入的5个字符串(每个最多50字符)
    int i,j;

    // 步骤1:输入并绑定指针
    for(i=0;i<5;i++)
    {
        scanf("%s",s[i]);    // 输入字符串到二维数组的第i行
        str[i]=s[i];         // 指针数组第i个元素指向s的第i行首地址
    }
    
    // 步骤2:冒泡排序(仅交换指针顺序)
    for(i=0;i<4;i++)         // 外层循环控制排序轮次
    {
        for(j=i+1;j<5;j++)   // 内层循环比较后续元素
        {
            if(strcmp(str[i],str[j])>0)  // 比较指针指向的字符串字典序
            {
                // 交换指针指向(不修改字符串本身内容)
                char *temp=str[i];  // 临时指针保存str[i]的地址
                str[i]=str[j];      // str[i]指向str[j]的字符串
                str[j]=temp;        // str[j]指向原str[i]的字符串
            }
        }
    }

    // 步骤3:输出排序结果
    for(i=0;i<5;i++)
    {
        printf("%s\n",str[i]);      // 按指针顺序输出字符串
    }
    return 0;
}

讲完了,怎么样,懂了吗?                                                                                   豪哥

                                                                                                                          2025.5.27

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值