making malloc function doesn't work correctly? - c

Hi i wrote a simple malloc and free function. The idea is basically from "The C Programming language". Now i have the problem like if I malloc 3 times and i free it in the same order. My allocp is near to the end but the whole memory is free. I give you my example now. My problem is with the 3 frees. Like when i free 4, 3, 2 its ok because you free the allocp down in the correct order, but i don't know how to solve my problem.
Any ideas?
Thanks for your help :)
#include "mystdlib.h"
#include <stdio.h>
#define ALLOCSIZE 1048576 /* Groeße des verfuegbaren Speichers */
static char allocbuf[ALLOCSIZE]; /* Speicher fuer mymalloc */
static char *allocp = allocbuf; /* next free position */
void *mymalloc(size_t size) {
if(allocp + size <= allocbuf + ALLOCSIZE) {
allocp += size;
return (allocp - size);
}
else {
return NULL;
}
}
void myfree(void *ptr) {
if(ptr >= (void*)allocbuf && ptr < (void*)allocbuf + ALLOCSIZE) {
allocp = ptr;
}
}
int main(){
char *buf1 = mymalloc(900000);
if (buf1 == NULL) {
printf("Error 404, allocated memory not found...\n");
return -1;
}
myfree(buf1);
char *buf2 = mymalloc(500000);
char *buf3 = mymalloc(400000);
char *buf4 = mymalloc(300000);
if (buf2 == NULL || buf3 == NULL) {
printf("Fix your mymalloc!\n");
return -1;
}
if (buf4 == NULL) {
printf("This was supposed to happen. Very good!\n");
} else {
printf("Nope. That's wrong. Very wrong.\n");
return -1;
}
myfree(buf2);
myfree(buf3);
myfree(buf4);
char *buf5 = mymalloc(900000);
if (buf5 == NULL) {
printf("You got so far, but your error free journey ends here. Because an error occured.\n");
return -1;
}
char *buf6 = myrealloc(buf5, 500000);
myfree(buf6);
printf("Congrats, you passed all tests. Your malloc and free seem to work\n");
}

Related

Will memory not freed cause segmentation fault in C?

