【问题标题】:Iteratively iterating Array of Strings (pointer value seems to get "stuck")迭代地迭代字符串数组(指针值似乎“卡住”了)
【发布时间】:2018-12-18 09:42:09
【问题描述】:

编辑: 我意识到我的 OP 中的代码很长且难以阅读。我用 4 行代码突出显示了问题。

char **t = {"Hello", "World"};
char **a = t;
++(a[0]);
printf("%c\n",**t);

我想在不丢失指向第一个字符的指针的情况下递增字符串数组。因此,我初始化了一个新指针“a”以指向第一个字符。但是,在我增加'a'指针之后,它似乎改变了't'指向的内容!在 printf 语句中,我希望 t 的指针值保持不变,但它似乎以 'a' 递增,现在指向第二个字符。为什么会这样?

已解决: 在上面的示例中,a 和 t 似乎是同一个指针,因此如果我更改一个(例如通过递增),更改也会反映在 pther 中。但是,如果我将 t 取消引用到另一个变量,那么我可以更改所述变量,而不会将该更改反映在 t 中。在上面的例子中,这看起来像

char *a = t[0];
++a;
printf("a value: %c\n", *a);
printf("t value: %c\n", **t);

我认为我最初对取消引用感到困惑,因为 t 指向一个指针。我得到的每个响应都是使用数组索引而不是指针,我明白为什么。

原帖: 说我有:

array1 {"arp", "live", "strong"}, and 
array2 {"lively", "alive", "harp", "sharp", "armstrong"}

我正在尝试查找array1 中的字符串,它们是array2 中任何字符串的子字符串。

为此,我编写了一个辅助函数 (compString),它接收来自 array1 的字符串、整个 array2 以及 array2 的长度。

本质上,函数所做的是为字符串指针和数组指针创建本地指针值。然后它从array2 中提取第一个字符串并开始遍历它以找到输入字符串的第一个字母的匹配项。如果没有找到匹配,该函数将移动到下一个字符串,直到找到一个完整的匹配或直到它遍历整个array2。然后它返回到它的调用环境。

我遇到了一些意想不到的行为。当我调用该函数时(使用相同的参数),在已经调用它之后,数组指针似乎准确地指向它在上一次调用中停止的位置。

例如,如果我调用 compString("arp", array2, 5),那么该函数将标记从 harp 中的 a 开始的匹配。

然后,如果我调用compString("live", array2, 5),该函数将从harp 中的a 开始,并在不标记匹配的情况下到达数组的末尾。

最后,当我调用compString("strong", array2, 5) 时,array2 现在指向垃圾,因为它已经被迭代,并且没有标记匹配。

由于辅助函数所做的第一件事就是“本地化”传递的指针(即创建一个局部指针变量并将传递给函数的指针的值分配给它,然后迭代该局部变量),我会假设对该函数的后续调用不会“保存”指针的先前值。有什么指点吗?

附上来源:

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

int compString(char *, char **, int);

int main(void)
{
    int sz1 = 3;
    int sz2 = 5;
    char *p, *p2;
    char *array1[] = {"arp\0", "live\0", "strong\0"};
    char *array2[] = {"lively\0", "alive\0", "harp\0", "sharp\0", "armstrong\0"};

    compString("arp\0",array2,5);
    compString("live\0",array2,5);
    compString("strong\0",array2,5);
}

