I need to dynamically append a char to a string, so I'm using realloc() to add more memory as I need it.
I'm new to C (coming from Python) so I've been reading a lot and this was the best I could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_to(char *array, char value) {
size_t buffer = (strlen(array) * sizeof(char)) + sizeof(char);
char *new_array = realloc(array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
array = new_array;
int position = strlen(array);
array[position] = value;
}
int main() {
char *list = malloc(sizeof(char));
for (int i = 1; i < 26; i++){
append_to(list, 'a');
printf("%d -> %s\n", i, list);
}
}
This is just an example to showcase the issue. The code runs flawlessly until iteration 24, see below:
1 -> a
2 -> aa
[...] //omitted
23 -> aaaaaaaaaaaaaaaaaaaaaaa
24 -> aaaaaaaaaaaaaaaaaaaaaaaa
25 ->
What am I missing?
First you forget to add another NUL char at the end of your c-string.
Second, realloc may change the memory location of the data, but you passed the list as value, so the relocation is not visible in the case of data relocation.
That should lokks like:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void append_to(char **array, char value) { // pass pointer's address
size_t buffer = (strlen(*array) * sizeof(char)) + sizeof(char) + sizeof(char); // one more to tackle the end of the string
char *new_array = realloc(*array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
*array = new_array;
int position = strlen(*array);
(*array)[position] = value;
(*array)[position+1] = 0; // end of string
}
int main() {
char *list = malloc(sizeof(char));
list[0] = 0; // end of string
for (int i = 1; i < 26; i++){
append_to(&list, 'a'); // pass address of array so that it can be changed by the call
printf("%d -> %s\n", i, list);
}
free(list); // always explicitly free unused resources
}
You didn't receive array as a double pointer, so you can't reassign the caller's pointer when realloc has to move the allocation.
To fix,
// Receive double pointer
void append_to(char **array, char value) {
// Add dereferencing as needed
size_t buffer = (strlen(*array) + 2) * sizeof(char);
char *new_array = realloc(*array, buffer);
if (new_array == NULL) {
printf("CRITICAL ERROR\n");
exit(-1);
}
*array = new_array;
int position = strlen(*array);
array[0][position] = value;
array[0][position+1] = '\0'; // Explicitly NUL terminate, don't assume new memory is zeroed
}
int main() {
char *list = malloc(sizeof(char));
for (int i = 1; i < 26; i++){
append_to(&list, 'a'); // Pass address of list
printf("%d -> %s\n", i, list);
}
}
Related
To allocate and free a single string in C, I do the following:
char[10] stringToCopy = "CopyString";
int length = strlen(stringToCopy) + 1;
char* CopiedString = malloc(length);
strcpy(CopiedString, stringToCopy, length);
printf("DatabasePath=%s\r\n", CopiedString);
free(CopiedString);
In my program, I need to copy such strings to a char** datatype. I need help in writing such a program. I am using a third party API which has a structure with this char** field entry.
I am not aware of how to allocate memory to this datatype and then copy the CopiedString into a list of such values. And also how to free the value after usage.
Allocating memory:
char **arr = malloc(r * sizeof(char *));
for (i=0; i<r; i++)
{
arr[i] = malloc(c * sizeof(char));
}
Freeing memory:
for (i=0; i<r; i++)
{
free(arr[i]);
}
free(arr);
I am not aware of how to allocate memory to this datatype ....
Follows is a useful C idiom for allocating.
size_t number_in_array = ...;
ptr = malloc(sizeof *ptr * number_in_array);
if (ptr == NULL) {
puts("Allocation failed");
} else {
puts("Success");
// Use ptr
free(ptr): // **
}
...
free(ptr): // **
// ** free(ptr) in 1 of 2 places.
Notice there is no need to code the type of the pointer in the allocation: ptr = malloc(sizeof *ptr * number_in_array);. This is easier to code right, review and maintain than attempting to code the type.
... then copy the CopiedString into a list of such values.
You already have good code to form a copied string. Make a helper function. Below is OP's with some improvements. Also research the common strdup() function.
char *my_strdup(const char *stringToCopy) {
size_t size = strlen(stringToCopy) + 1;
char* CopiedString = malloc(size);
if (CopiedString) {
memcpy(CopiedString, stringToCopy, size);
}
return CopiedString;
}
And also how to free the value after usage.
size_t number_in_array = 3;
char **ptr = malloc(sizeof *ptr * number_in_array);
if (ptr == NULL) {
puts("Allocation failed");
} else {
ptr[0] = my_strdup("Hello");
ptr[1] = my_strdup(" ");
ptr[2] = my_strdup("World");
// Use ptr (could check for ptr[] allocation failures first)
for (size_t i = 0; i < number_in_array; i++) {
free(ptr[i]);
}
free(ptr):
}
You can use strdup to create malloced string in c.
char ** presult = NULL;
for double pointer "presult" we can assign data like this.
if (presult) {
*presult = strdup("CopyString");
}
Sample code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int copy_data(char ** p) {
if (p) {
*p = strdup("Test data");
}
return 0;
}
int main () {
char * p = NULL;
copy_data(&p);
if (p) {
printf("daat : %s\n", p);
// free if not used
free(p);
p = NULL;
}
return 0;
}
Go through the below program which explains how to allocate memory for char** and also how to free the same.
Comments are in lined for understanding, please get back in case of any clarification needed.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define NUMBER_OF_STRINGS 5
#define MAX_LEN_OF_STRING 50
void printStrings_array(char ex_strings[][MAX_LEN_OF_STRING], int nstrings);
char** allocMemForStrings(int nStrings);
void printStrings_ptr(char** strings, int nStrings);
int main()
{
// example list of string for which we are going to allocate memory and store in char**
char ex_strings[NUMBER_OF_STRINGS][MAX_LEN_OF_STRING] =
{
"This is the First String",
"This is the Second String",
"This is the Third String",
"This is the Fourth String",
"This is the Fifth String",
};
printf("contents of char[][] \n");
printStrings_array(ex_strings, NUMBER_OF_STRINGS);
// list_of_strings is a variable which points to list of strings
char** list_of_strings = NULL;
// allocating memory for list of strings
if ( list_of_strings = allocMemForStrings(NUMBER_OF_STRINGS))
{
int i = 0;
printf("got %p for list_of_strings\n", (void*)list_of_strings);
while(i < NUMBER_OF_STRINGS)
{
size_t len = strlen(ex_strings[i])+1;
// allocate memory for each string as per its length
list_of_strings[i] = malloc(len);
printf("got %p for string %d\n", (void*)list_of_strings[i], i);
memset(list_of_strings[i], 0, len);
strncpy(list_of_strings[i], ex_strings[i], len);
i++;
}
printf("contents of char** \n");
printStrings_ptr(list_of_strings, NUMBER_OF_STRINGS);
// free ing the memory for char **
// first we need to free the memory for each of the strings pointed by the list_of_strings variable.
i = 0;
while(i < NUMBER_OF_STRINGS)
{
printf("\n freeing %p ", (void*)list_of_strings[i]);
free(list_of_strings[i]);
list_of_strings[i] = NULL;
i++;
}
//now free the list_of_strings pointer
printf("\n finally freeing %p ", (void*)list_of_strings);
free(list_of_strings);
// to avoid dangling pointers, its best practice to set the pointers = NULL after free.
list_of_strings = NULL;
}
else
{
printf("cannot allocate memory for strings\n");
}
return 0;
}
void printStrings_array(char ex_strings[][MAX_LEN_OF_STRING], int nstrings)
{
for(int i = 0; i< nstrings;i++)
printf(" %s\n", ex_strings[i]);
}
void printStrings_ptr(char** strings, int nStrings)
{
for(int i = 0; i< nStrings;i++)
printf(" %s\n", strings[i]);
}
char** allocMemForStrings(int nStrings)
{
// need to have memory to store nStrings
return malloc(nStrings * sizeof (char*));
}
in my last question, I've asked how to use function to free an malloc'ed array, I wanted to improve my code so that the function won't just free the memory but also will set the pointer to NULL once it finishes the clearing.
Also I want a single function to do both - setting and clearing, depending on the command I'm passing, this is what I've done so far:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
char **set_arr(int number, char *command);
int command_read(char *command);
void clear_arr(char *arr[], int size);
char set[] = "set";
char clear[] = "clear";
int main() {
int num = // get number from user;
char** my_arr = NULL;
my_arr = set_arr(num, set);
// so far the code works as excepted
set_arr((size_t)&my_arr, clear);
return 0;
}
int command_read(char *command) {
if (strcmp(command, set) == 0)
return 'S';
if (strcmp(command, clear) == 0)
return 'C';
}
char **set_arr(int number, char *command) {
static char **arr = NULL;
static int size;
switch (command_read(command)) {
case 'S':
size = (int)number;
arr = malloc((size + 1) * sizeof(char *));
for (int i = 0; i <= size; i++) {
arr[i] = NULL;
if (i == size)
break;
arr[i] = malloc((string_len) * sizeof(char));
}
break;
case 'C':
clear_arr(arr, size);
free(arr);
uintptr_t value = number;
uint64_t *temp = (void *)value;
*temp = 0x0;
break;
}
return arr;
}
void clear_arr(char *arr[], int size) {
for (int i = 0; i < size; i++) {
free(arr[i]);
arr[i] = NULL;
}
}
I know that there is better methods to clear (and allocate memory?) but my primary question is, did I free all the memory I allocated for the array, and after the clearing, does the pointer my_arr is set correctly to NULL?
Writing a generic function to achieve your goal is not possible in Standard C because pointers to different types of objects may have a different representation so you cannot pass the address of a pointer and expect the function to handle it in a generic manner.
Yet this provision in the C Standard is not used on most current systems today. In particular, the POSIX standard mandates that all pointers have the same representation. Hence your generic function can work on these systems, with some precautions to avoid compilation warnings:
// free an array of allocated things
void free_array(void ***p, size_t count) {
void **array = *p;
for (size_t i = 0; i < count; i++) {
free(array[i]);
array[i] = NULL; // for safety
}
free(array);
*p = NULL;
}
// deal with the non portable conversion with macros
#define FREE_ARRAY(p, n) free_array((void ***)(void *)&(p), n)
// allocate an array of pointers to allocated things of size `size`.
// return a pointer to the array or `NULL` if any allocation failed
void **malloc_array(size_t count, size_t size) {
void **array = malloc(count * sizeof(*array));
if (array) {
for (size_t i = 0; i < count; i++) {
array[i] = calloc(size, 1); // allocate and initialize to all bits zero
if (array[i] == NULL) {
while (i-- > 0) {
free(array[i]);
array[i] = NULL;
}
return NULL;
}
}
}
return array;
}
#define MALLOC_ARRAY(n, type) ((type **)(void *)malloc_array(n, sizeof(type)))
#define MALLOC_2D_ARRAY(n1, n2, type) ((type **)(void *)malloc_array(n1, (n2) * sizeof(type)))
Passing the command as a string is very inefficient. You should use an int or an enum for the command, but you can use the above macros and code in your program this way:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <stdint-gcc.h>
int main() {
int string_len = 100;
int num = 10; // get number from user;
char **my_arr = MALLOC_2D_ARRAY(num, string_len, char);
FREE_ARRAY(my_arr, num);
return 0;
}
Assuming there is a function like this
int foo (char** str, int x)
{
char* p = *str + x;
foo2(&p); // declared as int foo2 (char** );
}
(oversimplified of course, the real function is recursive and much more complicated)
I've tried to do this:
int foo (char** str, int x)
{
foo2(&(*str + x));
}
But the compiler failed with error:
error: lvalue required as unary '&' operand
Why did the compiler shoot out with this error and how do I pass the pointer to a pointer to string x-byte(s) forwards, without declaring a variable and use its own address?
EDIT
Seems like there is some misunderstanding so I will post a complete simulation of what I want to achieve.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* string = "This is a sample string.";
char* ptr;
int randomizer;
int receive_string (char* buffer, int size) // recv
{
int i = 0;
if(ptr == NULL)
ptr = string;
for(i = 0; *ptr != '\0' && i < size; ptr++)
{
if(randomizer == 2)
{
randomizer++;
break;
}
buffer[i] = *ptr;
i++;
randomizer++;
}
if(*ptr == '\0')
{
buffer[i] = *ptr;
i++;
}
return i;
}
int read_string (char* *buffer, int size, int alloc)
{
int bytes = 0;
printf("Reading string..\n");
if(*buffer == NULL && alloc == 1)
{
printf("Allocating buffer..\n");
*buffer = calloc(size, sizeof(char));
}
bytes = receive_string(*buffer, size);
if(bytes == (-1))
{
return(-1);
}
if(bytes == 0)
{
return 0;
}
if(bytes < size)
{
char* p = *buffer + bytes;
//int temp = read_string(&p, size - bytes, 0); // works
//int temp = read_string(&(char *){&(*buffer)[bytes]}, size - bytes, 0); // works
int temp = read_string(buffer + bytes, size - bytes, 0); // doesn't work
if(temp > 0)
bytes += temp;
else return bytes;
}
return bytes;
}
int main()
{
char* buffer = NULL;
int bytes = read_string(&buffer, strlen(string) + 1, 1);
printf("[%u][%s]\n", bytes, buffer);
if(buffer)
free(buffer);
return 0;
}
The randomizer is the dumbest quickie to "simulate" a recv() that can not receive all bytes. This implementation simulates recv() but instead of reading from a socket queue it reads from a global string.
(*str + x) is not an lvalue as it is a temporay value that does not have an address so you cannot take its address with &. Even if the compiler stored the value in a temporary variable in RAM so its address could be taken how would you reference its value afterwards if foo2() modified the contents of the temporay variable.
Therefore you need to store the value in a temporary variable yourself.
if you want to pass the pointer to pointer to the particular char
foo2(&(char *){&(*str)[x]});
or
I think the following code is what you are trying to do. For kicks, I made it recursive and tested it with the alphabet for a string. Variables cnt and lmt need to be global. It will show a shrinking string if you run it. Just be sure to keep p and lmt small enough to not overflow the string.
void foo(char *s, int p) {
cnt++;
printf("%s\n", s);
if(cnt != lmt) foo(&s[p], p);
}
i wrote a little console program which stores words in an array, represented by char** test_tab, and then print them.
The program works fine as long as it does not go through the conditionalrealloc() (e.g if i increase sizeto 1000).
But if realloc() get called the program crashes during the array printing, probably because the memory is messed up in there.
#include <stdio.h>
#include <stddef.h>
#include <stdlib.h>
char* get_word();
int main(int argc, char* argv[])
{
size_t size = 100;
size_t nb_pointer = 0;
char** test_tab = malloc(size * sizeof *test_tab);
char** temp_tab;
while((*(test_tab + nb_pointer) = get_word()) != NULL)
{
nb_pointer++;
if(nb_pointer >= size)
{
size += 100;
temp_tab = realloc(test_tab, size);
if(temp_tab != NULL)
test_tab = temp_tab;
else
{
free(test_tab);
exit(1);
}
}
}
for(nb_pointer = 0; *(test_tab + nb_pointer) != NULL; nb_pointer++)
printf("%s\n", *(test_tab + nb_pointer));
free(test_tab);
return 0;
}
Can someone explains me what i am doing wrong right here? Thanks.
The amount of memory in the realloc is not calculated correctly.
temp_tab = realloc(test_tab, size);
should be
temp_tab = realloc(test_tab, size * sizeof *test_tab);
Every time you are trying to push one string and at the same time take all the previously pushed string with you. Now string means char * & hence you need to use sizeof(char*) * size & then you need to allocate the memory to the string again to store the actual character..However you can also approach in this way
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
static int size = 0; // static global means no risk while including this file
char** push(char** memptr, char* data) {
size++;
if (size == 1)
memptr = (char**)malloc(size * sizeof(char*));
else
memptr = (char**)realloc(memptr, size* sizeof(char*));
memptr[size - 1] = (char*)malloc(sizeof(char) * strlen(data) + 1);
strncpy(memptr[size - 1], data, strlen(data));
memptr[size - 1][strlen(data) -1] = '\0'; // over writing the `\n` from `fgets`
return memptr;
}
int main() {
char buf[1024];
int i;
static char** memptr = NULL;
for (i = 0; i < 5; i++){
fgets(buf, 1024, stdin);
memptr = push(memptr, buf);
}
for (i = 0; i < size; i++)
printf("%s\n", memptr[i]);
return 0;
}
My program compiles but I am not working with pointers and realloc correctly. I have tried looking at other examples but I can't seem to translate it to my own program. The point of the program is to read in words from a file and increment the count if they appear more than once. Once the array of structs goes over my base (5), I want to realloc space, copy the array over and then add the next word.
Any help would be greatly appreciated!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define BASE 5
#define MAX 50
typedef char *string;
struct wordCount
{
string word;
unsigned int count;
};
int main (void)
{
unsigned int i;
unsigned int incremented;
unsigned int j;
char temp [40];
struct wordCount wordArray[BASE];
struct wordCount *holder;
FILE *infile;
j = 0;
infile = fopen("input.txt","r");
while (fscanf(infile, "%s", temp) == 1) {
incremented = 0;
for (i = 0; i < j; i++){
if(strcmp(temp,wordArray[i].word) == 0){
wordArray[i].count++;
incremented++;
}
}
if (incremented == 0){
if (j<BASE){
wordArray[j].word = (char *)malloc((strlen(temp)+1) *
sizeof(char));
strcpy(wordArray[j].word,temp);
wordArray[j].count = 1;
j++;
} else {
holder = realloc(wordArray, sizeof(wordArray) +1);
*wordArray = *holder;
wordArray[j].word = (char *)malloc((strlen(temp)+1) * sizeof(char));
strcpy(wordArray[j].word,temp);
wordArray[j].count = 1;
j++;
}
}
}
fclose(infile);
/* bring in next file*/
/*delete du plicates */
/*sort*/
for (i = 0; i < j; i++) {
printf("%s ", wordArray[i].word);
printf("%d\n", wordArray[i].count);
}
/* and when done:*/
for(i = 0; i < j; i++){
free(wordArray[i].word);
}
return 0;
}
Here's the most obvious place you're going wrong:
holder = realloc(wordArray, sizeof(wordArray) +1);
Note this line from the man page of realloc():
void *realloc(void *ptr, size_t size);
...
Unless ptr is NULL, it must have been returned by an earlier call to malloc(), calloc() or realloc().
Your wordArray is a statically allocated array, it was not dynamically allocated via malloc() or friends.