1. Limited time only! Sign up for a free 30min personal tutor trial with Chegg Tutors
    Dismiss Notice
Dismiss Notice
Join Physics Forums Today!
The friendliest, high quality science and math community on the planet! Everyone who loves science is here!

Simple C program involving arrays which cannot execute

  1. Mar 14, 2017 #1
    < Mentor Note -- thread moved to HH from the technical Computer forum, so no HH Template is shown >

    I have a homework problem which requires me to convert a word a user entered to Pig Latin by moving the first letter of the word to the end and adding an ay to it. For example, Tuesday becomes uesdayTay. This process should be repeated until the user types STOP.

    I'm really new to arrays, so I might be using them wrongly, but I can't find out why. The program I wrote can be compiled but crashes whenever I execute it. I don't know if the logic behind my code is right either. I'm sure this program is rather simple, but here is my code:

    Code (C):

    #include <stdio.h>
    #include <string.h>

    int main ()
    {
       char *input_word [100] = {0} , *temp [100] = {0} , *stop [4] = {0};
       int n = 0;

       printf("Enter a word: ");
       for( n = 0; n < 100; n++)
       {
           scanf("%s", input_word[n]);
       }


       while (  strcmp ( stop [4], "STOP" ) != 0 )
       {
           *temp = input_word [0];
           for ( int j = 1; j <= n-1; j++)
           {
               *input_word [j-1] = *input_word [j];
           }

           input_word [n-1] = *temp;

           printf("%s", *input_word);
           printf("ay\n");

           printf("Type STOP to terminate: ");
           for ( n = 0; n < 4; n++ )
           {
               scanf("%s", stop[n] );
           }

       }

       return 0;


    }
     
    Last edited by a moderator: Mar 14, 2017
  2. jcsd
  3. Mar 14, 2017 #2
    You don't need the '*' when declaring the array. The '*' defines the variable as a pointer which then means you need to allocate it memory to use it as an array ( using malloc function) .
    Also, I don't think it's a good idea to assign int to a char variable.
     
  4. Mar 14, 2017 #3
    Ok, so I've modified the code with your suggestions. Now, I'm wondering when am I able to use strcmp and so functions? Are they only able to be used with char * ? If so, how do I compare an array of commands that the user entered with the word STOP?

    My modified code ( which still does not work ):
    Code (C):

    #include <stdio.h>
    #include <string.h>

    int main ()
    {
        char input_word [100] = {'\0'}, temp [100]= {'\0'}, stop [4]= {"STOP"}, command [10] = {'\0'};
        int n = 0;
       
        printf("Enter a word: ");
        for( n = 0; n < 100; n++)
        {
            scanf("%s", &input_word[n]);
        }
       

        while (  strcmp ( stop [4], "STOP" ) != 0 )
        {
            temp [0] = input_word [0];
            for ( int n=0, j = 1; j <= n-1; j++)
            {
                input_word [j-1] = input_word [j];
            }
           
            input_word [n-1] = temp [0];
           
            printf("%s", input_word);
            printf("ay\n");
           
            printf("Type STOP to terminate: ");
            for ( n = 0; n < 4; n++ )
            {
                scanf("%s", &stop[n] );
            }
           
        }
       
        return 0;

           
    }
     
  5. Mar 14, 2017 #4
    I've modified my code again, and it still isn't working. I don't know why. Here is my edited code:
    Code (C):

    #include <stdio.h>

    int main ()
    {
        char input_word [100] = {'\0'}, temp [100]= {'\0'}, command [10] = {'\0'}, stop [4] = {"STOP"};
        int n = 0, match = 0;
       
        printf("Enter a word: ");
        for ( n=0; input_word[n] != '\0'; n++ )     //use scanf here because I need the n to manipulate the letter
        {
            scanf("%s", &input_word [n]);
        }
       
        match = 0;      
        while ( match == 0 )
        {
            temp [0] = input_word [0];                //this is to store very first letter in temporary place
            for ( int j = 1; j <= n; j++)            //This for loop is to shift the letters back by one. Eg: Red, e and d go to position 0 and 1
            {                                        //in input_word.
                input_word [j-1] = input_word [j];
            }
           
            input_word [n] = temp [0];            //this is to store the very first letter in the last position of input_word, that's why need n.
            input_word [n+1] = 'a';                //self explanatory I hope?
            input_word [n+2] = 'y';
           
            printf("%s", input_word);
           
            fgets ( command, 4, stdin );
           
            for ( n = 0; n < 4; n++ )
            {
                if (command [n] == stop [n])
                {
                    match = 1;
                    break;
                }
                else
                {
                    match = 0;
                }
            }
           
        }
       
        return 0;
    }
     
  6. Mar 14, 2017 #5

    wle

    User Avatar

    Hi. I can see technical problems with your code. But I don't think it's worth going through all of them because the most obvious problem is: I think you're doing way too much work. (I quickly wrote my own solution and found that the main function only needs to be about 8-10 lines long in order to do what I understood the exercise is asking for.)

    My suggestion is to break the problem down a bit. In particular, instead of trying to solve the whole problem up front, try to write a simpler, first version of the program that reads just one word and converts it, then run it and check that it's working. After that, it should be easy modify the program to just do that over and over again until the user enters "STOP".

    Here's a few tips/things to keep in mind:
    • You don't need to copy or modify the word you read in order to convert it. In C if you do something like
      Code (C):
      char my_string[] = "hello";
      then the variable my_string (in most contexts) evaluates to a pointer to the first character in the string (i.e., it is the same as &my_string[0]). This means you can do basic pointer arithmetic with it. For example, my_string + 1 points to the second character in the string (the 'e' in this case), and
      Code (C):
      printf("%s", my_string + 1);
      would print "ello" (i.e., everything from the second character up to the null character that terminates the string).
    • When you do
      Code (C):
      char input_word [100] ...
      you're reserving space for one 100-character-long string (and not 100 strings, in case you weren't clear on that), including the terminating null character. But when you read from standard input, functions like scanf and fgets have no way to know how much space you've reserved, and you need to tell these functions explicitly not to try to read more characters than you've reserved space for. You can tell scanf to read no more than a certain number of characters by putting a number in the '%s' directive:
      Code (C):
      scanf("%99s", input_word)
      which leaves place for the null terminator.
    • scanf("%42s", my_string) and fgets(my_string, 42, stdin) do similar but not identical things. Make sure you understand the differences.
    • You should use the strcmp or strncmp functions to compare strings (like you were trying to do before). Just make sure scanf or fgets actually construct the string you were expecting if you use one of these functions.

    They only work with character pointers. But: 1) strings are a type of array, and the name of an array is treated as a pointer to its first element in most contexts (the only exception I know of is when it is used as an argument to sizeof), and 2) string literals like "STOP" also evaluate to a pointer to their first element. So you can do
    Code (C):
    strcmp(input_word, "STOP")
    to find out if the string input_word is equal (character-by-character) to the string "STOP", or
    Code (C):
    strncmp(input_word, "STOP", 4)
    if you specifically want to compare no more than four characters.
     
  7. Mar 15, 2017 #6

    Mark44

    Staff: Mentor

    This is not completely true.
    Code (C):
    char *input_word [100] = {0};
    This declaration declares input_word as a 100-element array of pointers. input_word[0] can hold the address of a char, input_word[1] can hold a different address (of a char), and so on. The pointers (addresses) in the array are not initialized. They could be assigned values of existing strings, but the strings don't necessarily need to be allocated heap memory by the use of malloc().

    If all you need is a block of memory to hold a string of 99 charactacters, do this:
    Code (C):
    char input_word[100];
    Probably not, but the char type is an integral type. If the int value is small enough there won't be any overflow, but the compiler will probably issue a warning.

    The can be used for strings declared as arrays of type char; that is, like this: char str[30]; for example.
     
  8. Mar 16, 2017 #7
    Thanks so much for your help! However, I'm not sure if I could see any difference between both. Both reads from stdin, and stores it in the character array my_string.
     
  9. Mar 16, 2017 #8

    Mark44

    Staff: Mentor

    The difference that wle was alluding to was how the two functions differ when a newline character is encountered. One of these functions stores the newline character in the array being filled, and the other one doesn't.
     
  10. Mar 16, 2017 #9
    Thanks! Just to be sure, scanf stores the newline character, but fgets doesn't, right?
     
  11. Mar 16, 2017 #10

    Mark44

    Staff: Mentor

    No, it's just the opposite.
     
  12. Mar 16, 2017 #11

    wle

    User Avatar

    There are (at least) two other important differences:
    • scanf with the %ns directive ignores leading whitespace, then copies characters into the supplied array until it reads another whitespace character. fgets just reads and copies all characters up to and including the next newline. This means if you typed
      Code (Text):
         abc  def[ENTER]
      (three spaces then abc...) into the terminal, fgets would copy
      Code (Text):
      "   abc   def\n"
      into the supplied array while scanf would only copy "abc".
    • Both fgets and scanf will automatically add a '\0' byte at the end of the array for you. fgets counts this toward the maximum number of bytes it is allowed to copy into the array, but scanf doesn't. So fgets(my_string, 42, stdin) will copy at most 42 bytes into my_string while scanf("%42s", my_string) could copy 43 bytes.

    It's important to look up documentation for details like this. Which, incidentally, the [CODE="C"]...[/CODE] tags here turn some function names into hyperlinks to:
    Code (C):
     
    Last edited: Mar 16, 2017
  13. Mar 17, 2017 #12

    wle

    User Avatar

    By the way, I don't know if you're expected to resolve this but it's something to keep in mind anyway: putting arbitrary limits on what your program can handle (like just assuming the user won't enter a word more than 100 characters long) is a common enough habit to have its own entry in the Hacker's Dictionary.

    Fixing this properly in C often requires doing dynamic memory management, which you may or may not already have learned how to do. In the case of your exercise it's not necessary though: it is possible to correctly convert arbitrarily long input words using only one fixed-size array (it only needs to be long enough to save enough characters to tell if a word is "STOP" before you decide whether or not to print anything). You might like to think about how to do this.
     
  14. Mar 18, 2017 #13
    I haven't yet learned dynamic memory management, but I'll get back to it later after fixing a bug in my program. I have managed to make it work somewhat, but not exactly right, and also using much more lines of code than you said was necessary. I'm not sure if the question meant just constantly repeating the process without having the user press ENTER repeatedly,but mine needs the user to do so. If it is just looping automatically, I have no idea how to where to write code in my program to accept user input for STOP, because I have never done so before. And also, the bug I mentioned is that the first output involves 2 processes of the Pig Tay transformation, not 1. I don't know why. Anyway, here is my improved code:

    Code (C):

    #include <stdio.h>
    #include <string.h>

    int Stop (char command [] );

       
    int main ()
    {
        char input_word [100] = {'\0'}, temp = '0', command [100] = {'\0'};
        int n = 0, match = 1;
       
        printf("Enter a word: ");
        scanf("%99s", input_word );
        n = strlen ( input_word );
       
               
        while ( match == 1 )
        {
            temp = input_word [0];              
            for ( int j = 1; j <= n-1; j++)            
            {                                        
                input_word [j-1] = input_word [j];
            }
           
            input_word [n-1] = temp;      
            input_word [n] = 'a';              
            input_word [n+1] = 'y';
           
            n = strlen ( input_word );
           
            printf("%s\n", input_word);

            fgets ( command, 100, stdin );
           
            for ( int i = 0; i < 100; i++ )
            {
                if ( command [i] == '\n' )
                {
                    command [i] = '\0';
                }
            }
           
            match = Stop ( command );
            if ( match == 0 )
            {
                break;
            }
            else
            {
                match = 1;
            }
        }
       
       
       
       
        return 0;
    }

    int Stop ( char command[] )
    {
        int match = 1;
       
        match = strcmp ( command, "STOP" );
        if ( match == 0 )
        {
            return 0;
        }
        else
        {
            return 1;
        }
    }
     
  15. Mar 18, 2017 #14

    Mark44

    Staff: Mentor

    Your code is a lot more complicated than it needs to be. Here is the problem statement you wrote in the first post:
    You don't need two arrays to hold strings -- one will be sufficient. You don't need a special function to determine that the user has typed STOP.
    You also don't need to separated (and different) calls for input -- i.e., a call to scanf() and another call to fgets(). One call is sufficient.

    I rewrote your code in a much simplified form. I have only one declaration -- for a char array to hold the input word. I don't have any other variables.

    I have a single loop that runs forever, or until the user types STOP (exactly like that, not stop, not Stop, or any other permutation).

    The pseudocode looks like this:
    Code (Text):
    while (forever)
       Prompt the user to enter a word or to type STOP to exit
       Read the input word
       If the input word is STOP, exit the loop
       Print the substring starting at index 1, print the character at index 0, print "ay", print a newline character
       Flush the input buffer (using fflush(stdin))
    Output from my program:
    Code (Text):
    Enter a word, or STOP to exit: Tuesday
    uesdayTay
    Enter a word, or STOP to exit: Frodo
    rodoFay
    Enter a word, or STOP to exit: Programming
    rogrammingPay
    Enter a word, or STOP to exit: STOP
     
  16. Mar 21, 2017 #15

    wle

    User Avatar

    That's OK -- like I said, there's a way that you can handle arbitrary sized words in your problem using only a fixed sized array. (In fact, if it weren't for the complication of testing for the word "STOP", you could write the program without using any arrays at all: just save the first character of each word in a temporary variable, print the remaining characters one by one as you read them until you reach the end of the word, and then print the first character followed by "ay".)

    It looks like you're trying to write a program that alternates between reading a word to convert it then reading another word to see if it is the same as "STOP". The way I interpreted the exercise you should just convert words until the user enters the word "STOP", i.e., read a word and check if it is "STOP". If it is then end the program, otherwise print it in Pig Latin and read the next word.

    I'm not sure what you mean. You put a loop in your program so it will just loop until the test fails or you explicitly break out of it.

    One thing that it might help to clarify: the functions scanf and fgets don't necessarily return immediately. For example, fgets tries to read a whole line. If there isn't a complete line available from standard input then fgets will simply wait until there is, effectively pausing your program until you hit Enter (or you type more characters than the maximum fgets is allowed to read, or you press Ctrl-D or whatever key combination signals an end-of-file condition on the terminal you're using).

    The way your program is written at the moment, it reads a word once into the array input_word using scanf before entering the main loop, then repeatedly changes and prints input_word, reads a line using fgets into a different array command, and exits the loop if command is "STOP". In particular, you never read a new word into input_word, so your program keeps transforming the first word you entered. (So if you enter "Tuesday" it prints "uesdayTay" in the first loop, then "esdayTayuay" on the second, and so on.)

    The reason for the "double output" you're probably referring to is probably because of the way you called scanf and then started calling fgets. Remember that scanf("%s", ... doesn't read a full line, so if you type in "Tuesday[ENTER]" then scanf copies "Tuesday" into the array input_word, and since there's still a newline left over, fgets just copies a string containing only a newline into command and immediately returns, and since this is different from "STOP" you immediately go to the top of the loop and transform and print input_word again.

    Mark44 gave a good outline of how you should approach writing a short and working solution to the problem. I'll still give some specific comments/criticisms about your code to keep in mind for the next version:
    • You're using both a flag variable (match) and a break command to terminate the loop; you only need one of these. The test
      Code (C):
              if ( match == 0 )
              {
                  break;
              }
              else
              {
                  match = 1;
              }
      isn't accomplishing anything in your code since if match is zero then you will break out of the while loop anyway (because the test match == 1 will fail), and if it isn't zero then it is necessarily one, in which case setting it to one doesn't change anything.
    • You initialize n to 1 but never use this value.
    • The modification you do here:
      isn't necessary. Like Mark44 says, you can just print input_word starting from the second character, then print the first character, then print "ay". There are also two bugs in this code: 1) You declare that input_word is an array of size 100 but, in the first loop, in the worst case n could be 99, in which case the last line above would try to assign to input_word[100] (i.e., the 101st array element). 2) You don't null-terminate the string. (A minor nit: 3) the test j <= n-1 could be simplified to just j < n.)
    • You shouldn't need to call strlen. If you use and maintain properly null-terminated strings then you can use the null terminator to detect when you've reached the end of a string in a loop.
    • If you read using scanf instead of fgets, you won't need to manually remove the newline.
    • Your function Stop can be written much more concisely (or, like Mark44 suggests, just eliminated entirely). if (match == 0) { return 0; } else { return 1; } could just as well be return match != 0;, and the temporary variable match isn't really needed. (Also, there's a header file stdbool.h which defines slightly more explicit support for boolean variables.)

    These are just a couple of minor nits:
    • Strictly speaking you should use int main(void) to declare that the main function takes no arguments. (In C, unlike C++, int main() with an empty argument list means that the main function accepts an unspecified number of arguments of unspecified types. This means you could write a call to main with multiple arguments in your code, e.g. main(1, 2, 3, "hello"), and it would still compile.)
    • You have a function, "Stop", whose name is capitalised. This isn't wrong but it's not common to capitalise function or variable names in C.
     
    Last edited: Mar 21, 2017
  17. Mar 26, 2017 #16
    Sorry for the late reply! I have been busy, but I really appreciate yours and Mark44's help! It seems I've made the question to be more complicated than it needs to be. Here is my much improved (hopefully!) version of the code:

    Code (C):

    #include <stdio.h>
    #include <string.h>

    int main (void)
    {
        char input [100] = {'\0'};
       
        printf("Enter a word: ");
        scanf("%s", input );
       
        while ( strcmp ( input, "STOP" ) != 0 )
        {
            printf("%s", input + 1 );
            printf("%c", input [0] );
            printf("ay\n");
           
            printf("Enter another word, or STOP to terminate: ");
            scanf("%s", input );
        }
       
        return 0;
    }
    I have one minor confusion about why it works. From the above posts, I know that scanf reads until the newline character but does not store it. And also, it adds a null character at the end of a string. Then, if so, why am I able to printf input [0] right after the string without doing anything to address the null character at the end of the string?

    Thanks!
     
  18. Mar 26, 2017 #17

    Mark44

    Staff: Mentor

    You are printing input[0] as a char, not a string. A char is a single byte.
    So if the user enters the string "Tuesday", the while loop body prints "uesday" followed by the character 'T', followed by the string 'ay' and a newline character. does that answer your question?
     
  19. Mar 26, 2017 #18
    Won't there be a null character right after"uesday"?
     
  20. Mar 26, 2017 #19

    Mark44

    Staff: Mentor

    Yes, but printf() doesn't print it -- it's not a printable character. It's the character that most of the string functions use to let them know to stop.
     
  21. Mar 27, 2017 #20

    wle

    User Avatar

    That looks much better. You just might like to put a limit on how many characters scanf is allowed to read.


    This is important to understand about C strings compared with strings in other languages: as far as C and its library functions are concerned, strings are just a sequence of bytes in memory with a null byte somewhere that marks the end of the string. They have no structure beyond that.

    Here's a contrived example that shows this. On my laptop (Linux x86-64), if I compile this with the clang compiler and run it, it prints "hello, world":
    Code (C):
    #include <stdio.h>

    int main(void)
    {
        int i = 0;
        double x = 5.628648981310136e+175;
        char str[] = {'h', 'e', 'l', 'l'};

        puts(str);

        return 0;
    }
    The reason for this is that the program (compiled with clang) puts the array 'str' and variables 'x' and 'i' directly after one another in memory in that order. So, since the array 'str' isn't null terminated, after printing "hell" puts just starts reading and printing the bytes that make up the double precision number 5.628648981310136e+175 (which happens to be represented in memory by the same sequence of bytes as the characters "o, world"), and then the bytes of the integer 0 (which is made up of null bytes, so it stops there).

    Similarly, if I added scanf("%s", str) in this program and typed in four or more characters, scanf would start to overwrite the memory where the variables x and i are stored, and then whatever happens to follow them.

    (NB: this was engineered to work with the clang compiler on my laptop. Different compilers and processors generally do memory alignment differently, so you may get a different result.)
     
    Last edited: Mar 27, 2017
Know someone interested in this topic? Share this thread via Reddit, Google+, Twitter, or Facebook

Have something to add?
Draft saved Draft deleted



Similar Discussions: Simple C program involving arrays which cannot execute
Loading...