int compString(char *arr1, char **arr2, int sz2)
{
    printf("\n\n\n");
    printf("WORD: %s\n",arr1);
    int i = 0;
    char *a1 = arr1;
    char **a2 = arr2;
    char *p;
    char *p2;

    printf("BEGIN ITERATION %d\n",i);
    printf("Checking against word: %s\n",a2[i]);
    while (i < sz2)
    {
        printf("%c\n",*a2[i]);
        if (*a1 == *a2[i])
        {
            char *p = a1;
            char *p2 = a2[i];

            while ((*p == *p2) && (*p != '\0'))
            {
                ++p;
                ++p2;
            } 

            if (*p == '\0')
            {
                return 1;
            }

            else
            {
                *++(a2[i]);
                if (*(a2[i]) == '\0')
                {
                    ++i;
                    printf("BEGIN ITERATION %d\n",i);
                    printf("Checking against word: %s\n",a2[i]);
                }
            }
        }

        else 
        {
            *++(a2[i]);
            if (*(a2[i]) == '\0')
            {
                ++i;
                printf("BEGIN ITERATION %d\n",i);
                printf("Checking against word: %s\n",a2[i]);
            }
        }
    }
    return 0;
}

【问题讨论】:

  • 如果到目前为止您还没有尝试过,这可能是熟悉调试器的好时机。旁注:"something" 的末尾已经有一个\0(否则printf-s 也需要它,但它们不需要)
  • 不确定你想用 *++(a2[i]); 做什么,但它看起来不正确,取消引用它什么也没做。
  • 我对 *++(a2[i]) 的想法是 a2[i] 返回一个指向字符串的指针,++a2[i] 递增指针以指向所述的下一个字符字符串,并且 *++(a2[i]) 产生该字符的值。
  • 这完全正确,但是您不会将该 char 的值存储在任何地方,因此取消引用是没有意义的,并且增加指针意味着您正在更改 arr2 的内容,因此您的单词列表're check against 将随着时间的推移变得无用
  • @byrnesj1 在此处查看答案:Pointer expressions: *ptr++, *++ptr and ++*ptr。它应该很快澄清为什么它会降低你的文字数组。需要明确的是,因为您传递了 char ** 类型,所以您对数组具有读取权限,但仍然可以修改其元素。除非练习是使用指针运算,否则最好采用二维数组表示法。

标签: c arrays pointers scope iteration


【解决方案1】:

您的循环导致了一个错误。你想要做的是遍历你的 5 个字符串数组,所以从 0 到 4。我们可以看到,当你运行所有三个测试时,因为它们以某种方式依赖于彼此的结果(我也没有研究比较逻辑很多,它似乎相当模糊)。

我们可以通过一次测试来复制行为:

compString("test", array2, 5);

所以 5 应该告诉它从 0 循环到 4。在比较函数中,你有这个:

int i = 0;
printf("BEGIN ITERATION %d\n", i);
printf("Checking against word: %s\n", a2[i]);
while (i < sz2)

到目前为止,一切都很好。 i &lt; sz2 是正确的,假设你正确增加了i,它应该从0 循环到4

然后,你可以在函数末尾的某个地方执行此操作:

++i;
printf("BEGIN ITERATION %d\n", i);
printf("Checking against word: %s\n", a2[i]);

所以当i4 时,您将其增加到5,此时函数应该停止循环遍历数组,但此时您执行尝试访问a2[5] 的打印,这不存在。这就是我在 MSVC 上崩溃的地方。

我的建议是你将循环逻辑改写成这样:

for (int i = 0; i < sz2, i++){
    printf("BEGIN ITERATION %d\n", i);
    printf("Checking against word: %s\n", a2[i]);
    // do something with a2[i] and don't manually change the value of "i"
}

另外,我会整理一下那个字符串逻辑,它可能在某个地方存在错误。您不需要所有那些可疑的取消引用调用。当你想在a2 中访问字符串y 的字符x 时,a2[y][x] 就可以了。例如,如果您想查找某个字母,只需执行以下操作:

for (int n = 0; n < strlen(a2[y]), n++){
    if (a2[y][n] == 'a')
        printf("found letter 'a' at position %d\n", n);
}

此外,您不需要将\0 添加到字符串文字中。这些是自动添加的,因此您只需添加第二个。而不是这个:

char *array1[] = {"arp\0", "live\0", "strong\0"};

这样做:

char *array1[] = {"arp", "live", "strong"};

