使用qsort对二维字符数组排序疑难问题调试及解决过程
来源:优易学  2011-12-10 16:46:17   【优易学:中国教育考试门户网】   资料下载   IT书店

  先说说我这个程序的场景。我程序中有一个二维数组,代码段如下:
  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 来写当然没问题,但是既然代码已经写成这样,就没再重写,而是打算用 C 的 qsort 来排序,这也是一个很有挑战性的工作。
  对于自己的代码,先后尝试了很多办法也没能让它正确工作,最终想到调试 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 ,此时 argv 的 type 是 char** , value 是 0x003a4ee4 ,这段内存的内容是:
  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 调用, data 的 type 是 char[4][16] , value 是 0x0012ff14 。查看这个内存地址,发现这个内存地址处的值就是那几个字符串。坏了,没有一个二级指针指向它们!现在结果已经可以预料到,那就是 qsort 以为传给它的是一个二级指针数组的首地址 ( 但我们传给它的实际上却是 data ,是直接指向字符串的! ) ,所以 qsort 从 data 的地址开始,即从 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 看一看,任务成功完成!
  现在回过头来解决最初的那个问题,相信此时已经不是问题了。
  现在所有的问题都完美解决!原来的那个问题解决方法已经在上面的调试过程中有了!

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训