Trouble printing an array of strings after realloc? - c

The code below reads characters and splits them into C-style strings when a delimiter is encountered, then it stores the words (white-space-separated sequences of characters) in string array till a sentinel is encountered; updates size of string array:
#include <stdio.h> // printf()
#include <stdlib.h> // malloc(); realloc()
#include <string.h> // strcmp()
#include <stddef.h> // size_t
void print_array(char* arr[ ], size_t size); // forward declaration to use in to_array()
char* get_word(char delimiter)
{
size_t size = 8;
size_t index = 0;
int c = 0;
char* word = 0;
char* expand_word = 0;
word = (char*) malloc(sizeof(char) * size);
if (word == NULL)
{
perror("get_word::bad malloc!\n");
exit(-1);
}
while ((c = getchar()) != EOF && c != delimiter && c != '\n')
{
if (index >= size)
{
size *= 2;
expand_word = (char*) realloc(word, sizeof(char) * size);
if (expand_word == NULL)
{
perror("get_word::bad realloc!\n");
exit(-1);
}
word = expand_word;
}
word[index++] = c;
}
word[index] = 0;
return word;
}
//-------------------------------------------------------------------------------------
void to_array(char* arr[ ], size_t* size, char* sentinel)
{
size_t index = 0;
char* word = 0;
char** expand_arr = 0;
char delimiter = ' ';
while ((word = get_word(delimiter)) && strcmp(word, sentinel) != 0)
{
if (index >= (*size))
{
(*size) *= 2;
expand_arr = (char**) realloc(arr, sizeof(char*) * (*size));
if (expand_arr == NULL)
{
perror("to_array::bad realloc!\n");
exit(-1);
}
arr = expand_arr;
}
arr[index++] = word;
}
(*size) = index;
// print_array(arr, *size); // <---- here, all words printed OK.
// getchar();
}
//-------------------------------------------------------------------------------------
void print_array(char* arr[ ], size_t size)
{
size_t i = 0;
printf("{ ");
for (i; i < size; ++i)
{
printf("%s", arr[i]);
if (i < size - 1)
{
printf(", ");
}
}
printf(" }\n");
}
//-------------------------------------------------------------------------------------
int main()
{
size_t size = 4;
char** arr = 0;
char* sentinel = "quit";
arr = (char**) malloc(sizeof(char*) * size);
if (arr == NULL)
{
perror("array of strings::bad malloc!\n");
exit(-1);
}
printf("Type a sentence and get each word as an array element:\n");
to_array(arr, &size, sentinel);
printf("Words:\n");
print_array(arr, size); // <--------- here, error!
getchar();
}
When trying to print the string array, I get:
Access violation reading location 0xcd007361.
Why I can't print the strings in arr at the end?
P.S.: I guess that the problem comes from the pointer arithmetic and the reallocation of the char** arr within function to_array(). (If previous right) I'm not sure what would be the standard way to deal with it?

Problem: the first parameter in void to_array(), i.e. char* arr[ ] passes a copy of a pointer to array of char. Every change on the pointer made inside the function does not affect the actual pointer to char array outside, specifically the function realloc() may move the initial memory block to a new location, which would invalidate the pointer passed as first parameter.
Solution: either to modify the function void to_array() to return the modified arr, or to modify the first parameter of the function to char** arr[ ]. The latter was chosen and the modified code looks like this:
void to_array(char** arr[ ], size_t* size, char* quit)
{
size_t index = 0;
char* word = 0;
char** expand_arr = 0;
char sentinel = ' ';
while ((word = get_word(sentinel)) && strcmp(word, quit) != 0)
{
if (index >= (*size))
{
(*size) *= 2;
expand_arr = (char**) realloc((*arr), sizeof(char*) * (*size));
if (expand_arr == NULL)
{
perror("to_array::bad realloc!\n");
exit(-1);
}
(*arr) = expand_arr;
}
(*arr)[index++] = word;
}
(*size) = index;
}
then the function call must be done as:
to_array(&arr, &size, quit);

Related

Problem with c with dynamically allocating an array