I've just encountered a very strange bug. I was doing unit-test for a simple function as below.
UPDATE: Thanks #Bodo, here's the minimal working example. You can simply compile and run tokenizer.c.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
/* ============================= BOOL =============================== */
#ifndef _BOOL_
#define _BOOL_
typedef enum {
true, false
} bool;
#endif // _BOOL_
/* ============================= STACK =============================== */
#ifndef _STACK_
#define _STACK_
typedef void (*stack_freefn)(void *elemAddr);
typedef struct {
size_t size; // number of element allowed
int ite; // point to the current last element
size_t elemSize; // size of each element (how many bytes)
void *elems; // stockage of elements
stack_freefn freefn; // free memory allocated for each element if necessary
} stack;
/* constructor */
void new_stack(stack *s, const size_t size, const size_t elemSize, stack_freefn freefn) {
s->size = size;
s->ite = 0;
s->elemSize = elemSize;
s->elems = malloc(size * elemSize);
s->freefn = freefn;
}
/* free memory */
void dispose_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
}
free(s->elems);
s->elems = NULL;
}
/* push one new element on the top */
void push_stack(stack *s, const void *value, const size_t elemSize) {
if (s->ite == s->size) {
s->size *= 2;
s->elems = realloc(s->elems, s->size * s->elemSize);
}
void *elemAddr = (char *)s->elems + s->elemSize * s->ite++;
memcpy(elemAddr, value, s->elemSize);
}
/* pop our the element on the top */
void pop_stack(stack *s, void *res) {
if (s->ite > 0) {
void *elemAddr = (char *)s->elems + ((s->ite - 1) * s->elemSize);
memcpy(res, elemAddr, s->elemSize);
s->ite--;
}
}
void clear_stack(stack *s) {
if (s->freefn != NULL) {
while (s->ite > 0) {
void *elemAddr = (char *)s->elems + --s->ite * s->elemSize;
s->freefn(elemAddr);
}
} else {
s->ite = 0;
}
}
size_t stack_size(stack *s) {
return s->ite;
}
#endif // _STACK_
/* ============================= VECTOR =============================== */
#ifndef _VECTOR_
#define _VECTOR_
typedef int (*VectorCompareFunction)(const void *elemAddr1, const void *elemAddr2);
typedef void (*VectorFreeFunction)(void *elemAddr);
typedef struct {
int elemSize; //how many byte for each element
int elemNum; //number of current element in vector
int capacity; //maximum number of element vector can hold
void *elems; //pointer to data memory
VectorFreeFunction freefn; //pointer to the function used to free each element
} vector;
/**
* Reallocate a new memory of twice of original size
* return 1 if reallocation success, otherwise return -1.
*/
static void DoubleMemory(vector *v) {
void *tmp = realloc(v->elems, v->capacity * v->elemSize * 2);
assert(tmp != NULL);
v->elems = tmp;
v->capacity *= 2;
}
/**
* Constructor
*/
void VectorNew(vector *v, int elemSize, VectorFreeFunction freefn, int initialAllocation) {
v->elems = malloc(initialAllocation * elemSize);
assert(v->elems != NULL);
v->elemSize = elemSize;
v->elemNum = 0;
v->capacity = initialAllocation;
v->freefn = freefn;
}
/**
* Frees up all the memory of the specified vector.
*/
void VectorDispose(vector *v) {
if (v->freefn != NULL) {
for (; v->elemNum > 0; v->elemNum--) {
void *elemAddr = (char *)v->elems + (v->elemNum - 1) * v->elemSize;
v->freefn(elemAddr);
}
}
free(v->elems);
v->elems = NULL;
}
/**
* Returns the logical length of the vector.
*/
int VectorLength(const vector *v) {
return v->elemNum;
}
/**
* Appends a new element to the end of the specified vector.
*/
void VectorAppend(vector *v, const void *elemAddr) {
/* double size if neccessary */
if (v->elemNum == v->capacity) DoubleMemory(v);
memcpy((char *)v->elems + v->elemNum * v->elemSize, elemAddr, v->elemSize);
v->elemNum++;
}
/**
* Search the specified vector for an element whose contents match the element passed as the key.
*/
int VectorSearch(const vector *v, const void *key, VectorCompareFunction searchfn, int startIndex, bool isSorted) {
assert(key && searchfn);
if (v->elemNum == 0) return -1;
assert(startIndex >= 0 && startIndex < v->elemNum);
if (isSorted == true) {
/* binary search */
void *startAddr = (char *)v->elems + startIndex * v->elemSize;
int size = v->elemNum - startIndex;
void *resAddr = bsearch(key, startAddr, size, v->elemSize, searchfn);
return (resAddr != NULL)? ((char *)resAddr - (char *)v->elems) / v->elemSize : -1;
} else {
/* linear search */
for (int i = 0; i < v->elemNum; i++) {
if (searchfn((char *)v->elems + i * v->elemSize, key) == 0) {
return i;
}
}
return -1;
}
}
#endif // _VECTOR_
/* ============================= TOKENIZER =============================== */
/**
* Dump current string into vector as a new word.
* Strings are null-terminated.
*/
static void dumpstack(stack *s, vector *v) {
size_t len = stack_size(s);
char *word = (char *)malloc((len + 1) * sizeof(char)); // +1 for null-terminator
for (int i = len - 1; i >= 0; i--) {
pop_stack(s, word + i * sizeof(char));
}
word[len] = '\0';
VectorAppend(v, &word);
clear_stack(s);
}
static const size_t kTokenStackDefaultSize = 64;
static void tokenize(vector *words, char *stream) {
stack s;
new_stack(&s, kTokenStackDefaultSize, sizeof(char), NULL);
size_t len = strlen(stream);
bool begin = false;
char c;
for (int i = 0; i < len; i++) {
c = stream[i];
/* =============================== My printf() is here ============================== */
// printf("char c = [%c]\n", c);
/* =============================== My printf() is here ============================== */
if (isalpha(c) || isdigit(c)) {
if (begin == false) begin = true;
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
} else if (c == '-') {
if (begin == true) { // case: covid-19
push_stack(&s, &c, sizeof(char));
} else {
if (i < len - 1 && isdigit(stream[i + 1])) { // case: -9
begin = true;
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
} else if (c == '.' && begin == true) { // case: 3.14
if (isdigit(stream[i - 1])) {
push_stack(&s, &c, sizeof(char));
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
} else {
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
}
}
if (begin == true) {
dumpstack(&s, words);
begin = false;
}
dispose_stack(&s);
}
/* ============================= UNIT-TEST =============================== */
/**
* HashSetFreeFunction<char *>
*/
static void freestr(void *elemAddr) {
char *str = *(char **)elemAddr;
free(str);
}
/**
* HashSetCompareFunction<char *>
*/
static int compstr(const void *elemAddr1, const void *elemAddr2) {
char *str1 = *(char **)elemAddr1;
char *str2 = *(char **)elemAddr2;
return strcmp(str1, str2);
}
static void test_tokenize(void) {
printf("Testing Tokenizer.c::tokenize() ...\n");
char *sentence = "Covid-19: Top adviser warns France at 'emergency' virus moment - BBC News\nPi = 3.14\n-1 is negative.";
vector words;
VectorNew(&words, sizeof(char *), freestr, 256);
tokenize(&words, sentence);
char *musthave[] = {"covid-19", "top", "3.14", "-1"};
char *musthavenot[] = {"-", "1"};
assert(VectorLength(&words) == 16);
for (int i = 0; i < sizeof(musthave)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthave[i], compstr, 0, false) != -1);
}
for (int i = 0; i < sizeof(musthavenot)/sizeof(char *); i++) {
assert(VectorSearch(&words, &musthavenot[i], compstr, 0, false) == -1);
}
VectorDispose(&words);
printf("[ALL PASS]\n");
}
int main(void) {
test_tokenize();
}
I've got segmentation fault at first.
[1] 4685 segmentation fault testtokenizer
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
Just recall that before this test, I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault? Thx bros.
UPDATE:
Now I can't even reproduce this bug myself. tokenizer.c above can pass the unit-test. I thought it might caused by makefile prerequisite rules. gcc didn't re-compile some object files when source code is changed.
Thanks #Steve Summit, you make it clear that unfreed memory will not cause segmentation fault.
Thanks #schwern for code review, it's really helpful.
But when I add a printf() to debug, the segmentation fault was gone and the test passed. After comment out the printf, the function still works. I was so confused.
They call it undefined behavior, because its behavior is undefined. Seemingly unrelated operations might nudge things just a bit to make the code "work" but they're only tangentially related to the problem.
I tested some memory dispose function, and perhaps had left some unfreed blocks in memory. Will that be the reason for fleeting segmentation fault?
No. It does mean the memory is unreferencable and "leaked". The memory will be freed to the operating system when the process exits.
The problem must lie elsewhere. Without seeing your whole program we can't say for sure, but two fishy things stand out.
You're defining a fixed sized stack, but you're pushing onto it an indeterminate number of times. Unless push_stack has protection against this, you will walk off your allocated memory.
You're storing references to variables on the stack. lower, c
char lower = tolower(c);
push_stack(&s, &lower, sizeof(char));
Once lower goes out of scope it will automatically be freed and the memory reused. &lower is invalid once tokenize returns. This seems to be fine if your stack only lasts the length of the function, but it's worth noting.
And it's possible new_stack, push_stack, or dumpstack are doing something incorrect.

custom malloc, segmentation fault

I'm doing a custom malloc. I did a very simple one but now I'm trying to merge and split blocks in order to improve the efficiency of calls to sbrk(). when I try to execute a custom program with not many mallocs it works perfectly. But as soon as I try more mallocs or for example the command ls after some successful allocations, it ends giving a weird segmentation fault (core dumped) when calling the split function.
Any help or hint would be greatly appreciated.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <pthread.h>
#include "struct.h"
static p_meta_data first_element = NULL;
static p_meta_data last_element = NULL;
static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
#define ALIGN8(x) (((((x)-1)>>3)<<3)+8)
#define MAGIC 0x87654321
void *malloc(size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes);
p_meta_data request_space(size_t size_bytes);
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2);
void split(p_meta_data meta_data, size_t size_bytes);
void free(void *ptr);
void *calloc(size_t num_bytes, size_t num_blocs);
void *realloc(void *ptr, size_t size_bytes);
p_meta_data search_available_space(size_t size_bytes) {
p_meta_data current = first_element;
while (current && !(current->available && current->size_bytes >= size_bytes)){
fprintf(stderr, " %zu libre %d\n", current->size_bytes, current->available);
current = current->next;
}
if (current == NULL) {
fprintf(stderr, "null\n" );
} else {
fprintf(stderr, "%zu libre %d\n", current->size_bytes, current->available);
}
return current;
}
p_meta_data request_space(size_t size_bytes) {
if (size_bytes < 122880) {
size_bytes = 122880;
fprintf(stderr, "request %zu\n", size_bytes);
}
p_meta_data meta_data;
meta_data = (void *)sbrk(0);
if (sbrk(SIZE_META_DATA + size_bytes) == (void *)-1)
return (NULL);
meta_data->size_bytes = size_bytes;
meta_data->available = 0;
meta_data->magic = MAGIC;
meta_data->next = NULL;
meta_data->previous = NULL;
return meta_data;
}
p_meta_data merge(p_meta_data meta_data1, p_meta_data meta_data2) {
if (!meta_data1 || !meta_data2) {
return NULL;
}
meta_data1->size_bytes = meta_data1->size_bytes + SIZE_META_DATA + meta_data2->size_bytes;
meta_data1->next = meta_data2->next;
if (last_element == meta_data2) {
fprintf(stderr, "gleich\n");
last_element = meta_data1;
}
meta_data2 = NULL;
return meta_data1;
}
void free(void *ptr) {
p_meta_data meta_data;
if (!ptr)
return;
pthread_mutex_lock(&mutex);
meta_data = (p_meta_data)(ptr - SIZE_META_DATA);
if (meta_data->magic != MAGIC) {
fprintf(stderr, "ERROR free: value of magic not valid\n");
exit(1);
}
meta_data->available = 1;
fprintf(stderr, "Free at %x: %zu bytes\n", meta_data, meta_data->size_bytes);
p_meta_data meta_data_prev, meta_data_next;
meta_data_prev = meta_data->previous;
meta_data_next = meta_data->next;
if (meta_data_prev && meta_data_prev->available) {
meta_data = merge(meta_data_prev, meta_data);
}
if (meta_data_next && meta_data_next->available) {
meta_data = merge(meta_data, meta_data_next);
}
pthread_mutex_unlock(&mutex);
}
void split(p_meta_data meta_data, size_t size_bytes) {
if (!meta_data) {
fprintf(stderr, "no deberia entrar\n");
return;
}
p_meta_data meta_data2;
size_t offset = SIZE_META_DATA + size_bytes;
meta_data2 = (p_meta_data)(meta_data + offset);
fprintf(stderr, "size of metadata %d", meta_data->size_bytes - size_bytes - SIZE_META_DATA);
meta_data2->size_bytes = meta_data->size_bytes - size_bytes - SIZE_META_DATA;
meta_data2->available = 1;
meta_data2->magic = MAGIC;
meta_data2->previous = meta_data;
meta_data2->next = meta_data->next;
if (meta_data == last_element) {
last_element = meta_data2;
}
meta_data->size_bytes = size_bytes;
meta_data->next = meta_data2;
return;
}
void *malloc(size_t size_bytes) {
void *p, *ptr;
p_meta_data meta_data;
if (size_bytes <= 0) {
return NULL;
}
size_bytes = ALIGN8(size_bytes);
fprintf(stderr, "Malloc %zu bytes\n", size_bytes);
// Bloquegem perque nomes hi pugui entrar un fil
pthread_mutex_lock(&mutex);
meta_data = search_available_space(size_bytes);
if (meta_data) { // free block found
fprintf(stderr, "FREE BLOCK FOUND---------------------------------------------------\n");
meta_data->available = 0; //reservamos el bloque
} else { // no free block found
meta_data = request_space(size_bytes); //pedimos más espacio del sistema
if (!meta_data) //si meta_data es NULL (es decir, sbrk ha fallado)
return (NULL);
if (last_element) // we add the new block after the last element of the list
last_element->next = meta_data;
meta_data->previous = last_element;
last_element = meta_data;
if (first_element == NULL) // Is this the first element ?
first_element = meta_data;
}
fprintf(stderr, "die differenz %zu\n", meta_data->size_bytes - size_bytes);
if ((meta_data->size_bytes - size_bytes) > 12288) {
split(meta_data, size_bytes);
fprintf(stderr,"call split\n");
}
p = (void *)meta_data;
// Desbloquegem aqui perque altres fils puguin entrar
// a la funcio
pthread_mutex_unlock(&mutex);
// Retornem a l'usuari l'espai que podra fer servir.
ptr = p + SIZE_META_DATA; //p es puntero al inicio de meta_data, y ptr es el puntero al inicio del bloque de datos en sí (justo después de los metadatos)
return ptr;
}
void *calloc(size_t num_bytes, size_t num_blocs) {
size_t mem_to_get = num_bytes * num_blocs;
void *ptr = malloc(mem_to_get);
if (ptr == NULL) {
return ptr;
} else {
memset(ptr, 0, mem_to_get);
return ptr;
}
}
void *realloc(void *ptr, size_t size_bytes) {
fprintf(stderr, "realloc\n");
if (ptr == NULL) {
return malloc(size_bytes);
} else {
p_meta_data inic_bloc = (p_meta_data )(ptr - SIZE_META_DATA);
if (inic_bloc->size_bytes >= size_bytes) {
return ptr;
} else {
void *new_p = malloc(size_bytes);
memcpy(new_p, ptr, inic_bloc->size_bytes);
inic_bloc->available = 1;
return new_p;
}
}
}
where struct.h is:
#include <stddef.h>
#include <unistd.h>
#define SIZE_META_DATA sizeof(struct m_meta_data)
typedef struct m_meta_data *p_meta_data;
/* This structure has a size multiple of 8 */
struct m_meta_data {
size_t size_bytes;
int available;
int magic;
p_meta_data next;
p_meta_data previous;
};
Here are some remarks about your code:
it is confusing for the reader to hide pointers behind typedefs. Why not define m_meta_data as a typedef for struct m_meta_data and use m_meta_data * everywhere?
are you sure sbrk() is properly defined? The cast (void *)sbrk(0) seems to indicate otherwise. sbrk() is declared in <unistd.h> on POSIX systems.
BUG in split(), the computation meta_data2 = (p_meta_data)(meta_data + offset); is incorrect. It should be:
meta_data2 = (p_meta_data)((unsigned char *)meta_data + offset);
you should define strdup() and strndup() as the definitions from the C library might not call your redefined malloc():
char *strdup(const char *s) {
size_t len = strlen(s);
char *p = malloc(len + 1);
if (p) {
memcpy(p, s, len + 1);
}
return p;
}
char *strndup(const char *s, size_t n) {
size_t len;
char *p;
for (len = 0; len < n && s[n]; len++)
continue;
if ((p = malloc(len + 1)) != NULL) {
memcpy(p, s, len);
p[len] = '\0';
}
return p;
}
blocks allocated with malloc() should be aligned on 16-byte boundaries on 64-bit intel systems. As a matter of fact, the m_meta_data structure has a size of 32 bytes on 64-bit systems, but 20 bytes on 32-bit systems. You should adjust your m_meta_data structure for 32-bit systems.
you should check for overflow in size_t mem_to_get = num_bytes * num_blocs;
you should not rely on void * arithmetics, it is a gcc extension. Write this instead:
p_meta_data inic_bloc = (p_meta_data)ptr - 1;
in realloc(), when extending the size of the block, you just make the original block available but you do not coalesce it with adjacent blocks as you do in free(). You might just call free(ptr), especially since modifying inic_bloc->available = 1; without getting the lock seems risky.
you should check meta_data->available in free() and realloc() to detect invalid calls and prevent arena corruption.
in malloc(), you forget to release the lock in case of allocation failure.
calling fprintf while the lock is set is risky: if fprintf calls malloc, you would get a deadlock. You might assume that printing to stderr does not call malloc() because stderr is unbuffered, but you are taking chances.
when allocating a new block with sbrk(), you should use sbrk(0) after the allocation to determine the actual size made available as it may have been rounded up to a multiple of PAGE_SIZE.
you should split blocks if (meta_data->size_bytes - size_bytes) > SIZE_META_DATA. The current test is far too loose.

