【问题标题】:For loop is running too oftenFor循环运行太频繁
【发布时间】:2019-09-04 07:12:50
【问题描述】:

我有一个 for 循环,它应该运行 4 次,但运行了 6 次。 你能解释一下这种行为吗? 这很奇怪,因为 stringarr1 没有改变。

编辑:我想删除所有“!”来自我的第一个字符串,并希望将这些字母保存在第二个字符串中。

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

int main(){

  char stringarr1[] = "a!bc";
  char stringarr2[] = "";

  printf("%d\n", strlen(stringarr1));  // lenght --> 4

  for (size_t i = 0; i < strlen(stringarr1); i++)  
  {
      printf("i: %d\n", i);
      if (stringarr1[i] != '!') {
        stringarr2[strlen(stringarr2)] = stringarr1[i];
        printf("info: != '!'\n");
      }          
  }
}

【问题讨论】:

  • char stringarr2[] = ""; 这会生成一个大小为 1 的数组,stringarr2[0] 是空终止符。您不能以这种方式进行字符串操作。给它一个合适的大小。
  • 你的结论是什么?
  • 在我看其他东西之前,不要在 for 循环的条件中使用 strlen。它计算每次循环迭代的字符串长度。在循环之前将长度存储在变量中。
  • @ChrisRollins 字符被复制到字符串末尾时没有间隙。无论这可能在哪里......
  • 附带说明,使用printf()%d(需要int)打印strlen() 的返回值,它的类型为size_t(需要int)会导致未定义的行为。使用%zu

标签: c arrays string for-loop


【解决方案1】:

您超出了 stringarr2(长度 1)的缓冲区,在这种情况下破坏了内存相邻的 stringarr1,导致字符串长度通过覆盖其 nul 终止符而改变。

然后因为您在每次迭代中重新评估字符串长度,循环将运行不确定的迭代次数 - 在您的情况下只有 6 次,但可能会更糟;您观察到的行为只是几种可能性之一 - 它是未定义

除了更正stringarr2 的缓冲区长度之外,最佳做法是评估一次循环不变量(尽管在这种情况下,字符串长度由于错误而不是不变的)。所以如下:

  const size_t length = strlen( stringarr1 ) ;
  for( size_t i = 0; i < length; i++ )
  {
      ...

无论缓冲区溢出错误如何,都会运行 4 次迭代,因为在损坏后不会重新评估长度。重新评估循环不变量会导致代码执行速度非常慢。

【讨论】:

    【解决方案2】:

    您的代码可以运行任意次数。你写的超出了stringarr2 的结尾,所以你可能会破坏堆栈并覆盖局部变量。你的意思大概是这样的:

    #include <stdio.h>
    #include <math.h>
    #include <string.h>
    
    int main(){
    
      char stringarr1[] = "a!bc";
      char stringarr2[10];
      int len = strlen(stringarr1);
    
      printf("%d\n", len);  // lenght --> 4
    
      for (size_t i = 0; i < len; i++)  
      {
          printf("i: %d\n", i);
          if (stringarr1[i] != '!') {
            stringarr2[len] = stringarr1[i];
            printf("info: != '!'\n");
          }          
      }
    }
    

    就像其他人所说的那样,您在这里想要完成什么并不是很清楚。但是在 C 中,像 char s[] = "string" 这样的声明只分配足够的内存来存储赋值右侧的任何内容。如果在您的情况下这是一个空字符串,则只分配一个字节,以存储字符串结尾的“空”字符。您需要像我一样明确指定要分配的字节数作为数组大小,或者使用动态内存分配。

    【讨论】:

      【解决方案3】:

      问题是你写的超过了stringarr2 的结尾。这会触发undefined behaviour

      要解决此问题,您需要为stringarr2 分配足够的内存。

      【讨论】:

        【解决方案4】:

        首先,我们必须分配足够长的字符串。

        char stringarr1[] = "a!bc";
        
        //save this in a variable beforehand because strlen loops over the string every time it is called
        size_t len = strlen(stringarr1);  
        
        char stringarr2[1024] = { 0 };
        

        { 0 } 将字符串中的所有字符初始化为 0,这意味着在我们添加字符后,最后一个始终是空终止符。这告诉 C 字符串函数字符串在哪里结束。

        现在我们可以在里面放东西了。似乎您正在尝试追加,因此为第二个字符串保留一个单独的迭代器。这比每次循环调用 strlen 更有效。

        for(size_t i = 0, j = 0; i < len; i++){
          printf("i: %d\n", i);
          if (stringarr1[i] != '!') {
            stringarr2[j++] = stringarr1[i];
            printf("info: != '!'\n");
          } 
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-12-12
          • 1970-01-01
          • 1970-01-01
          • 2013-10-31
          • 1970-01-01
          • 1970-01-01
          • 2021-07-30
          • 1970-01-01
          相关资源
          最近更新 更多