【问题标题】:Trouble with pointers and pointers to pointers指针和指向指针的问题
【发布时间】:2016-05-31 05:30:54
【问题描述】:

我在理解为什么一段代码有效时遇到了一些麻烦。下面是stdlib实现qsort的比较函数:

int scmp(const void *p1, const void * p2)
{
  char *v1, *v2;

  v1 = (char *) p1;
  v2 = (char *) p2;
  return strcmp(v1,v2);
}

当然,这只适用于字符串。我的问题是:为什么下面的代码有效?

int scmp(const void *p1, const void * p2)
{
  char *v1, *v2;

  v1 = *(char **) p1;
  v2 = *(char **) p2;
  return strcmp(v1,v2);
}

在我看来,在第二个版本中,我将明显是 char* 的内容强制转换为 char**。问题是该变量仍将包含char 变量的地址。当我申请* 时,我的理解是C 将通过获取p1 的内容来处理这个命令,然后在存储在里面的地址之后读取8 个字节(在我的拱上),这样它最终会得到一个值char* 类型。 在我看来,这应该会导致将 8 个字符组合成无效的内存地址。

不过,这两个功能都同样有效。我哪里错了?

【问题讨论】:

  • 第二个版本对我来说似乎是正确的。如果不查看其余代码,很难说出第一个版本的工作原理。
  • 函数怎么调用?如果你像 scmp("hello", "hello"); 这样调用函数,只有第一个版本可以工作:ideone.com/P96Wmj
  • 它在某些情况下似乎有效,但并非对所有情况都有效。例如,如果 p1 指向字符串“abcdefgh”,p2 指向另一个字符串“abcdefgh”。现在字符串是相等的,所以它们都被解释为相同的地址(我们称之为 p)。然后strcmp会将p处的字符串与p处的字符串进行比较,由于两个参数都指向同一个地址,所以内容按照定义是一样的。
  • @RSahu:你能解释一下为什么它看起来是正确的吗?
  • 这两个函数都是正确的,具体取决于 p1p2 是什么,但第二个函数是 qsort() 在 char*(字符串)数组上用作比较函数的函数.

标签: c pointers


【解决方案1】:

假设您想使用qsortints 的数组进行排序。

int numbers[] = {10, 50, 35, 62, 22};

首先,您创建一个可以比较两个ints 的函数。

int intCompare(void* p1, void* p2)
{
   int n1 = *(int*)p1;
   int n2 = *(int*)p2;
   return (n1 < n2);
}

然后,你可以使用:

qsort(numbers, 5, sizeof(int), intCompare);

numbers 传递给qsort 时,它会衰减为int* 并作为void* 传递。当我们需要从intCompare 中的void* 中提取数字时,我们需要在取消引用指针并比较值之前将其强制转换为int*

以字符串类比,假设您要排序:

char* strings[] = { "abc", "xyz", "def" };

qsort 的调用将是:

qsort(strings, 3, sizeof(char*), scmp);

strings 传递给qsort 时,它会衰减为char** 并作为void* 传递。 qsort 传递给scmp 的指针的基础类型将是char** 类型,而不是char*。因此,正确使用:

int scmp(const void *p1, const void * p2)
{
  char *v1, *v2;

  v1 = *(char **) p1;
  v2 = *(char **) p2;
  return strcmp(v1,v2);
}

在某些情况下,由于幸运的巧合,第一个版本有效。这是一个示例程序,它显示了它不工作的几种情况,而第二个版本应该始终工作。

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

// First version of scmp
int scmp1(const void *p1, const void * p2)
{
  char *v1, *v2;

  v1 = (char *) p1;
  v2 = (char *) p2;
  return strcmp(v1,v2);
}

// Second version of scmp
int scmp2(const void *p1, const void * p2)
{
  char *v1, *v2;

  v1 = *(char **) p1;
  v2 = *(char **) p2;
  return strcmp(v1,v2);
}

void test1()
{
   char* strings[] = { "abc", "xyz", "def" };
   qsort(strings,  3, sizeof(char*), scmp1);
   for( int i = 0; i < 3; ++i )
   {
      printf("%s\n", strings[i]);
   }
   printf("\n");
}

void test2()
{
   char* strings[] = { "abc", "xyz", "def" };
   qsort(strings,  3, sizeof(char*), scmp2);
   for( int i = 0; i < 3; ++i )
   {
      printf("%s\n", strings[i]);
   }
   printf("\n");
}

void test3()
{
   char** strings = malloc(3*sizeof(char*));
   strings[0] = "abc";
   strings[1] = "xyz";
   strings[2] = "def";

   qsort(strings,  3, sizeof(char*), scmp1);
   for( int i = 0; i < 3; ++i )
   {
      printf("%s\n", strings[i]);
   }

   free(strings);
   printf("\n");
}

void test4()
{
   char** strings = malloc(3*sizeof(char*));
   strings[0] = "abc";
   strings[1] = "xyz";
   strings[2] = "def";

   qsort(strings,  3, sizeof(char*), scmp2);

   for( int i = 0; i < 3; ++i )
   {
      printf("%s\n", strings[i]);
   }

   free(strings);
   printf("\n");
}

int main()
{
   // Does not work.
   test1();

   // Should work always.
   test2();

   // Does not work.
   test3();

   // Should work always.
   test4();
}

输出(使用 gcc 4.8.4):

abc
xyz
def

abc
def
xyz

abc
xyz
def

abc
def
xyz

【讨论】:

  • 很高兴看到一些非常详细的答案。 +1
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-12-14
  • 2021-08-14
  • 2012-07-26
相关资源
最近更新 更多