Home > OS >  Modifying an array inside a while loop
Modifying an array inside a while loop

Time:08-24

I have this function that tries to remove non-alphabetic characters, however my code is not working. I tried to debug it and it seems that it is stuck on this line :

str[i]= str[i 1];

Can someone please explain me why it is stuck there?

Thank you.

int Removenonalfa(char *str) {
    int i =0;

    while (str[i]!='\0')
    {
        j=0;
        if ((str[i]>='A' && str[i]<='Z') || (str[i]>='a' && str[i]<='z'))
        ;
        else
        {

            str[i]= str[i 1];
            continue;

        }


        i  ;
    }
    return 0; }

CodePudding user response:

The continue; prevents the rest of the loop body from being executed, so i ; is never executed, so i never changes, so the loop continues with the same value of i forever.

You should rethink how you structure the loop. You have written it with one indicator of position in the string, i. But, since you want to remove non-letter characters and return a string with letter characters, what you really want to do is read each character in the string, and, if it is a letter, move it to its final place in the string. So you need two indicators of position. You have a j that is not used. Rewrite the loop so that i progresses through the string one character at a time—it is always incremented by one in each iteration, and you never skip that increment. Use j to track where each letter will be written to in the string. Then j will increment only when a letter is processed.

CodePudding user response:

try this one:

void KeepAlphabeticChars(char *str)
{
    int writer = 0, reader = 0;

    while (s[reader])
    {
        if ((str[reader]>='A' && str[reader]<='Z') || (str[reader]>='a' && str[reader]<='z'))
        {   
            str[writer  ] = str[reader];
        }

        reader  ;       
    }

    str[writer]=0;
}

you need to declare your string like this:

char str[] = "ab12cdef";

CodePudding user response:

The while loop gets stuck in a loop when more that one consecutive character fails the test.

Consider the string "A12" containing consecutive characters '1' and '2' that fail the test:

iter   i   str[i]   str[i 1]   str[i 2]   str[i 3]
----   --  ------   --------   --------   --------
1       0   'A'      '1'        '2'        '\0'
2       1   '1'      '2'        '\0'       N/A
3       1   '2'      '2'        '\0'       N/A
4       1   '2'      '2'        '\0'       N/A

Iterations 4 onwards are just repeating iteration 3.

One way to fix it is to use two indices, one for the source character position and one for the destination character position:

int Removenonalfa(char *str) {
    int i = 0;
    int j = 1;

    while (str[i]!='\0')
    {
        if ((str[i]>='A' && str[i]<='Z') || (str[i]>='a' && str[i]<='z'))
            ;
        else
        {
            str[i]= str[j];
            j  ;
            continue;
        }
        i  ;
        j  ;
    }
    return 0;
}

Consider the string "A12":

iter   i   str[i]   str[i 1]   str[i 2]   str[i 3]  j   str[j]   str[j 1]  str[j 2]
----   --  ------   --------   --------   --------  --  ------   --------  --------
1       0   'A'      '1'        '2'        '\0'      1   '1'      '2'       '\0'
2       1   '1'      '2'        '\0'       N/A       2   '2'      '\0'      N/A
3       1   '2'      '2'        '\0'       N/A       3   '\0'     N/A       N/A
4       1   '\0'     '2'        '\0'       N/A       4   N/A      N/A       N/A

The while loop terminates at the start of iteration 4 because str[i] equals '\0'. str[0] contains 'A' and str[1] contains '\0'. (str[2] contains '2' and str[3] contains '\0', but those do not matter.)

Demonstration:

#include <stdio.h>

int Removenonalfa(char *str) {
    int i = 0;
    int j = 1;

    while (str[i]!='\0')
    {
        if ((str[i]>='A' && str[i]<='Z') || (str[i]>='a' && str[i]<='z'))
            ;
        else
        {
            str[i]= str[j];
            j  ;
            continue;
        }
        i  ;
        j  ;
    }
    return 0;
}

int main(int argc, char *argv[])
{
    int i;
    for (i = 1; i < argc; i  )
    {
        printf("[%d] Before: %s\n", i, argv[i]);
        Removenonalfa(argv[i]);
        printf("[%d] After: %s\n", i, argv[i]);
    }
    return 0;
}

Run: ./a.out A12 abcde 12345 Output:

[1] Before: A12
[1] After: A
[2] Before: abcde
[2] After: abcde
[3] Before: 12345
[3] After:

N.B. The modification of main()'s argv[] string contents is allowed by the C standard, ref. C17 5.1.2.2.1/2: — The parameters argc and argv and the strings pointed to by the argv array shall be modifiable by the program, and retain their last-stored values between program startup and program termination.

Calling the function Removenonalfa with a pointer to a string literal will result in undefined behavior. See C17 6.4.5/7 regarding the unnamed character array used to store a string literal: It is unspecified whether these arrays are distinct provided their elements have the appropriate values. If the program attempts to modify such an array, the behavior is undefined.

CodePudding user response:

Note: In your posted code, the variable j is not declared, but is initialized to 0. This will not compile. Declare it and use it. You will need two points of reference to do what you are attempting to do. (See comments in solution code example below for reason.)

Other observations:

The loop is infinite only when there are two successive numbers in the string. eg if str = "asdf1234", then str[4] is changed to contain the value of str[5], which is also a diget. Because i is not incremented, the next test of str[4] will find it still remains a digit, and result in the same assignment to the value in str[5]. So str becomes "asdf2234", and remains that value, in an infinite loop.

In the case where numbers are not sequential, eg str = "asdf1a3a", even though the algorithm eventually exits the loop, the resulting string replaces any digits with the value contained in the following position, so the resulting string would look like: "asdfaaaa". I assume by "tries to remove non alphabetic characters" you would prefer the string be: "asdfaa"

Why:
Note the continue statement in C programming works somewhat like the break statement. Instead of forcing termination, it forces the next iteration of the loop to take place, skipping any code in between.

So in your code, once the conditions bring execution flow to the continue statement, i cannot be incremented preventing the exit condition in the while statement from ever being satisfied, and the flow is limited to the following four lines infinitely:

...
while (str[i]!='\0')
... //then 
if ((str[i]>='A' && str[i]<='Z') || (str[i]>='a' && str[i]<='z'))
... //finally
 str[i]= str[i 1];
 continue;//then continues back to the beginning of while loop  

Because i is not advanced (Note str[i]= str[i 1]; does not change i), The only elements of the string array beyond that point that are observed will remain str[i].

Following code removes digits from a string that contains them, regardless of whether the digits are sequential or not. Additionally, this example will shorten the string to contain only remaining non-numeric characters:

int Removenonalfa(char *str) 
{
     int i,j;//using both i AND j in this example
     for (i = 0; str[i] != '\0';   i) {
     while (str[i] >= '0' && str[i] <= '9') {//walking through original string
                                             //looking only for digits
         for (j = i; str[j] != '\0';   j) {//here we use j to track the next
                                           //occurrence to be changed so 
                                           //i does not change. 
            str[j] = str[j   1];//digit found, overwrite it with
                                //next value in array.
         }
         str[j] = '\0';//after advancing characters to fill position
                       //formerly occupied by digit, terminate new
                       //string
      }
   }
   return 0; 
}
  • Related