【问题标题】:Manipulating dynamically allocated 2D char arrays in C在 C 中操作动态分配的 2D 字符数组
【发布时间】:2019-02-18 15:51:38
【问题描述】:

我在尝试在 C 中操作 2d 动态数组时遇到问题。我想做的是在 2d 数组的每一行中存储一个 char 字符串,然后执行检查以查看该字符串是否包含某个字符,如果是这样删除所有出现然后转移到空位置。实际发生的是我收到了exit status 1

有关问题的更多信息,例如,如果我有

Enter string 1: testing
Enter string 2: apple
Enter string 3: banana

我希望输出变成

What letter? a // ask what character to search for and remove all occurences
testing
pple
bnn

这是我的完整代码:

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


  void removeOccurences2(char** letters, int strs, int size, char letter){
    // Get size of array
    // Shift amount says how many of the letter that we have removed so far.
    int shiftAmt = 0;
    // Shift array says how much we should shift each element at the end
    int shiftArray[strs][size];

    // The first loop to remove letters and put things the shift amount in the array
    int i,j;
    for(i=0;i < strs; i++){
        for(j = 0; j < size - 1; j++) {

              if (letters[i][j] == '\0'){
                  break;
              }

              else {
              // If the letter matches

              if(letter == letters[i][j]){

              // Set to null terminator
              letters[i][j] = '\0';
              // Increase Shift amount
              shiftAmt++;
              // Set shift amount for this position to be 0
              shiftArray[i][j] = 0;
              }else{
              // Set the shift amount for this letter to be equal to the current shift amount
              shiftArray[i][j] = shiftAmt;
              }
              }
        }
      }  

    // Loop back through and shift each index the required amount
    for(i = 0; i < strs; i++){
        for(j = 0; j < size - 1; j++) {
          // If the shift amount for this index is 0 don't do anything


          if(shiftArray[i][j] == 0) continue;
          // Otherwise swap
          letters[i][j - shiftArray[i][j]] = letters[i][j];
          letters[i][j] = '\0';

        }

        //now print the new string
        printf("%s", letters[i]);
    }
    return;
  }

  int main() {
      int strs;
      char** array2;
      int size;
      int cnt;
      int c;
      char letter;
      printf("How many strings do you want to enter?\n");
      scanf("%d", &strs);
      printf("What is the max size of the strings?\n");
      scanf("%d", &size);
      array2 = malloc(sizeof(char*)*strs);
      cnt = 0;
      while (cnt < strs) {
          c = 0;
          printf("Enter string    %d:\n", cnt + 1);
          array2[cnt] = malloc(sizeof(char)*size);
          scanf("%s", array2[cnt]);
          cnt += 1;
      }

        printf("What letter?\n");
      scanf(" %c", &letter);
      removeOccurences2(array2,strs,size,letter);

  }

提前致谢!

【问题讨论】:

  • 从字符串中删除字母的正确方法是在字符串上保留 2 个索引:一个读取索引,每一步递增 1,一个写入索引,仅当字母增加时才递增与要删除的字母不匹配。更简单意味着更强大。
  • OT:关于诸如array2[cnt] = malloc(sizeof(char)*size); 1) 表达式:sizeof(char) 在 C 标准中定义为 1。乘以 1 无效。建议在调用任何堆分配函数时删除表示2):malloccallocrealloc,始终检查(!= NULL)返回值以确保操作成功。如果不成功,则调用perror() 输出您的错误消息和系统认为发生错误的文本原因。
  • OT:当调用任何scanf() 系列函数时; 1) 始终检查返回值(不是参数值)以确保操作成功(在这种情况下,如果返回值不等于 1,则函数失败。) 2) 使用输入格式说明符时 '%s ' 和/或 '%[...]' 总是包含一个最大字符修饰符,它比输入缓冲区的长度小 1,因为这些说明符总是附加一个 NU1 字节。这也避免了缓冲区溢出和由此产生的未定义行为的任​​何可能性
  • OT:为什么这两个声明:c = 0;int c;?它们对发布的代码没有任何作用,并且会导致编译器输出一条关于变量被设置但未使用的消息
  • OT:为了便于阅读和理解:1) 请始终缩进代码。在每个左大括号“{”后缩进。在每个右大括号 '}' 之前取消缩进。建议每个缩进级别为 4 个空格 2) 请遵循公理:每行只有一个语句,并且(最多)每个语句一个变量声明。

标签: c arrays memory-management


【解决方案1】:

你可以从字符串中删除字母,因为你只能缩短字符串。

代码可以是:

void removeOccurences2(char** letters, int strs, int size, char letter){
    int i,j,k;
    // loop over the array of strings
    for(i=0;i < strs; i++){
        // loop per string
        for(j = 0, k=0; j < size; j++) {
              // stop on the first null character
              if (letters[i][j] == '\0'){
                  letters[i][k] = 0;
                  break;
              }
              // If the letter does not match, keep the letter
              if(letter != letters[i][j]){
                  letters[i][k++] = letters[i][j];
              }
        }
        //now print the new string
        printf("%s\n", letters[i]);
    }
    return;
  }

但是你应该在返回环境之前释放所有分配的数组,并在main的末尾显式返回0。

【讨论】:

  • 在现代 c 中,除非另有明确说明,否则 main() 的返回值假定为 0
【解决方案2】:

嗯,你的程序有几个问题,基本上你得到segmentation fault 错误,因为你正在访问你的程序没有分配的无效内存。以下是我发现的一些问题:

  1. shiftAmt 在处理/检查每个导致 shiftArray 值不正确的字符串后不会重置。
  2. shiftArray 的值仅设置为字符串长度的预期值,但之后(从每个字符串的长度到size 的值)是随机数。
  3. 删除出现字符的逻辑不正确 - 您需要将出现字符后的整个字符串向左移动,而不仅仅是像您正在做的那样操作单个字符。

1 & 2 导致分段错误错误(使程序崩溃),因为它导致此行letters[i][j - shiftArray[i][j]] = letters[i][j]; 访问意外内存。您可以查看我对您的removeOccurences2 方法的编辑版本以供参考:

int removeOccurences2(char* string, char letter) {
    if(!string) return -1;
    int i = 0;

    while (*(string+i) != '\0') {
        if (*(string+i) == letter) {
            memmove(string + i, string + i + 1, strlen(string + i + 1));
            string[strlen(string) - 1] = '\0'; // delete last character
        }
        i++;
    }
    return 0;
}

这只是一个例子,它的逻辑仍有一些缺陷等待你完成。提示:尝试案例:“bananaaaa123”

编码愉快!

【讨论】:

    【解决方案3】:

    "...如果字符串包含某个字符,如果是,则删除所有出现的字符,然后移过空位置。"

    可以通过增加最初包含相同内容的两个指针来就地编辑原始字符串。下图说明:

    void remove_all_chars(char* str, char c) 
    {
        char *pr = str://pointer read
        char *pw = str;//pointer write
        while(*pr) 
        {
            *pw = *pr++;     
            pw += (*pw != c);//increment pw only if current position == c
        }
        *pw = '\0';//terminate to mark last position of modified string
    }
    

    这是我见过的用于执行此任务的最简洁、最简单的形式。 Credit goes to this answer.

    【讨论】:

      猜你喜欢
      • 2012-11-23
      • 2013-04-28
      • 2015-06-08
      • 1970-01-01
      • 2015-07-14
      • 1970-01-01
      • 1970-01-01
      • 2013-03-09
      • 2011-10-21
      相关资源
      最近更新 更多