I haven't used C or C++ in over 6 years and am a bit rusty. I am writing some quick test code for a graph-traversal algorithms. The code accepts an adjacency-list style input. However I'm running into some issues with free/malloc.
There are two issues with my code:
When I run the code without free and without the getchar the code hangs when I use VC++ cntrl-f5. This is remedied when I use getchar(). Does anyone know why?
When I run the code with free the code hangs. I've tried to debug the code and it hangs exactly at the free statement. Any suggestions as to how I can fix this?
Also please let me know if I'm doing anything dangerous with this code. Header file is omitted.
void * s_malloc(size_t size){
void * ret_pntr = malloc(sizeof(size));
if (ret_pntr == NULL){
printf ("error");
exit(1);
}
return (void *)malloc(sizeof(size));
}
void initialize_graph(graph * G1, int num_vertices){
int i = 0 ;
G1->num_vertices = num_vertices;
G1->node_list = (node**)s_malloc(sizeof(node*)*num_vertices);
for (i = 0; i < num_vertices; i ++){
G1->node_list[i] = (node *)s_malloc(sizeof(node));
}
}
void free_everything(graph * G1){
int i = 0;
node * ref = NULL;
for (i = 0; i < G1->num_vertices; i++){
ref = G1->node_list[i];
recursive_remove(ref);
}
free(G1->node_list);
}
void recursive_remove(node * ref){
if (ref == NULL){
return;
}
else{
recursive_remove(ref->next);
}
free(ref);
}
int main(){
int i = 0;
graph * G1 = (graph*)s_malloc(sizeof(graph));
G1->init = &initialize_graph;
G1->init(G1, 10);
G1->remove = &free_everything;
G1->node_list[0]->value = 1;
G1->node_list[0]->next = (node*)s_malloc(sizeof(node));
G1->node_list[0]->next->value = 2;
G1->node_list[0]->next->next = NULL;
G1->node_list[1]->value = 10;
printf("%d\n", G1->node_list[0]->next->value);
printf("%d\n", G1->node_list[1]->value);
G1->remove(G1);
free(G1);
getchar();
}
One thing that jumps out immediately is that in
void * s_malloc(size_t size){
void * ret_pntr = malloc(sizeof(size));
if (ret_pntr == NULL){
printf ("error");
exit(1);
}
return (void *)malloc(sizeof(size));
}
you are allocating twice, leaking the first allocation, and are not checking result of the second allocation. Another is that your malloc call should be
malloc(size)
not
malloc(sizeof(size))
Because in your current code you underallocate all your memory (each allocation will only give you 4 bytes at a time), your accesses stomp all over... I'm surprised execution actually makes it to the getchar() or free().
What's not clear is why you are trying to emulate OOP in C while using VC++. If you rewrite this in C++ using STL containers to hold your nodes and with indices instead of pointers, I think a lot of your problems will disappear. But right now debugging this mess for you is not going to be fun for any one.
An even better solution is to use an existing graph library like Boost Graph
Related
I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)
Disclaimer, this is help with a school assignment. That being said, my issue only occurs about 50% of the time. Meaning if I compile and run my code without edits sometimes it will make it through to the end and other times it will not. Through the use of multiple print statements I know exactly where the issue is occurring when it does. The issue occurs in my second call to hugeDestroyer(right after the print 354913546879519843519843548943513179 portion) and more exactly at the free(p->digits) portion.
I have tried the advice found here (free a pointer to dynamic array in c) and setting the pointers to NULL after freeing them with no luck.
Through some digging and soul searching I have learned a little more about how free works from (How do malloc() and free() work?) and I wonder if my issue stems from what user Juergen mentions in his answer and that I am "overwriting" admin data in the free list.
To be clear, my question is two-fold.
Is free(p->digits) syntactically correct and if so why might I have trouble half the time when running the code?
Secondly, how can I guard against this kind of behavior in my functions?
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef struct HugeInteger
{
// a dynamically allocated array to hold the digits of a huge integer
int *digits;
// the number of digits in the huge integer (approx. equal to array length)
int length;
} HugeInteger;
// Functional Prototypes
int str2int(char str) //converts single digit numbers contained in strings to their int value
{
return str - 48;
}
HugeInteger *parseInt(unsigned int n)
{
int i = 0, j = 0;
int *a = (int *)calloc(10, sizeof(int));
HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger));
if(n == 0)
{
p->digits = (int *)calloc(1, sizeof(int));
p->length = 1;
return p;
}
while(n != 0)
{
a[i] = n % 10;
n = n / 10;
i++;
}
p->length = i;
p->digits = (int *)calloc(p->length, sizeof(int));
for(i = 0; i <= p->length; i++, j++)
p->digits[j] = a[i];
return p;
}
HugeInteger *parseString(char *str) //notice datatype is char (as in char array), so a simple for loop should convert to huge int array
{
int i = 0, j = 0;
HugeInteger *p = (HugeInteger *)calloc(1, sizeof(HugeInteger));
if(str == NULL)
{
free(p);
p = NULL;
return p;
}
else
{
for(i=0; str[i] != '\0'; i++)
;
p->length = i;
p->digits = (int *)calloc(p->length, sizeof(int));
for(; i >= 0; i--)
p->digits[j++] = str2int(str[i - 1]);
}
return p;
} //end of HugeInteger *parseString(char *str)
HugeInteger *hugeDestroyer(HugeInteger *p)
{
//printf("No problem as we enter the function\n");
if(p == NULL)
return p;
//printf("No problem after checking for p = NULL\n");
if(p->digits == NULL)
{
free(p);
p = NULL;
return p;
}
//printf("No Problem after checking if p->digits = NULL\n");
//else
//{
free(p->digits);
printf("We made it through free(p->digits)\n");
p->digits = NULL;
printf("We made it through p->digits = NULL\n");
free(p);
printf("We made it through free(p)\n");
p = NULL;
printf("We made it through p = NULL\n");
return p;
//}
//return NULL;
}//end of HugeInteger *hugeDestroyer(HugeInteger *p)
// print a HugeInteger (followed by a newline character)
void hugePrint(HugeInteger *p)
{
int i;
if (p == NULL || p->digits == NULL)
{
printf("(null pointer)\n");
return;
}
for (i = p->length - 1; i >= 0; i--)
printf("%d", p->digits[i]);
printf("\n");
}
int main(void)
{
HugeInteger *p;
hugePrint(p = parseString("12345"));
hugeDestroyer(p);
hugePrint(p = parseString("354913546879519843519843548943513179"));
hugeDestroyer(p);
hugePrint(p = parseString(NULL));
hugeDestroyer(p);
hugePrint(p = parseInt(246810));
hugeDestroyer(p);
hugePrint(p = parseInt(0));
hugeDestroyer(p);
hugePrint(p = parseInt(INT_MAX));
hugeDestroyer(p);
//hugePrint(p = parseInt(UINT_MAX));
//hugeDestroyer(p);
return 0;
}
First of all, really outstanding question. You did a lot of research on topic and generally speaking, solved this issue by yourself, I'm here mainly to confirm your findings.
Is free(p->digits) syntactically correct and if so why might I have trouble half the time when running the code?
Syntax is correct. #Shihab suggested in comments not to release p->digits and release p only, but such suggestion is wrong, it leads to memory leakages. There is a simple rule: for each calloc you must eventually call free, so your current approach in freeing p->digits and then p is totally fine.
However, program fails on a valid line. How is it possible? Quick answer: free can't do its work due to corruption of meta information responsible for tracking allocated/free blocks lists. At some point program corrupted meta information, but this was revealed only on attempt to use it.
As you already discovered, in most implementations memory routines such as calloc results into allocation of buffer with prepended meta-info. You receives pointer to buffer itself, but small piece of information right before this pointer is crucial for further buffer managing (e.g. freeing). Writing 11 integers into buffer intended for 10, you're likely to corrupt meta-info of block following the buffer. Whether corruption actually happens and what would be its consequences, is heavily dependent on both implementation specifics and current memory alignment (what block follows the buffer, what exactly meta-data is corrupted). It doesn't surprise me, that you see one crash per two executions, neither surprises me observing 100% crash reproduction on my system.
Secondly, how can I guard against this kind of behavior in my functions?
Let's start with fixing overflows. There are couple of them:
parseString: loop for(; i >= 0; i--) is executed length+1 times, so p->digits is overflown
parseInt: loop for (i = 0; i <= p->length; i++, j++) is executed length+1 times, so p->digits is overflown
Direct access to memory managing in C++ is error prone and troublesome to debug. Memory leakages and buffers overflows are the worst nightmare in programmers life, it's usually better to simplify/reduce direct usage of dynamic memory, unless you are studying to cope with it, of course. If you need to stick with a lot of direct memory managing, take a look at valgrind, it's intended to detect all such things.
By the way, there is also a memory leakage in your program: each call to parseInt allocates buffer for a, but never frees it.
int listLength(struct node *r) {
int *len = (int *)malloc(sizeof(int));
if(!r) {
free(len);
return *len;
}
while(r) {
r = r->next;
*len += 1;
}
free(len)
return *len;
}
I wrote this function to calculate the length of the linked list. I am still learning pointers by playing with them. I understand I could have used a simple len variable in the function,but I want to learn the basics of dynamic memory allocation. Why the length is always 0 after even though list has few elements? When should free() be called ?
You cannot use the memory once you free() it. So,
free(len);
return *len;
is wrong and undefined behavior.
Instead, you can use a local variable to hold the value and return it.
Also, FWIW,
int *len = (int *)malloc(sizeof(int));
if(!r) {
free(len);
return *len;
}
in the above code, you're trying to use the *len as return value, which is uninitialized. Even without the free()ing, you should not do that.
Also, you should always check for the success of malloc() vefore using the returned pointer.
A modified version:
int listLength(struct node *r) {
int *lenp = malloc(sizeof(int));
int len = 0;
if (!lenp) //check malloc success
exit(-1);
*lenp = len;
if(!r) {
free(lenp);
return len;
}
while(r) {
r = r->next;
*lenp += 1;
}
len = *lenp;
free(lenp);
return len;
}
EDIT:
In your case, there is no need to use dynamic memory allocation at all. AS suggested by Mr. # Barak Manos and Mr. # WhozCraig, you should use dynamic memory allocation only when the memory requirement is not known at compile time. Otherwise, in general, static (compile time) memory allocation should do just fine.
A better and cleaner approach to your code,
int listLength(struct node *r) {
int len = 0;
while(r) {
r = r->next;
len += 1;
}
return len;
}
In general: after you have free()'d a memory block, you must never access it anymore. Just think of have given that block away. It is not yours anymore!
From this follow that you have to free a block only if you don't want to access it anymore. That's a fundamental rule; never ever break it.
For this example, as others have stated, there is actually no need to use dynamic memory at all.
Oh, and: you really should never cast the result of malloc! It returns void * wich can be assigned to any other pointer type. Read the standard, section 6.5.16.1 .
Suppose this is a part of my code:
int foo()
{
char *p, *q ;
if((p = malloc(BUFSIZ)) == NULL) {
return ERROR_CODE;
}
if((q = malloc(BUFSIZ)) == NULL) {
free(p)
return ERROR_CODE;
}
/* Do some other work... */
free(p);
free(q);
}
Since it's possible that the first malloc is successful but the second one fails, I use free(p) in the second "error handler". But what if there are more malloc's and what if I want to modify the code (adjusting their orders, adding or deleting some malloc)?
I know in C++ there are things like RAII and exception safe, etc. But in general, what is the correct way to handle malloc failure in C? (maybe using some goto?)
Your code is fine, but for lots of variables, I'd prefer:
int
foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
goto error;
}
// insert similar repetitions
// hopefully do something here
error:
free (p);
free (q);
return ret;
}
Note that freeing NULL is defined as a no-op.
This avoids n levels of indent for n variables. You can clean up filehandles etc. similarly (though you'll have to put a condition around the close()).
Now, if you know you can allocate them all at once, then dasblinkenlight has a good answer, but here's another way:
int
foo()
{
int ret = 0;
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (!p || !q || !r)
{
ret = ERROR_CODE;
goto exit;
}
// do something
exit:
free(p);
free(q);
free(r);
return ret;
}
Final possibility: if you actually want to exit the program on malloc fail, consider using mallopt's M_CHECK_ACTION option. This makes malloc() faults get checked, and calls abort(), possibly printing a helpful message.
From the man page:
NAME
mallopt - set memory allocation parameters
SYNOPSIS
#include <malloc.h>
int mallopt(int param, int value);
DESCRIPTION
The mallopt() function adjusts parameters that control the behavior of the memory-allocation functions (see malloc(3)). The param argument specifies the parameter to be modified, and value specifies the new value for that parameter.
The following values can be specified for param:
M_CHECK_ACTION
Setting this parameter controls how glibc responds when various kinds of programming errors are detected (e.g., freeing the same pointer twice). The 3 least significant bits (2, 1, and 0) of the value assigned to this parameter determine the glibc behavior, as follows:
Bit 0: If this bit is set, then print a one-line message on stderr that provides details about the error. The message starts with the string "*** glibc detected ***", followed by the program name, the name of the memory-allocation function in which the error was detected, a brief description of the error, and the memory address where the error was detected.
Bit 1: If this bit is set, then, after printing any error message specified by bit 0, the program is terminated by calling abort(3). In glibc versions since 2.4, if bit 0 is also set, then, between printing the error message and aborting, the program also prints a stack trace in the manner of backtrace(3), and prints the process's memory mapping in the style of /proc/[pid]/maps (see proc(5)).
Bit 2: (since glibc 2.4) This bit has an effect only if bit 0 is also set. If this bit is set, then the one-line message describing the error is simplified to contain just the name of the function where the error was detected and the brief description of the error.
Since it is perfectly OK to pass NULL to free(), you could allocate everything that you need in a "straight line", check everything in a single shot, and then free everything once you are done, regardless of whether or not you have actually done any work:
char *p = malloc(BUFSIZ);
char *q = malloc(BUFSIZ);
char *r = malloc(BUFSIZ);
if (p && q && r) {
/* Do some other work... */
}
free(p);
free(q);
free(r);
This works as long as there are no intermediate dependencies, i.e. you do not have structures with multi-level dependencies. When you do, it is a good idea to define a function for freeing such a structure, without assuming that all memory blocks are non-NULL.
For large numbers of allocations, I would invest the time in creating a memory manager that keeps track of the allocations. That way, you never have to worry about leaks, regardless of whether or not the function succeeds.
The general idea is to create a wrapper for malloc that records successful allocations, and then frees them on request. To free memory, you simply pass a special size to the wrapper function. Using a size of 0 to free memory is appropriate if you know that none of your actual allocations will be for 0 sized blocks. Otherwise, you may want to use ~0ULL as the request-to-free size.
Here's a simple example that allows up to 100 allocations between frees.
#define FREE_ALL_MEM 0
void *getmem( size_t size )
{
static void *blocks[100];
static int count = 0;
// special size is a request to free all memory blocks
if ( size == FREE_ALL_MEM )
{
for ( int i = 0; i < count; i++ )
free( blocks[i] );
count = 0;
return NULL;
}
// using a linked list of blocks would allow an unlimited number of blocks
// or we could use an array that can be expanded with 'realloc'
// but for this example, we have a fixed size array
if ( count == 100 )
return NULL;
// allocate some memory, and save the pointer in the array
void *result = malloc( size );
if ( result )
blocks[count++] = result;
return result;
}
int foo( void )
{
char *p, *q;
if ( (p = getmem(BUFSIZ)) == NULL ) {
return ERROR_CODE;
}
if ( (q = getmem(BUFSIZ)) == NULL ) {
getmem( FREE_ALL_MEM );
return ERROR_CODE;
}
/* Do some other work... */
getmem( FREE_ALL_MEM );
return SUCCESS_CODE;
}
it is matter of habit, but I prefer:
int returnFlag = FAILURE;
if ((p = malloc...) != NULL)
{
if ((q = malloc..) != NULL)
{
// do some work
returnFlag = SUCCESS; // success only if it is actually success
free(q);
}
free(p);
}
return returnFlag; // all other variants are failure
IF you are expecting to allocate a large number of items, it Can get messy. Try to avoid the 'goto' approach. Not because of the old 'goto is bad' ethic, but because that way really can lie madness and memory leaks.
It's a little overkill for small numbers of malloc, but you can consider something like this approach:
void free_mem(void **ptrs, size_t len)
{
for (size_t i = 0; i < len; ++i)
{
free(ptrs[i]);
ptrs[i] = NULL;
}
}
int foo(...)
{
void *to_be_freed[N];
int next_ptr = 0;
for (size_t i = 0; i < N; ++i) to_be_freed[i] = NULL;
p = malloc(..);
if (!p)
{
free_mem(to_be_freed,N);
return ERROR_CODE;
}
to_be_freed[next_ptr++] = p;
// Wash, rinse, repeat, with other mallocs
free_mem(to_be_freed,N)
return SUCCESS;
}
In reality, you can probably wrap malloc with something which tracks this. Put the array and array size in a structure and pass that in with the desired allocation size.
I think the first answer is the most general purpose as it can be used for errors other than those caused by malloc. However I would remove the gotos and use a single pass while loop like so.
int foo()
{
char *p = NULL;
char *q = NULL;
int ret = 0;
do {
if (NULL == (p = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// possibly do something here
if (NULL == (q = malloc(BUFSIZ)))
{
ret = ERROR_CODE;
break;
}
// insert similar repetitions
// hopefully do something here
} while(0);
free (p);
free (q);
return ret;
}
I would love to use this code as a dynamic array. Unfortunately, I cannot figure out why the program does not use the allocated memory. Is there something wrong with the parameters of the AddToArray function, maybe?
It is possible to copy and paste this code directly into the IDE and compile, to take a look at the output. The memory seems to be allocated but not used?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
float x;
float y;
} DATA;
int AddToArray (DATA item,
DATA **the_array,
int *num_elements,
int *num_allocated)
{
if(*num_elements == *num_allocated)
{ // Are more refs required?
// Feel free to change the initial number of refs
// and the rate at which refs are allocated.
if (*num_allocated == 0)
{
*num_allocated = 3; // Start off with 3 refs
}
else
{
*num_allocated *= 2;
}// Double the number of refs allocated
// Make the reallocation transactional by using a temporary variable first
void *_tmp = realloc(*the_array, (*num_allocated * sizeof(DATA)));
// If the reallocation didn't go so well, inform the user and bail out
if (!_tmp)
{
printf("ERROR: Couldn't realloc memory!\n");
return(-1);
}
// Things are looking good so far, so let's set the
*the_array = (DATA*)_tmp;
}
(*the_array)[*num_elements] = item;
*num_elements++;
return *num_elements;
}
int main()
{
DATA *the_array = NULL;
int num_elements = 0; // To keep track of the number of elements used
int num_allocated = 0; // This is essentially how large the array is
// Some data that we can play with
float numbers1[4] = {124.3,23423.4, 23.4, 5.3};
float numbers2[4] = { 42, 33, 15, 74 };
int i;
// Populate!
for (i = 0; i < 4; i++)
{
DATA temp;
temp.x = numbers1[i];
temp.y = numbers2[i];
if (AddToArray(temp, &the_array, &num_elements, &num_allocated) == -1)
return 1; // we'll want to bail out of the program.
}
for (i = 0; i < 4; i++)
{
printf("(x:%f,y:%f)\n", the_array[i].x, the_array[i].y);
}
printf("\n%d allocated, %d used\n", num_allocated, num_elements);
// Deallocate!
free(the_array);
// All done.
return 0;
}
In your code, you need to change
*num_elements++;
to
(*num_elements)++;
because, without the explicit parenthesis, the ++ is having higher precedence over * operator. What you want is to increment the value stored in the address, not the other way around.
Check about operator precedence here.
try
(*num_elements)++;
in function
AddToArray()
The bug in your code is that the pointer is incremented, not the value.
..
(*the_array)[*num_elements] = item;
(*num_elements)++;
..
Is this some kind of programming assignment? The code can be improved in many areas here.
There are lots of good algorithms for this kind of problem that are well written and optimized, I suggest that you some research in that area.