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
}
}
I am trying to return an array of string from a function and then free the memory it used. The code is below:
int main(int argc, const char * argv[])
{
for (int m = 0; m < 10000; m++) {
char **data = dataTest();
int i = 0;
while(data[i]) {
printf("%p ",data[i]);
free(data[i]);
i++;
}
printf(" address= %p.\n",data);
free(data);
}
return 0;
}
Here is the function:
char **dataTest()
{
char *row[] = {"this", "is", "a", "data", "string", NULL};
char **str = row;
char **dataReturn = (char **) malloc(sizeof(char *) * 6);
int i = 0;
while (*str) {
dataReturn[i] = malloc(sizeof(char) * strlen(*str));
strcpy(dataReturn[i++], *str);
str++;
}
return dataReturn;
}
It runs well in the beginning, but soon the error occurs. Below is the result. The address goes wrong somehow and the malloc error happens. Anyone has met the same problem before?
0x100300030 0x100300040 0x100300050 0x100300060 0x100300070 address= 0x100300000.
0x100300030 0x100300040 0x100300050 0x100300060 0x100300070 address= 0x100300000.
0x100400030 0x100300030 0x100300040 0x100300050 0x100300060 address= 0x100400000.
testC(562,0x7fff73e71310) malloc: *** error for object 0x3000000000000:
pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
0x100300060 0x100300070 0x100300030 0x100300040 0x100300050 0x3000000000000
Program ended with exit code: 9
You need to add this to just before return dataReturn; in your dataTest function:
dataReturn[i] = NULL ;
otherwise your while (data[i]) {} will continue further than wanted.
And instead of:
dataReturn[i] = malloc( sizeof(char) * (strlen(*str)) );
write:
dataReturn[i] = malloc(strlen(*str) + 1);
in order to allocate space for the terminating zero.
BTW sizeof (char) is always 1.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char *string;
int end;
size_t length;
} str;
str* new_string(size_t len)
{
str *ptr;
ptr = (str*) malloc(sizeof(str));
ptr->string = (char *)malloc(sizeof(char) * len);
ptr->length = len;
ptr->string[len] = '\0';
ptr->end = 0;
return ptr;
}
void resize_string(str *ptr, size_t new_size)
{
ptr->string = (char *)realloc(ptr->string, sizeof(char) * new_size);
if (ptr->end == ptr->length) {
ptr->string[ptr->length] = ' ';
ptr->string[new_size] = '\0';
}
ptr->length = new_size;
}
void append_string(str *ptr, char ch)
{
if (ptr->end == ptr->length) {
size_t new_length = ptr->length + 5;
resize_string(ptr, new_length);
}
ptr->string[ptr->end] = ch;
ptr->end++;
ptr->string[ptr->end] = '\0';
}
char *raw_string(str *ptr)
{
return ptr->string;
}
void delete_string(str *ptr)
{
if (ptr != NULL) {
if (ptr->string != NULL) {
free(ptr->string); //deugger here is showing the break statement and HEAP CORRUPTION DETECTED.
}
free(ptr);
}
}
int main() {
char ch;
str *string = new_string(5);
while (1) {
ch = getchar();
if(ch=='\n'){
break;
}
append_string(string, ch);
}
printf("%s",raw_string(string));
delete_string(string);
return 0;
}
The above code runs fine in ubuntu , without any seg. fault,but in visual studio 2013 the code blows at runtime. Debugger is showing break statement when i try to free(ptr->string) and HEAP CORRUPTION DETECTED ,but i don't understand why?
Sounds like you should enable checked allocs in your gcc-based environment, you will see you're corrupting the heap regardless of underlying library.
That said, you're reallocating with 1 less than expected, e.g. indexing 'new_size' (to set your NUL byte after) is out of range.
Keep getting an error on my return ret before the main () class (end of process request)
buddy.c: In function `process_request':
buddy.c:89: warning: function returns address of local variable
Error I receive , what I'm trying to do is print the results I get from my process_request to my print near the end of my main() function, help?
//used a flag
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#include <string.h>
#define F_SIZE 2
#define A_SIZE 2
#define BUDDY_SIZE 4*1024 // in bytes
// compile using gcc-o buddy buddy.c -lm
// block information
struct block_info
{
char AF_flag; // flag
int data; // data in the block
};
typedef struct block_info block;
block buddy_block[BUDDY_SIZE]; // entire buddy system to be used in this array
int block_count = 0; // number of blocks in buddy_block
int get_block_size(int num)
{
int i = 0;
for (i = 0; num < pow(2.0, (double)i); ++i);
return (int)(pow(2.0, (double)i));
}
char *process_request(char *s, int len)
{
block b;
block n;
int i, j, count, block_size = 0;
int first_buddy_size = 0;
int second_buddy_size = 0;
char ret[BUDDY_SIZE] = { 0 };
char *response[BUDDY_SIZE] = { 0 };
if (!s)
return NULL;
first_buddy_size = buddy_block[0].data;
second_buddy_size = buddy_block[1].data;
block_size = get_block_size(atoi(s));
// get the matching FREE block in the power of 2
if (*s == 'A')
{ // Allocation request
int i = 0;
char *buff = NULL;
// split the block
char strf[F_SIZE] = { 0 };
char stra[A_SIZE] = { 0 };
strf[0] = 'F';
stra[0] = 'A';
for (i = 0; block_size <= first_buddy_size / 2; ++i)
{
first_buddy_size /= 2;
sprintf(buff, "%d", first_buddy_size);
response[i] = strcat(strf, buff);
}
sprintf(buff, "%d", block_size);
response[i] = strcat(stra, buff);
// update the array
count = i;
for (i = 0, j = count; j; --j, ++i)
{
char *str = response[j];
buddy_block[i].AF_flag = *str++;
while (*str)
buddy_block[i].data = *str;
}
}
else if (*s == 'F')
{ // Free request
for (i = 1; i < block_count; ++i)
{ // traversing through the array
if (buddy_block[i].data = block_size)
{ // b.AF_flag = 'B';
i << 1;
}
}
}
// update array
count = i;
for (i = 0, j = count; j; --j, ++i)
{
char *str = response[j];
buddy_block[i].AF_flag = *str++;
while (*str)
buddy_block[i].data = *str;
}
return ret; // ------------error: warning functions returns address
// of local variable----------
}
int main(int argc)
{
block t;
int i;
char ch;
char *ret = NULL;
char line[20];
t.AF_flag = 'X'; // some junk means memory block not even accessed
t.data = 0;
for (i = 0; i < BUDDY_SIZE; i++)
buddy_block[i] = t; // initialize with 0 bytes and no information about
// Allocation/Free
// initially there is only one Free block of 4K bytes
t.AF_flag = 'F';
t.data = BUDDY_SIZE;
buddy_block[0] = t; // started the buddy block to 4096 bytes, all free to be
// allocated
++block_count;
while (1)
{
// get user input
char request[5] = { 0 }; // 'F4096' or 'A4096', max 5 chars
int correct_input = 0;
char ch;
for (i = 0, ch = 'X'; ch != '\n'; ++i)
{
ch = getchar();
if ((i == 0) && (ch != 'A' || ch != 'F'))
{
printf("Illegal token!!! : should be A or F");
correct_input = 0;
break;
}
if (ch < '0' && ch > '9')
{ // illegal code
printf("Illegal token!!! : should be 0 and 9");
}
correct_input = 1;
request[i] = ch;
}
if (correct_input)
{
// process user input
ret = process_request(request, sizeof(request));
printf("%d", ret); // [512](512A)(128A)(128F)(256F)(1024F)(2048F)
// //fprintf(stderr, "I am in stderr");
fflush(stdout);
}
}
return 0;
}
You have allocated ret on the stack. Although it is not forbidden to return an address to that the stack will be reused by any function that is called afterwards thus overwriting whatever was at that address.
You may want to consider moving this data onto the caller's stack or into dynamic memory.
char * foo() {
char string[] = "Hello world\n";
return string;
}
int main () {
printf("%s", foo());
}
Will most likely not print "Hello World!".
One right way would be:
void foo(char * buffer) {
memcpy(buffer, "Hello world\n", sizeof("Hello world\n"));
}
int main () {
char buffer[100];
foo(&buffer);
printf("%s", buffer);
}
Or with dynamic memory (prone to memory leaks):
char * foo() {
char * string = malloc(sizeof("Hello world\n"));
memcpy(string, "Hello world\n", sizeof("Hello world\n"));
return string;
}
int main () {
char * string = foo();
printf("%s", string);
free(string);
}
It means exactly what it says. You are doing
char* process_request(char*s, int len) {
...
char ret[BUDDY_SIZE] = {0};
...
return ret;
}
ret is an address to a memory location. The issue is that such memory location points to a local variable. A local variable lies in the stack, and its memory may be (probably will) reused for other variables when you call new functions.
To avoid that, return a pointer to a memory location that has been dynamically allocated (that means malloc and friends).
You are returning a local pointer from a function and that is an undefined value.
char ret[BUDDY_SIZE] = {0};
SO, your compiler is throwing that error. Assign your pointer dynamically and the error should go away.
I got some C code:
typedef struct {
size_t len;
size_t alloclen;
char *buf;
} str;
void strnappnd(str **s, const char *buf, size_t n) {
if ((*s)->len + n >= (*s)->alloclen) {
size_t nalloclen = (*s)->len + n + 1;
void *tmp = realloc((*s)->buf, nalloclen);
if (!tmp) {
printf("failure");
exit(-1);
}
(*s)->buf = tmp;
(*s)->alloclen = nalloclen;
}
memccpy((*s)->buf + (*s)->len, buf, '\0', n);
(*s)->len += n;
(*s)->buf[(*s)->len] = '\0';
}
void strfree(str **s) {
free((*s)->buf);
free(*s);
*s = NULL;
}
Apparently, the strnappnd leaks at the realloc line. Why?
Consider:
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
strnappend(&s, " World!", 7);
free(s); /* courtesy of Eric */
}
If you had something like that, the memory allocated by realloc() would leak as f() is left.
If you wrote
(*s)->buf = realloc((*s)->buf, nalloclen)
that would be a memory leak, because if realloc fails and returns NULL, you lose the (*s)->buf pointer, which is still pointing to allocated memory.
Since you exit on failure it's not a problem, but maybe your static analyser ignores the exit?
Like this mtrace said "No memory leaks"
char *strnappnd(str **s, const char *buf, size_t n) {
...
return (*s)->buf;
}
void strfree(str *s){
free(s->buf);
free(s);
}
using the sample code given by Frerich
void f() {
str *s = (str *)malloc(sizeof(str));
s->len = 5;
s->alloclen = 5;
s->buf = strdup("Hello");
s->buf = strnappend(&s, " World!", 7);
strfree(s);
}
You create strfree() function and it is not used inside the code.
The memory need to be free always, if it is not used.
if (!tmp) {
printf("failure");
if (!(*s) && !((*s)->buf))
strfree(&(*s));
exit(-1);
}
Looking strfree(), looks as you reserved memory for *s too in somewhere.
Do the same before your code finish.
if (!(*s) && !((*s)->buf))
strfree(&(*s));