Thread: fgets only returns 3 characters

  1. #16
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Jesdisciple View Post
    \
    Partly... I also want to be able to check the type of any node's value in the calling/downstream code to make sure I know how to handle it.
    I think this means you are being not nearly generic enough here. If you build a generic list, that's great. That means it's the user's responsibility (as in the main program, or whoever owns the list itself, or whatever) to know how to manipulate the data. Your job is just to know where the data is.

    Look at the library qsort function -- it takes a generic array, and so it has to be told (1) how large the stuff is [you maybe know this is the sizeof struct node, although if you're using different things, there's going to be some sort of changing size somewhere along the way] (2) how many there are [you won't need this, since it's a list] and (3) how to process the information, via a function pointer [this is the part you're worrying about but shouldn't; it's the program that's building the list who knows what the data is and it's their job to provide this information to you].

  2. #17
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    >> Also, I'm not entirely sure that its correct to say that NULL is a pointer.

    I am well aware of what NULL really is, but assigning something like NULL to an integer is nonetheless incorrect and chiefly because of how NULL is defined.

    I could care less whether people defend the use of the NULL constant and it really has absolutely nothing to do with what's going on. The OP is punning a pointer as an integer - I dare say that in this case NULL prevented a disaster.

  3. #18
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by arpsmack View Post
    Some compilers will define NULL by casting the 0 to a pointer value, like so: ((void*) 0). This may or may not be a good thing.
    [/edit]
    I would say it is a good thing, given that the C standard requires NULL to have pointer type. Any use of it as a integer works only by luck.

  4. #19
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by citizen View Post
    It seems like C++ would be a better choice for what you want to do, because it does have that, is more object oriented, and has templates, so you can create a list of any type. In fact, std::list would solve all of your problems already.
    This is a purely educational project; I plan on moving to C++ once I get a handle on C.

    Quote Originally Posted by citizen View Post
    You talked about this later, but C doesn't have run-time type identification because it is a procedural language.
    OK. The next-best solution is to store programmer input regarding what type something has. Would a string associated to the value via a struct be the most elegant implementation?

    Quote Originally Posted by citizen View Post
    C has a type for generic pointers called "void*".
    Oh, thanks. They didn't use the most intuitive name...

    Quote Originally Posted by citizen View Post
    And then if I were implementing a library, I would separate list processing tasks into functions which don't care about what data really is.
    I know. I have more programming experience than my newbieness implies.

    Quote Originally Posted by citizen View Post
    When the type of data matters (such as when you need to remove a node) I would have to rely on external functions to "do the right thing."
    Do you mean callback functions?

    Quote Originally Posted by citizen View Post
    It's a lot of work to do correctly and if you do rely on void*, it has some drawbacks you will need to code around. The most important IMO is that it makes the code less type strict. If you try to do something like merge two lists of different types, you won't have much standing in your way. You'll just break your lists.
    Lol, from what I can see programming in C is inherently a lot of work.

    Quote Originally Posted by citizen View Post
    That's not the right way to do it for a compound type like node. node really should be a pointer if you want changes to the list to persist when the function ends.
    OK, that's done.

    Quote Originally Posted by citizen View Post
    Since the dereferencing operation is supposed to happen before member access, you would need to type (*node).next or node->next. Dereferencing is below member access on the operator precedence table.
    Thank you! I had seen the arrow syntax but couldn't find exactly what it meant.

    Good point, arpsmack... I hadn't thought of it from that perspective. But I don't understand why NULL wouldn't be a pointer to the 0th memory address.

  5. #20
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Jesdisciple View Post
    Good point, arpsmack... I hadn't thought of it from that perspective. But I don't understand why NULL wouldn't be a pointer to the 0th memory address.
    There aren't many memory models out there in current use where the 0th memory address physically exists.

  6. #21
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Does C support throwing & catching exceptions rather than just printing to stderr?

    I forgot to ask about this:
    The most important IMO is that it makes the code less type strict. If you try to do something like merge two lists of different types, you won't have much standing in your way. You'll just break your lists.
    Why would that break the lists?

    Quote Originally Posted by tabstop View Post
    I think this means you are being not nearly generic enough here. If you build a generic list, that's great. That means it's the user's responsibility (as in the main program, or whoever owns the list itself, or whatever) to know how to manipulate the data. Your job is just to know where the data is.
    Note that I said I want to know what type the stuff is in the calling/downstream code... I am the user of the library in this case.

    Quote Originally Posted by tabstop View Post
    There aren't many memory models out there in current use where the 0th memory address physically exists.
    That's part of my point... If it actually did exist, assigning it to NULL would be a waste. The idea behind NULL is that the relevant data is unknown/non-existent, right? And, as mentioned, it gets assigned to pointer variables, so I see it as a pointer to non-existent data at a non-existent address. But what do I know? (Don't answer that. )

  7. #22
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    >> Does C support throwing & catching exceptions

    No, nothing like C++ anyway.

    >> Why would that break the lists?

    Well, using the example I gave, after a merge the list is supposed to have a total order. How would you order a list if the elements weren't homogeneous? The compiler wont be able to distinguish what void * points to at compile time either. It is your responsibility to know the type at all times..

    >> This is a purely educational project; I plan on moving to C++ once I get a handle on C.

    You're doing it backward: you want things from C that you would have an easier time with in C++, and they're such different languages that you'll be no better equipped (or perhaps even worse off) if you knew C going into C++.

  8. #23
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Of course, we could make a generic list in a C++ style way:

    This would be "list.h":
    Code:
    sstruct objops
    {
        int (*compare)(void *a, void *b);
        void (*copy)(void *dest, void *src);
        size_t (*size)(void *a);
    };
    
    
    struct node;
    
    struct list
    {
        struct objops *ops;
        struct node *head;
        struct node *tail;
    };
    
    void ListInit(struct list *list, struct objops *ops);
    void *ListFind(struct list *list, void *obj);
    void ListAddAtEnd(struct list *list, void *obj);
    void ListAddAtStart(struct list *list, void *obj);
    void ListRemove(struct list *list, void *obj);
    I'm not going to implement ALL of those functions, but here is a sample of what list.c would look like:
    Code:
    #include "list.h"
    #include <stdlib.h>
    
    struct node 
    {
        struct node *next;
        void *obj;
    };
    
    
    void ListInit(struct list *list, struct objops *ops)
    {
        list->head = NULL;
        list->tail = NULL;
        list->ops = ops;
    }
    
    static struct node*FindNode(struct list *list, void *obj)
    {
        struct node *p;
        for(p = list->head; p; p = p->next)
        {
    	if (list->ops->compare(p->obj, obj) == 0)
    	{
    	    return p;
    	}
        }
        return NULL; 
    }
    
    
    void *ListFind(struct list *list, void *obj)
    {
        struct node *node = FindNode(list, obj);
        if (node)
            return node->obj;
        return NULL;
    }
    
    void ListAddAtEnd(struct list *list, void *obj)
    {
        struct node *node = malloc(sizeof(*node));
        void *newobj = malloc(list->ops->size(obj));
         
        if (newobj == NULL || node == NULL)     
        {
    	fprintf(stderr, "Fail to allocate memory\n");
    	exit(1);
        }
        list->ops->copy(newobj, obj);
        node->obj = newobj;
        node->next = NULL;
        if (list->head == NULL)
        {
    	list->head = list->tail = node;
        }
        else
        {
    	list->tail->next = node;
    	list->tail = node;
        }
    }
    To use it, we could consider something like this:
    Code:
    #include <stdio.h>
    #include <string.h>
    #include "list.h"
    
    int intCompare(void *a, void *b)
    {
        int *pa = a;
        int *pb = b;
        if (*pa == *pb)  return 0;
        if (*pa > *pb) return 1;
        return -1;
    }
    
    void intCopy(void *dest, void *src)
    {
        int *pdest = dest;
        int *psrc = src;
       
        *pdest = *psrc;
    }
    
    size_t intSize(void *obj)
    {
        return sizeof(int);
    }
    
    struct objops intOps = 
    {
        intCompare,
        intCopy,
        intSize
    };
    
    // Because of the way the functions are defined, we need to wrap the string functions:
    int mystrcmp(void *a, void *b)
    {
        return strcmp((char *)a, (char *)b);
    }
    
    void mystrcpy(void *dest, void *src)
    {
        strcpy((char *)dest, (char *)src);
    }
      
    size_t mystrlen(void *obj)
    {
        return strlen((char *)obj);
    }
    
    struct objops strOps = 
    {
        mystrcmp,
        mystrcpy,
        mystrlen
    };
    
    int main()
    {
        struct list intList;
        struct list strList;
        int *p;
        int x;
    
        char *a;
        char *b;
    
        ListInit(&intList, &intOps);
        x = 3;
        ListAddAtEnd(&intList, &x);
        x = 7;
        ListAddAtEnd(&intList, &x);
    
        x = 3;
        p = ListFind(&intList, &x);
        if (p) 
    	printf("Found &#37;d\n", *p);
        else
    	printf("Could not find %d\n", x);
       
        ListInit(&strList, &strOps);
        ListAddAtEnd(&strList, "Hello");
        ListAddAtEnd(&strList, "World");
    
        a = ListFind(&strList, "Hello");
        b = ListFind(&strList, "World");
        printf("%s %s\n", a, b);
    
        return 0;
    }
    I just typed that in, so it probably won't compile, but it should give you an idea of how to solve the problem.

    Of course, C++ solves this much neater.

    Edit: Updated the code after compiling it.
    Edit2: The "ops" is essentially the vtable of the "class" in this case.

    --
    Mats
    Last edited by matsp; 08-22-2008 at 04:10 AM.
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  9. #24
    Registered User
    Join Date
    Aug 2008
    Posts
    129
    Quote Originally Posted by citizen View Post
    No, nothing like C++ anyway.
    This is a bit cheeky, but may I have the complete answer?

    Quote Originally Posted by citizen View Post
    Well, using the example I gave, after a merge the list is supposed to have a total order. How would you order a list if the elements weren't homogeneous? The compiler wont be able to distinguish what void * points to at compile time either. It is your responsibility to know the type at all times..
    Oh, I thought you meant just concatenating them. I don't plan on ordering my lists.

    Quote Originally Posted by citizen View Post
    You're doing it backward: you want things from C that you would have an easier time with in C++, and they're such different languages that you'll be no better equipped (or perhaps even worse off) if you knew C going into C++.
    Well, I'm doing it this way because of someone else's opinion to the contrary. Now that I'm already started I'm not sure whether I should pull out or go on.

    Mats, is that C++-style just because it uses interfaces (abstract structs)?

  10. #25
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by Jesdisciple View Post
    This is a bit cheeky, but may I have the complete answer?
    The long answer is setjmp/longjmp (but I did not tell you this.)

    Quote Originally Posted by Jesdisciple View Post
    Oh, I thought you meant just concatenating them. I don't plan on ordering my lists.
    Concatenation is just as bad. How do you know when the ints stop and the doubles start? Answer: you don't. (Unless you want to keep that information separate from the list, and do some other extra housekeeping, or add an extra field into your struct which tells you what type it is, or....)
    Quote Originally Posted by Jesdisciple View Post
    Mats, is that C++-style just because it uses interfaces (abstract structs)?
    I'm guessing he means "like the list built into C++".

  11. #26
    Kernel hacker
    Join Date
    Jul 2007
    Location
    Farncombe, Surrey, England
    Posts
    15,677
    Quote Originally Posted by tabstop View Post
    I'm guessing he means "like the list built into C++".
    Using the same technique as C++ does - the only difference is that C++ hides all the uglyness of it inside the compiler, so you never see the function pointers, the extra pointer to hold the function pointers, etc, etc.

    And of course, using C++ templates, you can avoid some of the functions (e.g comparing integers) by the fact that the compiler already knows those things, and there's no need to use a function to return the compare result.

    --
    Mats
    Compilers can produce warnings - make the compiler programmers happy: Use them!
    Please don't PM me for help - and no, I don't do help over instant messengers.

  12. #27
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Code:
    #ifndef _LIST_H
    #define _LIST_H
    This is unrelated to your question, but still important. Using header guards like this is great, but you should not define a name that starts with an underscore. The name _LIST_H is reserved for use by your compiler/library.

    There is real potential for a problem here. Your implementation needs a way to guard its headers, the same as you, and so it will (generally) also use macros just as you are. It needs to be able to select names that won't clash with your names, and so it uses ones that begin with an underscore, knowing that you aren't allowed to use those names. But not being allowed to doesn't mean that your compiler will warn you about it; in this case, it means undefined behavior. C is fun like that.

    What could happen is that your implementation also has a list.h somewhere that gets included (for whatever reason), and it also uses _LIST_H to guard its contents. Now you could run into a problem like this:
    Code:
    #include <standard_header> /* This pulls in list.h (let's say) and defines _LIST_H */
    #include "list.h" /* _LIST_H is defined so your list.h does not get included! */
    Or the other way around, and the standard list.h would be ignored.

    I just checked some headers on my system, and _LIST_H is actually defined by my implementation (when using C++, but the idea is the same). If you include <list>, then _LIST_H gets defined. Again, this is a real, not theoretical problem.

    Names that begin with an underscore should be avoided (the rules are slightly more subtle: you can sometimes use a leading underscore if it's not followed by another underscore or capital letter... but it's easier just to avoid leading underscores; and in this case it doesn't apply anyway, because you're using a capital letter).

    I'd recommend using a project-specific prefix, something like (assume your project is called TTP):
    Code:
    #ifndef TTP_LIST_H
    #define TTP_LIST_H
    Just the same as you would do for "namespaces" elsewhere in C.

  13. #28
    Registered User
    Join Date
    Sep 2007
    Posts
    1,012
    Quote Originally Posted by tabstop View Post
    I would say it is a good thing, given that the C standard requires NULL to have pointer type. Any use of it as a integer works only by luck.
    The standard does allow NULL to be an unadorned 0. In C99, 7.17p3 says NULL is "an implementation-defined null pointer constant". The term "null pointer constant" is defined in 6.3.2.3p3 as "[a]n integer constant expression with the value 0, or such an expression cast to type void *". C89 has the same language.

    The upshot is that one should never assume that NULL is either an integral type or a pointer type, so:
    Code:
    printf("%p\n", NULL); /* bad */
    printf("%p\n", (void *)NULL); /* good, if useless */
    char x = NULL; /* bad; I've actually seen this */

  14. #29
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    Quote Originally Posted by cas View Post
    The standard does allow NULL to be an unadorned 0. In C99, 7.17p3 says NULL is "an implementation-defined null pointer constant". The term "null pointer constant" is defined in 6.3.2.3p3 as "[a]n integer constant expression with the value 0, or such an expression cast to type void *". C89 has the same language.

    The upshot is that one should never assume that NULL is either an integral type or a pointer type, so:
    Code:
    printf("%p\n", NULL); /* bad */
    printf("%p\n", (void *)NULL); /* good, if useless */
    char x = NULL; /* bad; I've actually seen this */
    You're right. For some reason I had thought that only C++ allowed straight 0 as NULL; I don't know where I picked that up from.

  15. #30
    Registered User
    Join Date
    Jul 2008
    Posts
    133
    Quote Originally Posted by tabstop View Post
    There aren't many memory models out there in current use where the 0th memory address physically exists.
    And i would just say that 0 is perfectly legal memory address (in virtual & pyshical sense) as far as CPU is concerned (unless it has some built-in protection for virtual). I've been using 0th memory address on C64 for "ages" ) (for bank switching, if i remember correctly)
    EDIT: now, on PC there are mapped interrupt vectors for real-mode, if i'm not wrong.. (address 0-1023)
    Last edited by rasta_freak; 08-22-2008 at 06:39 PM.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Lame null append cause buffer to crash
    By cmoo in forum C Programming
    Replies: 8
    Last Post: 12-29-2008, 03:27 AM
  2. HELP!!!!emergency Problem~expert please help
    By unknowppl in forum C++ Programming
    Replies: 9
    Last Post: 08-21-2008, 06:41 PM
  3. HELP!!!!emergency ~expert please help
    By unknowppl in forum C Programming
    Replies: 1
    Last Post: 08-19-2008, 07:35 AM
  4. gets vs fgets
    By strobo in forum C Programming
    Replies: 10
    Last Post: 03-27-2002, 05:28 PM