Search code examples
carrayscharundefined-behavior

C Unexpected inputs with character arrays


I was working with a group on a Google Code Jam practice problem (you can read it here). Our code is as follows:

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

main(){
  int c;
  int n = 0;
  int l = 0;
  int d = 0;
  int caseCount = 0;
  int i = 0;
  int j = 0;
  //A boolean value used in determining parenthesees.
  bool letBool = false;
  //A boolean value used in determining something;
  bool wordBool = false;
  //Temporary space for test characters
  char test[1000];
  //Gets word length
  while((c=getchar())!=' ' && c!= '\n'){
    l = (l * 10) + c - '0';
    //printf("%d\n", l);
  }
  //Gets number of valid words.
  while((c=getchar())!=' ' && c!= '\n'){
    d = (d * 10) + c - '0';
    //printf("%d\n", d);
  }
  //Gets number of test cases.
  while((c=getchar())!= '\n'){
    n = (n * 10) + c - '0';
    //printf("%d\n", n);
  }
  //Array of all valid words.
  char dict[d][l];
  c=getchar();
  //While we still have words to read in.
  while(i < d){
    //If not new line
    if(c!='\n'){
      //Then read character
      dict[i][j] = c;
    }
    else{
      i++;
      j=0;
    }
    c=getchar();
    j++;
  }
  i = 0;
  j = 0;
  while(i < n){
    j = 0;
    while((c=getchar())!='\n' && c!=EOF){
      putchar(c);
      test[j] = c;
      j++;
    }
    putchar('\n');
    test[j+1] = '\0';
    printf("%s\n", test);
    int word = 0;
    //Going through valid words
    while(word < d){
      wordBool=true;
      j = 0;
      int letter = 0;
      //Going through valid letters
      while(letter < l){
        letBool=false;
        if(test[j] == '('){
          while(test[j++]!=')'){
            if(dict[word][letter]==test[j]){
              letBool=true;
              //printf("%s%d%s%d\n" "letBool is true at word: ", word, "letter: ", letter);
            }
          }
        }
        else{
          if(test[j]==dict[word][letter]){
            letBool=true;
            //printf("%s%d%s%d\n" "letBool is true at word: ", word, "letter: ", letter);
          }
        }
        j++;
        letter++;
        if(!letBool){
          wordBool=false;
          //printf("%s%d%s%d\n" "wordBool is false at word: ", word, "letter: ", letter);
        }
      }
      if(wordBool){
        caseCount++;
      }
      word++;
    }
    printf("%s%d%s%d\n", "Case #", i+1, ": ", caseCount);
    i++;
    j=0;
    caseCount=0;
  }
}

The problem is the output seems to correctly read the input for the "recieved alien code" part with out putchar, but when it is entered into the test case, it becomes corrupted. Here is the part I am talking about:

while((c=getchar())!='\n' && c!=EOF){
      putchar(c);
      test[j] = c;
      j++;
    }
    putchar('\n');
    test[j+1] = '\0';
    printf("%s\n", test);

When we give it this input:

3 5 4
abc
bca
dac
dbc
cba
(ab)(bc)(ca)
abc
(abc)(abc)(abc)
(zyx)bc

we get

ab)(bc)(ca)
ab)(bc)(ca)
7
Case #1: 0
abc
abc
b
Case #2: 1
(abc)(abc)(abc)
(abc)(abc)(abc)
Case #3: 0
(zyx)bc
(zyx)bcb
Case #4: 0

(
Case #5: 0

So, we can't understand why there are random characters like 7 and b printed out.


Solution

  • Change this:

    test[j+1] = '\0';
    

    to this:

    test[j] = '\0';
    

    The reason is that you already incremented j's value, at the end of the while loop, before the terminating condition was met:

    j = 0;
    while((c=getchar())!='\n' && c!=EOF){
      putchar(c);
      test[j] = c;
      j++;    <------
    }
    putchar('\n');
    test[j] = '\0';
    

    So, with test[j + 1], you actually skipped a cell. That means that you are experiencing Undefined Behaviour, since that skipped cell contains junk, since test was not initialized to nothing. As a result, we do not know what that cell contains, it contains junk.

    Output with the corrected code:

    gsamaras@gsamaras:~$ gcc -Wall px.c
    gsamaras@gsamaras:~$ ./a.out 
    3 5 4
    abc
    bca
    dac
    dbc
    cba
    (ab)(bc)(ca)
    abc
    (abc)(abc)(abc)
    (zyx)bcab)(bc)(ca)
    ab)(bc)(ca)
    Case #1: 0
    abc
    abc
    Case #2: 1
    (abc)(abc)(abc)
    (abc)(abc)(abc)
    Case #3: 0
    
    (zyx)bc
    (zyx)bc
    Case #4: 0
    

    Moreover, use:

    int main(void) {
      ...
      return 0;
    }
    

    For more, read this: What should main() return in C and C++?