finding dynamic memory allocation errors in arrays of strings and structures

So, I have an array of a structures called vitorias (in English, "victories"), an array of structures and an array of that structure and an array of strings.
Structure and arrays:
char **sistema_eq;
typedef struct
{
int id;
char nome[MAX_CHARS];
int vit;
} vitorias;
The problem is that when I use cppcheck it gives an error saying:
(error) Common realloc mistake: 'conj_vit' nulled but not freed upon failure
(error) Common realloc mistake: 'sistema_eq' nulled but not freed upon failure
(error) Common realloc mistake: 'conj_jogos' nulled but not freed upon failure
And, if I use Valgrind, it says that I did 10 allocs and 2 free, but I don't understand what's wrong, because I freed everything in the end I think.
Program:
#include<stdlib.h>
#include<stdio.h>
#include <string.h>
#define MAX_CHARS 1024 /* max characters of a word */
#define MAX_SIZE 5
static int size_until = 0; /*conts the size of sistema_eq and conj_vit*/
static int line = 1; /* counts the number of lines of the stdin */
int ident = 0; /*conts the id of jogos*/
static int size_until = 0; /*counts the size of sistema_eq*/
static int size_until2 = 0;/*counts the size of conj_jogos*/
void a(char nome_jg[],char team1[],char team2[],int score1,int score2);
void A(char nome[]);
char **sistema_eq;
jogo *conj_jogos;
vitorias *conj_vit;
int main()
{
char c;
char nome_jg[MAX_CHARS], team1[MAX_CHARS], team2[MAX_CHARS];
int score1;
int score2;
int i;
conj_jogos = (jogo*)calloc(MAX_SIZE,sizeof(jogo));
memset(conj_jogos,0, MAX_SIZE*sizeof(jogo));
conj_vit = (vitorias*)calloc(MAX_SIZE,sizeof(vitorias));
memset(conj_vit,0, MAX_SIZE*sizeof(vitorias));
sistema_eq = (char**)calloc(MAX_SIZE,sizeof(*sistema_eq));
memset(sistema_eq,0, MAX_SIZE*sizeof(*sistema_eq));
for(i=0;i<MAX_SIZE;i++)
{
sistema_eq[i] = (char*)calloc(1024,sizeof(char));
memset(sistema_eq[i],0, sizeof(char)*1024);
}
while ((c = getchar())!= 'x') {
switch (c)
{
case 'A':
{
scanf("%1023[^:\n]",nome_jg);
remove_esp(nome_jg);
A(nome_jg);
break;
}
case 'a':
{
scanf("%1023[^:\n]:%1023[^:\n]:%1023[^:\n]:%d:%d",nome_jg,team1,team2,&score1,&score2);
remove_esp(nome_jg);
a(nome_jg,team1,team2,score1,score2);
line++;
break;
}
}
}
free(conj_vit);
free(conj_jogos);
free(sistema_eq);
return 0;
}
/*This functions adds a victory and a equipa (team in english) into the corresponding arrays and updates the vitories of each team*/
//Example in El Classico Barcelona vs Real Madrid 1:0, which means Barcelona won
void A(char nome[])
{
if (nome_in_sis(nome) == 1)
{
printf("%d Equipa existente.\n",line);
line++;
}
else
{
if (size_until < MAX_SIZE)
{
strcpy(sistema_eq[size_until],nome);
strcpy(conj_vit[size_until].nome,nome);
conj_vit[size_until].id = size_until;
size_until++;
line++;
}
else
{
conj_vit = realloc(conj_vit,sizeof(vitorias)*(size_until+1));
sistema_eq = realloc(sistema_eq,sizeof(char*)*(size_until+1));
sistema_eq[size_until] = calloc(1024,sizeof(char*));
strcpy(sistema_eq[size_until],nome);
strcpy(conj_vit[size_until].nome,nome);
conj_vit[size_until].id = size_until;
size_until++;
line++;
}
}
}
/*This functions adds a jogo (game in english) and a equipa (team in english) into the array conj_jogos (the array of jogos)*/
void a(char nome_jg[],char team1[],char team2[],int score1,int score2)
{
int vit;
if (jogo_in(nome_jg) == 1)
{
printf("%d Jogo existente.\n",line);
line++;
}
else if ((nome_in_sis(team1) == 0) || (nome_in_sis(team2) == 0))
{
printf("%d Equipa inexistente.\n",line);
line++;
}
else
{
if (size_until2 < MAX_SIZE)
{
conj_jogos[size_until2] = cria_jogo(nome_jg,team1,team2,score1,score2);
if (score1 > score2)
{
vit = procura_vit(team1);
conj_vit[vit].vit++;
}
else
{
vit = procura_vit(team2);
conj_vit[vit].vit++;
}
size_until2++;
}
else
{
size_until2++;
conj_jogos = realloc(conj_jogos,sizeof(jogo)*(size_until2+1));
conj_jogos[size_until2] = cria_jogo(nome_jg,team1,team2,score1,score2);
if (score1 > score2)
{
vit = procura_vit(team1);
conj_vit[vit].vit++;
}
else
{
vit = procura_vit(team2);
conj_vit[vit].vit++;
}
size_until2++;
}
}
}
Sorry if the code looks messy and thanks for the help.
As pointed out in the comments, you never free the data you allocated with calloc in the for loop. Add this loop (or something very similar) near the end of your main:
//...
for(i=0;i<MAX_SIZE;i++) free(sistema_eq[i]); // MUST be before the next line!
free(sistema_eq);
//...
Also, as you use calloc, you don't need any of the memset calls! From the linked documentation for void* calloc( size_t num, size_t size ):
Allocates memory for an array of num objects of size and initializes
all bytes in the allocated storage to zero.
For the 'errors' reported concerning realloc: in cases where a call to realloc fails, the code you use will prevent subsequent freeing of the original data (the address of which was in the pointer), as its address will be replaced with NULL on such a failure! To prevent this, use a temporary pointer, like this:
jogo* temp_jogo = realloc(conj_jogos,sizeof(jogo)*(size_until2+1));
if (temp_jogo != NULL) conj_jogos = temp_jogo;
else {
// In case of failure, we now still have the original conj_jogos
// pointer, which we can then pass to "free" at some point, presumably
// after we've signalled and/or handled the allocation error.
}
Finally (I think), you may like to read this: Do I cast the result of malloc? - which is equally valid for calls to calloc and realloc.