另外,我不知道你是否必须实现这个功能,因为这是你被赋予的任务,但如果你只是想找到子字符串,那么你不需要像strstr那样重新发明轮子已经这样做了。

【讨论】:

    【解决方案2】:

    你在寻找这样的东西吗:

    char *array1[] = {"arp", "live", "strong", NULL};
    char *array2[] = {"lively", "alive", "harp", "sharp", "armstrong", NULL};
    
    void findsrings(char **neadles, char **haystack)
    {
        while(*neadles)
        {
            char **hay = haystack;
            size_t pos = 0;
    
            printf("Searching for %s\n", *neadles);
            while(*hay)
            {
                if(strstr(*hay, *neadles))
                {
                    printf("Found!! Haystack word is: %s at index %zu in haystack\n", *hay, pos);
                }
                pos++;
                hay++;
            }
            neadles++;
        }
    }
    
    int main()
    {
        findsrings(array1, array2);
    
        return 0;
    }
    

    您不需要在字符串文字末尾添加 '\0',因为它们是由 C 编译器自动添加的。我添加了 NULL 来终止字符串指针的数组 - 所以你不需要提供数组的大小/。

    【讨论】:

      【解决方案3】:

      正如 cmets 中提到的,您注意到的副作用是由于 *++(a2[i]); 这一行正在改变您的第二个数组的内容。随着时间的推移,您最终会得到第二个数组,其中没有实际的单词。

      通常,您的代码过于复杂,而当for 循环更适合时,您会使用while 循环。

      例如,外部循环会更好:

      for(i=0;i<sz2;i++)
      {
          printf("BEGIN ITERATION %d\n",i);
          printf("Checking against word: %s\n",arr2[i]);
      

      然后,由于您想检查arr2[i] 中的每个子字符串,您可以使用for 循环...

          for(wordstart=arr2[i];*wordstart!='\0';wordstart2++)
          {
      

      最后,您有一个内部循环,它将arr1 的每个字符与wordstart 定义的子字符串进行比较。您需要确保p1p2 都没有超出各自字符串的末尾,并且它们指向同一个字符。

              for(p1=arr1,p2=wordstart;(*p1!='\0')&&(*p2!='\0')&&(*p1==*p2);p1++,p2++);
      

      一旦这 3 个条件中的任何一个不再成立,如果您检查 p1 是否已到达字符串的末尾,您就知道它一定找到了一个子字符串。

              if(*p1=='\0')
              {
                  printf("Matched %s\n",arr2[i]);
                  return 1;
              }
      

      生成的函数如下所示:

      int compString(char *arr1, char **arr2, int sz2)
      {
          printf("\n\n\n");
          printf("WORD: %s\n",arr1);
          int i = 0;
          char *p1;
          char *wordstart;
          char *p2;
      
          for(i=0;i<sz2;i++)
          {
              printf("BEGIN ITERATION %d\n",i);
              printf("Checking against word: %s\n",arr2[i]);
      
              for(wordstart=arr2[i];*wordstart!='\0';wordstart++)
              {
                  for(p1=arr1,p2=wordstart;(*p1!='\0')&&(*p2!='\0')&&(*p1==*p2);p1++,p2++);
                  if(*p1=='\0')
                  {
                      printf("Matched %s\n",arr2[i]);
                      return 1;
                  }
              }
          }
          return 0;
      }
      

      其他需要注意的是,您不需要将\0 隐式添加到字符串中。下面的就好了。

      char *array1[] = {"arp", "live", "strong"};
      

      您还可以将NULL 添加为字符串列表中的最后一个元素,这样您就无需跟踪有多少字符串。

      char *array2[] = {"lively", "alive", "harp", "sharp", "armstrong"};
      

      这意味着外部循环可以简化为

      for(i=0;arr2[i];i++)
      

      【讨论】:

        猜你喜欢
        • 2021-12-23
        • 2013-04-19
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-04-21
        • 1970-01-01
        • 2013-05-26
        • 1970-01-01
        相关资源
        最近更新 更多