#include <stdio.h>
#include <stdlib.h>
void input_all(char* array)
{
int c = 0;
int increse = 20;
int number_of_char = 0;
for (int increment = 0; (c = getchar()) != '\n'; increment++)
{
++number_of_char;
if (number_of_char % 10)
{
array = (char*)realloc(array, increse + sizeof(char));
if (array == NULL)
{
printf("not alocated!");
exit(22);
}
increse += 10;
}
array[increment] = c;
}
printf("%s\n", array);
}
int main(void)
{
char* array = (char*)malloc(10);
if (array == NULL)
{
printf("not alocated\n");
exit(33);
}
input_all(array);
printf("%s\n", array);
return 0;
}
So what I'am trying to do is to fill up "array" with getchar. When I try to print it out I get some garbage values at the end (most of the time). I think the problem is that I'am giving out to much space to "array" with realloc but I have no idea how to fix it. I also tried placing all the sizes to 1 in malloc and realloc and increse so that whenever i get a charcter the size of "array" increses but it still did not work.
Anyone have any idea how ot fix it?
Thanks in advance!
array must be null terminated, otherwise printf and other c-string functions don't know where the end of the string is at.
realloc may not return the same address (although that's not an issue on your PC), you have to use the address of pointer.
You can allocate the whole array realloc. If subsequent realloc fails then you don't necessarily have to exit, you can print error and return the string which was already allocated.
void input_all(char** parray)
{
char* arr = NULL;
int size = 0, i = 0;
while(1)
{
int c = fgetc(stdin);
if (c == '\n' || c == EOF)
break;
if (i == size)
{
size += 10;
char* temp = realloc(arr, size + 1);
if (temp == NULL) { printf("realloc failed\n"); break; }
arr = temp;
}
arr[i++] = (char)c;
}
if(arr)
arr[i] = '\0'; //<- don't forget to terminate the string with 0
*parray = arr;
}
int main(void)
{
char* array = NULL;
input_all(&array);
if (!array)
return 1;
printf("%s\n", array);
free(array);
return 0;
}

Printing array of strings produces bad output

I'm trying to solve a challenge, but I have no idea of what's wrong with my code!
The challenge is:
Create a function that splits a string of characters into words.
Separators are spaces, tabs and line breaks.
This function returns an array where each box contains a character-string’s address represented by a word. The last element of this array should be equal to 0 to emphasise the end of the array.
There can’t be any empty strings in your array. Draw the necessary conclusions.
The given string can’t be modified.
Note: The only allowed function is malloc()
The bug/problem:
I faced this problem and I tried to solve it but I wasn't able to identify what's wrong.
I created a function named split_whitespaces() to do the job.
When I print the array of strings inside of the split_whitespaces function, I get the following output:
Inside the function:
arr_str[0] = This
arr_str[1] = is
arr_str[2] = just
arr_str[3] = a
arr_str[4] = test!
And when I print the array of string inside the main function, I get the following output:
Inside the main function:
arr_str[0] = #X#?~
arr_str[1] = `X#?~
arr_str[2] = just
arr_str[3] = a
arr_str[4] = test!
I created a function word_count to count how many words in the input string so I can allocate memory using malloc and with word_count + 1 (null pointer).
int word_count(char *str) {
int i;
int w_count;
int state;
i = 0;
w_count = 0;
state = 0;
while (str[i]) {
if (!iswhitespace(str[i])) {
if (!state)
w_count++;
state = 1;
i++;
} else {
state = 0;
i++;
}
}
return (w_count);
}
And another function called strdup_w to mimic the behavior of strdup but just for single words:
char *strdup_w(char *str, int *index) {
char *word;
int len;
int i;
i = *index;
len = 0;
while (str[i] && !iswhitespace(str[i]))
len++, i++;;
word = (char *) malloc(len + 1);
if (!word)
return (NULL);
i = 0;
while (str[*index]) {
if (!iswhitespace(str[*index])) {
word[i++] = str[*index];
(*index)++;
} else
break;
}
word[len] = '\0';
return (word);
}
Here's my full code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **split_whitespaces(char *str);
char *strdup_w(char *str, int *index);
int word_count(char *str);
int iswhitespace(char c);
int main(void) {
char *str = "This is just a test!";
char **arr_str;
int i;
i = 0;
arr_str = split_whitespaces(str);
printf("\nOutside the function:\n");
while (arr_str[i]) {
printf("arr_str[%d] = %s\n", i, arr_str[i]);
i++;
}
return (0);
}
char **split_whitespaces(char *str) {
char **arr_str;
int i;
int words;
int w_i;
i = 0;
w_i = 0;
words = word_count(str);
arr_str = (char **)malloc(words + 1);
if (!arr_str)
return (NULL);
printf("Inside the function:\n");
while (w_i < words) {
while (iswhitespace(str[i]) && str[i])
if (!str[i++])
break;
arr_str[w_i] = strdup_w(str, &i);
printf("arr_str[%d] = %s\n", w_i, arr_str[w_i]);
w_i++;
}
arr_str[words] = 0;
return (arr_str);
}
char *strdup_w(char *str, int *index) {
char *word;
int len;
int i;
i = *index;
len = 0;
while (str[i] && !iswhitespace(str[i]))
len++, i++;;
word = (char *)malloc(len + 1);
if (!word)
return (NULL);
i = 0;
while (str[*index]) {
if (!iswhitespace(str[*index])) {
word[i++] = str[*index];
(*index)++;
} else
break;
}
word[len] = '\0';
return (word);
}
int word_count(char *str) {
int i;
int w_count;
int state;
i = 0;
w_count = 0;
state = 0;
while (str[i]) {
if (!iswhitespace(str[i])) {
if (!state)
w_count++;
state = 1;
i++;
} else {
state = 0;
i++;
}
}
return (w_count);
}
int iswhitespace(char c) {
if (c == ' ' || c == '\t' || c == '\n' || c == '\r')
return (1);
return (0);
}
I'm sorry, if anything this is my first time trying to seek help.
There are multiple problems in the code:
the size is incorrect in arr_str = (char **)malloc(words + 1); You must multiply the number of elements by the size of the element:
arr_str = malloc(sizeof(*arr_str) * (words + 1));
it is good style to free the array in the main() function after use.
the test while (iswhitespace(str[i]) && str[i]) is redundant: if w_count is computed correctly, testing str[i] should not be necessary. You should use strspn() to skip the white space and strcspn() to skip the word characters.
if (!str[i++]) break; is completely redundant inside the loop: str[i] has already been tested and is not null.
while (str[i] && !iswhitespace(str[i])) len++, i++;; is bad style. Use braces if there is more than a single simple statement in the loop body.
the last loop in strdup_w is complicated, you could simply use memcpy(word, str + *index, len); *index += len;
Here is a modified version:
#include <stdio.h>
#include <stdlib.h>
char **split_whitespaces(const char *str);
char *strdup_w(const char *str, int *index);
int word_count(const char *str);
int iswhitespace(char c);
int main(void) {
const char *str = "This is just a test!";
char **arr_str;
int i;
arr_str = split_whitespaces(str);
if (arr_str) {
printf("\nOutside the function:\n");
i = 0;
while (arr_str[i]) {
printf("arr_str[%d] = %s\n", i, arr_str[i]);
i++;
}
while (i --> 0) {
free(arr_str[i]);
}
free(arr_str);
}
return 0;
}
char **split_whitespaces(const char *str) {
char **arr_str;
int i;
int words;
int w_i;
i = 0;
w_i = 0;
words = word_count(str);
arr_str = malloc(sizeof(*arr_str) * (words + 1));
if (!arr_str)
return NULL;
printf("Inside the function:\n");
while (w_i < words) {
while (iswhitespace(str[i]))
i++;
arr_str[w_i] = strdup_w(str, &i);
if (!arr_str[w_i])
break;
printf("arr_str[%d] = %s\n", w_i, arr_str[w_i]);
w_i++;
}
arr_str[words] = NULL;
return arr_str;
}
char *strdup_w(const char *str, int *index) {
char *word;
int len;
int start;
int i;
i = *index;
start = i;
while (str[i] && !iswhitespace(str[i])) {
i++;
}
*index = i;
len = i - start;
word = malloc(len + 1);
if (!word)
return NULL;
i = 0;
while (i < len) {
word[i] = str[start + i];
i++;
}
word[i] = '\0';
return word;
}
int word_count(const char *str) {
int i;
int w_count;
int state;
i = 0;
w_count = 0;
state = 0;
while (str[i]) {
if (!iswhitespace(str[i])) {
if (!state)
w_count++;
state = 1;
} else {
state = 0;
}
i++;
}
return w_count;
}
int iswhitespace(char c) {
return (c == ' ' || c == '\t' || c == '\n' || c == '\r');
}
From my top comment ...
In split_whitespaces, try changing:
arr_str = (char **) malloc(words + 1);
into:
arr_str = malloc(sizeof(*arr_str) * (words + 1));
As you have it, words is a count and not a byte length, so you're not allocating enough space, so you have UB.
UPDATE:
But watched some tutorials and they said that malloc takes one argument which is the size of the memory to be allocated (in bytes), that's why I allocated memory for 5 bytes! can you please tell my an alternative of using malloc without sizeof() function. I'll appreciate it. – Achraf EL Khnissi
There's really no clean way to specify this without sizeof.
sizeof is not a function [despite the syntax]. It is a compiler directive. It "returns" the number of bytes occupied by its argument as a compile time constant.
If we have char buf[5];, there are 5 bytes, so sizeof(buf) [or sizeof buf] is 5.
If we have: int buf[5];, there are 5 elements, each of size int which is [typically] 4 bytes, so the total space, in bytes, is sizeof(int) * 5 or 4 * 5 which is 20.
But, int can vary depending on the architecture. On Intel 8086's [circa the 1980's], an int was 2 bytes (i.e. 16 bits). So, the above 4 * 5 would be wrong. It should be 2 * 5.
If we use sizeof(int), then sizeof(int) * 5 works regardless of the architecture.
Similarly, on 32 bit machines, a pointer is [usually] 32 bits. So, sizeof(char *) is 4 [bytes]. On a 64 bit machine, a pointer is 64 bits, which is 8 bytes. So, sizeof(char *) is 8.
Because arr_str is: char **arr_str, we could have done:
arr_str = malloc(sizeof(char *) * (words + 1));
But, if the definition of arr_str ever changed (to (e.g.) struct string *arr_str;), then what we just did would break/fail if we forgot to change the assignment to:
arr_str = malloc(sizeof(struct string) * (words + 1));
So, doing:
arr_str = malloc(sizeof(*arr_str) * (words + 1));
is a preferred idiomatic way to write cleaner code. More statements will adjust automatically without having to find all affected lines of code manually.
UPDATE #2:
You might just add why you removed the (char **) cast :) -- chqrlie
Note that I removed the (char **) cast. See: Do I cast the result of malloc?
This just adds extra/unnecessary "stuff" as the void * return value of malloc can be assigned to any type of pointer.
If we forgot to do: #include <stdlib.h>, there would be no function prototype for malloc, so the compiler would default the return type to int.
Without the cast, the compiler would issue an an error on the statement [which is what we want].
With the cast, this action is masked at compile time [more or less]. On a 64 bit machine, the compiler will use a value that is truncated to 32 bits [because it thinks malloc returns a 32 bit value] instead of the full 64 bit return value of malloc.
This truncation is a "silent killer". What should have been flagged as a compile time error produces a runtime fault (probably segfault or other UB) that is much harder to debug.

What am I doing wrong with malloc and realloc of array of struct?

I'm trying to build in C an array of structures without defining the length of the maximum size of the array.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *final;
} text;
int main() {
int n, sizearray = 10, i;
char *str;
text *testo;
testo = (text *)malloc(sizeof(text) * sizearray);
fgets(str, 1024, stdin);
i = 0;
while (str[0] != 'q') {
if (i == sizearray - 1) {
testo = (text *)realloc(testo, sizearray * 2 * sizeof(text));
}
n = strlen(str);
n = n + 1;
testo[i].finale = (char *)malloc(sizeof(char) * n);
strcpy(testo[i].finale, str);
i++;
fgets(str, 1024, stdin);
}
for (i = 0; i < sizearray; i++)
printf("%s \n", testo[i].finale);
return 0;
}
this gives me
process finished with exit code 139 (interrupted by signal 11:SIGSEV).
What am I doing wrong?
There are multiple issues in your code:
[major] str is an uninitialized pointer. You should make it an array of char defined with char str[1024].
[major] you do not adjust sizearray when you double the size of the array, hence you will never reallocate the array after the initial attempt at i = 9.
[major] the final loop goes to sizearray but there are potentially many uninitialized entries at the end of the array. You should stop at the last entry stored into the array.
you should also check the return value of fgets() to avoid an infinite loop upon premature end of file.
you should test for potential memory allocation failures to avoid undefined behavior.
Here is a modified version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct text {
char *finale;
} text;
int main() {
char str[1024];
text *testo = NULL;
size_t sizearray = 0;
size_t i, n = 0;
while (fgets(str, sizeof str, stdin) && *str != 'q') {
if (n == sizearray) {
/* increase the size of the array by the golden ratio */
sizearray += sizearray / 2 + sizearray / 8 + 10;
testo = realloc(testo, sizearray * sizeof(text));
if (testo == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
}
testo[n].finale = strdup(str);
if (testo[n].finale == NULL) {
fprintf(stderr, "out of memory\n");
return 1;
}
n++;
}
for (i = 0; i < n; i++) {
printf("%s", testo[i].finale);
}
for (i = 0; i < n; i++) {
free(testo[i].finale);
}
free(testo);
return 0;
}
str is uninitialized. Either allocate memory with malloc or define it as an array with char str[1024].

The function only works with a useless printf

I usually try hard and harder to solve myself any bugs I find in my code, but this one is totally out of any logic for me. It works really fine with whatever strings and char separators, but only with that useless printf inside the while of the function, otherwise it prints
-> Lorem
then
-> ▼
and crashes aftwerwards. Thanks in advance to anyone that could tell me what is happening.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <stdint.h>
char **strsep_(char *str, char ch) {
// Sub-string length
uint8_t len = 0;
// The number of sub-strings found means the same as the position where it will be stored in the main pointer
// Obviously, the number tends to increase over time, and at the end of the algorithm, it means the main pointer length too
uint8_t pos = 0;
// Storage for any found sub-strings and one more byte as the pointer is null-terminated
char **arr = (char**)malloc(sizeof(char **) + 1);
while (*str) {
printf("Erase me and it will not work! :)\n");
if (*str == ch) {
// The allocated memory should be one step ahead of the current usage
arr = realloc(arr, sizeof(char **) * pos + 1);
// Allocates enough memory in the current main pointer position and the '\0' byte
arr[pos] = malloc(sizeof(char *) * len + 1);
// Copies the sub-string size (based in the length number) into the previously allocated space
memcpy(arr[pos], (str - len), len);
// `-_("")_-k
arr[pos][len] = '\0';
len = 0;
pos++;
} else {
len++;
}
*str++;
}
// Is not needed to reallocate additional memory if no separator character was found
if (pos > 0) arr = realloc(arr, sizeof(char **) * pos + 1);
// The last chunk of characters after the last separator character is properly allocated
arr[pos] = malloc(sizeof(char *) * len + 1);
memcpy(arr[pos], (str - len), len);
// To prevent undefined behavior while iterating over the pointer
arr[++pos] = NULL;
return arr;
}
void strsep_free_(char **arr) {
char **aux = arr;
while (*arr) {
free(*arr);
*arr = NULL;
arr++;
}
// One more time to fully deallocate the null-terminated pointer
free(*arr);
*arr = NULL;
arr++;
// Clearing The pointer itself
free(aux);
aux = NULL;
}
int main(void) {
char **s = strsep_("Lorem ipsum four words", ' ');
char **i = s;
while (*i != NULL) {
printf("-> %s\n", *i);
i++;
}
strsep_free_(s);
}
Your program has undefined behavior, which means it may behave in unexpected ways, but could by chance behave as expected. Adding the extra printf changes the behavior in a way the seems to correct the bug, but only by coincidence. On a different machine, or even on the same machine at a different time, the behavior may again change.
There are multiple bugs in your program that lead to undefined behavior:
You are not allocating the array with the proper size: it should have space fpr pos + 1 pointers, hence sizeof(char **) * (pos + 1). The faulty statements are: char **arr = (char**)malloc(sizeof(char **) + 1); and arr = realloc(arr, sizeof(char **) * pos + 1);.
Furthermore, the space allocated for each substring is incorrect too: arr[pos] = malloc(sizeof(char *) * len + 1); should read arr[pos] = malloc(sizeof(char) * len + 1);, which by definition is arr[pos] = malloc(len + 1);. This does not lead to undefined behavior, you just allocate too much memory. If your system supports it, allocation and copy can be combined in one call to strndup(str - len, len).
You never check for memory allocation failure, causing undefined behavior in case of memory allocation failure.
Using uint8_t for len and pos is risky: what if the number of substrings exceeds 255? pos and len would silently wrap back to 0, producing unexpected results and memory leaks. There is no advantage at using such a small type, use int or size_t instead.
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **strsep_(const char *str, char ch) {
// Sub-string length
int len = 0;
// The number of sub-strings found, index where to store the NULL at the end of the array.
int pos = 0;
// return value: array of pointers to substrings with an extra slot for a NULL terminator.
char **arr = (char**)malloc(sizeof(*arr) * (pos + 1));
if (arr == NULL)
return NULL;
for (;;) {
if (*str == ch || *str == '\0') {
// alocate the substring and reallocate the array
char *p = malloc(len + 1);
char **new_arr = realloc(arr, sizeof(*arr) * (pos + 2));
if (new_arr == NULL || p == NULL) {
// allocation failure: free the memory allocated so far
free(p);
if (new_arr)
arr = new_arr;
while (pos-- > 0)
free(arr[pos]);
free(arr);
return NULL;
}
arr = new_arr;
memcpy(p, str - len, len);
p[len] = '\0';
arr[pos] = p;
pos++;
len = 0;
if (*str == '\0')
break;
} else {
len++;
}
str++;
}
arr[pos] = NULL;
return arr;
}
void strsep_free_(char **arr) {
int i;
// Free the array elements
for (i = 0; arr[i] != NULL; i++) {
free(arr[i]);
arr[i] = NULL; // extra safety, not really needed
}
// Free The array itself
free(arr);
}
int main(void) {
char **s = strsep_("Lorem ipsum four words", ' ');
int i;
for (i = 0; s[i] != NULL; i++) {
printf("-> %s\n", s[i]);
}
strsep_free_(s);
return 0;
}
Output:
-> Lorem
-> ipsum
-> four
-> words
The probable reason for the crash is most likely this: realloc(arr, sizeof(char **) * pos + 1).
That is the same as realloc(arr, (sizeof(char **) * pos) + 1) which does not allocate enough space for your "array". You need to do realloc(arr, sizeof(char **) * (pos + 1)).
Same with the allocation for arr[pos], you need to use parentheses correctly there too.
Good answer from #chqrlie. From my side, I think it would be better to count everything before copy, it should help to avoid realloc.
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
int count_chars(const char *str, const char ch)
{
int i;
int count;
i = 0;
count = 0;
if (*str == ch)
str++;
while (str[i] != ch && str[i] != '\0')
{
count++;
i++;
}
return (count);
}
int count_delimeter(const char *str, const char ch)
{
int i = 0;
int count = 0;
while (str[i])
{
if (str[i] == ch && str[i + 1] != ch)
count++;
i++;
}
return count;
}
char** strsep_(const char *str, const char ch)
{
char **arr;
int index = 0;
int size = 0;
int i = 0;
size = count_delimeter(str, ch) + 1;
if ((arr = malloc(sizeof(char *) * (size + 1))) == NULL)
return (NULL);
arr[size] = NULL;
while (i < size)
{
if (str[index] == ch)
index++;
if (str[index] && str[index] == ch && str[index + 1] == ch)
{
while (str[index] && str[index] == ch && str[index + 1] == ch)
index++;
index++;
}
int len = count_chars(&str[index], ch);
if ((arr[i] = malloc(sizeof(char) * (len + 1))) == NULL)
return NULL;
memcpy(arr[i], &str[index], len);
index += len;
arr[i++][len] = '\0';
}
return arr;
}
int main(void)
{
char *str = "Lorem ipsum ipsum Lorem lipsum gorem insum";
char **s = strsep_(str, ' ');
/* char *str = "Lorem + Ipsum"; */
/* char **s = strsep_(str, '+'); */
/* char *str = "lorem, torem, horem, lorem"; */
/* char **s = strsep_(str, ','); */
while (*s != NULL) {
printf("-> [%s]\n", *s);
s++;
}
/* dont forget to free */
return 0;
}

