I am a beginner in C. Below is my scenario - I have created a pointer variable in main function and it has been passed on to several functions(in this example 2 levels). And one of the functions frees it up. Now I need to have check in Main to see whether the pointer is freed or not, that means i need to setup the value of &str in main() to point to NULL. Not sure my approach is right here. Any help would be much appreciated
void func2(char *str)
{
free(str);
}
void func1(char *str)
{
func2(str);
}
int main()
{
char *str;
str=(char *) malloc(10);
func1(str);
if(str){ do something; } // if condition to check whether str is freed
}
#include <stdio.h>
#include <stdlib.h>
func2(char **str)
{
free(*str); //free
*str = NULL; //Set to NULL
}
func1(char **str) //func1 receives as **
{
func2(str); //Pass pointer to func2()
}
int main()
{
char *str = NULL;
str=(char *) malloc(10);
func1(&str); //Pass Address of pointer to func1()
if(str) //Check for NULL
{
printf("\n Not - Freed...\n");
}
else
{
printf("\n Freed...\n");
}
return 0;
}
In C all are pass by value. I suggest to study http://www.cs.fsu.edu/~myers/cgs4406/notes/pointers.html for understanding of this.
You could try something like this - first redefine malloc and free (track.h)
#ifndef track_h
#define track_h
extern void* trackmalloc(size_t size);
extern void trackfree(void* array);
extern void trackismalloc(void* array);
#define malloc trackmalloc
#define free trackfree
#endif
Then for every piece of code that uses malloc and free, replace #include with #include "track.h"
#include <stdlib.h>
#include <stdio.h>
#include "track.h" /* was <malloc.h> */
// A function which has a 20% chance of freeing the pointer
void twentypercent(char* array)
{
if (rand() < (RAND_MAX / 5))
free(array);
}
int main(int argc, char* argv[])
{
char* list = malloc(256);
int ii;
for (ii = 0; ii < 10; ++ii)
twentypercent(list);
if (trackismalloc(list)
printf("Not freed yet");
return 0;
}
Now define track.c. This will only free memory that has been allocated by by trackmalloc. If it was not allocated by trackmalloc, then it will report that the memory has already been freed.
#include <stdio.h>
#include <malloc.h>
#define TRACKER_MAX 2048
static void* tracker[TRACKER_MAX] = { 0 };
static int track_last = -1;
void* trackmalloc(size_t size)
{
// For simplicity, tracker will not be reused
tracker[++track_last] = malloc(size);
return tracker[track_last];
}
void trackfree(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
free(tracker[tt]);
tracker[tt] = 0;
break;
}
}
if (tt == track_last)
printf("%p already freed\n", array);
}
int trackismalloc(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt, result = 0;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
result = 1;
break;
}
}
return result;
}
void func1(char** str) {
free(*str);
*str = NULL;
}
void func2(char** str) {
free(*str);
*str = NULL;
}
int main() {
char *str;
str = (char*) malloc(10);
func1(&str);
if (str) {
do something;
}
}
void func2(char **str)
{
free(*str);
*str = 0;
}
void func1(char **str)
{
func2(str);
}
int main()
{
char *str;
// I'd recommend using sizeof(type_you_want) * amount_of_elements instead of
// a constant number: -> malloc(sizeof(char) * 10);
str=(char *) malloc(10);
func1(&str); // You must pass the address of the pointer, because you want
// to change "WHAT IT POINTS TO", not "WHAT IS POINTED BY IT"
if(str){ do something; } // if condition to check whether str is freed
}
When you call a function in C, you pass a copy of those arguments, so you are passing a copy of that pointer (that copy still points to the same place, so you can change that place that it points to) but you want to change the pointer value, so you need to pass its address.
I have explained a little bit how pointers inside functions can be used in here
#include <stdio.h>
#include <stdlib.h>
void func2(char **str)
{
printf("%d %s\n",__LINE__,__func__);
free(*str);
*str = NULL;
}
void func1(char **str)
{
printf("%d %s\n",__LINE__,__func__);
func2(str);
}
char * allocaMem(char **ptr)
{
*ptr=(char *) malloc(sizeof(char)* 10);
if(!*ptr)
{
perror("");
}
else
{
return *ptr;
}
}
int main()
{
char *str = allocaMem(&str);
if (!str) {
printf("Error in malloc()\n");
return -1;
}
func1(&str);
if (str) {
printf("Memory Not freed\n");
} else {
printf("Memory freed\n");
}
}
Related
I am working on a basic framework to dynamically allocate array with the C language. I have created a function to create an array of strings titled init_string_vector. Data can be appended to the array with the append_string_vector function and data can be de-allocated from the heap with the free_string_array function. I am currently working on a function titled replace_string_vector_index that allows a user to pass an array index to the function as well as a pointer to the string array. If the array is typed as a STRING array and the index is not out of bounds, the function should replace the existing data with the string that a user passes to the function.
The replace_string_vector_index function appears to work properly and does replace the string at the index with the other string the user passed to the function. However, the free_string_array function no longer works once I have used to replace_string_vector_index function to act on the array. This makes me think that the process within the function is causing an issue, but I cannot see how. An example is shown below. When the free_string_array function fails, I get the following error, free(): invalid pointer.
vector.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
typedef enum
{
FLOAT,
DOUBLE,
CHAR,
INT,
STRING
} dat_type;
// --------------------------------------------------------------------------------
typedef struct
{
char **array;
size_t len;
int elem;
dat_type dat;
} StringVector;
// --------------------------------------------------------------------------------
int string_vector_mem_alloc(StringVector *array, size_t num_indices);
// --------------------------------------------------------------------------------
StringVector init_string_vector();
// --------------------------------------------------------------------------------
int append_string_vector(StringVector *s, char *value);
// --------------------------------------------------------------------------------
void free_string_array(StringVector *array);
// --------------------------------------------------------------------------------
int replace_string_vector_index(StringVector *array, int index, char string[]);
// --------------------------------------------------------------------------------
vector.c
#include "vector.h"
int string_vector_mem_alloc(StringVector *array, size_t num_indices) {
// Determine the total memory allocation and assign to pointer
void *pointer;
pointer = malloc(num_indices * array->elem);
// If memory is full fail gracefully
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
return 0;
}
// Allocate resources and instantiate Array
else {
array->array = pointer;
array->len = 0;
return 1;
}
}
// --------------------------------------------------------------------------------
StringVector init_string_vector() {
StringVector array;
array.dat = STRING;
array.elem = sizeof(char *);
string_vector_mem_alloc(&array, array.elem);
return array;
}
// --------------------------------------------------------------------------------
int append_string_vector(StringVector *array, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
array->len++;
char **resized = realloc(array->array, sizeof(char *)*array->len + 1);
if (!resized) {
free(value);
return -1;
}
resized[array->len-1] = value;
array->array = resized;
return 0;
}
// --------------------------------------------------------------------------------
void free_string_array(StringVector *array) {
if (array != NULL) {
for (int i = 0; i < array->len; i++) {
free(array->array[i]);
}
}
free(array->array);
// Reset all variables in the struct
array->array = NULL;
array->len = 0;
array->elem = 0;
}
// --------------------------------------------------------------------------------
int replace_string_vector_index(StringVector *array, int index, char string[]) {
if (array->dat != STRING) {
printf("Array data type must be a STRING");
return 0;
}
if (index > array->len) {
printf("Index is greater than array length");
return 0;
}
* (char **) ((char *) array->array + index * array->elem) = string;
return 1;
}
// --------------------------------------------------------------------------------
main.c
#include <stdio.h>
#include "vector.h"
int main(int argc, const char * argv[]) {
StringVector arr_test = init_string_vector();
char one[] = "Hello";
char two[] = "World";
char three[] = "Hello";
char four[] = "Goodbye";
append_string_vector(&arr_test, one);
append_string_vector(&arr_test, two);
append_string_vector(&arr_test, three);
append_string_vector(&arr_test, four);
// I can free the array at this point
free_string_array(&arr_test)
StringVector arr_test = init_string_vector();
append_string_vector(&arr_test, one);
append_string_vector(&arr_test, two);
append_string_vector(&arr_test, three);
append_string_vector(&arr_test, four);
replace_string_vector_index(&arr_test, 1, one);
// - Once I envoke replace_string_vector_index, free_string_array
// no longer works, and I get an invalid pointer error.
free_string_array(&arr_test);
}
If I understand the requirements for your replace_string_vector_index function, you should first free the memory of array->array[index], then assign the result of strdup(string) to that element.
No casting needed, no complex pointer arithmetic. Just simply:
free(array->array[index]);
array->array[index] = strdup(string);
What happens now (I think) is that you make array->array[index] point to the array that contains the string (i.e. you forget the strdup step). An array that wasn't allocated by malloc, and which can't be passed to free.
Since you will pass it to free as part of free_string_array you will have undefined behavior.
When I try to free my struct, the program crashes because of a segfault. Inspecting the program with valgrind I have found:
==9761== Invalid free() / delete / delete[] / realloc()
==9761== at 0x484827F: free (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==9761== by 0x109242: destroyHashTable (hashtable.c:38)
==9761== by 0x10942E: main (hashtable_main.c:17)
==9761== Address 0x1ffefffa70 is on thread 1's stack
==9761== in frame #2, created by main (hashtable_main.c:7)
I cannot really say anything more useful than having no idea, how to solve it. The crash happens during the free(ht) in destroyHashTable(ht) in hashtable.c. What am I doing wrong?
Below the code hashTable_main.c:
#include <stdio.h>
#include <stdlib.h>
#include "hashtable.h"
int main() {
hashTable* ht = NULL;
initHashTable(&ht);
int totalColCount = 0;
totalColCount += addHashTableEntry(&ht, "PRPR2");
destroyHashTable(&ht);
return EXIT_SUCCESS;
}
hashtable.c:
#include <stdlib.h>
#include <stdio.h>
#include "hashtable.h"
/* private internal API */
int hash_funktion(char *string);
hashtableEntry* createTableEntry(char* newKey) ;
/* end of private internal API */
int hash_funktion(char *string) {
unsigned int hash_adresse;
unsigned char *pointer;
hash_adresse = 0;
pointer = (unsigned char *) string;
while(*pointer != '\0') {
hash_adresse = 19 * hash_adresse + *pointer;
pointer++;
}
return hash_adresse % MAX_HASH;
}
hashtableEntry* createTableEntry(char* newKey) {
hashtableEntry* e = (hashtableEntry*) malloc (sizeof(hashtableEntry));
e->hashKey = newKey;
return e;
}
void initHashTable(hashTable* ht) {
ht = (hashTable*) malloc (sizeof (struct hashTable));
ht->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
void destroyHashTable(hashTable* ht) {
if (ht) {
free(ht);
ht = NULL;
}
}
int addHashTableEntry(hashtableEntry* ht, char* keyValue) {
hashtableEntry *e = createTableEntry(keyValue);
int colCounter = 0;
int hashValue = hash_funktion(keyValue);
if (ht[hashValue].hashKey == NULL) {
ht[hashValue] = *e;
return 0;
} else {
int newVal = (hashValue + 1) % MAX_HASH;
colCounter++;
while (ht[newVal].hashKey != NULL && newVal != hashValue ) {
newVal = (newVal + 1) % MAX_HASH;
colCounter++;
}
if (newVal != hashValue) {
ht[newVal] = *e;
return colCounter;
} else {
return -1;
}
}
}
bool searchValue(hashtableEntry* ht, char* searchValue) {
for (int i = 0; i < MAX_HASH; i++)
{
if(ht[i].hashKey == searchValue) {
return true;
}
}
return false;
}
and hashtable.h:
#pragma once
#define MAX_HASH 20
#include <stdbool.h>
typedef struct hashtableEntry {
char* hashKey;
} hashtableEntry;
typedef struct hashTable {
hashtableEntry* table;
int elemCount;
} hashTable;
void initHashTable(hashTable* ht);
void destroyHashTable(hashTable* ht);
int addHashTableEntry(hashtableEntry* ht, char* keyValue);
bool searchValue(hashtableEntry* ht, char* searchValue);
There never was a hashtable to begin with. The issue lies in initHashTable. It should be accepting a double pointer since it is given a pointer to a pointer it should initialize. The reason it can segfault despite the check in destroyHashTable is that the pointer is left uninitialized and may be non-zero at the start of program execution.
void initHashTable(hashTable** ht) {
*ht = (hashTable*) malloc (sizeof (struct hashTable));
(*ht)->table = (hashtableEntry*) malloc (MAX_HASH * sizeof (hashtableEntry));
}
You may find it easier to instead return the newly created hash table. This better expresses that initHashTable is giving you a new hashTable * value.
hashTable *initHashTable() {
hashTable *ht = (hashTable *) malloc (sizeof (struct hashTable));
ht.table = (hashtableEntry *) malloc (MAX_HASH * sizeof (hashtableEntry));
return ht;
}
There are also a bunch of other places where pointers are not handled correctly.
void doThing(Foo *foo) {
// This changes foo, but not the data foo points to.
foo = something;
// This changes the data foo points to
*foo = someOtherThing;
}
void doStuff() {
Foo *foo;
// This is incorrect since it creates a double pointer. doThing would need to
// be defined as "void doThing(Foo **foo)" to be correct.
doThing(&foo);
// Instead we can just pass the existing pointer
doThing(foo);
// We only need to create a reference if the value does not start out as a pointer
Foo bar;
doThing(&bar);
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* foo (const char* str)
{
char szo[125];
char* vege;
int i,j=0;
int db=1;
szo[0] = str[0];
for(i=1; i<strlen(str)+1; i++)
{
if(str[i] == str[i-1] )
{
db++;
}
else
{
j++;
szo[j] = db + '0';
j++;
szo[j] = str[i];
db=1;
}
}
j++;
szo[j] = '\0';
vege = szo;
return vege;
}
int main()
{
char *s[] = {"hello", "world", "Mississippi"};
int i;
for (i = 0; i < sizeof(s) / sizeof(char *); ++i)
{
char *p = foo(s[i]);
printf("%s\n", p);
}
return EXIT_SUCCESS;
}
I've tried to do first a printf("\n"), then its good, but i have a empty first line.
Someone can please help me.Thanks a lot.
Or if anyone can help me, the original tasks say a free(p) in the main after the printf, how can i change everything to work with malloc?
szo ceases to exist right before foo() returns.
Yet you return a pointer to that array and try to use it outside the function.
If you're not ever going to call foo recursively, or in parallel in several threads, or as different arguments of another function, try static char szo[125]; and remember to document that the function returns a pointer to a static array so that you don't invoke UB in 6 months time
char *foo(const char *str) {
static char szo[125];
//...
return szo;
}
printf("%s\n", foo("something")); //ok
printf("%s\n", foo("some other thing")); //ok
//printf("%s\n%s\n", foo("something"), foo("some other thing")); //NO
char* foo (const char* str)
{
char szo[125];
...
vege = szo;
return vege;
}
you return pointer to the local variable which does not exist after the return from the function. One of the most common UBs
Desired outcome:
utilityfun("&xxx") must return a pointer to "xxx".
utilityfun("xxx") must return a pointer to "*xxx".
#include <stdio.h>
#include <string.h>
char* utilityfun(char *s)
{
if(*s=='&')
return s+1; // this case works fine
else
{
char r[strlen(s)+2];
memset(r,'*',1);
strcpy(r+1,s);
char* p=r;
return p; // !!! p is local to this stack frame!
}
}
void main()
{
char* q=utilityfun("xxx");
printf("%p\n",q);
printf("%s\n",q); // *xxx It seems to work if I use q right way, but...
utilityfun("eee");
printf("%s\n",q); // *eee
}
Allocating a new char array on the heap is the only way?
My problem with that is: being utilityfun a utility function, I don't want to have to free memory outside of it each time I use it. Is there a way?
Given that it only needs to work with string literals… you can use a macro.
#include <stdio.h>
#define utilityfun(x) utilityfun_("*" x)
char *utilityfun_(char *s) {
if (s[0] == '*' && s[1] == '&') {
return s + 2;
}
return s;
}
int main(int argc, char **argv) {
char *a = utilityfun("eee");
char *b = utilityfun("&fff");
// Prints "a = *eee; b = fff;\n"
printf("a = %s; b = %s;\n", a, b);
}
This is a pretty ugly hack but it doesn't allocate memory at runtime. It only works with string literals, because it uses string literal concatenation to avoid allocating—in C, "abc" "def" becomes "abcdef". This does not work with non-literals, e.g., "abc" x does not work.
You can define a public variable then fill and return it in function :
char *out;
char* utilityfun(char *s)
{
if(*s=='&')
return s+1; // this case works fine
else
{
// char r[strlen(s)+2]; // You cannot use of non-constant array length
char *r = (char*)malloc(strlen(s) + 2);
memset(r,'*',1);
strcpy(r+1,s);
strcpy(out, r); // Instead of char* p=r; use of this
free(r); // Then free r
return out;
}
}
In main function, allocate it to get the right size :
void main()
{
char *input = "xxx";
out = (char*)malloc(strlen(input) + 1);
char* q=utilityfun(input);
printf("%p\n",q);
printf("%s\n",q);
}
I think Dietrich's solution is the best for my problem, but would this be an alternative solution? Are there major drawbacks to this approach?
{
static char arr[10];
strncpy(arr,s,9);
if(arr[0]=='&')
{
return arr+1;
}
else
{
memcpy(&arr[1],&arr[0],strlen(s)+1);
arr[0]='*';
return arr;
}
}
The get_current_path function gets a pointer to a char string of the current working directory. printf("%s\n", buf); in the function itself prints exactly what I want, but then outside of the function, printf("%s", thisbuf); gives me a lot of garbage. I assume I've made some silly mistake here, but I can't figure out what it is.
#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <unistd.h>
int get_current_path(char *buf) {
long cwd_size;
char *ptr;
cwd_size = pathconf(".", _PC_PATH_MAX);
if ((buf = (char *) malloc((size_t) cwd_size)) != NULL)
ptr = getcwd(buf, (size_t)cwd_size);
else cwd_size == -1;
printf("%s\n", buf);
printf("%ld\n", cwd_size);
return cwd_size;
}
int main (int argc, char **argv)
{
char *thisbuf;
get_current_path(thisbuf);
printf("%s", thisbuf);
return 0;
}
You should pass a pointer to char *
int get_current_path(char **buf)
{
*buf = ...;
}
int main()
{
char *thisbuf;
get_current_path(&thisbuf);
}
Parameters in C are pass-by-value, which means that get_current_path can't change the value of "thisbuf" passed in by the caller.
To make the change, you would have to pass in a pointer to "thisbuf":
int get_current_path(char **resultBuf) {
char *buf = (char *) malloc((size_t) cwd_size);
...
*resultBuf = buf; // changes "thisbuf" in the caller
}
....
get_current_path(&thisbuf); // note - passing pointer to "thisbuf"
Try this instead:
int get_current_path(char **buf) {
*buf = something; // Set buf with indirection now.
And:
int main (int argc, char **argv)
{
char *thisbuf;
get_current_path(&thisbuf);
printf("%s", thisbuf);
return 0;
}
You were trying to pass a copy of buf to get_current_path, so when buf was modified, the original pointer to buf was not modified.