Thread: Using fgets() and memset() functions with a two-dimensional array.

  1. #16
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Gdias
    As you can see, once I'm not very fond of "break;" and "continue;" statements interrupting a loop in the middle of its body I opted for using a pointer(stopPtr) to hold the value returned by the fgets() function instead of a structure like
    Then I suggest writing a helper function instead, e.g.,
    Code:
    char *read_string(char *str, size_t i) {
        printf("\nEnter string[%zu]= : ", i);
        if (!fgets(str, MAX_LINES_LEN, stdin)) {
            return NULL;
        }
        str[strcspn(str, "\n")] = '\0';
        return str;
    }
    
    /* ... */
    
    for (i = 0; i < MAX_LINES && read_string(someString[i], i); i++) {
        /* do nothing here */
    }
    I have also fixed the off by one error with MAX_LINES that you introduced in post #12. The strcspn thing is a more concise way of removing the newline, though if you really want a strstr-type approach, then use strchr instead.
    Last edited by laserlight; 10-21-2015 at 11:31 PM.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  2. #17
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    I retract my comment in post #3 about strcspn having an off-by-one error; it was me that had an off-by-one error. It is indeed safe to use the way you (and laserlight) did.

    I guess I've been away too long, I'm a little rusty :/.

  3. #18
    Registered User Gdias's Avatar
    Join Date
    Oct 2015
    Location
    South America.
    Posts
    11
    Quote Originally Posted by laserlight View Post
    Then I suggest writing a helper function instead, e.g.,
    Code:
    char *read_string(char *str, size_t i) {
        printf("\nEnter string[%zu]= : ", i);
        if (!fgets(str, MAX_LINES_LEN, stdin)) {
            return NULL;
        }
        str[strcspn(str, "\n")] = '\0';
        return str;
    }
    
    /* ... */
    
    for (i = 0; i < MAX_LINES && read_string(someString[i], i); i++) {
        /* do nothing here */
    }
    I have also fixed the off by one error with MAX_LINES that you introduced in post #12. The strcspn thing is a more concise way of removing the newline, though if you really want a strstr-type approach, then use strchr instead.
    I hadn't thought about using a function for this, good point, thanks for helping. Here is how it looks like now:

    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LINES 50
    #define MAX_LINES_LEN 50
    #define MAX_CONCAT_LEN (MAX_LINES * MAX_LINES_LEN)
    
    void catString(char (*)[], char *, const size_t);
    int read_string(char *, size_t);
    
    int main(void)
    {
        char someString[MAX_LINES][MAX_LINES_LEN],      
        concatStr[MAX_CONCAT_LEN];
        size_t i;
    
        concatStr[0] = '\0';
    
        for (i = 0; i <= MAX_LINES_LEN && read_string(someString[i], i); i++)
        
        catString(someString, concatStr, i);
        printf("\n%s\n", concatStr);
    
        return 0;
    }
    void catString(char (*arrayPtr)[MAX_LINES_LEN], char *catPtr, const size_t maxConcatLen)
    {
        int j; 
    
        for(j = 0; j <= maxConcatLen; j++) 
                strcat(catPtr, arrayPtr[j]);
    }
    int read_string(char *str, size_t i) 
    {
        printf("\nEnter string[%zu]= : ", i);
        if (!fgets(str, MAX_LINES_LEN, stdin)) {
            return 0;
        }
        str[strcspn(str, "\n")] = '\0';
        return 1;
    }
    I made some changes so the function will return the integer values 0; and -1; if the input was successful or failed(respectively) instead of NULL and char *str once I don't really need a pointer to the entered string in this case. Well, it's still open to suggestions...

    Question: How could I prevent the user from entering a single string greater then the maximum size of my input array? e.g:

    Code:
    char array[ARRAY_SIZE];
    
    puts("Enter string");
    fgest(array, ARRAY_SIZE, stdin);
    If the user input some string, lets say string1. If string1's size is [ARRAY_SIZE + 1], I assume that fgets() will cut the string at string1[ARRAY_SIZE - 1] position and append a null terminator '\0' on string1[ARRAT_SIZE]. The remaining character will overstep the array's boundary and be lost(am I right here?). How could I prevent it?

    Thanks.
    Last edited by Gdias; 10-22-2015 at 12:47 PM.

  4. #19
    C++ Witch laserlight's Avatar
    Join Date
    Oct 2003
    Location
    Singapore
    Posts
    28,413
    Quote Originally Posted by Gdias
    Here is how it looks like now:
    I think you have a slight mixup with MAX_LINES and MAX_LINES_LEN, and you need to be careful when to use < and when to use <=

    Quote Originally Posted by Gdias
    I made some changes so the function will return the integer values 0; and -1; if the input was successful or failed(respectively) instead of NULL and char *str once I don't really need a pointer to the entered string in this case. Well, it's still open to suggestions...
    Actually, the code shows that you return 1 and 0 respectively. This is fine as you basically have a boolean return value.

    Quote Originally Posted by Gdias
    If the user input some string, lets say string1. If string1's size is [ARRAY_SIZE + 1], I assume that fgets() will cut the string at string1[ARRAY_SIZE - 1] position and append a null terminator '\0' on string1[ARRAT_SIZE]. The remaining character will overstep the array's boundary and be lost(am I right here?). How could I prevent it?
    No. Here's what the standard has to say about fgets:
    Quote Originally Posted by C11 Clause 7.21.7.2 Paragraphs 1, 2
    Code:
    #include <stdio.h>
    char *fgets(char * restrict s, int n, FILE * restrict stream);
    The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s. No additional characters are read after a new-line character (which is retained) or after end-of-file. A null character is written immediately after the last character read into the array.
    So, if string1 has a length of ARRAY_SIZE (i.e., a size of ARRAY_SIZE + 1 if one includes the null character), fgets will still read ARRAY_SIZE - 1 characters, and then set array[ARRAY_SIZE - 1] to be a null character, i.e., it will not write beyond the bounds of the array. The trouble is that there is still one character left in the input buffer along with the newline entered.

    Quote Originally Posted by Gdias
    Question: How could I prevent the user from entering a single string greater then the maximum size of my input array?
    You cannot prevent the user from doing that. However, you do have two options:
    • Reject the input: use strchr to search for '\n', and if it is found, well and good, you just remove it and that's that. If it is not found, then you read again into another array: if the first character is '\n', well and good, if not you know the input exceeded the array size so you reject the input.
    • Use dynamic memory allocation: read with fgets in a loop. On each iteration, search for '\n', and if it is found, you remove it and end the loop. If not, you loop again, appending the input read to the dynamic array that keeps expanding to hold the input.
    Quote Originally Posted by Bjarne Stroustrup (2000-10-14)
    I get maybe two dozen requests for help with some sort of programming or design problem every day. Most have more sense than to send me hundreds of lines of code. If they do, I ask them to find the smallest example that exhibits the problem and send me that. Mostly, they then find the error themselves. "Finding the smallest program that demonstrates the error" is a powerful debugging tool.
    Look up a C++ Reference and learn How To Ask Questions The Smart Way

  5. #20
    Registered User Gdias's Avatar
    Join Date
    Oct 2015
    Location
    South America.
    Posts
    11
    Quote Originally Posted by laserlight View Post
    I think you have a slight mixup with MAX_LINES and MAX_LINES_LEN, and you need to be careful when to use < and when to use <=.
    Yes, I was a little sloppy with the counters and the arrays dimensions . If you notice something off again, please let me know.

    Quote Originally Posted by laserlight View Post
    Actually, the code shows that you return 1 and 0 respectively. This is fine as you basically have a boolean return value.
    Indeed. It was a typo, sorry.

    Quote Originally Posted by laserlight View Post
    You cannot prevent the user from doing that. However, you do have two options:

    • Reject the input: use strchr to search for '\n', and if it is found, well and good, you just remove it and that's that. If it is not found, then you read again into another array: if the first character is '\n', well and good, if not you know the input exceeded the array size so you reject the input.
    • Use dynamic memory allocation: read with fgets in a loop. On each iteration, search for '\n', and if it is found, you remove it and end the loop. If not, you loop again, appending the input read to the dynamic array that keeps expanding to hold the input.
    While I see that using dynamic memory allocation in this program would be viable I'm not allowed to do so once this program is part of a set of activities where one of the main requisites is not to use dynamic memory-based algorithms, thus I choose the first suggestion and after some research and modifications here is the result:

    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_LINES 50
    #define MAX_LINES_LEN 10
    #define MAX_CONCAT_LEN (MAX_LINES * MAX_LINES_LEN)
    
    void catString(char (*)[], char *, const size_t);
    char *read_string(char *, size_t);
    
    int main(void)
    {
        char someString[MAX_LINES][MAX_LINES_LEN],     
        concatStr[MAX_CONCAT_LEN];
        size_t i;
    
        concatStr[0] = '\0';
    
        for (i = 0; i < MAX_LINES && read_string(someString[i], i); i++);            
    
        catString(someString, concatStr, i);
        printf("\n%s\n",concatStr);
    
        return 0;
    }
    void catString(char (*arrayPtr)[MAX_LINES_LEN], char *catPtr, const size_t maxConcateTimes)
    {
        int j; 
    
        for(j = 0; j < maxConcateTimes; j++) 
                strcat(catPtr, arrayPtr[j]);
    }
    
    char *read_string(char *str, const size_t i) 
    {
        char *fPtr;
        
        char tmp[10];    
        
        printf("\nEnter string[%zu]= ", i);
    
        fPtr = fgets(str, MAX_LINES_LEN, stdin);
    
        if (fPtr != NULL) {
            if (str[strlen(str) - 1] != '\n')                                  // <= Checking if string was greater then array limit.
                do {
                    fPtr = fgets(tmp, sizeof tmp, stdin);                      //<= flushing the buffer.                       
                } while (fPtr != NULL && tmp[strlen(tmp) - 1] != '\n');
            else
                str[strlen(str) - 1] = '\0';                                  // <= trimming '\n' if input doesn't pass array's borders.
        } 
        return fPtr;
    }
    Well, I'm looking forward for more suggestions of how to improve this code since I'm using the concepts acquired during this discussion as a reference for my other programs in the mentioned activity. Thanks for all the help.
    Last edited by Gdias; 10-23-2015 at 01:59 PM.

  6. #21
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I'm not sure if I agree with "flushing the buffer."
    Code:
     fPtr = fgets(str, MAX_LINES_LEN, stdin);
     
        if (fPtr != NULL) {
            if (str[strlen(str) - 1] != '\n')                                  // <= Checking if string was greater then array limit.
                do {
                    fPtr = fgets(tmp, sizeof tmp, stdin);                      //<= flushing the buffer.                       
                } while (fPtr != NULL && tmp[strlen(tmp) - 1] != '\n');
            else
                str[strlen(str) - 1] = '\0';                                  // <= trimming '\n' if input doesn't pass array's borders.
        }
    That's valuable input you are throwing away, for no other reason than there was no \n in the initial chunk that you read.

    If you really want to get all of the possible input than you will need to edit/replace read_string() so that it stores the string it returns dynamically. There are functions out there to use or base your work on if you decide to roll your own, such as the POSIX getline(). If you do this though, it's equally wise to make the list of words dynamic as well. So, you would be working with char **someString, instead of having an arbitrary limit.

    If you don't agree, I would at least use a simpler function for the "flushing" purpose, such as fgetc.
    Code:
    void my_flush(FILE *in_stream)
    {
        int ch;
        clearerr(in_stream);
        while ((ch = fgetc(in_stream)) != '\n' && ch != EOF)
            /* void */ ;
    }
    
    /* in use */
    my_flush(stdin);
    Last edited by whiteflags; 10-23-2015 at 03:44 PM.

  7. #22
    Registered User Gdias's Avatar
    Join Date
    Oct 2015
    Location
    South America.
    Posts
    11
    Quote Originally Posted by whiteflags View Post

    That's valuable input you are throwing away, for no other reason than there was no \n in the initial chunk that you read.

    If you really want to get all of the possible input than you will need to edit/replace read_string() so that it stores the string it returns dynamically. There are functions out there to use or base your work on if you decide to roll your own, such as the POSIX getline(). If you do this though, it's equally wise to make the list of words dynamic as well. So, you would be working with char **someString, instead of having an arbitrary limit.

    [/code]
    Thank you whiteflags, I was so "tunnel minded" on solving this "I-am-fgets-and-I-eat-everything-in-my-way" problem that I hadn't even thought about how I was just throwing away valid input. Here is my attempt to fix it(having in mind that I can't use dynamic memory allocation for aforementioned reasons):

    Code:
    #include <stdio.h>
    #include <string.h>
     
    #define MAX_LINES 50
    #define MAX_LINES_LEN 5
    #define MAX_CONCAT_LEN (MAX_LINES * MAX_LINES_LEN)
     
    int catString(char *, char *);
    char *read_string(char *, size_t);
    char *get_surplus(char *);
     
    int main(void)
    {
            char string[MAX_LINES_LEN],catStr[MAX_CONCAT_LEN], *nlPtr;
            size_t i;
     
            catStr[0] = '\0';
     
            for (i = 0;read_string(string, i); i++) {
                    if (catString(string, catStr))
                            get_surplus(catStr);
            
            }                
            printf("\n%s\n",catStr); 
            return 0;
    }
    
    int catString(char *str, char *concat)
    {
            int result = 1;
            char *nlPtr;
    
            nlPtr = strchr(str, '\n');
    
            if (nlPtr != NULL) {
                    *nlPtr = '\0';
                     result = 0;
            }
    
            strcat(concat, str);
            memset(str, 0, sizeof(str));
            return result;     
    }
     
    char *read_string(char *str, const size_t i) 
    {  
             printf("\nEnter string[%zu]= ", i);
             return fgets(str,sizeof(str), stdin);
    }
    
    char *get_surplus(char *concat)
    {
            char *fPtr, tmp[10];                     
            
            do {    
                    fPtr = fgets(tmp, sizeof(tmp), stdin);    
            } while (catString(tmp, concat) && fPtr != NULL);
                                        
            return fPtr;    
    }
    Again, thank you.
    Last edited by Gdias; 10-23-2015 at 08:49 PM.

  8. #23
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,787
    Line 48 looks very suspicious to me. What does sizeof(str) evaluate to?

    Edit: Since I am in a good mood, I've written some "test cases" and exercise for you.

    Given the following 3 programs:
    Code:
    #include <stdio.h>
    
    void test(char *str);
    
    int main(void)
    {
        char a[10], b[20], c[256], d[42];
    
        test(a);
        test(b);
        test(c);
        test(d);
    
        return 0;
    }
    
    void test(char *str)
    {
        printf("sizeof(*str) is %d\n", sizeof(str));
    }
    Code:
    #include <stdio.h>
    
    int main(void)
    {
        char a[10], b[20], c[256], d[42];
    
        printf("sizeof(a) is %d\n", sizeof(a));
        printf("sizeof(b) is %d\n", sizeof(b));
        printf("sizeof(c) is %d\n", sizeof(c));
        printf("sizeof(d) is %d\n", sizeof(d));
    
        return 0;
    }
    Code:
    #include <stdio.h>
    
    int main(void)
    {
        long a[10], b[20], c[256], d[42];
    
        printf("sizeof(a) is %d\n", sizeof(a));
        printf("sizeof(b) is %d\n", sizeof(b));
        printf("sizeof(c) is %d\n", sizeof(c));
        printf("sizeof(d) is %d\n", sizeof(d));
    
        return 0;
    }

    For each of the programs:
    a) What do you expect the program to print?
    b) What does it actually print?
    c) Why?
    Last edited by Hodor; 10-24-2015 at 08:11 PM.

  9. #24
    Registered User Gdias's Avatar
    Join Date
    Oct 2015
    Location
    South America.
    Posts
    11
    Code:
    #include <stdio.h>
    
    void test(char *str);
    
    int main(void)
    {
        char a[10], b[20], c[256], d[42];
    
        test(a);
        test(b);
        test(c);
        test(d);
    
        return 0;
    }
    
    void test(char *str)
    {
        printf("sizeof(*str) is %d\n", sizeof(str));
    }
    a) 8 for test(a), test(b), test(c) and test(d) as well.
    b) 8 for all test calls as said above.
    c) When an array is passed as argument to a function it decays to a pointer, thus the expression sizeof(str) will actually returns the size in bytes of the actual pointer in memory and not the size of the pointed array.

    Code:
    #include <stdio.h>
    
    int main(void)
    {
        char a[10], b[20], c[256], d[42];
    
        printf("sizeof(a) is %d\n", sizeof(a));
        printf("sizeof(b) is %d\n", sizeof(b));
        printf("sizeof(c) is %d\n", sizeof(c));
        printf("sizeof(d) is %d\n", sizeof(d));
    
        return 0;
    }
    a) 10, 20, 256 and and 42 in that order.
    b) 10, 20, 256 and 42 as said above.
    c) The sizeof operator determines the size in bytes of an array whose name is passed to it, as these are char type arrays each position(i.e a[0]) has exactly 1 byte size, therefore it's natural that a char [10] array will have 10 bytes of size.

    Code:
    #include <stdio.h>
    
    int main(void)
    {
        long a[10], b[20], c[256], d[42];
    
        printf("sizeof(a) is %d\n", sizeof(a));
        printf("sizeof(b) is %d\n", sizeof(b));
        printf("sizeof(c) is %d\n", sizeof(c));
        printf("sizeof(d) is %d\n", sizeof(d));
    
        return 0;
    }
    a) 60, 160, 2048 and 336 in that order.
    b) 60, 160, 2048 and 336 as said above.
    c) The same explanation of item c) from the previous example but this time the arrays are of type long which has an allocation of 8 bytes size for each position. Multiplying the dimension of the arrays by 8 gives the expected results.

    Thank you for having provided the activity and for pointing out the misused sizeof operator too. I normally use it alongside with fgets to represent the input's limit in a more easier way but this time I didn’t even thought about being applying it on a pointer to an "outer" array(Yes, lack of attention is a problem for me). But a good thing is that seeing it made me also notice that the limit size of the input and arrays doesn't matter anymore in this program, the only one left is the size of the destination string for the concatenation process (MAX_CONCAT_LEN). Anyway, I was able to change the code into something more simple and clear:

    EDIT: The sizes don't matter as long as they are greater than zero(by sizes I mean the arrays' dimensions in the code).

    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define MAX_CONCAT_LEN 100
     
    void catString(char *str, char *concat);
    char *read_string(char *str, size_t i);
     
    int main(void)
    {
            char string[10], catStr[MAX_CONCAT_LEN];
            size_t i;
     
            catStr[0] = '\0';
     
            for (i = 0;read_string(string, i); i++)
                    catString(string, catStr);
                                    
            printf("\n%s\n",catStr); 
            return 0;
    }
    
    void catString(char *str, char *concat)
    {
            char *nlPtr;
    
            if((nlPtr = strchr(str, '\n')) != NULL) {
                    *nlPtr = '\0';
                    strcat(concat, str);
            } else {
                    while((nlPtr = strchr(str, '\n')) == NULL) {
                            strcat(concat, str);
                            fgets(str, 10, stdin);
                    }
            *nlPtr = '\0';
            strcat(concat, str);                                              
            }
    }
        
    char *read_string(char *str, const size_t i) 
    {
            printf("\nEnter string[%zu]= ", i);
            return fgets(str, 10, stdin);    
    }
    Last edited by Gdias; 10-24-2015 at 11:45 PM.

  10. #25
    Registered User Gdias's Avatar
    Join Date
    Oct 2015
    Location
    South America.
    Posts
    11
    a) 60, 160, 2048 and 336 in that order.
    b) 60, 160, 2048 and 336 as said above.
    Actually I meant 80 and not 60 (I don't know why I wrote 60, my brain probably just shut down there, it's the dimension of array "a" times 8 ).

  11. #26
    misoturbutc Hodor's Avatar
    Join Date
    Nov 2013
    Posts
    1,787
    Quote Originally Posted by Gdias View Post
    Actually I meant 80 and not 60 (I don't know why I wrote 60, my brain probably just shut down there, it's the dimension of array "a" times 8 ).
    It's cool. Thanks for taking the time to actually do the exercises

    Edit
    Code:
    return fgets(str, 10, stdin);
    Instead of hard-coding the value 10 I'd probably pass the size of the buffer (str) to the function instead.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Assigning One dimensional array to two dimensional array
    By lightning_star in forum C Programming
    Replies: 1
    Last Post: 03-19-2014, 09:44 PM
  2. Replies: 12
    Last Post: 09-02-2013, 07:50 PM
  3. Replies: 4
    Last Post: 09-02-2013, 11:19 AM
  4. return(array), 2 dimensional arrays to functions.
    By nyekknyakk in forum C Programming
    Replies: 10
    Last Post: 08-19-2010, 07:07 PM
  5. pass array to memset
    By sef0 in forum C Programming
    Replies: 5
    Last Post: 05-26-2009, 09:37 AM

Tags for this Thread