I'm trying to create an array from another array, if I have char *arr[100] = {"Hi", "&&", "hello", 0}; I want to make it be new[0] = "hi"; new[1] = "hello"; my code below doesn't seem to work. How can I fix this?
#include <stdio.h>
#include <string.h>
void split_by_word(char *av[], char **arr, char *word)
{
int i = 0;
int j = 0;
while (strcmp(*arr, word) == 0)
arr++;
if (!arr)
return ;
while (arr[i])
{
strcat(av[j], arr[i]);
if (strcmp(*arr, word) == 0)
j++;
i++;
}
}
int main()
{
char *av[100];
char *arr[100] = {"hi", "&&", "hello", 0};
memset(av, 0, sizeof(char *) * 100);
split_by_word(av, arr, "&&");
return 0;
}
Given the array
char *arr[] =
{
"Hello", "good",
"morning", "out",
"hello", "good",
"afternoon", 0
};
Output when I split by out (split_by_word(av, arr, "out"));
av[0] = "hello good morning";
av[1] = "hello good afternoon";
You need to allocate space for the new 2D array for a start. For simplicity, I allocated one with a size of 100 x 10.*
Moreover, the logic is more simple, I would say, loop over your array and if it is not the word, then copy it, otherwise do nothing (skip it, if it's the word in other words).
So, a basic, good example to start, is:
#include <stdio.h>
#include <string.h>
void split_by_word(char av[100][10], char **arr, char *word)
{
int i = 0, j = 0;
while(arr[i])
{
// if not 'word', copy
if(strcmp(arr[i], word))
strcpy(av[j++], arr[i]);
++i;
}
}
int main()
{
int i;
char av[100][10] = {{0}};
char *arr[100] = {"hi", "&&", "hello", 0};
split_by_word(av, arr, "&&");
for(i = 0; i < 2; ++i)
printf("%s\n", av[i]);
return 0;
}
Output:
Georgioss-MacBook-Pro:~ gsamaras$ gcc -Wall main.c
Georgioss-MacBook-Pro:~ gsamaras$ ./a.out
hi
hello
*For a 2D dynamically allocated array, I would do it like this 2d-dynamic-array-c.
Here's some code that seems to work according to the requirements of your revised question. I have little doubt that it could be improved with some diligence — particularly in split_by_word(). Your revised requirement seems to concatenate strings where it was certainly not clear that your original requirement did.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static void split_by_word(char **av, char **arr, char *word)
{
while (*arr != 0)
{
if (strcmp(*arr, word) == 0)
av++;
else if (*av == 0)
*av = strdup(*arr);
else
{
size_t len = strlen(*av) + strlen(*arr) + 2; // 1 for null byte, 1 for blank
void *space = realloc(*av, len);
if (space == 0)
{
fprintf(stderr, "Memory allocation failed (%zu bytes)\n", len);
exit(EXIT_FAILURE);
}
*av = space;
strcat(*av, " ");
strcat(*av, *arr);
}
arr++;
}
*++av = 0; // Null terminate pointer list
}
static void free_words(char **words)
{
while (*words != 0)
{
free(*words);
*words++ = 0;
}
}
static void print_words(char **words)
{
for (int i = 0; words[i] != 0; i++)
printf("%d: [%s]\n", i, words[i]);
}
int main(void)
{
char *av[100] = { 0 };
char *arr1[100] = { "hi", "&&", "hello", 0 };
split_by_word(av, arr1, "&&");
print_words(av);
free_words(av);
char *arr2[] =
{
"Hello", "good",
"morning", "out",
"hello", "good",
"afternoon", 0
};
split_by_word(av, arr2, "out");
print_words(av);
free_words(av);
return 0;
}
Sample output:
0: [hi]
1: [hello]
0: [Hello good morning]
1: [hello good afternoon]
You need to insure you understand that your arr is an array of pointers to string literals within which you have tokens that indicate where to separate the contents of the array into separate strings made up of the literals up to that point, and that arr is ultimately terminated by a sentinel nul.
One issue that has been skirted, is how to handle changes in the length of the strings created by the words in arr. Depending on the length of the words in arr, how do you insure you have adequate space for the combined strings that make up the results array?
You can either guess and set a static storage size for each element in the result array (hopefully large enough for any arr you need to separate), or you can dynamically allocate (allocate/reallocate as needed). That way you insure you can handle the contents of arr in your result array.
There are many ways to do this and many routines you can use. Regardless, the approach is basically the same. Read each word in arr, insure the result string has adequate storage, and then concatenate the word from arr to the result string. One approach would be as follows:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXS 16
int split_by_word (char **res, char **arr, char *tok);
void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc);
int main (void) {
char *arr[] = { "Hello", "good",
"morning", "out",
"hello", "good",
"afternoon", 0 },
*res[sizeof arr/sizeof *arr] = { NULL },
*tok = "out";
if (split_by_word (res, arr, tok) > 0)
for (int i = 0; res[i]; i++) {
printf ("%s\n", res[i]);
free (res[i]);
}
return 0;
}
int split_by_word (char **res, char **arr, char *tok)
{
int aidx = 0, cidx = 0, ridx = 0; /* array, current and result index */
size_t szres = MAXS; /* current size of res[ridx] */
if (!res || !arr || !tok) return -1; /* validate parameters */
if (!(res[ridx] = calloc (szres, sizeof *(res[ridx])))) /* allocate result */
return -1;
while (arr[aidx]) {
if (strcmp (arr[aidx], tok) == 0) { /* separator found */
*(res[ridx] + cidx) = 0; /* nul-terminate */
ridx++; /* advance result index */
szres = MAXS; /* reset alloc size, alloc */
if (!(res[ridx] = calloc (szres, sizeof *(res[ridx]))))
return -1;
cidx = 0; /* reset current index */
}
else { /* append word from arr to res */
size_t len = strlen (arr[aidx]), /* get length */
reqd = cidx ? len + 2 : len + 1; /* add space and nulbyte */
if (cidx + reqd > szres) /* check space, realloc */
res[ridx] = xrealloc (res[ridx], sizeof *(res[ridx]), &szres,
cidx + reqd);
/* write word to result */
snprintf (res[ridx] + cidx, reqd, cidx ? " %s" : "%s", arr[aidx]);
cidx += reqd - 1; /* advance current index */
}
aidx++; /* advance array index */
}
*(res[ridx] + cidx) = 0; /* nul-terminate */
return ridx ? ridx : cidx ? 1 : ridx; /* return strings in results */
}
/** realloc 'ptr' to 'nelem' of 'psz' to 'nelem + inc' of 'psz'.
* returns pointer to reallocated block of memory with all new
* memory initialized to 0/NULL. return must be assigned to
* original pointer in caller.
*/
void *xrealloc (void *ptr, size_t psz, size_t *nelem, size_t inc)
{ void *memptr = realloc ((char *)ptr, (*nelem + inc) * psz);
if (!memptr) {
fprintf (stderr, "realloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
} /* zero new memory (optional) */
memset ((char *)memptr + *nelem * psz, 0, inc * psz);
*nelem += inc;
return memptr;
}
Above split_by_word returns an integer value indicating the number of strings within the results array or -1 on error.
Example Use/Output
$ ./bin/splitap
Hello good morning
hello good afternoon
Verify Your Memory Use
If you allocate memory, it is your responsibility to preserve a pointer to the begninning of each block, so it can be freed when no longer needed. On Linux, valgrind is the tool of choice. Simply run your program through it. (there are similary memory error checking routines for each OS)
$ valgrind ./bin/splitap
==13491== Memcheck, a memory error detector
==13491== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==13491== Using Valgrind-3.11.0 and LibVEX; rerun with -h for copyright info
==13491== Command: ./bin/splitap
==13491==
Hello good morning
hello good afternoon
==13491==
==13491== HEAP SUMMARY:
==13491== in use at exit: 0 bytes in 0 blocks
==13491== total heap usage: 4 allocs, 4 frees, 104 bytes allocated
==13491==
==13491== All heap blocks were freed -- no leaks are possible
==13491==
==13491== For counts of detected and suppressed errors, rerun with: -v
==13491== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
You want to validate that each allocation has been freed, no memory leaks are possible, and that there are no errors in the way you have used the memory you have allocated (e.g. invalid reads/writes, etc..)
Since it looks (from your declarations) like you only want to store pointers in the new array, there is no need for strcat() or strcpy(). The first loop in your function appears to be skipping initial delimiters, but you can do this in the main loop. Here is a modified version of your code:
#include <stdio.h>
#include <string.h>
void split_by_word(char *av[], char **arr, char *word)
{
size_t i = 0;
size_t j = 0;
while (arr[i]) {
if (strcmp(arr[i], word)) {
av[j] = arr[i];
++j;
}
++i;
}
}
int main(void)
{
char *av[100];
char *arr[100] = {"hi", "&&", "hello", 0};
memset(av, 0, sizeof(char *) * 100);
split_by_word(av, arr, "&&");
for (size_t i = 0; av[i]; i++)
puts(av[i]);
return 0;
}
After arr is passed to split_by_word(), av contains pointers to the string literals "hi" and "hello":
λ> ./a.out
hi
hello
If, on the other hand, you actually want the new array to contain copies of the strings, you must declare av so that there is space for these copies, and you need to use strcpy(), or some similar function, to copy the characters to the array. Here is another version that accomplishes this. Note that the size of the largest string must be known in advance; here I have #defined a constant for this purpose. Also note that the display loop is slightly different from the previous loop. The first display loop continued until a NULL pointer was encountered, but in the second version the loop continues until an empty string is encountered. The output is the same as before.
#include <stdio.h>
#include <string.h>
#define MAXWORD 100
void split_by_word(char av[][MAXWORD], char **arr, char *word)
{
size_t i = 0;
size_t j = 0;
while (arr[i]) {
if (strcmp(arr[i], word)) {
strcpy(av[j], arr[i]);
++j;
}
++i;
}
}
int main(void)
{
char av[100][MAXWORD] = { { 0 } };
char *arr[100] = {"hi", "&&", "hello", 0};
split_by_word(av, arr, "&&");
for (size_t i = 0; av[i][0]; i++)
puts(av[i]);
return 0;
}
UPDATE
I have modified the previous solution to meet the refined requirements suggested in your revised example. The constant MAXWORD is now MAXLEN, and is large enough to hold quite a few words. strcat() is used instead of strcpy(), and an additional space character is added to the end of the string every time a word is added. The string-index j is incremented only when the delimiter string is encountered.
Note that there are no checks to ensure that there is room for a new string of words in av (which can currently hold up to 99 strings and one empty string as a terminator), or room for a new word in a string (999 characters plus room for a '\0' terminator seems reasonably generous). There is no dynamic allocation here, and if you need this perhaps Jonathan Leffler's solution is more to your taste.
#include <stdio.h>
#include <string.h>
#define MAXLEN 1000
void split_by_word(char av[][MAXLEN], char **arr, char *word)
{
size_t i = 0;
size_t j = 0;
while (arr[i]) {
if (strcmp(arr[i], word)) {
strcat(av[j], arr[i]);
strcat(av[j], " ");
} else {
++j;
}
++i;
}
}
int main(void)
{
char av[100][MAXLEN] = { { 0 } };
char *arr[] =
{
"Hello", "good",
"morning", "out",
"hello", "good",
"afternoon", 0
};
split_by_word(av, arr, "out");
for (size_t i = 0; av[i][0]; i++)
puts(av[i]);
return 0;
}
Here is the output of this program:
λ> ./a.out
Hello good morning
hello good afternoon
Bounds Checking
I couldn't bear to leave this without adding some checks on array bounds to avoid undefined behavior in case of unexpected input sizes. Here is a version of the split_by_word() function that only adds a new string to av if there is room, and only adds a new word to a string if there is room. If there is not enough space for the new word, the function skips to the next delimiter, or the end of arr, whichever comes first. I added a MAXNUM constant for the maximum number of strings to be stored, to replace the hard-coded 100 from previous versions. I have no doubt that you could improve this function.
#define MAXNUM 100
#define MAXLEN 1000
void split_by_word(char av[][MAXLEN], char **arr, char *word)
{
size_t i = 0;
size_t j = 0;
while ((j + 1) < MAXNUM && arr[i]) {
if (strcmp(arr[i], word)) {
/* Verify space for word + extra space */
if ((strlen(av[j]) + strlen(arr[i]) + 1) < MAXLEN) {
strcat(av[j], arr[i]);
strcat(av[j], " ");
} else { // No space: skip to next delimiter
++i;
while (arr[i] && strcmp(arr[i], word)) {
++i;
}
++j; // increment to next string
}
} else {
++j; // increment to next string
}
if (arr[i]) ++i; // increment i if not already at end
}
}
Related
So I'm currently trying to write a C program to track the longest word(s) from argv.
It's been going great! Until I tried to reallocate a character double pointer, it seems to think it's an invalid pointer.
The exact error I'm getting is;
realloc(): invalid pointer
fish: Job 1, './longest-strings.o hello...' terminated by signal SIGABRT (Abort)
I'm creating this double character pointer through the return of a function, is this possibly the error? I'm pretty sure my use of realloc is correct, and I can't quite seem to trace the issue.
Any help would be massively appreciated!
/*
* Author: Smallblue2
* Description: Program finds the longest word in an input string
*
* Input: A string from cmd line
* Output: The longest word in a string
*/
// Header files
#include <stdio.h>
#include <stdlib.h>
// Function prototypes
int stringLength(char *string);
void longestWords(char **strings, int amt);
char **reset(char *string);
void display(char **longest, int len_array);
// Main function
int main(int argc, char **argv)
{
char **strings = &*(argv + 1);
longestWords(strings, argc - 1);
return 0;
}
// Find the length of a string
int stringLength(char *string)
{
int length = 0;
while (*string != '\0')
{
length++;
string++;
}
return length;
}
// Finds the longest word(s) from argv
void longestWords(char **strings, int amt)
{
// Set up variables & pointers
int len_array = 1;
// Assign the first string to be the longest
char **longest = reset(*(strings));
int longest_len = stringLength(*(longest));
int length = 0;
// Loop through the rest of the strings
for (int i = 1; i < amt; i++)
{
// Find the length of the current string
length = stringLength(*(strings + i));
// If it is larger, reset the longest array and place the
// new string inside
if (length > longest_len)
{
longest_len = length;
longest = reset(*(strings + i));
len_array = 1;
// Else, expand the longest array's memory and add the
// additional string inside
} else if (length == longest_len) {
len_array++;
char **temp_longest = (char **)realloc(longest, len_array * sizeof(char *));
if (!longest)
{
printf("Error: Memory allocation failed!\n");
free(longest);
return;
}
longest = temp_longest;
*(longest + len_array - 1) = *(strings + i);
}
}
// Display the longest word(s)
display(longest, len_array);
free(longest);
longest = NULL;
return;
}
// Resets the longest word array
char **reset(char *string)
{
char **longest = (char **)malloc(sizeof(char *));
if (!longest)
{
printf("Error: Memory Allocation Failed!\n");
return NULL;
}
longest = &string;
return longest;
}
// Displays the longest word(s)
void display(char **longest, int len_array)
{
for (int i = 0; i < len_array; i++)
{
printf("%s\n", *(longest + i));
}
return;
}
I've tried to use both calloc and malloc, I tried executing the script where realloc wouldn't occur and then apparently free() believes there's an invalid pointer too. Really lost here.
Here are the two minimal changes:
stringLength should handle a NULL pointer.
int stringLength(char *string)
{
int length = 0;
while (string && *string != '\0')
{
length++;
string++;
}
return length;
}
Or perhaps:
#include <string.h>
size_t stringLength(char *string)
{
return string ? strlen(string) : 0;
}
reset() leaks the memory you just allocated, and you don't want to take the address of an argument which is out of scope when the function returns. Not entirely sure what the point of the function is but try this instead:
char **reset(char *string)
{
char **longest = malloc(sizeof(char *));
if (!longest)
{
printf("Error: Memory Allocation Failed!\n");
return NULL;
}
*longest = string;
return longest;
}
and example output:
$ ./a.out hello...
hello...
./a.out hello world!
world!
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 make a function that receives a dynamic string and removes from it all occurrences of the character also passed as a parameter.
The string should finally contain just enough space to contain characters not deleted
void delete(char *cad, char c){
int i, cont = 0;
char *aux = NULL;
i = 0;
while(cad[i] != '\0'){
if(cad[i] != c){
aux = (char*)realloc(aux, sizeof(char) * cont + 1);
aux[cont] = cad[i];
cont++;
}
i++;
}
cad = (char*)realloc(cad, sizeof(char) * cont);
i = 0;
while(aux[i] != '\0'){
cad[i] = aux[i];
i++;
}
}
Now I have a segmentation fault
You do not check the result of the realloc.
IMO it will be better to return the pointer to the new string instead of using double pointer. Double pointer may cause hard to track memory leaks, and function will not work with the const strings - for example string literals
You do not null character terminate the string.
In this example, I did not change your allocation algorithm but in real life more efficient will be first to count how much memory you need to allocate, allocate it and then process the string again:
char *delete(const char *cad, char c){
size_t nchars = 0;
char *aux = NULL;
char *temp;
while(*cad)
{
if(*cad != c)
{
temp = realloc(aux, sizeof(*temp) * nchars + 1);
if(temp)
{
aux = temp;
aux[nchars++] = *cad;
}
else
{
/* handle allocation error */
free(aux);
aux = NULL;
break;
}
}
cad++;
}
if(aux) aux[nchars] = 0;
return aux;
}
Some minor changes: use objects instead of types in sizeof and do not cast result of malloc. You can also add NULL pointer parameter check.
Every time you are reallocing inside the while loop, you are essentially giving the variable aux a new address each time.
I advise you to not do that and allocate the memory you want to allocate at the start of the function.
You will need to calculate how much memory you would need before allocating the memory. That is, count how much element you would delete.
If you want me to further elucidate or add a code fragment, please feel free to ask it in the comments.
Instead of many calls to realloc() I would just perform an in-place substitution of the characters; this substitution leaves unused allocated characters at the end of the string and is illustrated by the delete_no_realloc() function below.
If you want to get rid of these unused ending characters in the allocated string, then only one call to realloc() is needed as illustrated by the delete() function below.
Note that when a function uses realloc() on a parameter which is a pointer, it must obtain the address of this pointer to adjust it with the result of realloc().
/**
gcc -std=c99 -o prog_c prog_c.c \
-pedantic -Wall -Wextra -Wconversion \
-Wwrite-strings -Wold-style-definition -Wvla \
-g -O0 -UNDEBUG -fsanitize=address,undefined
**/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
size_t // new length
delete_no_realloc(char *cad,
char c)
{
size_t w=0;
for(size_t r=0; cad[r]; ++r)
{
char ch=cad[r];
if(ch!=c)
{
cad[w++]=ch; // store and advance write index
}
}
cad[w]='\0'; // ensure string termination
return w;
}
void
delete(char **cad_ptr,
char c)
{
char *cad=*cad_ptr; // forget this embarrassing indirection
size_t new_length=delete_no_realloc(cad, c);
cad=realloc(cad, new_length+1);
if(cad==NULL)
{
abort();
}
*cad_ptr=cad; // don't forget to adjust the string
}
int
main(void)
{
const char *msg="this is a message";
char *cad=malloc(strlen(msg)+1);
if(cad==NULL)
{
abort();
}
strcpy(cad, msg);
printf("before: <%s>\n", cad);
delete(&cad, 's'); // pass the address of the string
printf("after: <%s>\n", cad);
free(cad);
return 0;
}
You can simplify your delete() function by simply using a read and write index within the original string, removing all c characters found, and then make a single call to realloc() to reallocate storage to exactly fit the remaining characters.
You can do something like:
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
A full example would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void delete (char **cad, char c)
{
if (!*cad || !**cad) /* check if cad is NULL or empty-string */
return;
size_t write = 0; /* write index */
for (size_t read = 0; (*cad)[read]; read++) { /* loop over each char in cad */
if ((*cad)[read] != c) /* if char not c */
(*cad)[write++] = (*cad)[read]; /* copy incrementing write */
}
(*cad)[write] = 0; /* nul-terminate */
void *tmp = realloc (*cad, write + 1); /* realloc to exact size */
if (!tmp) { /* validate realloc */
perror ("realloc-cad");
return;
}
*cad = tmp; /* assign reallocated block to *cad */
}
int main (int argc, char **argv) {
if (argc < 3) {
fputs ("usage: ./prog \"string with c\" c\n", stderr);
return 1;
}
size_t len = strlen (argv[1]);
char *s = malloc (len + 1);
if (!s) {
perror ("malloc-s");
return 1;
}
memcpy (s, argv[1], len + 1);
printf ("%s (%zu chars)\n", s, len);
delete (&s, *argv[2]);
printf ("%s (%zu chars)\n", s, strlen(s));
free (s);
}
Example Use/Output
$ ./bin/delete_c_realloc "nmyn ndogn nhasnn nnfleasnnn" n
nmyn ndogn nhasnn nnfleasnnn (28 chars)
my dog has fleas (16 chars)
Look things over and let me know if you have questions.
There are four main problems with your function implementation.
The first one is that the function accepts the pointer to the source string by value. That is the parameter cad is initialized by the value of the pointer used as an argument. As a result changing the variable cad does not influence on the original pointer.
The second one is that you are not checking whether a call of realloc was successful. As a result the function can invoke undefined behavior.
The third one is that it is inefficient to reallocate the string each time when a new character is appended.
And at last the fourth one is that the result dynamically allocated array does not contain a string because you forgot to append the terminating zero character '\0'.
If you want to change within the function a value of the original pointer you should either to return from the function the result pointer obtained in the function and assign it to the original pointer in the caller. Or you should pass the original pointer to the function by reference. In C passing by reference means passing an object (that can be a pointer) indirectly through a pointer to it.
Here is a demonstrative program that shows the function implementation when the original pointer is accepted by the function by reference.
The function also returns a pointer to the result string that can be checked in the caller whether the reallocation of dynamic memory within the function was successful.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_char( char **s, char c )
{
char * result = *s;
if ( c != '\0' )
{
char *dsn = *s;
const char *src = *s;
do
{
if ( *src != c )
{
if ( dsn != src )
{
*dsn = *src;
}
++dsn;
}
} while ( *src++ );
char *tmp = realloc( *s, ( dsn - *s ) * sizeof( char ) );
if( tmp != NULL ) *s = tmp;
result = tmp;
}
return result;
}
int main(void)
{
char *s = malloc( 12 );
strcpy( s, "H#e#l#l#o!" );
puts( s );
if ( remove_char( &s, '#' ) ) puts( s );
free( s );
return 0;
}
The program output is
H#e#l#l#o!
Hello!
Another approach is to write a function that does not change the source string but creates dynamically a new string that contains the source string excluding the specified character. Such a function is more flexible because you can call it with string literals. If the source string also was dynamically allocated then the caller of the function after a successful call it can just free the source string.
Here is a demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * remove_copy( const char *s, char c )
{
size_t src_len = strlen( s );
size_t dsn_len = src_len;
if ( c != '\0' )
{
for ( const char *p = s; ( p = strchr( p, c ) ) != NULL; ++p )
{
--dsn_len;
}
}
char *result = malloc( ( dsn_len + 1 ) * sizeof( char ) );
if ( result != NULL )
{
const char *src_s = s;
char *dsn_s = result;
if ( dsn_len != src_len )
{
for ( const char *p = src_s;
( p = strchr( src_s, c ) ) != NULL;
src_s = p + 1 )
{
if ( p - src_s != 0 )
{
memcpy( dsn_s, src_s, p - src_s );
dsn_s += p - src_s;
}
}
}
strcpy( dsn_s, src_s );
}
return result;
}
int main(void)
{
char s[] = "H#e#l#l#o!";
puts( s );
char *p = remove_copy( s, '#' );
if ( p != NULL ) puts( p );
free( p );
return 0;
}
The program output is the same as shown for the preceding demonstrative program that is
H#e#l#l#o!
Hello!
For C++ - I can add each char to each index in string array:
string *x=new string[10];
x[0] += "a";
x[0] += "b";
x[0] += "c";
x[1] += "x";
x[1] += "y";
x[1] += "z";
cout << "x[0]=" << x[0] << endl; // would be "abc"
cout << "x[1]=" << x[1] << endl; // would be "xyz"
How can I do same functionality in C? I have buff2 pointer to a char array and am trying to add char value from each index of buf. I keep getting weird values when I print out buff2 value.
char buf[255];
char *buff2;
int i=0, count=0;
buff2=(char*)malloc(512*sizeof(char));
while((n = read(fd, buf, sizeof(buf[g]))) > 0){
for(i=0; i<n; i++){
if(buf[i] == '\n'){
l++;
count2++;
}
else
{
buff2[count2]+=buf[i];
}
}
There are several problems in your C code
buff is an array for nothing because you only use buff[0]
the variable l seems never defined/initialized, and you modify it for nothing
buff2[count2]+=buf[i]; always modify the same buff2[count2] until a newline because you do not increase buff2 in that case but only when reading a newline, are you sure you want that ?
you do not end buff2 with a null character, that probably explain I keep getting weird values when I print out buff2 value.
you do not have a protection in case you write out of buff2 producing an undefined behavior
string *x=new string[10];
can be in C
char ** x = calloc(10, sizeof(char *));
I use calloc to initialize with null pointers
and an equivalent of :
x[0] += "a";
can be
strCat(&x[0], "a");
with:
char * const strCat(char ** p, const char * s)
{
if (s != NULL) {
if (*p == NULL)
*p = strdup(s);
else {
size_t len = strlen(*p);
*p = realloc(*p, len + strlen(s) + 1); /* may be detect realloc returns NULL on error */
strcpy(*p + len, s);
}
}
return *p;
}
So for instance :
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * const strCat(char ** p, const char * s)
{
if (s != NULL) {
if (*p == NULL)
*p = strdup(s);
else {
size_t len = strlen(*p);
*p = realloc(*p, len + strlen(s) + 1); /* may be detect realloc returns NULL on error */
strcpy(*p + len, s);
}
}
return *p;
}
int main()
{
char ** x = calloc(10, sizeof(char *));
strCat(&x[0], "a");
strCat(&x[0], "b");
strCat(&x[0], "c");
strCat(&x[1], "x");
strCat(&x[1], "y");
strCat(&x[1], "z");
printf("x[0]=%s\n", x[0]);
printf("x[1]=%s\n", x[1]);
free(x[0]);
free(x[1]);
free(x);
return 0;
}
Compilation and execution:
% gcc -Wall a.c
% ./a.out
x[0]=abc
x[1]=xyz
%
Running under valgrind:
% valgrind ./a.out
==113490== Memcheck, a memory error detector
==113490== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==113490== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==113490== Command: ./a.out
==113490==
x[0]=abc
x[1]=xyz
==113490==
==113490== HEAP SUMMARY:
==113490== in use at exit: 0 bytes in 0 blocks
==113490== total heap usage: 7 allocs, 7 frees, 98 bytes allocated
==113490==
==113490== All heap blocks were freed -- no leaks are possible
==113490==
==113490== For counts of detected and suppressed errors, rerun with: -v
==113490== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
%
Note however each time you concatenate a new string it is needed to go through the current string to know its length, this is not done by std::string whose knows the used length whatever the way for that, as this is the case in the answer of KamilCuk
How can I do same functionality in C?
First implement/invent a "string".
After that you can implement the functionality. Remember about proper error handling. I just used abort() for brevity below, in normal code destructors should be run.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
typedef struct string {
char *begin;
char *end;
size_t free;
} string;
void string_init(string *t) {
t->begin = t->end = NULL;
t->free = 0;
}
void string_fini(string *t) {
free(t->begin);
}
// iadd convention from python
int string_iadd_cstr(string *t, const char *str) {
const size_t addlen = strlen(str);
if (t->free < addlen + 1) {
const size_t curlen = t->end - t->begin;
const size_t newlen = curlen + 1 + addlen;
void *tmp = realloc(t->begin, newlen);
if (tmp == NULL) {
return -1;
}
t->begin = tmp;
t->end = t->begin + curlen;
t->free = newlen - curlen;
}
memcpy(t->end, str, addlen + 1);
t->end += addlen;
t->free -= addlen;
return 0;
}
int string_print(string *t) {
return printf("%s", t->begin);
}
int main() {
// string *x=new string[10];
string *x = malloc(10 * sizeof(*x));
if (x == NULL) abort();
for (size_t i = 0; i < 10; ++i) {
string_init(&x[i]);
}
if (string_iadd_cstr(&x[0], "a")) abort();
if (string_iadd_cstr(&x[0], "b")) abort();
if (string_iadd_cstr(&x[0], "c")) abort();
if (string_iadd_cstr(&x[1], "x")) abort();
if (string_iadd_cstr(&x[1], "y")) abort();
if (string_iadd_cstr(&x[1], "z")) abort();
// cout << "x[0]=" << x[0] << endl;
printf("%s", "x[0]=");
string_print(&x[0]);
printf("\n");
fflush(stdout);
// cout << "x[1]=" << x[1] << endl;
printf("%s", "x[1]=");
string_print(&x[1]);
printf("\n");
fflush(stdout);
// run destructors
for (size_t i = 0; i < 10; ++i) {
string_fini(&x[i]);
}
free(x);
}
Here is a simple example if you do not want to use strcat. This is a minimal example to demonstrate how one could concatenate strings, things like reallocation of memory have not been implemented.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
char buf[30] = "LOREM \n IPSUM DOLOR SIT AMET\0";
char *buff2;
int i = 0, n = 30;
buff2 = (char*)malloc(512 * sizeof(char));
//set first element of buff2 to be terminating null char
buff2[0] = '\0';
for(i = 0; i < n; i++) {
if(buf[i] != '\n') {
buff2[i + 1] = '\0'; // shift terminating null char forward
buff2[i] = buf[i];
} else {
buff2[i + 1] = '\0'; // shift terminating null char forward
buff2[i] = 'n';
}
}
printf("%s\n",buff2);
return EXIT_SUCCESS;
}
It replaces newlines with an "n" character; you can change this if you want. Of course, you want to be careful only to address elements which have actually been allocated. Does not include function to read from file because we don't have the file; this is easily implemented. I suggest you look into fgetc.
I am working a function that needs to be re-entrant - the function is given a memory buffer as an argument and should use such buffer for all its memory needs. In other words, it can't use malloc, but rather should draw the memory the supplied buffer.
The challenge that I ran into is how to overlay an array of strings over a char array of given size (the buffer is supplied as char *), but my result is array of strings (char **).
Below is a repro:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFER_SIZE 100
#define INPUT_ARRAY_SIZE 3
char *members[] = {
"alex",
"danny",
"max"
};
int main() {
// this simulates a buffer that is presented to my func
char *buffer = malloc(BUFFER_SIZE);
char *orig = buffer;
memset(buffer, NULL, BUFFER_SIZE);
// pointers will be stored at the beginning of the buffer
char **pointers = &buffer;
// strings will be stored after the pointers
char *strings = buffer + (sizeof(char *) * INPUT_ARRAY_SIZE);
for(int i = 0; i < INPUT_ARRAY_SIZE; i++) {
strncpy(strings, members[i], (strlen(members[i]) + 1));
// Need to store pointer to string in the pointers section
// pointers[i] = strings; // This does not do what I expect
strings += ((strlen(members[i]) + 1));
}
for (int i=0; i < BUFFER_SIZE; i++) {
printf("%c", orig[i]);
}
// Need to return pointers
}
With the problematic line commented out, the code above prints:
alex danny max
However, I need some assistance in figuring out how to write addresses of the strings at the beginning.
Of course, if there an easier way of accomplishing this task, please, let me know.
Here take a look at this.
/* conditions :
*
* 'buffer' should be large enough, 'arr_length','arr' should be valid.
*
*/
char ** pack_strings(char *buffer, char * arr[], int arr_length)
{
char **ptr = (char**) buffer;
char *string;
int index = 0;
string = buffer + (sizeof(char *) * (arr_length+1)); /* +1 for NULL */
while(index < arr_length)
{
size_t offset;
ptr[index] = string;
offset = strlen(arr[index])+1;
strcpy(string,arr[index]);
string += offset;
++index;
}
ptr[index] = NULL;
return ptr;
}
usage
char **ptr = pack_strings(buffer,members,INPUT_ARRAY_SIZE);
for (int i=0; ptr[i] != NULL; i++)
puts(ptr[i]);