all possible combinations in a string in C

I am trying to print all possible combinations of the string 'abc' using C. Can someone help point out where in this code I am going wrong ? I am using the algorithm mentioned here:
http://hackercs.com/videos/Combinations-of-a-String-Part-2/
Thanks for your time and help. ( BTW, the goal is to use recursion here)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void combination(char **curString,int allowedStart,char **outBuffer,int length)
{
//check for allowed chars.
// if not return.
// else
// for each char in the allowed char
// copy into buffer
//print buffer
//combine buffer and over next allowed chars
//remove from buffer.
if (allowedStart == length)
return;
else
{
int curr;
for (curr = allowedStart;curr<length; curr++){
//need to copy 'a' into outbuffer and keep appending to outbuffer.
printf("allowedStart = %d\n",allowedStart);
printf("curr = %d\n",curr);
(*outBuffer)[curr] = (*curString)[allowedStart];
printf("outbuff is %s\n",*outBuffer);
combination(curString,curr+1,outBuffer,length);
printf("return\n");
(*outBuffer)[length-1] = '\0';
} //else
} //for
}
main()
{
char *var = "abc";
int length = strlen(var);
printf("length = %d\n",length);
char *outBuffer = malloc ( length * sizeof (char));
bzero(outBuffer,3);
combination(&var,0,&outBuffer,length);
}
For starters, you're going wrong here:
char *var = "abc";
int length = strlen(var);
printf("length = %d\n",length);
char *outBuffer = malloc ( length * sizeof (char));
bzero(outBuffer,3);
This is very confused code. It's mixing dynamic buffer length handling (the strlen() call) with static ones (the 3 in the bzero() call). It's also Doing It Wrong, by using sizeof (char) (which is guaranteed to be 1 by the C language, and thus just adds noise and confusion). Also, the number of characters needed to hold a 3-character printable string in C is not 3, but 4 since you need one character for the terminating '\0'.
Got this working.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void combination(char **curString,int allowedStart,char **outBuffer,int length)
{
//check for allowed chars.
// if not return.
// else
// for each char in the allowed char
// copy into buffer
//print buffer
//combine buffer and over next allowed chars
//remove from buffer.
int curr;
if ( allowedStart == length )
return;
else {
for (curr = allowedStart;curr<length; curr++){
//need to copy 'a' into outbuffer and keep appending to outbuffer.
// printf("allowedStart= %d curr= %d \n",allowedStart,curr);
(*outBuffer)[curr] = (*curString)[allowedStart];
//adjust for appending.
int i = 0;
while (i < length)
{
if ( ((*outBuffer)[i] == '\0') && ((*outBuffer)[i+1] != '\0') )
{
// printf("in here\n");
(*outBuffer)[i] = (*outBuffer)[i+1];
(*outBuffer)[i+1] = '\0';
}
i++;
}
// printf("added curr%d %c \n",curr, (*outBuffer)[curr]);
printf("***************************************COMBO: %s\n",*outBuffer);
allowedStart = curr+1;
// printf("allowedStart%d %c \n",allowedStart,(*curString)[allowedStart]);
combination(curString,allowedStart,outBuffer,length);
// printf("removing%d %c\n",curr,(*outBuffer)[curr]);
(*outBuffer)[curr] = ' ';
// printf("**************RETURNCOMBO: %s\n",*outBuffer);
} //else
} //for
}
main()
{
char *var = "abcd";
int length = strlen(var);
printf("length = %d\n",length);
// printf("Intial word is %s\n",var);
char *outBuffer = malloc ( (length+1) * sizeof (char));
bzero(outBuffer,length);
combination(&var,0,&outBuffer,length);
}
#include <iostream>
#define PRINTLN(STR) std::cout << STR << std::endl
#define SWAP(ARRAY, I, J) if(I != J) { ARRAY[I] ^= ARRAY[J]; ARRAY[J] ^= ARRAY[I]; ARRAY[I] ^= ARRAY[J]; }
void PrintCombinations_Rec(char* str, size_t idx)
{
const size_t len = strlen(str);
if( len == idx)
PRINTLN(str);
else
{
for (size_t i = idx; i < len; ++i)
{
SWAP(str, idx, i);
PrintCombinations_Rec(str, idx + 1);
SWAP(str, i, idx);
}
}
}
void PrintCombinations(const char* input)
{
const size_t len = strlen(input);
char* str = new char[len + 1];
strncpy_s(str, len + 1, input, len);
// Recursive call
PrintCombinations_Rec(str, 0);
delete[] str;
}
int main(int argc, char** argv)
{
PrintCombinations("ABCD");
return EXIT_SUCCESS;
}

Resources