0

I'm having trouble with trying to manipulate 2d dynamic arrays in C. What I want to do is to store a char string in every row of the the 2d array then perform a check to see if the string contains a certain character, if so remove all occurrences then shift over the empty positions. What's actually happening is I get an exit status 1.

More about the problem, for example if I have

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

I would want the output to become

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

Here is my full code:

  #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);

  }

Thanks in advance!

  • 2
    The correct way to remove letters from a string is simply to keep 2 indexes on the string: one read index which is incremented by one on each step, one write index which is incremented only if the letter does not match the letter to remove. More simple just means more robust. – Serge Ballesta Feb 18 '19 at 16:35
  • OT: regarding statements like: `array2[cnt] = malloc(sizeof(char)*size);` 1) the expression: `sizeof(char)` is defined in the C standard as 1. Multiplying by 1 has no effect. Suggest removing that expressing 2) when calling any of the heap allocation functions: `malloc` `calloc` `realloc`, always check (!=NULL) the returned value to assure the operation was successful. If not successful, then call `perror()` to output both your error message and the text reason the system thinks the error occurred. – user3629249 Feb 19 '19 at 02:08
  • OT: when calling any of the `scanf()` family of functions; 1) always check the returned value (not the parameter values) to assure the operation was successful (in this case if the returned value is not equal to 1, then the function failed.) 2) when using the input format specifiers '%s' and/or '%[...]' always include a max characters modifier that is 1 less than the length of the input buffer because those specifiers always append a NUl byte. This also avoids any possibility of a buffer overrun and the resulting undefined behavior – user3629249 Feb 19 '19 at 02:11
  • 1
    OT: why these two statement: `c = 0;` and `int c;`? They do nothing for the posted code and will result in the compiler outputting a message about a variable being set but not used – user3629249 Feb 19 '19 at 02:15
  • 1
    OT: for ease of readability and understanding: 1) please consistently indent the code. Indent after every opening brace '{'. Unindent before every closing brace '}'. Suggest each indent level be 4 spaces 2) please follow the axiom: *only one statement per line and (at most) one variable declaration per statement.* – user3629249 Feb 19 '19 at 02:23

3 Answers3

3

You can remove letters from a string in place, because you can only shorten the string.

The code could simply be:

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;
  }

But you should free all the allocated arrays before returning to environment, and explicitely return 0 at the end of main.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
2

"...if the string contains a certain character, if so remove all occurrences then shift over the empty positions."

The original string can be edited in place by incrementing two pointers initially containing the same content. The following illustrates.:

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
}

This is the cleanest, simplest form I have seen for doing this task. Credit goes to this answer.

ryyker
  • 22,849
  • 3
  • 43
  • 87
2

Well, there are several issues on your program, basically you are getting segmentation fault error because you are accessing invalid memory which isn't allocated by your program. Here are some issues I found:

  1. shiftAmt isn't reset after processing/checking each string which lead to incorrect value of shiftArray.
  2. Values of shiftArray only set as expected for length of string but after that (values from from length of each string to size) are random numbers.
  3. The logic to delete occurrence character is incorrect - you need to shift the whole string after the occurrence character to the left not just manipulating a single character like what you are doing.

1 & 2 cause the segmentation fault error (crash the program) because it causes this line letters[i][j - shiftArray[i][j]] = letters[i][j]; access to unexpected memory. You can take a look at my edited version of your removeOccurences2 method for reference:

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;
}

It's just an example and there is still some flaw in its logics waiting for you to complete. Hint: try the case: "bananaaaa123"

Happy coding!

CIMinuv
  • 213
  • 1
  • 5