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.
Related
I am trying to find a way to create a dynamically allocated array of C strings. So far I have come with the following code that allows me to initialize an array of strings and change the value of an already existing index.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void replace_index(char *array[], int index, char *value) {
array[index] = malloc(strlen(value) + 1);
memmove(array[index], value, strlen(value) + 1);
}
int main(int argc, const char * argv[]) {
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
replace_index(strs, 2, "new_value");
// The above code works fine, but I can not use it to add a value
// beyond index 4.
// The following line will not add the string to index 5.
replace_index(strs, 5, "second_value");
}
The function replace_index will work to change the value of a string already include in the initializer, but will not work to add strings beyond the maximum index in the initializer. Is there a way to allocate more memory and add a new index?
First off, if you want to do serious string manipulation it would be so much easier to use almost any other language or to get a library to do it for you.
Anyway, onto the answer.
The reason replace_index(strs, 5, "second_value"); doesn't work in your code is because 5 is out of bounds-- the function would write to memory unassociated with strs. That wasn't your question, but that's something important to know if you didn't. Instead, it looks like you want to append a string. The following code should do the trick.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct {
char **content;
int len;
} string_array;
void free_string_array(string_array *s) {
for (int i = 0; i < s->len; i++) {
free(s->content[i]);
}
free(s->content);
free(s);
}
int append_string(string_array *s, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->len++;
char **resized = realloc(s->content, sizeof(char *)*s->len);
if (!resized) {
s->len--;
free(value);
return -1;
}
resized[s->len-1] = value;
s->content = resized;
return 0;
}
string_array* new_string_array(char *init[]) {
string_array *s = calloc(1, sizeof(string_array));
if (!s || !init) {
return s;
}
while (*init) {
if (append_string(s, *init)) {
free_string_array(s);
return NULL;
}
init++;
}
return s;
}
// Note: It's up to the caller to free what was in s->content[index]
int replace_index(string_array *s, int index, char *value) {
value = strdup(value);
if (!value) {
return -1;
}
s->content[index] = value;
return 0;
}
int main() {
string_array *s = new_string_array((char *[]) {"help", "me", "learn", "dynamic", "strings", NULL});
if (!s) {
printf("out of memory\n");
exit(1);
}
free(s->content[2]);
// Note: No error checking for the following two calls
replace_index(s, 2, "new_value");
append_string(s, "second value");
for (int i = 0; i < s->len; i++) {
printf("%s\n", s->content[i]);
}
free_string_array(s);
return 0;
}
Also, you don't have to keep the char ** and int in a struct together but it's much nicer if you do.
If you don't want to use this code, the key takeaway is that the array of strings (char ** if you prefer) must be dynamically allocated. Meaning, you would need to use malloc() or similar to get the memory you need, and you would use realloc() to get more (or less). Don't forget to free() what you get when you're done using it.
My example uses strdup() to make copies of char *s so that you can always change them if you wish. If you have no intention of doing so it might be easier to remove the strdup()ing parts and also the free()ing of them.
Static array
char *strs[] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of pointer to char and initializes it with 5 elements, thus the implied [] is [5]. A more restrictive const char *strs[] would be more appropriate if one were not intending to modify the strings.
Maximum length
char strs[][32] = {"help", "me", "learn", "dynamic", "strings"};
This declares strs as an array of array 32 of char which is initialized with 5 elements. The 5 elements are zero-filled beyond the strings. One can modify this up to 32 characters, but not add more.
Maximum capacity singleton for constant strings
static struct str_array { size_t size; const char *data[1024]; } strs;
This will pre-allocate the maximum capacity at startup and use that to satisfy requests. In this, the capacity is 1024, but the size can be any number up to the capacity. The reason I've made this static is this is typically a lot to put the stack. There is no reason why it couldn't be dynamic memory, as required.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
static struct { size_t size; const char *data[1024]; } strs;
static const size_t strs_capacity = sizeof strs.data / sizeof *strs.data;
/** Will reserve `n` pointers to strings. A null return indicates that the size
is overflowed, and sets `errno`, otherwise it returns the first string. */
static const char **str_array_append(const size_t n) {
const char **r;
if(n > strs_capacity - strs.size) { errno = ERANGE; return 0; }
r = strs.data + strs.size;
strs.size += n;
return r;
}
/** Will reserve one pointer to a string, null indicates the string buffer is
overflowed. */
static const char **str_array_new(void) { return str_array_append(1); }
int main(void) {
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new())) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
return success;
}
Dynamic array
struct str_array { const char **data; size_t size, capacity; };
I think you are asking for a dynamic array of const char *. Language-level support of dynamic arrays is not in the standard C run-time; one must write one's own. Which is entirely possible, but more involved. Because the size is variable, it will probably be slower, but in the limit as the problem grows, by a constant average.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
/** A dynamic array of constant strings. */
struct str_array { const char **data; size_t size, capacity; };
/** Returns success allocating `min` elements of `a`. This is a dynamic array,
with the capacity going up exponentially, suitable for amortized analysis. On
resizing, any pointers in `a` may become stale. */
static int str_array_reserve(struct str_array *const a, const size_t min) {
size_t c0;
const char **data;
const size_t max_size = ~(size_t)0 / sizeof *a->data;
if(a->data) {
if(min <= a->capacity) return 1;
c0 = a->capacity < 5 ? 5 : a->capacity;
} else {
if(!min) return 1;
c0 = 5;
}
if(min > max_size) return errno = ERANGE, 0;
/* `c_n = a1.625^n`, approximation golden ratio `\phi ~ 1.618`. */
while(c0 < min) {
size_t c1 = c0 + (c0 >> 1) + (c0 >> 3);
if(c0 >= c1) { c0 = max_size; break; } /* Unlikely. */
c0 = c1;
}
if(!(data = realloc(a->data, sizeof *a->data * c0)))
{ if(!errno) errno = ERANGE; return 0; }
a->data = data, a->capacity = c0;
return 1;
}
/** Returns a pointer to the `n` buffered strings in `a`, that is,
`a + [a.size, a.size + n)`, or null on error, (`errno` will be set.) */
static const char **str_array_buffer(struct str_array *const a,
const size_t n) {
if(a->size > ~(size_t)0 - n) { errno = ERANGE; return 0; }
return str_array_reserve(a, a->size + n)
&& a->data ? a->data + a->size : 0;
}
/** Makes any buffered strings in `a` and beyond if `n` is greater then the
buffer, (containing uninitialized values) part of the size. A null on error
will only be possible if the buffer is exhausted. */
static const char **str_array_append(struct str_array *const a,
const size_t n) {
const char **b;
if(!(b = str_array_buffer(a, n))) return 0;
return a->size += n, b;
}
/** Returns a pointer to a string that has been buffered and created from `a`,
or null on error. */
static const char **str_array_new(struct str_array *const a) {
return str_array_append(a, 1);
}
/** Returns a string array that has been zeroed, with zero strings and idle,
not taking up any dynamic memory. */
static struct str_array str_array(void) {
struct str_array a;
a.data = 0, a.capacity = a.size = 0;
return a;
}
/** Erases `a`, if not null, and returns it to idle, not taking up dynamic
memory. */
static void str_array_(struct str_array *const a) {
if(a) free(a->data), *a = str_array();
}
int main(void) {
struct str_array strs = str_array();
const char **s;
size_t i;
int success = EXIT_FAILURE;
if(!(s = str_array_append(&strs, 5))) goto catch;
s[0] = "help";
s[1] = "me";
s[2] = "learn";
s[3] = "dynamic";
s[4] = "strings";
strs.data[2] = "new_value";
if(!(s = str_array_new(&strs))) goto catch;
s[0] = "second_value";
for(i = 0; i < strs.size; i++) printf("->%s\n", strs.data[i]);
{ success = EXIT_SUCCESS; goto finally; }
catch:
perror("strings");
finally:
str_array_(&strs);
return success;
}
but will not work to add strings beyond the maximum index in the initializer
To do that, you need the pointer array to be dynamic as well. To create a dynamic array of strings is one of the very few places where using a pointer-to-pointer to emulate 2D arrays is justified:
size_t n = 5;
char** str_array = malloc(5 * sizeof *str_array);
...
size_t size = strlen(some_string)+1;
str_array[i] = malloc(size);
memcpy(str_array[i], some_string, size);
You have to keep track of the used size n manually and realloc more room in str_array when you run out of it. realloc guarantees that previous values are preserved.
This is very flexible but that comes at the cost of fragmented allocation, which is relatively slow. Had you used fixed-size 2D arrays, the code would perform much faster but then you can't resize them.
Note that I used memcpy, not memmove - the former is what you should normally use, since it's the fastest. memmove is for specialized scenarios where you suspect that the two arrays being copied may overlap.
As a side-note, the strlen + malloc + memcpy can be replaced with strdup, which is currently a non-standard function (but widely supported). It seems likely that strdup will become standard in the upcoming C23 version of C, so using it will become recommended practice.
I am trying to produce a library that contains C functions to create a dynamically allocated array as well as other basic array operations. I have defined a typedef struct that contains a pointer variable array which stores the data points in the array. The struct also contains other variables such as the allocated array size the array length (i.e. len) as well as the array name and datatype. The goal is that the array should be dynamically allocated and the array container should be able to hold any data type. I have created a function titled init_array, which is a wrapper around array_mem_alloc to instantiate the array container. Finally I have another function titled append_array where the user can pass a scalar or another defined array that will be appended to the data already within array.
I am trying to create a function titled pop_array, but I am struggling with how to write it. Normally you could just iterate over a for loop from indice to the assigned length, overwrite the first indice and move all others to the left. Unfortunately in this case array is a void variable, and I run into problems assigning data to a void. I have tried some implementations with memcp and memmove, but I can not find a solution where the compiler allows me to assign the data to a void type. Any thoughts or help would be appreciated. The code is shown below.
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
void *array; // Pointer to array
size_t len; // Active length of array
size_t size; // Number of allocated indizes
int elem; // Memory consumption per indice
char *name; // The array name
char *dtype; // A string representing the datatype
} Array;
void array_mem_alloc(Array *array, size_t num_indices);
Array init_array(char *dtype, size_t num_indices, char *name);
int append_array(Array *array, void *elements, size_t count);
int pop_array(Array *array, int indice);
Array.c
void array_mem_alloc(Array *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);
exit(0);
}
// Allocate resources and instantiate Array
else {
array->array = pointer;
array->len = 0;
array->size = num_indices;
}
}
// --------------------------------------------------------------------------------
Array init_array(char *dtype, size_t num_indices, char *name) {
// Determine memory blocks based on data type
int size;
if (strcmp(dtype, "float") == 0) size = sizeof(float);
else if (strcmp(dtype, "int") == 0) size = sizeof(int);
else if (strcmp(dtype, "double") == 0) size = sizeof(double);
else if (strcmp(dtype, "char") == 0) size = sizeof(char);
else {
printf("Data type not correctly entered into init_array, exiting program!\n");
exit(0);
}
// Allocate indice size and call array_mem_alloc
Array array;
array.dtype = dtype;
array.elem = size;
array_mem_alloc(&array, num_indices);
array.name = name;
return array;
}
// --------------------------------------------------------------------------------
int append_array(Array *array, void *elements, size_t count) {
// Allocae more memory if necessary
if (array->len + count > array->size) {
size_t size = (array->len + count) * 2;
void *pointer = realloc(array->array, size * array->elem);
// If memory is full return operations
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
return 0;
}
// Allocate memory to variables and increment array size
array->array = pointer;
array->size = size;
}
// Append variables and increment the array length
memcpy((char *)array->array + array->len * array->elem, elements, count * array->elem);
array->len += count;
return 1;
}
// --------------------------------------------------------------------------------
int pop_array(Array *array, int indice) {
if (indice >= array->len) {
printf("Indice %d out of bounds for pop_array", indice);
return 0;
}
for (int i = index; i < array->len; i++) {
// This does not work because I cannot assign to a void type
// - I have tried to several solutions with append_array and memcpy
// but all solutions seem to run into a problem where assigning data
// already in the array is not possible.
array->array[i] = array->array[i+1];
}
// Decrement array length
array->len -= 1;
return 1;
}
main.c
int main(int argc, const char * argv[]) {
size_t indices = 10;
char name[6] = "array";
char dtype[4] = "int";
Array arr_test = init_array(dtype, indices, name);
int a[3] = {10, 9, 8};
append_array(&arr_test, &a, 3);
// pop array function call here
}
Belaying potential alignment issues, you don't need a loop. I believe this is all you need to shift the right-most desired elements 'back' one 'slot':
unsigned char *dst = (unsigned char*)array->array + indice * array->elem;
memmove(dst, dst + array->elem, array->elem * (array->len - indice - 1));
There are lot of things that go wrong in your implementation.
For example, the way you use your name char pointer can lead to it pointing to deallocated memory.
The following code :
void foo(Array *array) {
char name[] = "abc";
char type[] = "int";
init_array(type, name, array);
}
int main(int args, char **argv) {
Array array;
foo(&array);
puts(array.name);
}
leads to undefined behavior, because array.name and array.dtype point to chunks of memory which have been popped from the stack.
One can also mention that you only manage very few cases. What happens if the user input "short" as dtype ?
For your specific problem, memmove is your friend :
void* slot = array->array + array->elem * indice;
memcpy(array->array + array->len * array->elem, slot, array->elem);
--vector->len;
memmove(slot, (const void *) slot + array->elem, (array->len - indice) * array->elem);
I am working on a problem in C where I want to create a dynamic growing array, and if possible utilize the same functions for different data types. Presently I have a struct titled Array that uses a void data type titled *array which is a pointer to the array. It also holds len which stores the active length of the array, size which holds that length of the allocated memory, and elem which stores the length of a datatype that is used to dynamically grow the array.
In addition, I am using three functions. The function initiate_array does the heavy lifting of allocating memory for the array variable in the struct and instantiating all but one of the struct elements. The function init_array acts as a wrapper around initiate_array and also instantiates the variable elem in the struct. Finally, the function append_array adds data/indices to the array and reallocates memory if necessary.
At this point the Array struct, and the functions initiate_array and init_array are independent of data type; however, append_array is hard coded for int variables. I have tried to make append_array somewhat data type independent by making the input int item into void item, but then I get a compile time error at each location with the code ((int *)array->array)[array->len - 1] = item that tells me I cannot cast to a void.
My code is below, does anyone have a suggestion on how I can implement the append_array function to be independent of the datatype of item?
NOTE: I also have a function to free memory at the end of execution, but I am omitting it from this question since it is not relevant.
array.h
#ifndef ARRAY_H
#define ARRAY_H
#include <stdlib.h>
#include <stdio.h>
typedef struct
{
void *array;
size_t len;
size_t size;
int elem;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, int item);
#endif /* ARRAY_H */
array.c
#include "array.h"
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
array->len = 0;
array->size = num_indices;
}
}
Array init_array(int size, size_t num_indices) {
Array array;
array.elem = size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, int item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(pointer);
exit(0);
}
else {
array->array = pointer;
((int *)array->array)[array->len - 1] = item;
}
}
else
((int *)array->array)[array->len - 1] = item;
}
main.c
#include <stdio.h>
#include <stdlib.h>
#include "array.h"
int main(int argc, char** argv)
{
int i, j;
size_t indices = 20;
Array pointers = int_array(sizeof(int), indices);
for (i = 0; i < 50; i++)
{
append_int_array(&pointers, i);
}
for (i = 0; i < pointers.len; i++)
{
printf("Value: %d Size:%zu \n",((int *) pointers.array)[i], pointers.len);
}
return (EXIT_SUCCESS);
}
I would start by looking at append_array. void is not a complete type, which means that you can not pass in void objects by value. You can pass them by reference though, and void * can refer to any other type as well:
void append_array(Array *array, void *item) {
Right now, you are assuming that you are passing in a single element. But why stop there? You can make your function signature look like this:
void append_array(Array *array, void *items, size_t count) {
The additional caveat here is that array->size * 2 may be insufficient to hold the appended data. You could use (array->len + count) * 2 instead.
Assuming that array->size is large enough, you can copy the elements directly using memcpy and a cast to char *, which is guaranteed by the standard to have size-1 elements:
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
Notice that I used array->len for the index here. That is because my next suggestion is to increment array->len only after you make the copy. That would make your size check simpler, and not subject to reallocation with one element to spare, as you have now. Remember that array->len is not only the size of the array, it is also the zero based index that you want to append to.
if (array->len + count > array->size) {
For a single element, the condition would be
if (array->len >= array->size) {
Finally, I strongly suggest you return an integer error code instead of exiting. The user of this function should expect to be able to do cleanup at the very least in case of a memory error, or possibly free up cache elements, not unilaterally crash.
Here is what the final function would look like:
int append_array(Array *array, void *items, size_t count)
{
if (array->len + count > array->size) {
size_t size = (array->len + count) * 2;
void *pointer = realloc(array->array, size * array->elem);
if (pointer == NULL) {
return 0;
}
array->array = pointer;
array->size = size;
}
memcpy((char *)array->array + array->len * array->elem, items, count * array->elem);
array->len += count;
return 1;
}
Writing the function this way has one slight drawback: since rvalues don't have an address, you can't call
append_array(&array, &3, 1);
You can work around this in two ways.
Make a temporary variable or buffer to hold the value:
int tmp = 3;
append_array(&array, &tmp, 1);
Make a type-specific wrapper that can accept elements for complete types. This works because C is purely pass-by-value (i.e., copy), so you can do
int append_int(Array *array, int value)
{
return append_array(array, &value, 1);
}
In this case, you are effectively using a new stack frame to hold the value of tmp in the first example.
The type void is an incomplete type, and one which cannot be completed, so you can't assign to or from it, or use it as an array parameter type.
What you can do is change append_array to take a void * as an argument which points to the data to be added. Then you convert your data pointer to char * so you can do single byte pointer arithmetic to get to the correct offset, then use memcpy to copy in the data.
void append_array(Array *array, void *item) {
array->len++;
if (array->len == array->size){
array->size *= 2;
void *pointer;
pointer = realloc(array->array, array->size * array->elem);
if (pointer == NULL) {
printf("Unable to reallocate memory, exiting.\n");
free(array->array);
exit(0);
}
else {
array->array = pointer;
}
}
char *p = (char *)array->array + (array->len - 1) * array->elem;
memcpy(p, item, array->elem);
}
You won't be able to call this function by passing an integer literal to add, but you can use the address of a compound literal.
append_array(array, &(int){ 3 });
It should be something like this, or you could use typeof to improve it.
Or, use void **array;
#include <assert.h>
#include <memory.h>
#include <stdio.h>
#include <stdlib.h>
typedef struct {
void *array;
size_t size;
size_t capacity;
int elem_size;
} Array;
void initiate_array(Array *array, size_t num_indices);
Array init_array(int size, size_t num_indices);
void append_array(Array *array, void *item);
void initiate_array(Array *array, size_t num_indices) {
void *pointer;
pointer = malloc(num_indices * array->elem_size);
if (pointer == NULL) {
printf("Unable to allocate memory, exiting.\n");
// free(pointer);
exit(0);
} else {
array->array = pointer;
array->size = 0;
array->capacity = num_indices;
}
}
Array init_array(int elem_size, size_t num_indices) {
Array array;
array.elem_size = elem_size;
initiate_array(&array, num_indices);
return array;
}
void append_array(Array *array, void *item) {
if (array->size == array->capacity) {
// extend the array
}
memcpy(array->array + array->size * array->elem_size, item,
array->elem_size);
array->size++;
}
int main(void) {
Array arr = init_array(sizeof(int), 10);
int item = 1;
append_array(&arr, &item);
item = 2;
append_array(&arr, &item);
item = 3;
append_array(&arr, &item);
return 0;
}
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");
}
}
I messed around with this enough but I really don't get it.
Here is what I want to do: Take a 2D char array as an input in a function, change the values in it and then return another 2D char array.
That's it. Quite simple idea, but ideas do not get to work easily in C.
Any idea to get me started in its simplest form is appreciated. Thanks.
C will not return an array from a function.
You can do several things that might be close enough:
You can package your array in struct and return that. C will return structs from functions just fine. The downside is this can be a lot of memory copying back and forth:
struct arr {
int arr[50][50];
}
struct arr function(struct arr a) {
struct arr result;
/* operate on a.arr[i][j]
storing into result.arr[i][j] */
return result;
}
You can return a pointer to your array. This pointer must point to memory you allocate with malloc(3) for the array. (Or another memory allocation primitive that doesn't allocate memory from the stack.)
int **function(int param[][50]) {
int arr[][50] = malloc(50 * 50 * sizeof int);
/* store into arr[i][j] */
return arr;
}
You can operate on the array pointer passed into your function and modify the input array in place.
void function(int param[][50]) {
/* operate on param[i][j] directly -- destroys input */
}
You can use a parameter as an "output variable" and use that to "return" the new array. This is best if you want the caller to allocate memory or if you want to indicate success or failure:
int output[][50];
int function(int param[][50], int &output[][50]) {
output = malloc(50 * 50 * sizeof int);
/* write into output[i][j] */
return success_or_failure;
}
Or, for the caller to allocate:
int output[50][50];
void function(int param[][50], int output[][50]) {
/* write into output[i][j] */
}
You cannot return an array from a function.
You have several options:
wrap arrays inside structs
struct wraparray {
int array[42][42];
};
struct wraparray foobar(void) {
struct wraparray ret = {0};
return ret;
}
pass the destination array, as a pointer to its first element (and its size), to the function; and change that array
int foobar(int *dst, size_t rows, size_t cols, const int *src) {
size_t len = rows * cols;
while (len--) {
*dst++ = 42 + *src++;
}
return 0; /* ok */
}
// example usage
int x[42][42];
int y[42][42];
foobar(x[0], 42, 42, y[0]);
change the original array
int foobar(int *arr, size_t rows, size_t cols) {
size_t len = rows * cols;
while (len--) *arr++ = 0;
return 0; /* ok */
}
char **foo(const char * const * bar, size_t const *bar_len, size_t len0) {
size_t i;
char** arr = malloc(sizeof(char *) * len0);
for (i = 0; i < len0; ++i) {
arr[i] = malloc(bar_len[i]);
memcpy(arr[i], bar[i], bar_len[i]);
}
/* do something with arr */
return arr;
}
Somewhere else in your code:
char **pp;
size_t *pl;
size_t ppl;
/* Assume pp, pl are valid */
char **pq = foo(pp, pl, ppl);
/* Do something with pq */
/* ... */
/* Cleanup pq */
{
size_t i;
for (i = 0; i < ppl; ++i)
free(pq[i]);
free(pq);
}
Because you're passing by-pointer instead of by-value and you want to write to the input array, you have to make a copy of it.
Here's another example. Tested and works.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void test(char**,unsigned int,unsigned int);
const unsigned int sz_fld = 50 + 1;
const unsigned int sz_ffld = 10;
int main(void) {
char fld[sz_ffld][sz_fld];
for (unsigned char i=0;i<sz_ffld;++i) {
strcpy(fld[i],"");
}
strcpy(fld[0],"one");
strcpy(fld[1],"two");
strcpy(fld[2],"three");
char** pfld = malloc(sz_ffld*sizeof(char*));
for (unsigned int i=0;i<sz_ffld;++i) {
*(pfld+i) = &fld[i][0];
}
test(pfld,sz_ffld,sz_fld);
printf("%s\n",fld[0]);
printf("%s\n",fld[1]);
printf("%s\n",fld[2]);
free(pfld);
return(0);
}
void test(char** fld,unsigned int m,unsigned int n) {
strcpy(*(fld+0),"eleven");
strcpy(*(fld+1),"twelve");
return;
}
Note the following:
For compiling, I am using gcc with the C99 option.
I defined the function to include the two sizes information, but I wrote very basic code and am not actually using the information at all, just the strcpy(), so this certainly is not security-safe code in any way (even though I'm showing the "m" and "n" for such facility). It merely shows a technique for making a static 2D char array, and working with it in a function through the intermediate of an array of pointers to the "strings" of the array.
When you pass a 2D array to a function as a parameter, you need to explicitly tell it the size of the arrays second dimension
void MyFunction(array2d[][20]) { ... }
The following will do what you want. it will print "One" and "Ten". Also note that it is typed to the exact array dimensions of 10 and 8.
char my_array[10][8] =
{
{"One"},
{"Two"},
{"One"},
{"One"},
{"One"},
{"One"},
{"One"},
{"One"},
{"Nine"},
{"Ten"},
};
void foo ( char (**ret)[10][8] )
{
*ret = my_array;
}
void main()
{
char (*ret)[10][8];
foo(&ret);
printf("%s\r\n", (*ret)[0] )
printf("%s\r\n", (*ret)[9] )
}
The original question was about RETURNING the array, so I'm updating this to show returning a value. You can't "return an array" directly, but you CAN make a typedef of an array and return that...
char my_array[10][8];
typedef char ReturnArray[8];
ReturnArray* foo()
{
return my_array;
}