使用qsort对二维字符数组排序疑难问题调试及解决过程

来源:互联网 发布:淘宝客网站搭建 编辑:程序博客网 时间:2024/04/29 19:01

 

先说说我这个程序的场景。我程序中有一个二维数组,代码段如下:
char files[101][64];   // files[i][0] sotres the length of the i-the file name
正如注释中说的,files[i][0]用来存储files[i]这个字符串的长度,字符串是从files[i][1]开始存储的,每个字符串长度保证不超过60,所以才考虑这样来存储。现在突然发现在字符串都存储到files中去后,必须还要对files中的字符串进行排序。如果是使用c++string来写当然没问题,但是既然代码已经写成这样,就没再重写,而是打算用Cqsort来排序,这也是一个很有挑战性的工作。
对于自己的代码,先后尝试了很多办法也没能让它正确工作,最终想到调试MSDN上给出的那个使用qsort对字符串进行排序的程序。那个程序如下:
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
 
int compare( const void *arg1, const void *arg2 );
 
void main( int argc, char **argv )
{
   int i;
   /* Eliminate argv[0] from sort: */
   argv++;
   argc--;
 
   /* Sort remaining args using Quicksort algorithm: */
   qsort( (void *)argv, (size_t)argc, sizeof( char * ), compare );
 
   /* Output sorted list: */
   for( i = 0; i < argc; ++i )
      printf( "%s ", argv[i] );
   printf( "/n" );
}
 
int compare( const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   return _stricmp( * ( char** ) arg1, * ( char** ) arg2 );
}
 
qsort调用那一句设置断点,为了看看argv是什么指针类型(char**还是char(*)[]),同时看看调用qsort时传递的第一个参数的值是多少,另外在compare上设置断点,为了看看这个函数被回调时实参的值是多少。(这个程序的命令行参数是this is a test.)
单步,执行到qsort,此时argvtypechar**value0x003a4ee4,这段内存的内容是:
0x003A4EE4  35 4f 3a 00 3a 4f 3a 00 3d 4f 3a 00 3f 4f 3a 00 00 00 00 00 64 3a 5c 4d 79 44 6f 63 75  5O:.:O:.=O:.?O:.....d:/MyDocu
0x003A4F01  6d 65 6e 74 73 5c 56 69 73 75 61 6c 20 53 74 75 64 69 6f 20 32 30 30 35 5c 50 72 6f 6a  ments/Visual Studio 2005/Proj
0x003A4F1E  65 63 74 73 5c 4a 4f 4a 5c 64 65 62 75 67 5c 4a 4f 4a 2e 65 78 65 00 54 68 69 73 00 69  ects/JOJ/debug/JOJ.exe.This.i
0x003A4F3B  73 00 61 00 74 65 73 74 2e 00 fd fd fd fd ab ab ab ab ab ab ab ab fe ee fe ee fe ee fe  s.a.test.....................
可以看到,在argv的值指示的地址处不是字符串,而是几个指针,再根据指针所指向的内在查看一下,恰恰是那几个字符串的地址,这样,这个程序就没什么难理解的,现在改一下这个程序,看看这个模式是不是可以在普通的二维字符数组上实现。主要改动了以下代码,改动后是:
   char data[4][16] = { "this", "is", "a", "test." };
   /* Sort remaining args using Quicksort algorithm: */
   qsort( (void *)data, (size_t)4, sizeof( char * ), compare );
上述对于qsort的调用就是做了个替换,把以前的argv替换成了data,把以前的argc替换成了4,运行,出现异常。异常信息是说在stricmp.c文件的98行,一个assret(dst != NULL)的断言失败了。于是,对这个修改后的程序进行调试,看看差别在哪里:
运行到qsort调用,datatypechar[4][16]value0x0012ff14。查看这个内存地址,发现这个内存地址处的值就是那几个字符串。坏了,没有一个二级指针指向它们!现在结果已经可以预料到,那就是qsort以为传给它的是一个二级指针数组的首地址(但我们传给它的实际上却是data,是直接指向字符串的!),所以qsortdata的地址开始,即从0x0012ff14开始,读取四个字节,把这四个字节作为指针,去检索内存!为了验证这个想法,继续F5到下一个断点:果然如此!执行到compare函数,两个参数的值是0x0012ff14, 0x0012ff18
怎么办!我们必须传给qsort函数这样的一个序列:&data[0]&data[1], &data[2], &data[3]。在调试器里面看到,&data[0] = 0x0012ff14, &data[1] = 0x0012ff24, &data[2] = 0x0012ff34, &data[3] = 0x0012ff34。现在,抱着这样的希望,再次修改qsort函数调用:
qsort( (void *)data, (size_t)4, sizeof( char (*)[16] ), compare );
红字并加粗的部分是我修改过的,这时候再调试看看。但我发现自己搞错了,qsort的第三个参数是size_t类型,我却又传递进去一个指针,任何指针的类型都是4个字节。修改成这样试试:
qsort( (void *)data, (size_t)4, 16, compare );
执行到compare函数,YES!!!这一次两个参数的值是0x0012ff14,0x0012ff24,但还是异常了,问题出在compare函数上面,这一次传进去的指针可完全是字符串首地址了,所以,把compare函数由原来的:int compare( const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   return _stricmp( * ( char** ) arg1, * ( char** ) arg2 );
}
修改为:
int compare( const void *arg1, const void *arg2 )
{
   /* Compare all of both strings: */
   //return _stricmp( * ( char** ) arg1, * ( char** ) arg2 );
     return _stricmp((char*)arg1, (char*)arg2);
}
这一次再运行程序,OK!没有异常!
遗憾的是没有改输出,这次输出排序后的data看一看,任务成功完成!
现在回过头来解决最初的那个问题,相信此时已经不是问题了。
现在所有的问题都完美解决!原来的那个问题解决方法已经在上面的调试过程中有了!
 
 
原创粉丝点击