函数returns char**s,运行函数两次导致return值不同

Function returns char**s, running the function twice causes the return value to differ

描述我的函数试图做什么

我的函数获取一个字符串,例如“Ab + abc EF++aG hi jkL”,并将其转换为 ["abc", "hi"]

另外,函数只考虑字母,而且字母必须全部小写

问题是

char* str1 = "Ab +  abc EF++aG hi  jkL";
char* str2 = "This is a very famous quote";

char** tokens1 = get_tokens(str1); 
printf("%s", tokens1[0]);            <----- prints out "abc" correct output
char** tokens2 = get_tokens(str2);
printf("%s", tokens1[0]);            <----- prints out "s" incorrect output

get_tokens函数(Returns二维数组)

char** get_tokens(const char* str) {
  // implement me
  int num_tokens = count_tokens(str); 

  char delim[] = " ";
  int str_length = strlen(str);
  char* new_str = malloc(str_length); 
  strcpy(new_str, str); 

  char* ptr = strtok(new_str, delim);
  int index = 0;

  char** array_2d = malloc(sizeof(char*) *num_tokens);

  while (ptr != NULL){
    if (check_string(ptr) == 0){

      array_2d[index] = ptr; 
      index++;
    }

    ptr = strtok(NULL, delim); 
  } 

  free(new_str); 
  new_str = NULL; 

  free(ptr);
  ptr = NULL; 

  return array_2d;
}  

count_tokens函数(returns有效字符串的个数)

例如 count_tokens("AB + abc EF++aG hi jkL") returns 2 因为只有 "abc" 和 "hi" 有效

int count_tokens(const char* str) {
  // implement me
  //Seperate string using strtok

  char delim[] = " ";
  int str_length = strlen(str);
  char* new_str = malloc(str_length); 
  strcpy(new_str, str); 

  char* ptr = strtok(new_str, delim); 

  int counter = 0; 

  while (ptr != NULL){
    if (check_string(ptr) == 0){
      counter++;
    }

    ptr = strtok(NULL, delim); 
  }
  free(new_str);     
  return counter;
}  


最后 check_string() 检查字符串是否有效

例如check_string("Ab")是无效的,因为里面有一个A。

使用 strtok 将“Ab + abc EF++aG hi jkL”拆分为单独的部分

int check_string(char* str){ 
  // 0 = false 
  // 1 = true
  int invalid_chars = 0; 

   for (int i = 0; i<strlen(str); i++){
     int char_int_val = (int) str[i];
     if (!((char_int_val >= 97 && char_int_val <= 122))){
        invalid_chars = 1; 
    }
   }

  return invalid_chars;  
}


如有任何帮助,我们将不胜感激。感谢阅读。

如果您对代码的工作原理有任何疑问,请问我。另外我是 Whosebug 的新手,请告诉我是否需要更改某些内容。

您的代码中存在一些问题。首先,我将重复我在评论中所说的话:

  • 没有为字符串副本分配足够的 space。 strlen 没有 在其长度中包含 NUL 终止符,所以当你这样做时
char* new_str = malloc(str_length); 
strcpy(new_str, str);

new_strstrcpy 添加 '[=17=]' 时溢出 1,调用 undefined behavior。您需要额外分配一个:

char* new_str = malloc(str_length + 1); 
strcpy(new_str, str);
  • 你不应该free any pointer returned from strtok. You only free memory that's been dynamically allocated using malloc and friends. strtok does no such thing, so it's 。这样做也会调用 UB。

你最后的问题是因为:

// copy str to new_str, that's correct because strtok
// will manipulate the string you pass into it
strcpy(new_str, str);  
// get the first token and allocate size for the number of tokens,
// so far so good (but you should check that malloc succeeded)
char* ptr = strtok(new_str, delim);
char** array_2d = malloc(sizeof(char*) *num_tokens);

while (ptr != NULL){
    if (check_string(ptr) == 0){
      // whoops, this is where the trouble starts ...
      array_2d[index] = ptr; 
      index++;
    }
    // get the next token, this is correct
    ptr = strtok(NULL, delim); 
  } 
  // ... because you free new_str
  free(new_str); 

ptr 是指向 new_str 中某个标记的指针。一旦你 free(new_str),任何指向那个 now-deallocated 内存的指针都是无效的。您已加载 array_2d 指向不再分配的内存的指针。再次尝试访问这些位置会调用未定义的行为。我可以想到两种方法来解决这个问题:

  1. 不是保存偏移到 new_str 的指针,而是在 str(来自 main 的字符串)中找到相同的标记并指向它们。由于这些是在 main 中定义的,因此只要程序存在,它们就会一直存在。
  2. 分配更多内存,并将令牌 strcpy 放入 array_2d[index]。我将在下面进行演示:
while (ptr != NULL){
    if (check_string(ptr) == false)
    {
      // allocate (enough) memory for the pointer at index
      array_2d[index] = malloc(strlen(ptr) + 1);
      // you should _always_ check that malloc succeeds
      if (array_2d[index] != NULL)
      {
          // _copy_ the string pointed to by ptr into our new space rather
          // than simply assigning the pointer
          strcpy(array_2d[index], ptr);
      }
      else { /* handle no mem error how you want */ }
      index++;
    }

    ptr = strtok(NULL, delim); 
}

// now we can safely free new_str without invalidating anything in array_2d
free(new_str); 

我有一个工作 demonstration here。注意演示中的一些其他更改:

  • #include <stdbool.h> 并使用它代替 0 和 1 ints.
  • 将您的 get_tokens 函数稍微更改为“return”令牌数。这在 main 打印出来时很有用。
  • 用字符替换了 ASCII 幻数。
  • 删除了无用的 freedPointer = NULL 行。
  • 将涉及尺寸的所有内容的 int 类型更改为 size_t 类型。

最后一点,虽然这是一个有效的实现,但它可能做的工作比它需要的要多一些。与其在第一次通过时计算令牌的数量,然后在第二次通过时检索它们,您当然可以在一次通过中做任何您想做的事情,但如果您愿意的话,我会把它作为练习留给您。