Hi I am at beginner level and I am using concept of variable argument functions to concatenate strings. Same function is called for different number of strings.
I am not able to calculate the length of the concatenated string which in turn means i am not allocating memory properly. My dear peers, please help!
/* Program to do string concatenation using the concept of variable arguments */
/********************************************************************************
* REQUIRED HEADER FILES
*********************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdarg.h>
/********************************************************************************
* REQUIRED MACROS DEFINED
*********************************************************************************/
#define ERROR_CHECKER(result)\
if(result == FAILURE)\
{\
printf("\n CONCATENATION FAILED");\
}
typedef enum {SUCCESS = 0, FAILURE = 1} return_type;
/********************************************************************************
* REQUIRED FUNCTION PROTOTYPES
*********************************************************************************/
return_type string_concat(char* string_pointer, ...);
/********************************************************************************
*
* FUNCTION_NAME : STRING_CONCAT
*
* DESCRIPTION : concatenates incoming strings and displays the result
*
* RETURNS : SUCCESS OR FAILURE
*
*********************************************************************************/
return_type string_concat(
char* string_pointer,
...)
{
/********************************************************************************
* REQUIRED DECLARATIONS
*********************************************************************************/
// 1. arg_list that will point to variable number of arguments
va_list arg_list;
// 2. pointer to concatenated string
char* concatenated_string;
// 3. character pointer to point to an individual element in the argument list
char* individual_string_pointer;
// 4. amount of memory required to be allocated
int length;
/*********************************************************************************
* REQUIRED INITIALIZATIONS
*********************************************************************************/
va_start(arg_list, string_pointer);
concatenated_string = NULL;
individual_string_pointer = string_pointer;
length = 0;
/*********************************************************************************
* PERFORMING REQUIRED TASKS
**********************************************************************************/
// 1. calculate length till you reach quit
while(strcmp(individual_string_pointer,"quit") == 0)
{
individual_string_pointer = va_arg(arg_list, char*);
length = length + strlen(individual_string_pointer);
}
// individual_string_pointer reinitialized to be used for concatenation
individual_string_pointer = string_pointer;
printf("\nlength of concatenated string : %d", length);
// 2. allocate memory for the concatenated string
concatenated_string = (char*) malloc(sizeof(char) * length + 1);
// 3. use strncpy to copy first string and then use strncat to concatenate others
strncpy(concatenated_string, string_pointer, sizeof(*(string_pointer)));
while(strcmp(individual_string_pointer, "quit") == 0)
{
individual_string_pointer = va_arg(arg_list, char*);
strncat(concatenated_string, individual_string_pointer, sizeof(*(individual_string_pointer)));
}
printf("\n concatenated string : %s",concatenated_string);
va_end(arg_list);
return SUCCESS;
}
/********************************************************************************
*
* FUNCTION_NAME : MAIN
*
* DESCRIPTION : CALLS STRING_CONCAT FUNCTION
*
* RETURNS : SUCCESS
*********************************************************************************/
int main(void)
{
/********************************************************************************
* REQUIRED DECLARATIONS
*********************************************************************************/
// 1. character array as the first argument
char string_one[5] = "hello" ;
// 2. variable to store result from the string_concat function.
int result;
/*********************************************************************************
* REQUIRED INITIALIZATIONS
**********************************************************************************/
result = 0;
/*********************************************************************************
* PERFORMING REQUIRED TASKS
**********************************************************************************/
// 1. call string_concat function with 2 arguments
result = string_concat(string_one, "my", "name","is","amninder","quit");
// handle error from string_concat
ERROR_CHECKER(result);
// 2. call string_concat function with 3 arguments
result = string_concat(string_one, "I", "Like","fruits","quit");
// handle error from string_concat
ERROR_CHECKER(result);
// 3. call string_concat function with 4 arguments
result = string_concat(string_one, "awesome","quit");
// handle error from string_concat
ERROR_CHECKER(result);
/* doubt: do I need to send my first argument as same always " */
return SUCCESS;
}
Besides other issues: This sizeof(*(individual_string_pointer))); returns the size of what individual_string_pointer points to, namely a char, so it returns 1.
Either use strlen(ndividual_string_pointer) instead, or just switch to using strcat(), like this:
strcat(concatenated_string, individual_string_pointer)
Your first problem is that you are not doing anything with the data. Your second is an annonying rule preventing iteration over argument lists twice.
Return the concatenated string as malloced memory, or 0 on fail
char *concat(const char *first, ...)
{
char *answer;
int lenght;
va_list va;
char *nest;
int nextlen;
length = strlen(first);
answer = malloc(length + 1);
if(!answer)
goto out_of_memory;
strcpy(first, answer);
va_start(va, &first);
do
{
next = va_arg(va, char *);
if(!strcpy(nest, "quit))
break;
nextlen = strlen(next);
temp = realloc(answer, length + nextlen+1);
if(!temp)
goto out_of_memory;
answer = temp;
strcpy(answer, next);
length += nextlen;
} while(1);
va_end(va);
return answer;
out_of_memory:
free(answer);
return 0;
}
Rather than iterate the va_list twice, consider realloc() and append each sub-string. Additional ideas in comments.
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<stdarg.h>
#define return_type int
#define FAILURE -1
#define SUCCESS 0
// use const
return_type string_concat(const char* string_pointer, ...) {
va_list arg_list;
va_start(arg_list, string_pointer);
char* concatenated_string = NULL;
size_t length = 0; // Use type size_t, not int
const char* individual_string_pointer = string_pointer;
while (strcmp(individual_string_pointer, "quit")) {
// Find sub-string length _once_
size_t individual_length = strlen(individual_string_pointer);
size_t new_length = length + individual_length;
char *new_ptr = realloc(concatenated_string, new_length + 1);
if (new_ptr == NULL) {
free(concatenated_string); // do not forget to free old string
printf("\n MALLOC FALIED");
return FAILURE;
}
concatenated_string = new_ptr;
// or use memcpy(concatenated_string + length,
// individual_string_pointer, individual_length+1)
strcpy(concatenated_string + length, individual_string_pointer);
length = new_length;
individual_string_pointer = va_arg(arg_list, const char*);
}
va_end(arg_list);
// Add <> to detect leading/trailing white-space
printf("Concatenated string : <%s>\n", concatenated_string);
free(concatenated_string);
return SUCCESS;
}
int main(void) {
// not [5], let compiler size it to include the null character
char string_one[] = "hello";
string_concat(string_one, "my", "name", "is", "amninder", "quit");
string_concat(string_one, "I", "Like", "fruits", "quit");
return 0;
}
Ouput
Concatenated string : <hellomynameisamninder>
Concatenated string : <helloILikefruits>
Your approach is not bad, but could be improved, "quit" is not very safe to mark the end of va_list, NULL is a better choice. Because the user can make a mistake in "quit" like "qui", "qit" or whatever. And how concat "quit"? if you use string_concat without knowing what is inside the function could stop early when one of the strings is "quit":
char *string1 = "foo";
char *string2 = "quit";
char *string3 = "bar";
string_concat(string1, string2, string3, "quit");
Only "foo" will be used.
You don't return the string generate so your function is not really useful.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#define END_STRING_CONCAT ((char const *)NULL)
char *string_concat(char const *first, ...);
char *string_concat(char const *first, ...) {
va_list ap;
va_start(ap, first);
va_list cp_ap;
va_copy(cp_ap, ap); // copy for future use
size_t size = 1; // need the size of the future string
for (char const *ptr = first; ptr != NULL; ptr = va_arg(ap, char const *)) {
size += strlen(ptr);
}
va_end(ap);
char *result = malloc(size);
if (result == NULL) {
va_end(cp_ap);
return NULL;
}
size_t used = 0;
for (char const *ptr = first; ptr != NULL; ptr = va_arg(cp_ap, char const *)) {
size_t len = strlen(ptr);
if (size < used || size - used < len) {
free(result);
va_end(cp_ap);
return NULL;
}
memcpy(result + used, ptr, len); // use memcpy because it's faster in this case
used += len;
}
va_end(cp_ap);
if (size < used || size - used != 1) {
free(result);
return NULL;
}
result[used] = '\0'; // don't forget
return result;
}
int main(void) {
char hello[] = "hello, ";
char *result1 = string_concat(hello, "my ", "name ", "is ", "amninder",
END_STRING_CONCAT);
if (result1 != NULL) {
printf("%s\n", result1);
free(result1);
}
char *result2 = string_concat(hello, "I ", "Like ", "fruits", END_STRING_CONCAT);
if (result2 != NULL) {
printf("%s\n", result2);
free(result2);
}
char *result3 = string_concat(hello, "awesome", END_STRING_CONCAT);
if (result3 != NULL) {
printf("%s\n", result3);
free(result3);
}
return 0;
}
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 write a method that takes a string and splits it into two strings based on a delimiter string, similar to .split in Java:
char * split(char *tosplit, char *culprit) {
char *couple[2] = {"", ""};
int i = 0;
// Returns first token
char *token = strtok(tosplit, culprit);
while (token != NULL && i < 2) {
couple[i++] = token;
token = strtok(NULL, culprit);
}
return couple;
}
But I keep getting the Warnings:
In function ‘split’:
warning: return from incompatible pointer type [-Wincompatible-pointer-types]
return couple;
^~~~~~
warning: function returns address of local variable [-Wreturn-local-addr]
... and of course the method doesn't work as I hoped.
What am I doing wrong?
EDIT: I am also open to other ways of doing this besides using strtok().
A view things:
First, you are returning a pointer to a (sequence of) character(s), i.e. a char
* rather than a pointer to a (sequence of) pointer(s) to char. Hence, the return type should be char **.
Second, you return the address of a local variable, which - once the function has finished - goes out of scope and must not be accessed afterwards.
Third, you define an array of 2 pointers, whereas your while-loop may write beyond these bounds.
If you really want to split into two strings, the following method should work:
char ** split(char *tosplit, char *culprit) {
static char *couple[2];
if ((couple[0] = strtok(tosplit, culprit)) != NULL) {
couple[1] = strtok(NULL, culprit);
}
return couple;
}
I'd caution your use of strtok, it probably does not do what you want it to. If you think it does anything like a Java split, read the man page and then re-read it again seven times. It is literally tokenizing the string based on any of the values in delim.
I think you are looking for something like this:
#include <stdio.h>
#include <string.h>
char* split( char* s, char* delim ) {
char* needle = strstr(s, delim);
if (!needle)
return NULL;
needle[0] = 0;
return needle + strlen(delim);
}
int main() {
char s[] = "Fluffy furry Bunnies!";
char* res = split(s, "furry ");
printf("%s%s\n", s, res );
}
Which prints out "Fluffy Bunnies!".
First of all strtok modifies the memory of tosplit so be certain that, that's what you wish to do. If so then consider this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
* NOTE: unsafe (and leaky) implementation using strtok
*
* *into must point to a memory space where tokens can be stored
* or if *into is NULL then it allocates enough space.
* Returns:
* allocated array of items that you must free yourself
*
*/
char **__split(char *src, const char *delim)
{
size_t idx = 0;
char *next;
char **dest = NULL;
do {
dest = realloc(dest, (idx + 1)* sizeof(char *));
next = strtok(idx > 0 ? NULL:strdup(src), delim);
dest[idx++] = next;
} while(next);
return dest;
}
int main() {
int x = 0;
char **here = NULL;
here = __split("hello,there,how,,are,you?", ",");
while(here[x]) {
printf("here: %s\n", here[x]);
x++;
}
}
You can implement a much safer and non leaky version (note the strdup) of this but hopefully this is a good start.
The type of couple is char** but you have defined the function return type as char*. Furthermore you are returning the pointer to a local variable. You need to pass the pointer array into the function from the caller. For example:
#include <stdio.h>
#include <string.h>
char** split( char** couple, char* tosplit, char* culprit )
{
int i = 0;
// Returns first token
char *token = strtok( tosplit, culprit);
for( int i = 0; token != NULL && i < 2; i++ )
{
couple[i] = token;
token = strtok(NULL, culprit);
}
return couple;
}
int main()
{
char* couple[2] = {"", ""};
char tosplit[] = "Hello World" ;
char** strings = split( couple, tosplit, " " ) ;
printf( "%s, %s", strings[0], strings[1] ) ;
return 0;
}
I am using the below function to replace a sub-string in a given string
void ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *result = NULL;
int i, cnt = 0;
int tolen = strlen(to);
int fromlen = strlen(from);
if (*inputString == NULL)
return;
// Counting the number of times old word
// occur in the string
for (i = 0; (*inputString)[i] != '\0'; i++)
{
if (strstr((&(*inputString)[i]), from) == &(*inputString)[i])
{
cnt++;
// Jumping to index after the old word.
i += fromlen - 1;
}
}
// Making new string of enough length
result = (char *)malloc(i + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return;
memset(result, 0, i + cnt * (tolen - fromlen) + 1);
i = 0;
while (&(*inputString))
{
// compare the substring with the result
if (strstr(*inputString, from) == *inputString)
{
strncpy(&result[i], to, strlen(to));
i += tolen;
*inputString += fromlen;
}
else
{
result[i++] = (*inputString)[0];
if ((*inputString)[1] == '\0')
break;
*inputString += 1;
}
}
result[i] = '\0';
*inputString = result;
return;
}
The problem with the above function is memory leak. Whatever memory is allocated for inputString will be lost after this line.
*inputString = result;
since I am using strstr and moving pointer of inputString *inputString += fromlen; inputString is pointing to NULL before the above line. So how to handle memory leak here.
Note: I dont want to return the new memory allocated inside the function. I need to alter the inputString memory based on new length.
You should use a local variable to iterate over the input string and avoid modifying *inputString before the final step where you free the previous string and replace it with the newly allocated pointer.
With the current API, ReplaceSubStr must be called with the address of a pointer to a block allocated with malloc() or similar. Passing a pointer to local storage or a string literal will have undefined behavior.
Here are a few ideas for improvement:
you could return the new string and leave it to the caller to free the previous one. In this case, you would take the input string by value instead of by address:
char *ReplaceSubStr(const char *inputString, const char *from, const char *to);
If the from string is empty, you should either insert the to string between each character of the input string or do nothing. As posted, your code has undefined behavior for this border case.
To check if the from string is present at offset i, use memcmp instead of strstr.
If cnt is 0, there is nothing to do.
You should return an error status for the caller to determine if memory could be allocated or not.
There is no need to initialize the result array.
avoid using strncpy(). This function has counter-intuitive semantics and is very often misused. Read this: https://randomascii.wordpress.com/2013/04/03/stop-using-strncpy-already/
Here is an improved version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int ReplaceSubStr(char **inputString, const char *from, const char *to) {
char *input = *inputString;
char *p, *q, *result;
size_t cnt;
size_t tolen = strlen(to);
size_t fromlen = strlen(from);
if (input == NULL || fromlen == 0)
return 0;
// Counting the number of times old word occurs in the string
for (cnt = 0, p = input; (p = strstr(p, from)) != NULL; cnt++) {
p += fromlen;
}
if (cnt == 0) // no occurrence, nothing to do.
return 0;
// Making new string of enough length
result = (char *)malloc(strlen(input) + cnt * (tolen - fromlen) + 1);
if (result == NULL)
return -1;
for (p = input, q = result;;) {
char *p0 = p;
p = strstr(p, from);
if (p == NULL) {
strcpy(q, p0);
break;
}
memcpy(q, p0, p - p0);
q += p - p0;
memcpy(q, to, tolen);
q += tolen;
p += fromlen;
}
free(*inputString);
*inputString = result;
return 0;
}
int main() {
char *p = strdup("Hello world!");
ReplaceSubStr(&p, "l", "");
printf("%s\n", p); // prints Heo word!
free(p);
return 0;
}
You cannot obviously free the input as it can be a literal, some memory you don't control. That would cripple your function even more than now.
You could return the old value of inputString so you'd be able to free it if needed.
char *ReplaceSubStr(char **inputString, const char *from, const char *to)
{
char *old_string = *inputString;
...
return old_string;
}
The caller is responsible to free the contents of old_string if needed.
If not needed (we have to workaround the char ** input by assigning a valid writable array to a pointer to be able to pass this pointer:
char input[]="hello world";
char *ptr = input;
ReplaceSubStr(&ptr, "hello", "hi");
// input is now "hi world" in a different location
free(ptr); // when replaced string isn't needed
if needed:
char *input = strdup("hello world");
char *old_input = ReplaceSubStr(&input, "hello", "hi");
free(old_input);
or just
free(ReplaceSubStr(&input, "hello", "hi"));
then always (when replaced string isn't needed):
free(input);
The only constraint is that you cannot use a constant string literal as input (const char *input = "hello world") because of the prototype & the possible return of a char * to pass to free.
For example:
input(string): foo $$ foo ## foo []
search(string): foo
output(array): $$ ,## ,[]
I tried it:
char * str = "foo $$ foo ## foo []";
char * s = "foo";
int buf_len = 0;
int len = strlen(s);
int i = 0;
char ** buffer = malloc(MAX_BUFFER_SIZE);
char * tmpbuf = malloc(MAX_BUFFER_SIZE);
char * p = str;
char ** buf = buffer;
char * tbuf = tmpbuf;
while(*p)
{
if(*p == *s)
{
while(*p == *(s + i))
{
i++;
p++;
}
if(i == len)
{
*buf ++ = tbuf;
memset(tbuf,0,buf_len);
i = buf_len = 0;
}
}
else
{
*tbuf ++= *p;
buf_len ++;
}
p++;
}
*buf ++= NULL;
int x;
for(x = 0; buffer[x]; x++)
{
printf("%s\n", buffer[x]);
}
free(buffer);
free(tmpbuf);
that show the following output:
$$ ## []
## []
[]
but the expected is:
$$
##
[]
how to fix this?
Here's a function for splitting a string into an array of strings:
#include <assert.h>
#include <string.h>
/*
* Split a string by a delimiter.
*
* This function writes the beginning of each item to #pointers_out
* (forming an array of C strings), and writes the actual string bytes
* to #bytes_out. Both buffers are assumed to be big enough for all of the
* strings.
*
* Returns the number of strings written to #pointers_out.
*/
size_t explode(const char *delim, const char *str,
char **pointers_out, char *bytes_out)
{
size_t delim_length = strlen(delim);
char **pointers_out_start = pointers_out;
assert(delim_length > 0);
for (;;) {
/* Find the next occurrence of the item delimiter. */
const char *delim_pos = strstr(str, delim);
/*
* Emit the current output buffer position, since that is where the
* next item will be written.
*/
*pointers_out++ = bytes_out;
if (delim_pos == NULL) {
/*
* No more item delimiters left. Treat the rest of the input
* string as the last item.
*/
strcpy(bytes_out, str);
return pointers_out - pointers_out_start;
} else {
/*
* Item delimiter found. The bytes leading up to it form the next
* string.
*/
while (str < delim_pos)
*bytes_out++ = *str++;
/* Don't forget the NUL terminator. */
*bytes_out++ = '\0';
/* Skip over the delimiter. */
str += delim_length;
}
}
}
Usage:
#include <stdio.h>
/* ... */
#define BIG_ENOUGH 1000
int main(void)
{
char *items[BIG_ENOUGH];
char item_bytes[BIG_ENOUGH];
size_t i;
size_t count;
count = explode("foo", "foo $$ foo ## foo []", items, item_bytes);
for (i = 0; i < count; i++)
printf("\"%s\"\n", items[i]);
return 0;
}
Output:
""
" $$ "
" ## "
" []"
This does not produce the exact output you asked for, as I'm not sure how you want to handle surrounding spaces and occurrences of the item delimiter (in your example, "foo") at the beginning of the string. Instead, I mimicked PHP's explode function.
I'd like to point out how my explode function punts on memory management. It is up to the caller to ensure the buffers are big enough. This is fine for a quick script, but might be annoying in a more serious program, where you'll have to do some math to use this function correctly. I could have written a more "robust" implementation that performs its own allocation, but:
That would clutter the implementation.
It doesn't give the caller the option of using their own memory allocator.
So implementing explode the way I did is "bad" because it is hard to use correctly, and worse, easy to use incorrectly. On the other hand, it is "good" in that it separates the concerns of functionality and memory management.
It is because you do not copy the contents of tbuf to buf when you say:
*buf ++ = tbuf;
What you do is save a reference to the current position in tbuf (or tmpbuf if you like).
tmpbuf get filled with everything but the delimiter.
It is something like, at end of loop:
01234567 <- offset
tmpbuf = "$$ ## []"
buf[0] = tmpbuf+0;
buf[1] = tmpbuf+3;
buf[2] = tmpbuf+6;
Or very simplified memory table:
memory
address value
tmpbuf -> 0x01 [ $] <- buffer[0] points here
0x02 [ $]
0x03 [ ]
0x04 [ #] <- buffer[1] points here
0x05 [ #]
0x06 [ ]
0x07 [ [] <- buffer[2] points here
0x08 [ ]]
0x09 [ ]
...
buffer -> 0x3A [0x01]
0x3B [0x04]
0x3C [0x07]
0x3D [ ]
0x3E [ ]
...
EDIT
For the phun of it; a pointer, dynamic, way, not using strstr().
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int is_needle(char *hay, char *needle)
{
while (*hay && *++hay == *++needle);
return *needle == '\0';
}
char *find(char *hay, char *needle)
{
while (*hay) {
if (*hay == *needle && is_needle(hay, needle))
return hay;
++hay;
}
return hay;
}
int pushstr(char ***vs, size_t *vslen, char *val, size_t slen)
{
char **vsp = *vs + *vslen;
if ((*vsp = realloc(*(*vs + *vslen), slen + 1)) == NULL) {
perror("pushstr.1"); exit(1);
}
memcpy(*vsp, val, slen);
*(*vsp + slen) = '\0';
if ((*vs = realloc(*vs, sizeof(char*) * (++*vslen + 1))) == NULL) {
perror("pushstr.2"); exit(1);
}
*(*vs + *vslen) = NULL;
return *vslen;
}
int main(void)
{
char *hay = "foo $$ foo ## foo [] fox ## foo ??";
char *needle = "foo";
char *np;
char **vs;
size_t vslen = 0;
size_t nlen = strlen(needle);
if ((vs = malloc(sizeof(char*))) == NULL) {
perror("main");
return 1;
}
*vs = NULL;
while (*(np = find(hay, needle))) {
if (np != hay) {
pushstr(&vs, &vslen, hay, np - hay);
hay = np + nlen;
} else {
hay += nlen;
}
}
if (np != hay)
pushstr(&vs, &vslen, hay, np - hay);
while (*vs)
printf("V: '%s'\n", *vs++);
vs -= vslen;
while (*vs)
free(*vs++);
vs -= vslen;
free(vs);
return 0;
}
This is a task for strstr(). I changed your code a little bit to make use of it.
int add_to_buf(char *str, size_t len, char ***buf)
{
if (len <= 0) return 0;
**buf = malloc (len);
strncpy (**buf, str, len);
++*buf;
return 1;
}
int main()
{
char *str = "foo $$ foo ## foo []";
char *s = "foo";
char **buffer = malloc (MAX_BUFFER_SIZE*sizeof(*buffer)), **buf = buffer;
char *start, *end;
int s_len = strlen (s);
start = str;
end = strstr (str, s);
while (end) {
add_to_buf (start, end-start, &buf);
start = end + s_len;
end = strstr (start, s);
}
add_to_buf (start, strlen (str) - (start-str), &buf);
*buf = 0;
for (buf = buffer; *buf; ++buf)
printf ("%s\n", *buf);
free (buffer);
return 0;
}
You are using too many pointers for a simple program and the the way you used them makes it hard to understand. One straightforward bug I see is you are using buffer** (array of strings) but you are only allocating a single string. You are this this array of strings to store the tokens, which will do some memory violation somewhere.
Since you want to print the tokens, you don't need to store them in a separate array. This will do:
#include<stdio.h>
#include<string.h>
int main(int ac, char*argv[]) {
char str[] = "foo $$ foo ## foo []";
char * s = "foo";
char *p;
p = strtok(str, " "); // tokenize
while(p!=NULL)
{
if(strcmp(p, s)) //print non matching tokens
printf("%s\n", p);
p = strtok(NULL, " ");
}
return 0;
}
Note that here the delimiter is whitespace which makes it easier here.
The strtok function was designed for this task:
#include <string.h>
...
char *token;
char *line = "LINE TO BE SEPARATED";
char *search = " ";
/* Token will point to "LINE". */
token = strtok(line, search);
/* Token will point to "TO". */
token = strtok(NULL, search);
I'm C# man, new in C language working with points first time.
I have this function that works with malloc(), realloc() and free() at future:
char ** split(char * delimiter, char * input) {
int i = 0;
int size = sizeof(char *);
char ** tokens;
char * token;
char * state;
tokens = (char **) malloc(size);
if(tokens == NULL) {
printf("Allocation failed.");
return;
}
for(token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state),
i++, size *= i) {
tokens = (char **) realloc(tokens, size);
if(tokens == NULL) {
printf("Realloc failed.");
return;
}
tokens[i] = state;
}
return tokens;
}
when I call:
char * IPNumber = "127.0.01";
char * delimiter = ".";
char ** parts = split(delimiter, IPNumber);
it gives segmentation fault.
I'm looking for an explanation how to get(calculate) the size value to be used in the second argument of realloc() function. Thanks in advance.
Ok, I guessed what you intended was to return an array of strings:
include
char ** split(char * delimiter, char * input) {
int i;
char ** tokens;
char * token;
char * state;
tokens = (char **) malloc(sizeof(char *) * (2));
if(tokens == NULL) {
printf("Allocation failed.");
return NULL;
}
tokens[0]=(char *)1; /* one element populated */
tokens[1]=NULL; /* no tokens */
for(i=1, token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state),
i++) {
/* grow array by one element - originally made with 2 */
{
char **new =(char **) realloc(tokens, (i+2) * sizeof(char *));
if(new == NULL) {
printf("Realloc failed.");
free(tokens);
return NULL;
}
else
{
tokens = new;
tokens[i+1] = NULL; /* initialize new entry */
}
}
tokens[i] = token;
tokens[0] = (char *)i;
}
return tokens;
}
int main( void )
{
char str[] = "129.128.0.1";
char delim[] = ".";
char **ret;
ret = split( delim, str );
printf( "tokens = %d\n", (int)ret[0] );
printf( "tokens[1] = %s\n", ret[1] );
printf( "tokens[2] = %s\n", ret[2] );
printf( "tokens[3] = %s\n", ret[3] );
printf( "tokens[4] = %s\n", ret[4] );
printf( "tokens[5] = %s\n", ret[5] );
}
return explicit values, not garbage.
change in realloc function. You grow the array by one element during each loop.
Fix memory leak
save the value returned by strtok_r, not its private internal state variable.
the array is one larger then it needs to be, so make sure it gets initialized to NULL
entry zero of the array is the size, which should not overflow unless you are handling HUGE strings
The sizes of your malloc/calloc are wrong (you multiply by the intended count, which makes the array grow by count!)
On the first item: i=0, size=sizeof(char*);
On the second item i=1, size=sizeof(char) /*that is too small for two elements */
char ** split(char * delimiter, char * input) {
unsigned size , used;
char ** array = NULL;
char * token;
char * state;
size = used = 0;
for(token=strtok_r(input, delimiter, &state); token; token=strtok_r(NULL, delimiter, &state) ) {
if (used+1 >= size) {
size = size ? 2*size: 4;
array = realloc(array, size * sizeof *array);
if (!array) { printf("Realloc failed."); return NULL ; /*leak here*/ }
}
array[used++] = state;
}
/* NOTE: need a way to communicate the number of elements back to the caller */
if (array) array[used] = NULL;
return array;
}
UPDATE: here is a test driver
int main(void)
{
char stuff[] = "this is the stuff";
char **ppp;
unsigned idx;
ppp = split( " " , stuff);
for (idx = 0; ppp && ppp[idx]; idx++) {
fprintf(stdout, "%u: %s\n", idx, ppp[idx] );
}
return 0;
}
Complete rewrite. There are some issues with the original code as posted.
The reallocation size computation is incorrect.
The passing of a string constant to strtok_r is not valid. It modifies the first argument, so that could result in an access violation when passed the string literal.
The assignment of the token into the result array starts at position 1 instead of 0.
The assignment uses the state variable instead of the token (probably not at all the desired result and probably undefined behavior).
There is no way for the caller to know how many tokens are in the returned array.
A failed call to realloc does not free the original pointer, so it would leak.
So rather than attempt to describe the changes, I'll follow the same pattern as others and show what might be a cleaner implementation with a single allocation based on the max possible number of tokens.
char ** split(char * delimiter, char * input) {
int size;
int maxsize;
char ** tokens;
char * token;
char * state;
// compute max possible tokens, which is half the input length.
// Add 1 for the case of odd strlen result and another +1 for
// a NULL entry on the end
maxsize = strlen( input ) / 2 + 2;
tokens = (char**)malloc( maxsize * sizeof( char*) );
if(tokens == NULL) {
printf("Allocation failed.");
return NULL;
}
size = 0;
for(token = strtok_r(input, delimiter, &state);
token != NULL;
token = strtok_r(NULL, delimiter, &state) ) {
tokens[size++] = token;
}
assert( size < maxsize );
// Put a NULL in the last entry so the caller knows how many entries
// otherwise some integer value would need to be returned as an output
// parameter.
tokens[size] = NULL;
// NOTE: could use realloc from maxsize down to size if desired
return tokens;
}
Usage might look like the following. Note the use of strdup to avoid passing the string constant to the function:
char * IPNumber = strdup( "127.0.01" );
char * delimiter = ".";
char ** parts = split(delimiter, IPNumber);
int i;
if ( parts ) {
for ( i = 0; parts[i] != NULL; i++ )
printf( "%s\n", parts[i] );
free( parts );
}
free( IPNumber );
I was going to point out things to fix, but instead just rewrote it as follows:
char **split(char *delim, char *input)
{
char *save; /* saved state for strtok_r */
char **tmp, /* temporary result from realloc (for error handling) */
**res; /* result - NULL-terminated array of tokens */
int i, /* index of current/last token */
count; /* number of elements in res (including NULL) */
/* Allocate first element for res */
if ( !(res = malloc(sizeof(res[0]))) ) {
/* return NULL if malloc() fails */
fprintf(stderr,"split(): malloc() failed\n");
return NULL;
}
/* res[0] = first token, or NULL */
res[0] = strtok_r(input,delim,&save);
/* if it was a token, grab the rest. Last one will be the NULL
* returned from strtok_r() */
if (res[0])
i = 0;
count = 1;
do {
/* Resize res, for next token */
/* use a temporary pointer for realloc()'s result, so that
* we can check for failure without losing the old pointer */
if ( tmp = realloc(res, sizeof(res[0]) * ++count) )
res = tmp;
else {
/* if realloc() fails, free res and return NULL */
free(res);
fprintf(stderr,"split(): realloc() failed.\n");
return NULL;
}
/* get next token, or NULL */
res[++i] = strtok_r(NULL,delim,&save);
} while (res[i]); /* done when last item was NULL */
return res;
}
So the size for realloc is the number of elements needed, multiplied by the size of an element.
The above version of the code returns a NULL-terminated array. Another approach would be to return the number of array elements somehow (like via an int * or size_t * argument); but in any case you need a way for the caller to know where the end of the results array is.
Using strtok_r() for this also adds another catch: The original input string is not left intact. So you'll need to bear that in mind when using this (or your original) function as well -- either use it when you don't need to preserve the original string, or make a duplicate of the original first.