Thread: Accessing next entry in a queue.

  1. #76
    Registered User
    Join Date
    Apr 2013
    Posts
    122
    And why couldn't I use what I just wrote (post #75)? I mean, why won't that also work?

  2. #77
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by peripatein View Post
    And why couldn't I use what I just wrote (post #75)? I mean, why won't that also work?
    Am I missing some posts here? Anyway, post #75 looks ok to me. An alternative version, but it's not any "better", just an alternative version:

    Code:
    void freeSystem(TaxiSystem* system){
        TaxiSystem *tmpdrv;
        TaxiSystem *tmpord;
        while (system->driversQueue){
            tmpdrv = system->driversQueue;
            system->driversQueue = system->driversQueue->next;
            free(tmpdrv);
        }
        while (system->ordersQueue){
            tmpord = system->ordersQueue;
            system->ordersQueue = system->ordersQueue->next;
            free(tmpord);
        }
        free(system);
    }

  3. #78
    Registered User
    Join Date
    Apr 2013
    Posts
    122
    And what vart posted (#74) is obviously also an alternative version, similarly correct, right?

  4. #79
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by peripatein View Post
    And what vart posted (#74) is obviously also an alternative version, similarly correct, right?
    Yes, I was trying to update my post, but couldn't get back to the forum. Here is the alternate version I wanted to show, similar to vart's example with only one temp pointer. The only difference is temp is used for the current instance to be freed. I don't know if there's any advantage to either version (vart's or this one), they both do the same thing and I've seen it done both ways.

    Code:
    void freeSystem(TaxiSystem* system){
        void * temp;
        while (system->driversQueue){
            temp = system->driversQueue;
            system->driversQueue = system->driversQueue->next;
            free(temp);
        }
        while (system->ordersQueue){
            temp = system->ordersQueue;
            system->ordersQueue = system->ordersQueue->next;
            free(temp);
        }
        free(system);
    }
    Last edited by rcgldr; 06-17-2013 at 02:41 PM.

  5. #80
    Registered User
    Join Date
    Apr 2013
    Posts
    122
    I preferred writing it this way:
    Code:
    void freeSystem(TaxiSystem* system){
    	void* tmp = NULL;
    	while (system->driversQueue){
        	tmp = system->driversQueue->next;
        	free(system->driversQueue);
        	system->driversQueue = tmp ;
    	}
    	while (system->ordersQueue){
    		tmp = system->ordersQueue->next;
        	free(system->ordersQueue);
        	system->ordersQueue = tmp ;
    	}
    	free(system);
    }

  6. #81
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,907
    I would recommend abstracting out the freeing of driversQueue and ordersQueue into separate functions, like so:
    Code:
    void freeDriver(Driver *drivers) {
        Driver *tmp;
        while (drivers) {
            tmp = drivers->next;
            free(drivers);
            drivers = tmp;
        }
    }
    
    void freeOrder(Order *orders) {
        // similar to freeDriver
    }
    
    void freeSystem(TaxiSystem* system) {
        if (system) {  // ensure system is non-null
            freeDriver(system->driversQueue);
            freeOrder(system->ordersQueue);
            free(system);
        }
    }
    This is a better design, and has several benefits:
    1. If you need a Driver or Order queue that is not part of a TaxiSystem, you can still free it without re-writing the whlie loop in a separate function.
    2. The freeSystem function is easier to read, write and maintain.
    3. Separation of concerns. The TaxiSystem should not care how a Driver or Order queue is implemented. This way you can change the underlying implementation of a Driver or Order queue (say a priority queue, FIFO queue, etc) without worrying about changing the TaxiSystem implementation.

    NOTE: This sort of abstraction should be used throughout your program. That goes for your add, lookup, remove, etc functions.

  7. #82
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    If you put the pointers to next first in structures, you can use common code to work with linked lists similar to C++ class inheritance. For example:

    Code:
    typedef struct node{
        struct node* next;
    }Node;
    
    typedef struct driver{
        struct driver* next;
        char name[101];
        int priority;
    }Driver;
    
    typedef struct order{
        struct order* next;
        int orderID;
        int isSpecific;
        char specificDriver[101];
    }Order;
    
    void freeNodes(Node * nodes) {
        Node *tmp;
        while (nodes) {
            tmp = nodes->next;
            free(nodes);
            nodes = tmp;
        }
    }
    
    void freeSystem(TaxiSystem* system) {
        if (system) {  // ensure system is non-null
            freeNodes((Node *)(system->driversQueue));
            freeNodes((Node *)(system->ordersQueue));
            free(system);
        }
    }

  8. #83
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,907
    @rcgldr:
    That's not a very safe way to do it:
    Quote Originally Posted by C11 6.3.2.3p7
    A pointer to an object or incomplete type may be converted to a pointer to a different
    object or incomplete type. If the resulting pointer is not correctly aligned for the
    pointed-to type, the behavior is undefined.
    Pointers can have different sizes and alignments in some systems (uncommon on most desktop environments but still possible). A safer alternative:
    Code:
    typedef struct node     Node;
    struct node {
        Node *next;
    };
    
    typedef struct driver    Driver;
    struct driver {
        Node node;
        char name[101];
        int priority;
    };
    
    typedef struct order    Order;
    struct order {
        Node node;
        int orderID;
        int isSpecific;
        char specificDriver[101];
    };
    
    void freeNodes(Node *nodes) {
        Node *tmp;
        while (nodes) {
            tmp = nodes->next;
            free(nodes);
            nodes = tmp;
        }
    }
    
    void freeSystem(TaxiSystem* system) {
        if (system) {  // ensure system is non-null
            freeNodes((Node *) system->driversQueue);
            freeNodes((Node *) system->ordersQueue);
            free(system);
        }
    }
    This is allowed since the first member is guaranteed to have the same address as the struct itself:
    Quote Originally Posted by C11 6.7.2.1p13
    Within a structure object, the non-bit-field members and the units in which bit-fields
    reside have addresses that increase in the order in which they are declared. A pointer to a
    structure object, suitably converted, points to its initial member (or if that member is a
    bit-field, then to the unit in which it resides), and vice versa. There may be unnamed
    padding within a structure object, but not at its beginning.
    There's a tiny bit more work to build and traverse the list, but at least it's guaranteed to work.

    I still don't like that system however. I prefer to use a generic list container:
    Code:
    typedef struct list    List;
    struct list {
        List *next;
        void *data;
    };
    It costs a few more calls to malloc and free, but it makes a great library and is highly reusable across all my programs. But better still, there exist tons of free libraries out there that already do this, and do it well.

  9. #84
    Registered User
    Join Date
    Apr 2013
    Posts
    1,658
    Quote Originally Posted by anduril462 View Post
    @rcgldr: that's not a very safe way to do it ...
    I had the impression that all pointers to structures would be of the same size on any system, or more generally, that all conventional pointers would be of the same size on the same system, and as mentioned in C11 6.7.2.1p13 , that there would never be any unnamed padding at the beginning of any structure.

    I'm also thinking that if pointer size is variable, then how would malloc() and free() work for a resized pointer?

    However a one time check could be added to make sure that the code in my previous post would not be an issue, by comparing sizeof(...->next) and offsetof(..., next) for all structures used in a program to make sure they are equal.

    I've also use the generic list structure, with a few additional members as part of a messaging interface between threads on embedded systems using a preemptive multi-threading kernel.
    Last edited by rcgldr; 06-17-2013 at 07:44 PM.

  10. #85
    Master Apprentice phantomotap's Avatar
    Join Date
    Jan 2008
    Posts
    5,108
    There's a tiny bit more work to build and traverse the list, but at least it's guaranteed to work.
    O_o

    Nope.

    Code:
    driver * s1 = malloc(sizeof(*s1) * 1);
    tmp1->next = s1;  // We aren't assigning to a `driver *'.
                      // We aren't assigning to a `order *'.
    static order s2;  // We are assigning to a `node *'.
    tmp2->next = &s2; // If `node *' is smaller we lose information.
                      // If `node *' is larger it could conceivably
                      // require different alignment which may break
                      // where `driver *' or `order *' are created
                      // independentaly of this `node *'.
    You have moved the cast from the cleanup routines to the generation routines, and if the sizes or alignment aren't compatible, the assertion leading you to forward an alternative, the generation side of the code loses or misalignes information leading to failure.

    Your code relies on being able to "roundtrip" a pointer to a structure of one type through a pointer to a structure of a different type yielding the `void *' address returned from `malloc' exactly as much as the code rcgldr posted.

    The first member having the same address as the structure proper, which chains giving you the address of a `Node *', is a "red herring"; (The code posted by rcgldr works the same in that the first member is reliably accessed.) the reliable access to the first member says nothing about the size of that member or how a compiler may use low or high bits of a `void *' to store addresses of different types. (For example, an environment using 4 bytes for function pointers, 4 bytes for near data pointers, and 8 bytes for far data pointers with an 8 byte `void *' may use the lowest 4 bytes for function pointers and the highest 4 bytes for near data pointers allowing a debugger optimization.) So, the allocation, offset, and alignment made part of the `node *' in the `node' structure tells only part of the story when we will be allocating, assigning, and otherwise using `driver *' and `order *' through that `node *'.

    The standard says you only get this "roundtrip" thing through `void *'; you aren't guaranteed to get it through another pointer.

    If the pointers aren't compatible, they just aren't compatible.

    I think this is a silly thing to worry about. The standard allowing different sized pointers and alignment has more to do with near/far data pointers, function pointers, and in C++ member pointers than allowing for different sized pointers to user defined types.

    Soma
    Last edited by phantomotap; 06-17-2013 at 08:13 PM. Reason: *derp*

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. (queue*)this)->queue::qput’ does not have class type
    By brack in forum C++ Programming
    Replies: 13
    Last Post: 11-11-2010, 03:41 PM
  2. DNS entry changes
    By Thantos in forum Tech Board
    Replies: 4
    Last Post: 09-02-2003, 08:57 PM
  3. Min, Max field entry?
    By JCCC in forum C Programming
    Replies: 1
    Last Post: 04-16-2002, 07:46 PM
  4. Registry entry from AIM help!
    By SyntaxBubble in forum Windows Programming
    Replies: 3
    Last Post: 01-02-2002, 05:00 PM
  5. Queue and Priority Queue
    By Pamela in forum C++ Programming
    Replies: 1
    Last Post: 12-07-2001, 11:09 PM