Deallocated memory functions C

I have problems with memory deallocation in C. Without the division of functions everything is OK, but unfortunately it does not work on the same functions. Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
typedef struct {
char *name;
enum {
summer,
winter
} index;
} student;
bool init(student *s) {
printf("The next stage in the allocation of memory\n");
s = (student*)malloc(sizeof(*s));
if (&s == NULL) {
printf("Allocation Failed\n");
return 0;
} else {
printf("Allocation completed successfully\n");
}
}
void delete(student *s) {
if (&s != NULL) {
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
int main() {
student *s;
init(s);
delete(s);
return 0;
}
I do not know what I'm doing wrong. Please help.
First of all the function init has undefined bbehaviour because it returns nothing in the case of successful memory allocation.
You can check whether the memory was allocated or not by returning pointer to the allocated memory or NULL.
Also this statement
if(&s==NULL){
is wrong. The condition will always yield false because the address of the local variable s is not equal to NULL.
So the function can be rewritten either the following way
student * init()
{
printf("The next stage in the allocation of memory\n");
student *s = ( student* )malloc( sizeof( *s ) );
if ( s == NULL )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return s;
}
And called like
int main( void )
^^^^^
{
student *s = init();
//...
Or it can be defined the following way
int init( student **s )
{
printf("The next stage in the allocation of memory\n");
*s = ( student* )malloc( sizeof( **s ) );
int success = *s != NULL;
if ( !success )
{
printf("Allocation Failed\n");
}
else
{
printf("Allocation completed successfully\n");
}
return success;
}
and called like
int main( void )
^^^^^
{
student *s;
init( &s );
//...
The function delete should be defined at least like
void delete(student *s) {
if (s != NULL) {
^^^
printf("begin removal\n");
free(s);
printf("Released memory");
}
}
Firstly, free is NULL safe. If variable is NULL already, basically nothing happens. You do not have to check if it is NULL. (you can check page 313 ISO-IEC 9899 )
Also, when you initialize student->name and allocate, there will be memory leak. You have to free that too.
So, your delete function could be like this ;
void delete(student *s) {
printf("begin removal\n");
free(s->name);
free(s);
printf("Released memory");
}
And if (&s == NULL) is wrong. They must be changed with if (s == NULL).
Your allocation may cause really big troubles in the big codes. If you allocate s = (student*)malloc(sizeof(*s)); it means that "allocate s with size of *s". But pointer size is fixed memory block (mostly 8 bytes). It means that you blocks certain size of memory. If you have bigger struct than that, this kind of allocation will corrupt the memory and your executable will be killed by the OS(you can try add some more variables to your struct and initialize them). In small structs and very short runtimes, mostly this allocation works too. But i guarantee that this is not safe for run-time. And it will not give any warnings or errors in compile-time. True way is s = malloc(sizeof(student)). With this way, you exactly allocates all the memory blocks. And your memory stay safe in run-time.
Lastly, your init function should return the initialized variable. And your init function could be like this ;
#define NAME_LENGHT 128
...
student * init(student *s) {
printf("The next stage in the allocation of memory\n");
s = malloc(sizeof(student));
if (s == NULL) {
printf("Allocation Failed\n");
return NULL;
}
s->name = malloc(NAME_LENGHT);
if (s->name == NULL) {
printf("Allocation Failed\n");
return NULL;
} else {
printf("Allocation completed successfully\n");
}
//alternatively you can strdup directly without any allocation
// s->name = strdup("some name");
return s;
}
You never change the s in main.
Solution 1: Pass a pointer to the variable in main for init to populate.
void init_student(student** s_ptr) {
*s_ptr = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name = malloc(MAX_NAME_SIZE + 1);
if ((*s_ptr)->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
(*s_ptr)->name[0] = 0;
(*s_ptr)->gpa = 0;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 1b: Same, but a cleaner implementation.
void init_student(student** s_ptr) {
student* s = (student*)malloc(sizeof(student));
if (*s_ptr == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
*s_ptr = s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s;
init_student(&s);
delete_student(s);
return 0;
}
Solution 2: Return the allocated point to main.
student* init_student() {
student* s = (student*)malloc(sizeof(student));
if (s == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name = malloc(MAX_NAME_SIZE + 1);
if (s->name == NULL) {
fprintf(stderr "panic: Allocation Failed\n");
exit(1);
}
s->name[0] = 0;
s->gpa = 0;
return s;
}
void delete_student(student* s) {
free(s->name);
free(s);
}
int main() {
student* s = init_student();
delete_student(s);
return 0;
}
Note that &s == NULL will never be true because &s is the address of the variable itself. You want just s == NULL to check the value of the pointer in s.

C Linked List Memory Usage (Possible memory leak)

I'm having trouble with what should be a simple program.
I've written a single linked list implementation in C using void* pointers. However, I have a problem, as there is a possible memory leak somewhere, however I checked the code using valgrind and it detected no such errors.
But when all the memory is free'd there is still some memory un-freed (see comments)... I tried passing everything to the add function by reference too, but this didn't fix the issue either.
I just wondered if anyone here had any comments from looking at the code. (This should be simple!, right?)
/*
Wrapping up singley linked list inside a struct
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h> /* Needed for: memcpy */
void waitKey(){
printf("Press any key to continue...");
getchar();
}
/* Define a structure for a list entry */
struct ListEntry {
void* data;
struct ListEntry* pNext;
};
/* Struct for list properties */
struct ListProperties {
struct ListEntry* g_pLast;
struct ListEntry* g_pHead;
struct ListEntry* pCurrent;
unsigned int size;
int getHead;
};
/* Add:
args: list, data, dyn (0 if not, else size of dynamic data)
*/
void add(struct ListProperties* l, void* d, unsigned long dyn) {
struct ListEntry* pNew = malloc(sizeof(struct ListEntry));
/* Set the data */
if (dyn > 0){
/* Allocate and copy array */
pNew->data = malloc(dyn);
pNew->data = memcpy(pNew->data,d,dyn);
} else {
pNew->data = d;
}
/* Set last element to point to new element */
if (l->g_pLast != NULL){
l->g_pLast->pNext = pNew;
/* Get head of list */
if (l->g_pHead == NULL && l->getHead == 0){
l->g_pHead = l->g_pLast;
l->getHead = 1;
}
} else {
/* 1 elem case */
l->g_pHead = pNew;
l->pCurrent = pNew;
}
/* New element points to NULL */
pNew->pNext = NULL;
/* Save last element for setting
pointer to next element */
l->g_pLast = pNew;
/* Inc size */
l->size++;
}
/* Create new list and return a pointer to it */
struct ListProperties* newList(){
struct ListProperties* nList = malloc (sizeof(struct ListProperties));
nList->g_pHead = NULL;
nList->g_pLast = NULL;
nList->getHead = 0;
nList->size = 0;
return nList;
}
/* Reset pointer */
int reset(struct ListProperties *l){
if (l->g_pHead != NULL){
l->pCurrent = l->g_pHead;
return 0;
}
return -1;
}
/* Get element at pointer */
void* get(struct ListProperties *l) {
if (l->size > 0){
if (l->pCurrent != NULL){
return l->pCurrent->data;
}
}
return NULL;
}
/* Increment pointer */
int next(struct ListProperties *l){
if (l->pCurrent->pNext != NULL){
l->pCurrent = l->pCurrent->pNext;
return 1;
}
return 0;
}
/* Get element at n */
void* getatn(struct ListProperties *l, int n) {
if (l->size > 0){
int count = 0;
reset(l);
while (count <= n){
if (count == n){
return l->pCurrent->data;
break;
}
next(l);
count++;
}
}
return NULL;
}
/* Free list contents */
void freeList(struct ListProperties *l){
struct ListEntry* tmp;
/* Reset pointer */
if (l->size > 0){
if (reset(l) == 0){
/* Free list if elements remain */
while (l->pCurrent != NULL){
if (l->pCurrent->data != NULL)
free(l->pCurrent->data);
tmp = l->pCurrent->pNext;
free(l->pCurrent);
l->pCurrent = tmp;
}
}
}
l->g_pHead = NULL;
l->g_pLast = NULL;
l->size = 0;
l->getHead = 0;
free(l);
}
void deleteElem(struct ListProperties *l, int index){
struct ListEntry* tmp;
int count = 0;
if (index != 0)
index--;
reset(l);
while (count <= index){
if (count == index){ // Prev element
if (l->pCurrent != NULL){
if (l->pCurrent->pNext != NULL){
free(l->pCurrent->pNext->data); // Free payload
tmp = l->pCurrent->pNext;
l->pCurrent->pNext = l->pCurrent->pNext->pNext;
free(tmp);
if (l->size > 0)
l->size--;
} else {
// Last element
free(l->pCurrent->data);
free(l->pCurrent);
l->g_pHead = NULL;
l->g_pLast = NULL;
l->getHead = 0;
l->size = 0;
}
}
break;
}
if (next(l) != 1)
break;
count++;
}
}
int size(struct ListProperties *l){
return l->size;
}
int main( int argc, char* argv )
{
int j = 0;
unsigned long sz = 0;
/*=====| Test 1: Dynamic strings |=====*/
/* Create new list */
struct ListProperties* list = newList();
if (list == NULL)
return 1;
char *str;
str = malloc(2);
str = strncat(str,"A",1);
sz = 2;
printf("Dynamic Strings\n===============\n");
/* Check memory usage here (pre-allocation) */
waitKey();
/* Add to list */
for (j = 0; j < 10000; j++){
add(list,(char*)str, sz);
str = realloc(str, sz+2);
if (str != NULL){
str = strncat(str,"a",1);
sz++;
}
}
/* Allocated strings */
waitKey();
/* TESTING */
freeList(list);
free(str);
/* Check memory usage here (Not original size!?) */
waitKey();
return 0;
}
Thanks!
You don't say how you are checking memory usage, but I'm going to guess that you are using ps or something similar to see how much memory the OS has given the process.
Depending on your memory allocator, calling free may or may not return the memory to the OS. So even though you are calling free, you will not see the memory footprint decrease from the OS's point of view.
The allocator may keep a cache of memory that is given to it by the OS. A call to malloc will first look in this cache to see if it can find a big enough block and if so, malloc can return without asking the OS for more memory. If it can't find a big enough block, malloc will ask the OS for more memory and add it to it's cache.
But free may simply add the memory back to the cache and never return it to the OS.
So, what you may be doing is seeing the allocators cache and not any memory leak.
As was mentioned, I would not trust the memory usage reported by the task manager as there are other factors beyond your control that impact it (how malloc/free are implemented, etc).
One way you can test for memory leaks is by writing your own wrapper functions around the existing malloc and free functions similar to:
void* my_malloc(size_t len) {
void* ptr = malloc(len);
printf("Allocated %u bytes at %p\n", len, ptr);
return ptr;
}
void my_free(void* ptr) {
printf("Freeing memory at %p\n", ptr);
free(ptr);
}
Now, you will get a log of all memory that is dynamically allocated or freed. From here, it should be fairly obvious if you leak a block of memory (the more complex your program is, the longer your log will be and the more difficult this task will be).
Your program contains incorrect argv in main, incorrect usage of strncat, and strange memory allocation. Some of these should of shown up as warnings. The argv is a non-issue, but if the others showed up as warning, you needed to heed them. Don't ignore warnings.
These changes clean it up. The biggest thing was that you don't seem to have a good grasp on the NUL ('\0') character (different than NULL pointer) used to terminate C strings, and how that effects str(n)cat.
The mixed usage of str* functions with memory functions (*alloc/free) was likely part of the confusion. Be careful.
#include <assert.h>
...
int main( int argc, char* argv[] ) /* or int main(void) */
...
sz = 2;
str = (char*) malloc(sz); /* allocate 2 bytes, shortest non-trivial C string */
assert(str != NULL);
strncpy(str, "A", sz); /* copy 'A' and '\0' into the memory that str points to */
...
/* Add to list */
for (j = 0; j < 10000; j++){
add(list, str, sz);
str = realloc(str, ++sz); /* realloc str to be one (1) byte larger */
assert(str != NULL);
strncat(str, "a", sz - strlen(str)); /* now insert an 'a' between last 'A' or 'a' and '\0' */
assert(str != NULL);
}

Resources