Simple C program involving arrays which cannot execute

  • #1
341
4
< 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:

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:

Answers and Replies

  • #2
227
58
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.
 
  • #3
341
4
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.
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 ):
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;

       
}
 
  • #4
341
4
I've modified my code again, and it still isn't working. I don't know why. Here is my edited 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;
}
 
  • #5
wle
336
158
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
    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
    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
    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:
    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.

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?

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
C:
strcmp(input_word, "STOP")
to find out if the string input_word is equal (character-by-character) to the string "STOP", or
C:
strncmp(input_word, "STOP", 4)
if you specifically want to compare no more than four characters.
 
  • #6
35,428
7,287
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) .
This is not completely true.
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:
C:
char input_word[100];
Also, I don't think it's a good idea to assign int to a char variable.
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.

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 * ?
The can be used for strings declared as arrays of type char; that is, like this: char str[30]; for example.
 
  • #7
341
4
scanf("%42s", my_string) and fgets(my_string, 42, stdin) do similar but not identical things. Make sure you understand the differences.
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.
 
  • #8
35,428
7,287
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.
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.
 
  • #9
341
4
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.
Thanks! Just to be sure, scanf stores the newline character, but fgets doesn't, right?
 
  • #11
wle
336
158
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:
       abc  def[ENTER]
    (three spaces then abc...) into the terminal, fgets would copy
    Code:
    "   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:
C:
scanf fgets
 
Last edited:
  • Like
Likes toforfiltum
  • #12
wle
336
158
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.
 
  • #13
341
4
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.
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:

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;
    }
}
 
  • #14
35,428
7,287
Your code is a lot more complicated than it needs to be. Here is the problem statement you wrote in the first post:
... 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.
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;
    }
}
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:
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:
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
 
  • #15
wle
336
158
I haven't yet learned dynamic memory management, but I'll get back to it later after fixing a bug in my program.

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".)

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.

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.

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.

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).

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.

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
    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:
    C:
            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';
    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:
  • C:
    int main ()
    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:
  • Like
Likes toforfiltum
  • #16
341
4
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
    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.
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:

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!
 
  • #17
35,428
7,287
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?
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?
 
  • #18
341
4
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?
Won't there be a null character right after"uesday"?
 
  • #19
35,428
7,287
Won't there be a null character right after"uesday"?
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.
 
  • Like
Likes toforfiltum
  • #20
wle
336
158
Here is my much improved (hopefully!) version of the code

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


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.

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":
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:
  • Like
Likes toforfiltum
  • #21
341
4
That looks much better. You just might like to put a limit on how many characters scanf is allowed to read.
So would it be something like %100s? And also, if I were to do this with fgets instead, I should put 99 to account for the null character, right?

Wow, I didn't know that there could be so many bytes in just a few letters! The double x and int i gets printed after the string. Is this something related to the stack?
 
  • #22
wle
336
158
So would it be something like %100s? And also, if I were to do this with fgets instead, I should put 99 to account for the null character, right?

No, it's the other way around. With fgets, the size argument is the number of array bytes fgets is allowed to use, including the byte for the null terminator. With scanf, the "%100s" directive matches up to 100 characters from standard input, which would fill up 101 array bytes when you count the null terminator it adds. So you want to use "%99s" for a 100-byte array.


Wow, I didn't know that there could be so many bytes in just a few letters!

I'm not sure what you mean. Each character (char) is one byte. Other data types are typically larger. On my PC using clang, the double type is 64 bits (8 bytes) in size and int is 32 bits (4 bytes). This is why having the double x resulted in 8 extra characters being printed.


The double x and int i gets printed after the string. Is this something related to the stack?

It's due to the order in which the program compiled with clang puts the variables on the stack, so the characters "hell" end up directly followed in memory by the bytes of x and then i. I don't know if there's a specific reason clang does it in that order. (I found that gcc can produce the same result if you compile with the -Os switch, but then you also need to put in some code that prints or does something else with the variables i and x, otherwise gcc notices that these variables aren't explicitly being used and just removes them from the program.)
 
Last edited:

Related Threads on Simple C program involving arrays which cannot execute

  • Last Post
Replies
8
Views
1K
Replies
3
Views
4K
Replies
3
Views
2K
  • Last Post
Replies
7
Views
664
Replies
5
Views
1K
  • Last Post
Replies
2
Views
700
Replies
3
Views
802
Replies
11
Views
4K
  • Last Post
Replies
6
Views
1K
Replies
3
Views
1K
Top