Thread: Functions for C strings

  1. #1
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77

    Functions for C strings

    I am revisiting C strings, trying to get hang of the pesky things. I am attempting to make a set of functions to get, parse and manipulate strings etc.

    This is my first function which gets user input. My hope was to create a very generic function which can be used in most circumstances. I wondered if there is a chance of getting some critical feedback on it - are there better ways to do it, are there risks of writing past the array limits, how could it be improved etc

    would people go for dynamic memory allocation instead? I had the impression there is a trade off between extra resources in using dynamic memory vs flexibility it offers. Would it be better to write 2 functions - one for dynamic allocation (potentially very large character arrays) and one for static allocation (smaller arrays) or just one catch all function?

    Sorry for all the questions. Just want to get the hang of this once and for all. Thanks in advance for any help with this

    Here's the function

    Code:
    char * GC_GetUserInput() {
        printf("\n::  ");
        char temp[256];
        for (int i = 0; i < 256; i++) {
            temp[i] = '\0';
        }
    
        int len = 0;
        char * text;
        char c;
        c = fgetc(stdin);
        while (c != '\n') {
            printf("\n%c", c);
            if ((len = strlen(temp)) < 255) {
                temp[len] = c;
                c = fgetc(stdin);
            }
            else {
                temp[len] = '\0';
                c = '\n';
            }            
        }
        text = temp;
        return text;
    }

  2. #2
    Registered User
    Join Date
    Aug 2009
    Posts
    198
    Why not use scanf()?

  3. #3
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    strlen may be easy, but it's not free (it has to walk through the char array every time) -- why not just add one to len each time you put a character in the array?

    I hope that printf("\n%c") thing is just for debugging, otherwise seeing your entire input be echoed going down the screen can't be fun.

    Unlikely to happen with stdin (unless its redirected), but fgetc can and will return EOF on an error.

    The variable text is completely useless. For that matter, so is the return -- you may think you are returning that array, but you are not; once the function ends the array (and the input) vanishes. If you intend to still have the input later, you must either (one) pass in a buffer and a size or (two) use malloc instead.

  4. #4
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    Thanks for these quick replies

    MTK - I had read somewhere about the difficulties in using scanf so have avoided this in the past. I had wanted such a generic 'whatever the user throws at us' function that I doubted whether scanf could work in a generic catch all function.

    Tabstop - this explains a lot as I have been getting variable output when using this function - sometimes works as planned, sometimes doesn't, which I guess must be because sometimes the memory is overwritten. The other problem I had was in declaring arrays of size which could be defined at runtime. I'll rework the whole thing, drop strlen, drop text and try malloc instead.

  5. #5
    Jack of many languages Dino's Avatar
    Join Date
    Nov 2007
    Location
    Chappell Hill, Texas
    Posts
    2,332
    If you are going to use a stack variable, this code
    Code:
    char temp[256];
        for (int i = 0; i < 256; i++) {
            temp[i] = '\0';
        }
    can be replaced with this code
    Code:
    char temp[256] = {0} ;
    Mainframe assembler programmer by trade. C coder when I can.

  6. #6
    Registered User
    Join Date
    Apr 2009
    Posts
    187
    why dont you use getchar to read them inside a while then test inside the while better than retesting inside the loop will save you less time

  7. #7
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    Thank you for these comments. I am working through this puzzle bit by bit, so hopefully it will come good in the end.

    Current problem I'm having is getting a good understanding of stdin and the way fgetc works, so I can use it with more confidence. My readings so far haven't really explained it in a way I can grasp completely. My understanding so far (please correct me if this is wrong):

    Somewhere in memory, there is a place which stores keyboard strokes in sequence as bit patterns. stdin is a pointer to a data structure of type FILE. This data structure contains various information about the location and content of the memory for the keyboard strokes.

    When I use fgetc(stdin), it returns an integer corresponding to the bit pattern of the next keystroke in memory or EOF if there is nothing more there and the function fgetc also moves the pointer to the next keystroke.

    does stdin keep some record of all keystrokes, and if not, how does it know which ones to keep track of? Or is it fgetc which....
    - initialises stdin
    - sets the pointer position to 0 in the stdin struct
    - pauses the program waiting for user input
    - continually updates the EOF position
    - waits for '\n' to return
    (I read one implementation which simply wraps up a call to _sgetc(stdin)... leaving me no wiser)

    I tried having a look at the file position using ftell(stdin), but it does not move incrementally, 0,1,2, but instead jumps to 19... etc on subsequent characters.

    Sorry for all the questions, just wanting to get this one clear before moving on

  8. #8
    Registered User
    Join Date
    Sep 2004
    Location
    California
    Posts
    3,268
    does stdin keep some record of all keystrokes, and if not, how does it know which ones to keep track of?
    stdin holds all characters that are in the input buffer. This is different than recording all "keystrokes". For instance, hitting the Shift key is a keystroke, but it doesn't add a character to the input buffer.

    - initialises stdin
    stdin is initialized by the runtime environment. That means it is initialized when your application is loaded.
    - sets the pointer position to 0 in the stdin struct
    This is an implementation detail which you should not worry yourself with, but again it is something that is initialized by the runtime.
    - pauses the program waiting for user input
    The program blocks waiting for something to appear in the input buffer. If the input buffer is not empty, the application will not block.
    - continually updates the EOF position
    Not sure what you mean by this. In a file, EOF is returned by fgetc() when there are not characters left. If you are reading from stdin, EOF is returned when the user sends EOF to the terminal (Ctrl-Z or Ctrl-D depending on your OS).
    - waits for '\n' to return
    The input buffer is not flushed until the '\n' character is entered by the user. This is why your application blocks until the user presses "Enter".
    bit∙hub [bit-huhb] n. A source and destination for information.

  9. #9
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    that's really helpful, thanks for the speedy reply bithub.

  10. #10
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    Just a bit more experimentation with fgetc

    So if I have a program which does this:

    Code:
    int c;
    
    c = fgetc(stdin);
    c = fgetc(stdin);
    c = fgetc(stdin);
    and I simply enter the letter 'r' and press return,

    On the first call to fgetc
    - it finds nothing in the buffer, so waits for a keyboard entry
    - the return will flush the 'r' and '\n' into the buffer
    - triggering fgetc to read the first character in the buffer
    - the first printf will display 'r'

    On the second call to fgetc
    - the stdin will be looked at and found to contain '\n'
    - it will take this and pass it to the variable c

    On the third call to fgetc
    - nothing will be found in the buffer,
    - so it waits for keyboard entry again

    The main point I am trying to understand is when does fgetc wait for keyboard input and when does it get on and read the buffer rather than wait for more input.... and does this mean that if I do not read all the characters in the buffer (including '\n') the the next call to fgetc will start off reading the remaining characters in the input buffer.... meaning I would have to either make sure I read them all and discard them if I don't need them... or call a function such as fflush?

  11. #11
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    If you entered the text "r\n" as the stream for stdin, then your code above would hang on the thrid fgetc as it is waiting for more input. c would hold the value '\n' prior to this block. Another '\n' down the stream would unblock you and you'd continue on with c being '\n'.

  12. #12
    Registered User
    Join Date
    Oct 2008
    Location
    TX
    Posts
    2,059
    Yep! fgetc() will wait for input on stdin until the buffer is full or it is flushed. If you don't read all the characters in the input, the next call to fgetc() will start where it left off. And no it is not a good idea to call fflush(stdin) as it is used only for flushing stdout.

  13. #13
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    Thanks for the replies. I am learning this very slowly. So, my understanding is that if I want to read input from the keyboard, because '\n' flushes the character strokes to the input buffer for stdin, I could use '\n' as a sentry for the while loop should be sufficient for reading all the text input by the user whilst clearing the input buffer. And I could also potentially use this sentry to mark the point I could replace '\n' with '\0'. (the function does nothing especially useful, just checks the process works ok)

    Code:
    void getUserInput() {
        int c;
        while( (c = fgetc(stdin)) != '\n') {
            printf("\nchar: %c", c);
        }
    }
    But if I want the same programme to read a text file, I would need to use EOF instead of '\n' as the sentry for the while loop (because many '\n's might be embedded in the text file, thereby stopping the loop before the file is read)

    Code:
    void getUserInput() {
        int c;
        while( (c = fgetc(stdin)) != EOF) {
            printf("\nchar: %c", c);
        }
    }
    Have I understood this right? And is this robust enough to work in all circumstances?

  14. #14
    {Jaxom,Imriel,Liam}'s Dad Kennedy's Avatar
    Join Date
    Aug 2006
    Location
    Alabama
    Posts
    1,065
    Sounds good to me. I would wonder, though, why would you want to parse a file a char at a time? fgets() would be a bit better at this.
    Quote Originally Posted by man fgets
    GETS(3) Linux Programmer's Manual GETS(3)

    NAME
    fgetc, fgets, getc, getchar, gets, ungetc - input of characters and strings

    SYNOPSIS
    #include <stdio.h>

    int fgetc(FILE *stream);

    char *fgets(char *s, int size, FILE *stream);

    int getc(FILE *stream);

    int getchar(void);

    char *gets(char *s);

    int ungetc(int c, FILE *stream);

    DESCRIPTION
    fgetc() reads the next character from stream and returns it as an unsigned char cast to an int, or EOF on end of file or error.

    getc() is equivalent to fgetc() except that it may be implemented as a macro which evaluates stream more than once.

    getchar() is equivalent to getc(stdin).

    gets() reads a line from stdin into the buffer pointed to by s until either a terminating newline or EOF, which it replaces with
    '\0'. No check for buffer overrun is performed (see BUGS below).

    fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops
    after an EOF or a newline. If a newline is read, it is stored into the buffer. A '\0' is stored after the last character in the
    buffer.

    ungetc() pushes c back to stream, cast to unsigned char, where it is available for subsequent read operations. Pushed-back charac-
    ters will be returned in reverse order; only one pushback is guaranteed.

    Calls to the functions described here can be mixed with each other and with calls to other input functions from the stdio library
    for the same input stream.

    For non-locking counterparts, see unlocked_stdio(3).

    RETURN VALUE
    fgetc(), getc() and getchar() return the character read as an unsigned char cast to an int or EOF on end of file or error.

    gets() and fgets() return s on success, and NULL on error or when end of file occurs while no characters have been read.

    ungetc() returns c on success, or EOF on error.

    CONFORMING TO
    C89, C99, POSIX.1-2001. LSB deprecates gets(). POSIX.1-2008 removes the specification of gets().

    BUGS
    Never use gets(). Because it is impossible to tell without knowing the data in advance how many characters gets() will read, and
    because gets() will continue to store characters past the end of the buffer, it is extremely dangerous to use. It has been used to
    break computer security. Use fgets() instead.

    It is not advisable to mix calls to input functions from the stdio library with low-level calls to read(2) for the file descriptor
    associated with the input stream; the results will be undefined and very probably not what you want.

    SEE ALSO
    read(2), write(2), ferror(3), fgetwc(3), fgetws(3), fopen(3), fread(3), fseek(3), getline(3), getwchar(3), puts(3), scanf(3),
    ungetwc(3), unlocked_stdio(3)

    COLOPHON
    This page is part of release 3.15 of the Linux man-pages project. A description of the project, and information about reporting
    bugs, can be found at Linux man-pages home.

    GNU 2008-08-06 GETS(3)

  15. #15
    Registered User officedog's Avatar
    Join Date
    Oct 2008
    Posts
    77
    Thanks kennedy. I seem to remember there was something quite useful with fgetc, but can't quite remember what it was exactly! Probably something to do with parsing the file, like csv's.

    Well fgets feels a little more complicated, but could also be much quicker. This seems to work OK. I'm still struggling with when it's ok to use a char * and when I should really use a char array[]. I know they are both effectively pointers, but there are also some important differences - been reading K&R chapter on this. At some point I'll try getting the string passed back from the function or via arguments...

    Code:
    void getUserInputLine(FILE * fp) {
        char buf[100];
        char * pString; //trying string pointer idea
        if ( ((pString = fgets(buf, sizeof(buf), fp))) != NULL) {
            printf("\n%s", buf);
            printf("\n%s", pString);
        }
    }
    
    int main (int argc, const char * argv[]) {
        getUserInputLine(stdin);
        return 0;
    }
    or maybe something like this would be ok. I'll have to do some more reading on this

    Code:
    void getUserInputLine(FILE * fp, char * ps) {
        char buf[100];
        if ( ((ps = fgets(buf, sizeof(buf), fp))) != NULL) {
            printf("\n%s", buf);
            printf("\n%s", ps);
        }
    }
    
    int main (int argc, const char * argv[]) {
        char * pString;
        getUserInputLine(stdin, pString);
        return 0;
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. strings and functions
    By simo_mon in forum C Programming
    Replies: 8
    Last Post: 09-16-2008, 05:18 AM
  2. Passing strings to functions and modifying them
    By diddy02 in forum C Programming
    Replies: 6
    Last Post: 08-11-2008, 01:07 AM
  3. Reading strings input by the user...
    By Cmuppet in forum C Programming
    Replies: 13
    Last Post: 07-21-2004, 06:37 AM
  4. Attaching functions to a class/struct
    By VirtualAce in forum Tech Board
    Replies: 2
    Last Post: 08-04-2003, 10:56 AM
  5. Replies: 2
    Last Post: 04-13-2003, 08:40 PM