I'm trying to use a "fixed memory scheme" and pre-allocate memory & reuse it via alloc, init, free fashion as many times as possible.
free() will called at shutdown only, but I want to test many iterations.
Although I call my alloc function bn_tree_alloc_node_space_heap() & init function bn_tree_init_node_heap(), I can only call free function bn_tree_free_node_space once.
Below is a complete reproducible snippet of my memory management, maint_test.c:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <float.h>
#define BN_TREE_HEAP_SIZE 100
/*variables internal*/
typedef struct bntree_internals;
/*bn_tree_node is single bntree_t leaf*/
typedef struct bn_tree_node {
struct bn_tree_node* left;
struct bn_tree_node* right;
float* dataset;
float distance_to_neighbor;
int visited;
int heap_index;
} bn_tree_node;
/*tree*/
typedef struct {
/*in order to keep track of the bn-tree root*/
bn_tree_node* _root;
/*pointer to internal variables struct*/
struct bntree_internals* _internals;
} bntree_t;
/*bn tree leaf nodes heap*/
bn_tree_node* node_processing_space = NULL;
/*leaf nodes*/
void bn_tree_alloc_node_space_heap(int max_dimensions);
bn_tree_node*
get_pre_allocated_bn_tree_node_heap();
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions);
void bn_tree_free_node_space(bn_tree_node* nodes);
int main(int argc, char** argv) {
/*PROBLEM:called the alloc,init,free cycle several times, problem,
getting seg fault on 2nd call of free()*/
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
bn_tree_alloc_node_space_heap(3);
assert(get_pre_allocated_bn_tree_node_heap());
printf("alloc\n");
bn_tree_init_node_heap(node_processing_space, 3);
printf("init\n");
bn_tree_free_node_space(node_processing_space);
printf("free\n");
return (EXIT_SUCCESS);
}
void bn_tree_alloc_node_space_heap(int max_dimensions) {
if (NULL == node_processing_space) {
node_processing_space = (bn_tree_node*) calloc(BN_TREE_HEAP_SIZE, sizeof (bn_tree_node));
//TODO: bn_tree_set_k_dimensions (max_dimensions);
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
node_processing_space[i].dataset = (float*) calloc(max_dimensions, sizeof (float));
}
//bn_heap_tail_index = bn_heap_head_index = 0;
}
}
bn_tree_node* get_pre_allocated_bn_tree_node_heap() {
return node_processing_space;
}
void bn_tree_init_node_heap(bn_tree_node* nodes, int max_dimensions) {
int i = 0;
int c = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
/*reset values */
if (NULL != nodes[i].dataset) {
c = 0;
for (; c < max_dimensions; c++) {
nodes[i].dataset[c] = FLT_MIN;
}
}
nodes[i].visited = 0;
nodes[i].distance_to_neighbor = FLT_MAX;
nodes[i].left = NULL;
nodes[i].right = NULL;
nodes[i].heap_index = -1;
}
}
/*PROBLEM is subsequent call to free(), but if I alloc again why cant I free again?*/
void bn_tree_free_node_space(bn_tree_node* nodes) {
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if (nodes[i].dataset) {
free(nodes[i].dataset);
}
}
free(nodes);
nodes = NULL;
}
Here is the output that I expect/want:
alloc
init
free
alloc
init
free
alloc
init
free
alloc
init
free
But Im getting this output/error:
alloc
init
free
alloc
init
double free or corruption (!prev)
Aborted (core dumped)
How can fix this?
Can't I do alloc,init,free as many times as I want (as long as I called alloc before free) OR I can do only alloc() once, then many init(), free() once?
Thanks a million & please be kind enough to provide concise answers with minimal changes.
The problem is that your bn_tree_free_node_space function takes, as its argument, a copy of the pointer variable - that is, you are passing the pointer by value - thus, the line nodes = NULL; at the end of that function only sets the local variable to NULL and does not change the value of the node_processing_space variable.
To fix this (with minimal changes to your code logic1), you need to pass that function a pointer to the pointer, and dereference that in the function. So, your function should look like this:
void bn_tree_free_node_space(bn_tree_node** nodes) // Argument is pointer-to-pointer
{
int i = 0;
for (; i < BN_TREE_HEAP_SIZE; i++) {
if ((*nodes)[i].dataset) { // Now we need to use (*nodes) to get the underlying pointer
free((*nodes)[i].dataset); // ... same here
}
}
free(*nodes); /// ... and here
*nodes = NULL;
}
You will, of course, also need to change the function prototype (just before your main) to match the new definition:
void bn_tree_free_node_space(bn_tree_node** nodes); // Must match definition!
Fruther, you will (clearly) need to change the calls to that function to pass the address of the node_processing_space pointer:
bn_tree_free_node_space(&node_processing_space); // Likewise for the other 3 calls!
Feel free to ask for further clarification and/or explanation.
1 EDIT: There are other ways (some may argue better ways) to implement your system, and also other 'minor' issues in your code. However, you did explicitly ask for "concise answers with minimal changes," so I have endeavoured to comply with that request!
Since the array address may change when memory is reallocated,
the main part of the program (in the body of the function main ()) should refer to the elements by
indexes, not pointers. Why?
Can you show an example of accessing items with pointers?
(Sorry for my English).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Nameval Nameval;
struct Nameval {
char *name;
int value;
};
struct NVtab {
int nval; /* current number of values */
int max; /* allocated number of values */
Nameval *nameval; /* array of name-value pairs */
};
enum {NVINIT = 1, NVGROW = 2};
/* addname: add new name and value to nvtab */
int addname(struct NVtab *nvtab, Nameval newname) {
Nameval *nvp;
if (nvtab->nameval == NULL) { /* first time */
nvtab->nameval = (Nameval *) malloc(NVINIT * sizeof(Nameval));
if (nvtab->nameval == NULL)
return -1;
nvtab->max = NVINIT;
nvtab->nval = 0;
} else if (nvtab->nval >= nvtab->max) { /* grow */
nvp = (Nameval *) realloc(nvtab->nameval,
(NVGROW*nvtab->max)*sizeof(Nameval));
if (nvp == NULL)
return -1;
nvtab->max *= NVGROW;
nvtab->nameval = nvp;
}
nvtab->nameval[nvtab->nval] = newname;
return nvtab->nval++;
}
int main(void) {
struct NVtab nvtab = {0, 0, NULL};
int curnum;
curnum = addname(&nvtab, (Nameval) {.name="Andy", .value=12});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%d\n", curnum);
curnum = addname(&nvtab, (Nameval) {.name="Jack", .value=71});
printf("%d\n", curnum);
for (int i = 0; i < nvtab.nval; i++) {
printf("%s %d\n", nvtab.nameval[i].name,
nvtab.nameval[i].value);
}
}
For example, why can`t we show array like this:
for (int i = 0; i < nvtab.nval; i++)
printf("%s %d\n", nvtab.*(nameval+i).name, nvtab.*(nameval+i).value);
You are not supposed to assign a pointer calculated for a specific index to a variable with storage duration which could extend over an insert operation.
That pointer could become invalid, so the lesson behind that example is to always re-evaluate iterators on dynamic data structures.
E.g. what not to do:
auto *foo = &nvtab.nameval[i];
addname(&nvtab, (Nameval) {.name="Billy", .value=18});
printf("%s %d\n", foo->name, foo->value);
In the last line it can work or crash. Depending on whether realloc moved the allocation or resized in-place. Except that you can never know for sure until you execute it, as it isn't even fully deterministic.
This is not valid syntax:
nvtab. *(nameval+i).name
The member access operator . expects to be followed by the name of the member. What you want is:
(*(nvtab.nameval+i)).name
I am new to C and wondering how to do some pointer stuff. Specifically here I am wondering how you can pass a pointer into a function and "get a value out of the function". Sort of like this (semi-pseudocode):
assign_value_to_pointer(void* pointer) {
if (cond1) {
pointer = 10;
} else if (cond2) {
pointer = "foo";
} else if (cond3) {
pointer = true;
} else if (cond4) {
pointer = somestruct;
} else if (cond5) {
pointer = NULL;
} else if (cond6) {
// unknown type!
pointer = flexiblearraymember.items[index];
}
}
main() {
void* pointer = NULL;
assign_value_to_pointer(&pointer);
if (cond1) {
assert(pointer == 10);
} else if (cond2) {
assert(pointer == "foo");
} else if (cond3) {
assert(pointer == true);
} else if (cond4) {
assert(pointer == somestruct);
} else if (cond5) {
assert(pointer == NULL);
}
}
Put another way:
p = new Pointer()
assign_a_value(p)
assert(p.value == 10) // or whatever
Basically it is passing the pointer into the function, the function is assigning a value to the pointer, and then you can use that value outside of the function when it returns. You may not know what kind of value you are getting from the function (but that can be handled by extending this to use structs and such), hence the void pointer. The main goal though is just passing a pointer into some function and having it absorb some value.
Wondering how to do this properly in C with a quick example implementation. Doesn't have to cover every case just enough to get started.
I would like to use this to implement stuff like passing in a NULL error object to a function, and if there is an error, it sets the pointer of the error to some error code, etc.
I don't think this should be a broad question, but if it is, it would be helpful to know where to look for a more thorough explanation or examples in source code.
First, I'll answer your question directly, hopefully you understand why you need to be reaaally careful. This can be a useful technique for implementing queues, or communication stacks - but you need to be CERTAIN that you can regain track of what types are being stored or your program logic will totally break. I'll then try to briefly cover some of the use cases and some methods of making it safe(r).
Simple example doing exactly what you said
#include <stdio.h>
#include <stdlib.h>
//Some basic error type for reporting failures
typedef enum my_error
{
ERROR_NONE = 0,
ERROR_FAIL = 1,
} my_error;
struct my_struct
{
int age;
char *name;
int order_count;
};
int someCond = 1;
//Let's start with a simple case, where we know the type of the pointer being passed (an int)
//If int_out is NULL, then this function will invoke undefined behavior (probably a
//runtime crash, but don't rely on it).
my_error assign_int(int *int_out)
{
if(someCond)
*int_out = 5;
else
*int_out = 38;
return ERROR_NONE;
}
//Need to use a 'double pointer', so that this function is actually changing the pointer
//that exists in the parent scope
my_error dynamically_assign_value_to_pointer(void **pointer)
{
//A pointer internal to this function just to simplify syntax
void *working_ptr = NULL;
if(someCond)
{
//Allocate a region of memory, and store its location in working_ptr
working_ptr = malloc(sizeof(int));
//store the value 12 at the location that working_ptr points to (using '*' to dereference)
*((int *) working_ptr) = 12;
}
else
{
//Allocate a region of memory, and store its location in working_ptr
working_ptr = malloc(sizeof(struct my_struct));
//Fill the struct with data by casting (You can't dereference a void pointer,
//as the compiler doesn't know what it is.)
((struct my_struct *) working_ptr)->age = 22;
((struct my_struct *) working_ptr)->name = "Peter";
((struct my_struct *) working_ptr)->order_count = 6;
}
//Set the pointer passed as an argument to point to this data, by setting the
//once-dereferenced value
*pointer = working_ptr;
return ERROR_NONE;
}
int main (int argc, char *argv[])
{
int an_int;
void *some_data;
assign_int(&an_int);
//an_int is now either 5 or 38
dynamically_assign_value_to_pointer(&some_data);
//some_data now points to either an integer OR a my_struct instance. You will need
//some way to track this, otherwise the data is useless.
//If you get this wrong, the data will be interpreted as the wrong type, and the
//severity of the issue depends what you do with it.
//For instance, if you KNOW FOR SURE that the pointer contains the int, you could
//print it by:
printf("%d", *((int *) some_data));
//And because it is dynamically allocated, you MUST free it.
free(some_data);
return 0;
}
In practice, this is useful for queues, for instance, so you can write a generic queue function and then have different queues for different data types. This is partial code, so won't compile and is a bad idea in this limited case, when a type-safe alternative would be trivial to design, but hopefully you get the idea:
extern my_queue_type myIntQueue;
extern my_queue_type myStructQueue;
my_error get_from_queue(void *data_out, my_queue_type queue_in);
int main (int argc, char *argv[])
{
//...
int current_int;
struct my_struct current_struct;
get_from_queue(¤t_int, myIntQueue);
get_from_queue(¤t_struct, myStructQueue);
//...
}
Or if you really want to store lots of different types together, you should at least track the type along with the pointer in a struct, so you can use a 'switch' in order to cast and handle logic appropriately when necessary. Again, partial example so won't compile.
enum my_types
{
MY_INTEGER, MY_DOUBLE, MY_STRUCT
};
struct my_typed_void
{
void *data;
enum my_types datatype;
};
my_error get_dynamic_from_global_queue(struct my_typed_void *data_out)
{
//...
data_out->data = malloc(sizeof int);
*((int *)(data_out->data)) = 33;
data_out->datatype = MY_INTEGER;
//...
}
int main (int argc, char *argv[])
{
struct my_typed_void current;
if(get_dynamic_from_global_queue(¤t) == ERROR_NONE)
{
switch(current.datatype)
{
//...
case MY_INTEGER:
printf("%d", *((int *) current.data));
break;
//...
}
free(current.data);
}
return 0;
}
Either return the pointer or pass a pointer to pointer (the function then will change the pointer):
void* f1(void* p)
{
p = whatever(p, conditions);
return p;
}
void f2(void** p)
{
*p = whatever(*p, conditions);
}
void assign_value_to_pointer(int** pointer) {
**pointer = 20;
}
void main() {
void* pointer = NULL;
pointer=malloc(sizeof(int));
*(int *)pointer=10;
assign_value_to_pointer(&pointer);
}
I'm not 100% sure what you are looking for, but could it be something like this:
enum pointer_type{INTEGER, STRUCTURE_1, STRUCTURE_2, INVALID};
int assign_value_to_pointer(void ** ptr)
{
uint8_t cond = getCondition();
switch(cond)
{
case 1:
*ptr = (void*) 10;
return INTEGER;
case 2:
*ptr = (void*) someStructOfType1;
return STRUCTURE_1;
case 3:
*ptr = (void*) someStructOfType2;
return STRUCTURE_2;
default:
*ptr = NULL;
return INVALID;
};
}
void main(void)
{
void * ptr = NULL;
int ptrType = assign_value_to_pointer(&ptr);
switch(ptrType)
{
case INTEGER:
assert(ptr == (void*)10);
break;
case STRUCTURE_1:
assert( ((structType1*) ptr)->thing == something);
break;
case STRUCTURE_2:
assert( ((structType2*) ptr)->something == something);
break;
default:
assert(ptr == NULL);
}
}
You can actually type cast the pointer in main() according to the case (condition) and use. However, in my opinion, you can use a union for this purpose.
Create a union with all possible data types.
typedef union _my_union_type_ {
int intVal;
char* stringVal;
bool boolVal;
SomestructType somestruct;//Assuming you need a structure not structure pointer.
void* voidPtrType;
} my_union_type;
Now in main(), create variable of this union type and pass the address of the union to the function.
main() {
my_union_type my_union;
memset(&my_union, 0x00, sizeof(my_union));
assign_value_to_pointer(&my_union);
if (cond1) {
assert(my_union.intVal == 10);
} else if (cond2) {
assert(strcmp(my_union.stringVal, "foo")); //String comparison can not be done using '=='
} else if (cond3) {
assert(my_union.boolVal == true);
} else if (cond4) {
assert(memcmp(&my_union.somestruct, &somestruct, sizeof(somestruct)); //Assuming structure not structure pointer.
} else if (cond5) {
assert(my_union.voidPtrType == NULL);
} else if (cond5) {
//Check my_union.voidPtrType
}
}
And in assign_value_to_pointer, you can store the required value in union variable.
assign_value_to_pointer(my_union_type* my_union) {
if (cond1) {
my_union->intVal = 10;
} else if (cond2) {
my_union->stringVal = "foo";
} else if (cond3) {
my_union->boolVal = true;
} else if (cond4) {
memcpy(&(my_union->somestruct), &somestruct, sizeof(somestruct));
} else if (cond5) {
my_union->voidPtrType = NULL;
} else if (cond6) {
// unknown type!
my_union->voidPtrType = flexiblearraymember.items[index];
}
}
I would like to use this to implement stuff like passing in a NULL error object to a function, and if there is an error, it sets the pointer of the error to some error code, etc.
From the above quote and from the code in the question, it seems you are looking for a variable that can "hold" different types, i.e. sometimes you want it to be an integer, at other times a float, at other times a string and so on. This is called a variant in some languages but variants doesn't exist in C. (see this https://en.wikipedia.org/wiki/Variant_type for more about variants)
So in C you'll have to code your own variant type. There are several ways to do that. I'll give examples below.
But first a few words on pointers in C because the code in the question seem to reveal a misunderstanding as it assigns values directly to the pointer, e.g. pointer = somestruct; which is illegal.
In C is very important to understand the difference between the "value of a pointer" and the "value of the pointed to object". The first, i.e. value of a pointer, tells where the pointer is pointing, i.e. the value of a pointer is the address of the pointed to object. Assignments to a pointer changes where the pointer is pointing. To change the value of the pointed to object, the pointer must be dereferenced first. Example (pseudo code):
pointer = &some_int; // Make pointer point to some_int
*pointer = 10; // Change the value of the pointed to object, i.e. some_int
// Notice the * in front of pointer - it's the dereference
// that tells you want to operate on the "pointed to object"
pointer = 10; // Change the value of the pointer, i.e. where it points to
// In other words, pointer no longer points to some_int
Now back to the "variant" implementation. As already mentioned there are several ways to code that in C.
From your question it seems that you want to use a void-pointer. It's doable and I'll start by showing an example using void-pointer and after that an example using a union.
It's not clear in your question what cond are so in my examples I'll just assume it's a command line argument and I just added some interpretation in order to have a running example.
The common pattern for the examples is the use of a "tag". That is an extra variable that tells the current type of objects value (aka meta-data). So the general variant data type looks like:
struct my_variant
{
TagType tag; // Tells the current type of the value object
ValueType value; // The actual value. ValueType is a type that allows
// storing different object types, e.g. a void-pointer or a union
}
Example 1 : void-pointer and casts
The example below will use a void-pointer to point to the object containing the real value. A value that sometimes is an integer, sometimes a float or whatever is needed. When working with a void-pointer, it's necessary to cast the void-pointer before dereferencing the pointer (i.e. before accessing the pointed to object). The tag field tells the type of the pointed to object and thereby also how the cast shall be.
#include <stdio.h>
#include <stdlib.h>
// This is the TAG type.
// To keep the example short it only has int and float but more can
// be added using the same pattern
typedef enum
{
INT_ERROR_TYPE,
FLOAT_ERROR_TYPE,
UNKNOWN_ERROR_TYPE,
} error_type_e;
// This is the variant type
typedef struct
{
error_type_e tag; // The tag tells the type of the object pointed to by value_ptr
void* value_ptr; // void pointer to error value
} error_object_t;
// This function evaluates the error and (if needed)
// creates an error object (i.e. the variant) and
// assigns appropriate values of different types
error_object_t* get_error_object(int err)
{
if (err >= 0)
{
// No error
return NULL;
}
// Allocate the variant
error_object_t* result_ptr = malloc(sizeof *result_ptr);
// Set tag value
// Allocate value object
// Set value of value object
if (err > -100) // -99 .. -1 is INT error type
{
result_ptr->tag = INT_ERROR_TYPE;
result_ptr->value_ptr = malloc(sizeof(int));
*(int*)result_ptr->value_ptr = 42;
}
else if (err > -200) // -199 .. -100 is FLOAT error type
{
result_ptr->tag = FLOAT_ERROR_TYPE;
result_ptr->value_ptr = malloc(sizeof(float));
*(float*)result_ptr->value_ptr = 42.42;
}
else
{
result_ptr->tag = UNKNOWN_ERROR_TYPE;
result_ptr->value_ptr = NULL;
}
return result_ptr;
}
int main(int argc, char* argv[])
{
if (argc < 2) {printf("Missing arg\n"); exit(1);}
int err = atoi(argv[1]); // Convert cmd line arg to int
error_object_t* err_ptr = get_error_object(err);
if (err_ptr == NULL)
{
// No error
// ... add "normal" code here - for now just print a message
printf("No error\n");
}
else
{
// Error
// ... add error handler here - for now just print a message
switch(err_ptr->tag)
{
case INT_ERROR_TYPE:
printf("Error type INT, value %d\n", *(int*)err_ptr->value_ptr);
break;
case FLOAT_ERROR_TYPE:
printf("Error type FLOAT, value %f\n", *(float*)err_ptr->value_ptr);
break;
default:
printf("Error type UNKNOWN, no value to print\n");
break;
}
free(err_ptr->value_ptr);
free(err_ptr);
}
return 0;
}
Some examples of running this program:
> ./prog 5
No error
> ./prog -5
Error type INT, value 42
> ./prog -105
Error type FLOAT, value 42.419998
> ./prog -205
Error type UNKNOWN, no value to print
As the example above shows, you can implement a variant type using void-pointer. However, the code requires a lot of casting which makes the code hard to read. In general I'll not recommend this approach unless you have some special requirements that forces the use of void-pointer.
Example 2 : pointer to union
As explained earlier C doesn't have variants as they are known in other languages. However, C has something that is pretty close. That is unions. A union can hold different types at different times - all it misses is a tag. So instead of using a tag and a void-pointer, you can use a tag and a union. The benefit is that 1) casting will not be needed and 2) a malloc is avoided. Example:
#include <stdio.h>
#include <stdlib.h>
typedef enum
{
INT_ERROR_TYPE,
FLOAT_ERROR_TYPE,
UNKNOWN_ERROR_TYPE,
} error_type_e;
// The union that can hold an int or a float as needed
typedef union
{
int n;
float f;
} error_union_t;
typedef struct
{
error_type_e tag; // The tag tells the current union use
error_union_t value; // Union of error values
} error_object_t;
error_object_t* get_error_object(int err)
{
if (err >= 0)
{
// No error
return NULL;
}
error_object_t* result_ptr = malloc(sizeof *result_ptr);
if (err > -100) // -99 .. -1 is INT error type
{
result_ptr->tag = INT_ERROR_TYPE;
result_ptr->value.n = 42;
}
else if (err > -200) // -199 .. -100 is FLOAT error type
{
result_ptr->tag = FLOAT_ERROR_TYPE;
result_ptr->value.f = 42.42;
}
else
{
result_ptr->tag = UNKNOWN_ERROR_TYPE;
}
return result_ptr;
}
int main(int argc, char* argv[])
{
if (argc < 2) {printf("Missing arg\n"); exit(1);}
int err = atoi(argv[1]); // Convert cmd line arg to int
error_object_t* err_ptr = get_error_object(err);
if (err_ptr == NULL)
{
// No error
// ... add "normal" code here - for now just print a message
printf("No error\n");
}
else
{
// Error
// ... add error handler here - for now just print a message
switch(err_ptr->tag)
{
case INT_ERROR_TYPE:
printf("Error type INT, value %d\n", err_ptr->value.n);
break;
case FLOAT_ERROR_TYPE:
printf("Error type FLOAT, value %f\n", err_ptr->value.f);
break;
default:
printf("Error type UNKNOWN, no value to print\n");
break;
}
free(err_ptr);
}
return 0;
}
In my opinion this code is easier to read than the code using void-pointer.
Example 3 : union - no pointer - no malloc
Even if example 2 is better than example 1 there is still dynamic memory allocation in example 2. Dynamic allocation is part of most C programs but it is something that shall be used only when really needed. In other words - objects with automatic storage duration (aka local variables) shall be prefered over dynamic allocated objects when possible.
The example below shows how the dynamic allocation can be avoided.
#include <stdio.h>
#include <stdlib.h>
typedef enum
{
NO_ERROR,
INT_ERROR_TYPE,
FLOAT_ERROR_TYPE,
UNKNOWN_ERROR_TYPE,
} error_type_e;
typedef union
{
int n;
float f;
} error_union_t;
typedef struct
{
error_type_e tag; // The tag tells the current union usevalue_ptr
error_union_t value; // Union of error values
} error_object_t;
error_object_t get_error_object(int err)
{
error_object_t result_obj;
if (err >= 0)
{
// No error
result_obj.tag = NO_ERROR;
}
else if (err > -100) // -99 .. -1 is INT error type
{
result_obj.tag = INT_ERROR_TYPE;
result_obj.value.n = 42;
}
else if (err > -200) // -199 .. -100 is FLOAT error type
{
result_obj.tag = FLOAT_ERROR_TYPE;
result_obj.value.f = 42.42;
}
else
{
result_obj.tag = UNKNOWN_ERROR_TYPE;
}
return result_obj;
}
int main(int argc, char* argv[])
{
if (argc < 2) {printf("Missing arg\n"); exit(1);}
int err = atoi(argv[1]); // Convert cmd line arg to int
error_object_t err_obj = get_error_object(err);
switch(err_obj.tag)
{
case NO_ERROR:
printf("No error\n");
break;
case INT_ERROR_TYPE:
printf("Error type INT, value %d\n", err_obj.value.n);
break;
case FLOAT_ERROR_TYPE:
printf("Error type FLOAT, value %f\n", err_obj.value.f);
break;
default:
printf("Error type UNKNOWN, no value to print\n");
break;
}
return 0;
}
Summary
There are many ways of solving the problem addressed by OP. Three examples have been given in this answer. In my opinion example 3 is the best approach as it avoids dynamic memory allocation and pointers but there may be situations where example 1 or 2 is better.
You are not far from success, you just miss an asterisk to dereference the argument:
void assign_value_to_pointer(void* pointer) {
if (cond1) {
*pointer = 10; // note the asterisk
...
}
void main() {
void* pointer = NULL;
assign_value_to_pointer(&pointer);
}
In C language, arguments to functions are always passed by value. If you want the function to modify the argument, you must pass the address of the variable you want to modify. In main(), you are doing that - correct. The called function can write where its argument points to, hence modifying the original variable; to do this, you must dereference the argument.
The compiler should get angry on the assignment, because it does not know how many bytes to write (I'm keeping it simple). So, you have to say what kind of object the pointer points to, like this:
*(int *) pointer = 10;
The typecast you choose is up to you, it depends on the context.
At this point... why not declare differently the function:
void assign_value_to_pointer(int* pointer) {
if (cond1) {
*pointer = 10; // note the asterisk
}
Now the typecast is no more necessary because the compiler knows the kind of object (again I am keeping it simple - void is quite special).
******* EDIT after comments
Well, I am not a guru in C language and, besides, I wanted to keep a low profile to better help the OP.
For simple cases, the right declaration is naive. The typecast can be more flexible because the function can have several assignment statements to choose from depending on context. Lastly, if the function is passed the pointer and some other parameter, everything is possible, including using memcpy(). But this last solution opens up a world...
To reply to Lance (comment below): well, I think that there is no way to do an assignment if you don't know the type of object you are writing to. It seems a contracdition to me...
when a pointer is initialized to NULL, getting "FORWARD_NULL" coverity errors and when the NULL initialization is removed, it throws UNINIT coverity errors. The code is as below.
I am very new to coverity. If its a very basic question also, please help.
I am
1) declaring a pointer,
2) Initializing it to NULL and
3) deferring it without assigning anything to it.
This deference is an argument in a function call inside which, it will be filled in. Getting FORWARD_NULL errors for the same. Started with Coverity works, from yesterday only.
int fn1()
{
strct1 *pvarA = NULL;
if (fn2(&pvarA) != 0) // derefering NULL pointer error.
{
return 1;
}
...
/* some code */
}
int fn2(strct1 **pvarA)
{
...
/* some code */
*pvarA = varA;
/* some code */
return 0;
}
Thanks,
Preethi
In such code:
int fn1(int **ar)
{
int *a;
a = *ar;
}
The variable a is not initialized (thus UNINIT) and the variable ar is dereferenced without checking for null (thus FORWARD_NULL).
Probably this code will work:
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
int fn1(int **ar)
{
int *a = NULL;
if (ar == NULL) {
fprintf(stderr, "Omg! you passed NULL as first argument to fn1 function. What to do now? Break the program flow for sure - return or abort() or exit() !");
abort();
return EXIT_FAILURE;
}
a = *ar;
return EXIT_SUCCESS;
}
This helps easily:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
strct1
{
int a;
int b;
char c;
};
int fn1()
{
strct1 *pvarA = NULL;
strct1 varA;
MEMSET(&varA, 0, sizeof (strct1));
pvarA = &varA;
if (fn2(&pvarA) != 0) // derefering NULL pointer error.
{
return 1;
}
/* some code */
}
int fn2(strct1 **pvarA)
{
/* some code */
*pvarA = varA;
/* some code */
return 0;
}
As TLPI exercise 6-3 required, I made an implementation of setenv() and unsetenv() using putenv(), getenv() and via modifing environ variable directly.
Code:
// setenv() / unsetenv() impl
// TLPI exercise 6-3
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#define ENV_SEP '='
extern char **environ;
// setenv() impl using putenv() & getenv()
int setenv_impl(const char * name , const char * value , int overwrite ) {
if(!overwrite && getenv(name)) { // exists & don't overwrite
return 0;
} else {
// construct the new variable
char *env_var = malloc(strlen(name) + strlen(value) + 2);
strcpy(env_var, name);
env_var[strlen(name)] = ENV_SEP;
strcpy(env_var+(strlen(name)+1), value);
int result = putenv(env_var);
if(result==0) {
return 0;
} else {
errno = result;
return -1;
}
}
}
// unsetenv() impl via modifing environ directly,
int unsetenv_impl(const char * name ) {
char **ep, **sp;
size_t len;
len = strlen(name);
for(ep = environ; *ep != NULL;) {
if(strncmp(*ep, name, len)==0 && (*ep)[len] == ENV_SEP) {
// shift all successive elements back 1 step,
for(sp=ep; *sp != NULL; sp++) {
*sp = *(sp+1);
}
} else {
ep++;
}
}
return 0;
}
// setenv_impl() test
int setenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric2", 0);
printf("%s\n", getenv(key));
setenv_impl(key,"Eric3", 1);
printf("%s\n", getenv(key));
return 0;
}
// unsetenv_impl() test
int unsetenv_impl_test() {
char *key = "name";
setenv_impl(key,"Eric", 1);
printf("%s\n", getenv(key));
unsetenv_impl(key);
char *val = getenv(key);
printf("%s\n", val==NULL?"NULL":getenv(key));
return 0;
}
int main(int argc, void *argv[]) {
// setenv_impl_test();
unsetenv_impl_test();
return 0;
}
In my setevn_impl(), I use malloc() to allocate memory for new environment variable.
But I don't know how the memory of process's default environment allocated.
My question is:
In my unsetenv_impl() implementation, is it necesary / proper to free the memory of removed environment string by free()?
If I don't free it, will it be a problem, or it won't take much memory thus could be ignored?
Tip:
putenv() won't duplicate the string, it just make global variable environ point to the string that pass to it.
In your case it is not necessary if you don't plan to set your environment variables very frequently leading to exhaust of your memory resources.
But it would be great if you always deallocate resources after you are done with using them, be it file handles/memory/mutexs. By doing so you will not make that sort of mistake when building servers.
Some servers are expected to run 24x7. In those cases, any leak of any sort means that your server will eventually run out of that resource and hang/crash in some way. A short utility program, ya a leak isn't that bad. Any server, any leak is death. Do yourself a favor. Clean up after yourself. It's a good habit