I am a bit confused about this because I was looking at some code for a dispatcher, and they defined a struct PCB (process control block) that basically contains a bunch of information about a running process and a struct queue. The queue basically just manages the order that the processes are executed, but also ocassionaly moves the processes across queues (eg. move a PCB from queue1 to queue2). The queue struct is essentially defined as
struct Queue{
pcbptr front;
pcbptr back;
}
where pcbptr is defined as
typedef pcb * pcbptr
I am a bit confused about why you would use a pcbptr in this case and not simply define queue to use pcb?
Thanks for any help
The reason is simply because of time. It is way faster to have a pointer to a different struct than to have a copy of the struct by value.
Also, the people using the Queue may wish to modify the PCBs, which they would not be able to do if the PCBs were passed by value.
Also, if you were asking about why they didn't simply use PCB * and not typedef a PCB * to be pcbptr, that is all just down to naming convention. They are exactly the same functionally.
Just think about a queue and what it means at different lengths.
If it is 0, you need to tell that somehow (we do this normally by just setting the pointers for front and back to NULL).
If it is 1, both the front and the back need to point to the same thing.
If it is 2, they point at different things, but have a link going from one to the other.
If it is 3, front points at first one, the first one points at the middle one, and the middle one points at the last done, and back points at the last one.
So no matter what you do, with a variable size like this you need to use pointers. for middle ones.. why bother making the front and back anything but a pointer? (especially since it's faster to change a pointer than to try to move a struct around and the code is about 100x more complicated)
Related
I am implementing a linked list in C and I am running into the issue where C does not implement any specific scheme for memory management other than just giving you the ability to allocate and free memory by passing a pointer. There is no concept of whether the value might be needed later on in the program.
The typical implementation I find online for a linked list basically deallocs the deleted node but does not dealloc the node's value.
Whose responsibility should it be to release the memory taken up by the value when deleted from the list ? The linked list's or the normal flow of the program ?
example:
// allocate 10 bytes
char *text = malloc(sizeof(char) * 10);
// create the linked list
LinkedList *list = list_create();
// add the text pointer to the linked list
list_append(list, text);
// remove the pointer from the linked list
list_remove_last(list);
In this case text would end up not getting deallocated as list_remove_last just frees the memory that the new node takes up. What would be the proper way to release the memory taken up by text ?
that is a very common way of container implementation in C.
basically you dynamically allocate the contents of the list and pass the pointer to the container, now the container is responsible for freeing it.
You can also pass in a function pointer to list_create() so it knows how to do list_remove_last() properly, this is especially useful for using a generic container that does not know what type of elements it will contain (it will just hold void * pointers).
think of the case where the data itself is a struct that contains other pointers. in this case list_remove() can not do a simple free() on its data field, instead it should use the function pointer that was passed in to free the data.
your approach has a small problem:
if you have list* as the return type of list_create(), then you will have to do a free(list) in your main function. alternatively, you can have list_create() return a list, as opposed to a list*, this is a logical choice because a list has its bulk of information dynamically allocated and accessible through a pointer anyway.
in the second case you would need a function list_destroy(list) that would destroy any element your list holds.
C does not implement any specific scheme for memory management other than just giving you the ability to allocate and free memory by passing a pointer
Yes, C lacks any kind of automatic memory management, so you have to be careful to deallocate any memory blocks that you instantiate.
Whose responsibility should it be to release the memory taken up by the value when deleted from the list? The linked list's or the normal flow of the program?
It's your responsibility. You can do it however you like. You can write a general purpose linked list where the caller has to be responsible for allocating and deallocating space for each value in the list because the list management functions don't know how much space each value might require, or whether the values might be needed beyond the lifetime of the node. Or, you can write a list implementation that manages every aspect of the node, including space for the value stored in the node. In some cases, a list node includes the value in the node definition, like:
struct Node {
struct Node *next;
int value;
};
and other times the node has a pointer to some other block that has the actual value:
struct Node {
struct Node *next;
void *value;
};
Another approach is to define a structure with just the part needed for the list operation (i.e. the next pointer), and then piggyback data onto that structure:
struct Node {
struct Node *next;
};
struct MyNode {
struct Node node;
int price;
int quantity;
};
So, there are lots of ways to do it, and none of them are wrong. You should choose the style that makes sense for your needs. Do you have big, complex values that you don't want to duplicate, that you want to store in a linked list, but which you want to continue to use even after they're removed from the list? Go with the first style above. Do you want to manage everything related to the linked list in one place? Then go with the second style.
The point is: C dictates a lot less than other languages do, and while that means that you have to think harder about program correctness, you also get the freedom to do things very directly and in a style of your choosing. Embrace that.
My guide line is: the one who allocates memory is also responsible for de-allocating it.
If you implement a linked list that allocates the memory for the values, the implementation should also take care of freeing this memory when the entries are removed from the list. For strings this could be done by copying the strings to a newly allocated buffer of adequate size.
If your implementation of a linked list only stores plain values (e.g. pointers) without allocating extra memory for the values, it should also avoid freeing memory it did not allocate, because it doesn't know what the allocator planned for this memory in the future.
The proper way would be to have list_remove_node() a function that would free not only the list (node) itself, but also the value that was allocated for that specific node. Also, you shouldn't need to search for a specific node according to your text as you should be able to just call free(node->text) (which can be done even in the current list_remove_last() function)
The main C logic is that you are supposed to free() anything that you allocated yourself. Certain libraries will allocate memory for their own work, which most often you are supposed to clean up as well (as you were the one who asked for it).
I'm implemented a Binary-Search-Tree in C code. Each of my tree nodes looks like this:
typedef struct treeNode {
int key;
struct treeNode *right;
struct treeNode *left;
} treeNode_t;
The construction of the Tree made by the Host. The query of the tree made by the device.
Now, let's assumed that I'm already finished building my Tree in host memory.
I'm want to copy the root of my tree to the memory of my device.
Copying the root of the tree it self isn't enough. Because the right \ left child isn't located in the device memory. This is a problem.
So, my question is what is the easiest way to copy my whole tree to the device memory?
The easiest (and likely also best) way is to change your structure to use node indexes instead of pointers. The issue with pointers is that the device has different pointers and even if you copy all nodes separately, it would still not work as the pointers also need to be updated to device pointers. And unfortunately OpenCL 1.2 does not even guarantee that device pointers stay valid longer than a single kernel invocation. For this reason you have to use indexes instead of pointers at least on the device.
Modify your structure like this:
typedef struct treeNode {
int key;
int left;
int right;
} treeNode_t;
Before you build the tree you allocate one big array of tree nodes, large enough to hold all nodes.
treeNode_t nodes[MAX_NODES]; // or dynamic allocation
int first_free_node=0;
Every time you would normally allocate a new node, you now use nodes[first_free_node] to store the data and increment the first_free_node counter. When you are done building your tree, you can just use a single clEnqueueCopyBuffer call to copy all nodes to the device. You only need to copy first_free_node*sizeof(treeNode_t) bytes from the start of the nodes array to the device. If you cannot change you host tree building code, you can use a simple recursive deep first travesal of the tree to count the number of nodes and convert the nodes from the pointer based format to the index based format.
On some devices you might get a higher performance if you convert the structure of your tree from array of structures to structure of arrays. Padding the structure to 16 byte per node could also help.
If your device supports OpenCL 2.0 then you can use Shared Virtual Memory. The pointers created on the host will be valid on the device too. Here is the description and the binary search tree example: opencl-2-shared-virtual-memory.
In C we define structure for linked list or binary tree like that:
struct list{
int val;
list *next;
};
OR
struct tree_node{
int val;
tree_node *left, *right;
};
we can easily assign pointer of next memory location in serial programming. My question is how do I handle pointer in MPI where multiple processor has its local memory? How do I keep track it? How to implement linked list/binary tree in MPI? I know about MPI_Graph. But it is not useful in my scenario.
I appreciate your answer. Thanks in advance.
I'll discuss a linked list, but all of this applies to a binary tree just as easily with a little extra work.
Implementing a linked list in the classical sense isn't exactly possible in MPI because, as you said, each process has its own local memory which won't be consistent on other processes. So that essentially limits using something simple like point to point messaging unless you want to do a lot of work that wouldn't really make sense.
However, it is possible to do something using one sided communication, or RMA. In fact, there's some example code here. The basic idea of RMA is that each rank exposes a region of memory to the other processes. Then, with the appropriate accessors and synchronization calls, each process can get data from and put data into the other processes memory.
The example uses a dynamic window to allow the application to allocate memory as needed, but it's also possible to statically allocate all your memory up front and point each process to it at the beginning of the application, which might make it a little easier to understand.
Whether or not all of this is efficient or the right thing to do is a different argument. For sufficiently large lists, this can be powerful because you can store more data that you would be able to in a single node's memory. However, for small data structures, the costs of traversing the list become rather high, so it's pretty inefficient to distribute the list and it might be more practical to replicate it on each node.
I am looking at two linked list implementations (queue.h [source] and utlist.h [source]), and I have a few questions regarding their respective implementations:
What is _Q_INVALIDATE all about in queue.h? I suppose it is part of some debugging, but I don't really understand the macro define logic.
Both implementations offer FOREACH and FOREACH_SAFE. The former is straight forward, but what is the logic behind the second? Also, if the former is unsafe in anyway, why has it been implemented in the first place?
Why has queue.h implemented its struct as having different types for next and prev (struct *le_next and struct **le_prev)?
In both implementations, why are there extra parenthesis inserted here and there? Eg. around head in #define LIST_FIRST(head) ((head)->lh_first)
For Question 1:
_Q_INVALIDATE is a macro that sets a pointer that should no longer be used to a value of -1. The intent is that if it is used subsequently, debugging will be made easier because using the pointer will cause an immediate crash. In non-debug mode the macro does nothing, so the pointer is left with its current value - if there's a bug that results in the pointer being used, the problem may be a much more subtle defect.
For Question 2:
The 'safe' versions of these macros take an additional pointer argument that the macro uses internally to point to the next item in the list while the current one is being processed. This allows the code inside the loop to remove the current item from the list. Since the next item has already been remembered in the temp pointer, the macro has no problem picking it up for the next iteration. The non-safe version of the macro doesn't use a temp pointer, so you can't remove the current item from the list while iterating it.
For Question 3:
This makes it easier to add a new element before the current one or to remove the current element from the list without concern about whether the current element is at the head of the list (and therefore only 'pointed to' by the list pointer) or if the current element is elsewhere in the list (and therefore pointed to by another element's le_next pointer). If le_prev were a struct type* then dealing with the first element in the list would need special case code. Since le_prev is a struct type** it can refer to a simple struct type* (like the list head pointer) just as easily as a struct type* that's embedded at some arbitrary offset inside type (like the le_next links in each element).
Question 4 was answered in a comment above.
I am trying to learn how structs work in C. I am familiar with constructors in Java. Now, I have an example of creating a tree in C with structs.
struct a_tree_node{
int value;
struct a_tree_node *leftPTR, *rightPTR;
};
I am currently trying to visualize how this works, I am a little confused because this struct contains itself.
I am a little confused because this struct contains itself.
The struct doesn't contain itself, but rather two pointers to the same kind of structure. That's the key point to understand.
The struct containing itself would be nonsense and wouldn't compile because it's an infinitely recursive dependency.
I think your confusion is comparing a struct to a constructor in Java. The closest equivalent in Java would be class:
class ATreeNode{
int value;
ATreeNode left;
ATreeNode right;
}
As the other answers have said, the left and right node in the struct are pointers - much like (but not quite the same as) references from Java.
The struct doesn't contain it self. It contains two pointers to its type. A very important distinction. Pointers are not of the type the point to but can rather be dereferenced into what they point to at a later time.
It doesn't contain itself it contains two pointers to the same defenition. The * in front of the leftPTR and rightPTR point to memory location where other a_tree_node's are stored.
The struct is defined in such a way that it forms a linked list. Inside the struct you define two pointers to structs. So, the struct does not contain itself, rather, it contains two pointers to two different instantiations of a struct. It is even possible the pointer is a pointer to the struct itself.
When coming from Java, you already know the necessary concepts, but lack the rigor C enforces on the concepts of data and pointers. leftPtr is just like a variable of class type (like Object) in Java, that is, it points to another object, might be Null or might point to another object.
It's just a linked list of int representing a binary tree.
It contains the address of a simlar structure.
Like lets take a tree node.
it means that a single tree node also stores the address of two other similar tree nodes.
Here in the question contains a pointer to struct a_tree_node.
The size of a pointer type is always constant i.e. sizeof(unsigned integer)
so it won't create any problem in defining the size of a struct a_tree_node.
It will not be a nested struct... :) :)
struct a_tree_node{int value;struct a_tree_node *leftPTR, *rightPTR; };
This code will work fine as we are referring pointer to structure not its object as size of pointer is not data type specific. It will depend on how much bit is your OS effectively your integer will take how much byte
e.g on gcc sizeof(int) is 4 so sizeof(leftPTR) is also same
so at run time there will be no recursion sizeof(a_tree_node)=12 (Not considering structure padding as it is compiler specific)
struct a_tree_node{int value;struct a_tree_node left;};
This declaration will leads to error as compiler wouldn't be able to compute its size
goes in infite recursion.