Array of dynamic length - c

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *names = NULL;
int capacity = 0;
int size = 0;
printf("Type 'end' if you want to stop inputting names\n");
while (1) {
char name[100];
printf("Input:\n");
fgets(name, sizeof(name), stdin);
if (strncmp(name, "end", 3) == 0) {
break;
}
if (size == capacity) {
char *temp = realloc(names, sizeof(char) * (size + 1));
if (!temp) {
if (names) {
return 1;
}
}
names = temp;
capacity++;
}
names[size] = name;
size++;
}
for (int i = 0; i < size; i++) {
printf("OUTPUT :%c\n", names[i]);
}
if (names) {
free(names);
}
}
I am trying to create an array of dynamic length in C but I don't know what is wrong with my code? I think it is cause of how I take the user input and the problem occurs when the code names[size] = name is executed.

You need to declare an array of pointers to strings (char**), not a pointer to a single character string (char*).
This means that when you want to add a new entry, not only do you need to make space for the new pointer in your array (type is char* not char) but you also have to separately allocate storage for the string itself and then set the pointer you just made space for to that allocated string.

There are multiple problems in the code:
names should be defined as a pointer to an array of char *: char **names = NULL;
you must make copies of the strings read with fgets(), otherwise all entries in names will point to the same array name.
you actually do not need to distinguish capacity and size if you reallocate one extra slot at a time.
the format "OUTPUT :%c\n" is incorrect for a string.
Here is a corrected version:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char **names = NULL;
int capacity = 0;
int size = 0;
printf("Type 'end' if you want to stop inputting names\n");
for (;;) {
char name[100];
printf("Input:\n");
if (!fgets(name, sizeof(name), stdin)) {
break;
}
if (strncmp(name, "end", 3) == 0) {
break;
}
if (size == capacity) {
// reallocate array with geometric growth
int new_capacity = capacity + (capacity / 2) + 4;
char *temp = realloc(names, sizeof(*names) * new_capacity);
if (!temp) {
free(names);
printf("out of memory\n");
return 1;
}
names = temp;
capacity = new_capacity;
}
name[strcspn(name, "\n")] = '\0'; // strip the trailing newline if any
names[size++] = strdup(name); // allocate a copy of the string
}
for (int i = 0; i < size; i++) {
printf("OUTPUT: %s\n", names[i]);
}
free(names);
return 0;
}

Related

How to copy a string to a 2D array in c

I want to create a c program that when the user enters some words like this: "some,words, in, c, proramming." the program save words in the string "str", then it creates Dynamically a 2D array and copies the words into the 2D array:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <math.h>
#include <conio.h>
void freeMememory(int**array, int row){
for(int i=0;i<row;i++)
free(array[i]);
free(array);
}
int lettersCount(char *arr){
int space=0, letters=0;
do{
if(*arr !=' '&& *arr!='\t' && *arr!=','&& *arr!='.'){
letters =letters+1;
}
++arr;
}while(*arr);
return letters;
}
int wordCount(char *arr){
int space=0, words=0;
for(int i=0; arr[i]!='\0'; i++){
if(arr[i] ==' '|| arr[i]=='\t'|| arr[i]=='\n'||arr[i]==','||arr[i]=='.'){
space++;
}
if(space>0){
words++;
space=0;
}
}
return words;
}
int main (){
char arr[100];
int i, j, row, column;
scanf("%[^\n]s", &arr);
int *words = wordCount(arr);
int *letters = lettersCount(arr);
row=words;
column=letters;
int **ptr = (int **)malloc(row*column*sizeof(int));
for(i=0;i<row;i++){ptr[i]=(int*)malloc(column*sizeof(int));}
/*
//how should I write here to copy only words from arr to ptr?
like this:
arr = "some words, two,three,four."
ptr = {
"some", "words", "two", "", "three", "four",
}
*/
freeMememory(ptr, row);
return 0;}
So any ideas how to copy only the words from the string into the 2D array without copying (periods, spaces, cammas)?
What you might be looking for is strtok from <string.h>. I will also replace row with rows and column with columns in the following code snippet, as suggested by tadman in the comments.
/* no need to cast `malloc` */
char *ptr[rows];
for (int i = 0; i < rows; ++i) {
ptr[i] = malloc(columns);
if (!token) {
fprintf(stderr, "Error: memory allocation failed\n");
exit(EXIT_FAILURE);
}
}
const char *delims = " \t\n,.";
/* second argument are delimiters */
strcpy(ptr[0], strtok(arr, delims));
for (int i = 1; i < rows; ++i)
strcpy(ptr[i], strtok(NULL, delims));
I would also suggest simplifying your functions. For example your wordCount function could probably be simplified to this:
int count_words(char *str, const char *delims)
{
words = 1;
for (int i = 0; str[i] != '\0'; ++i)
if (strchr(delims, str[i]))
++words;
return words;
}
The function count_words could then be called like this:
const char *delims = " \t\n,.";
int words = count_words(arr, delims);
First notice that your code isn't using a 2D array. It's using an array of char-pointers that each point to a char-array. It's a different thing but it can be used in much the same way.
Below is an implementation that uses strtok to split the input string. Further, it uses realloc to make the array of char-pointers grow when a new word is found. Finally it uses a sentinel (i.e. NULL) to indicate end-of-words.
The code is pretty simple but the performance is poor.
Example:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char** split(const char* str)
{
if (str == NULL) exit(1);
// Copy input string as strtok changes its input
char* str_cpy = malloc(strlen(str) + 1);
if (str_cpy == NULL) exit(1);
strcpy(str_cpy, str);
unsigned num_rows = 0;
char** arr = NULL;
// Get first token
const char *delims = " \t\n,.";
char* ptr = strtok(str_cpy, delims);
while (ptr)
{
// Allocate one more row
arr = realloc(arr, (num_rows + 1) * sizeof *arr);
if (arr == NULL) exit(1);
// Allocate memory for one more word
arr[num_rows] = malloc(strlen(ptr) + 1);
if (arr[num_rows] == NULL) exit(1);
strcpy(arr[num_rows], ptr);
++num_rows;
// Get next token
ptr = strtok(NULL, delims);
}
// Add a sentinel to indicate end-of-words
arr = realloc(arr, (num_rows + 1) * sizeof *arr);
if (arr == NULL) exit(1);
arr[num_rows] = NULL;
free(str_cpy);
return arr;
}
int main(void)
{
char* str = "some,words, in, c, programming.";
char** arr = split(str);
printf("Original string: %s\n", str);
for (int i=0; arr[i] != NULL; ++i)
{
printf("Word[%d]: %s\n", i, arr[i]);
}
// Free array
for (int i=0; arr[i] != NULL; ++i)
{
free(arr[i]);
}
free(arr);
return 0;
}
Output:
Original string: some,words, in, c, programming.
Word[0]: some
Word[1]: words
Word[2]: in
Word[3]: c
Word[4]: programming

Dynamic memory allocation for an array of pointers to char in C

I'm building a word counter program. To achieve this, I was thinking about saving the string the user inputted, and using strtok() to split the sentence with space as the delimiter. But first I want to allocate enough memory for each word. Let's say the sentence is "Hello World". I've already dynamically allocated memory for the string itself. Now I want to split Hello World into 2 strings, "Hello" and "World". My goal is to allocate enough memory so that there's not too much empty space but I also don't want to allocate too little space. Here is my code so far:
#include <stdio.h>
#include <stdlib.h>
char *strmalloc(char **string);
char *user_input = NULL;
char *word_array[];
int main(void) {
printf("Enter a sentence to find out the number of words: ");
user_input = strmalloc(&user_input);
return 0;
}
char *strmalloc(char **string) {
char *tmp = NULL;
size_t size = 0, index = 0;
int ch;
while ((ch = getchar()) != '\n' && ch != EOF) {
if (size <= index) {
size += 1;
tmp = realloc(*string, size);
if (!tmp) {
free(*string);
string = NULL;
break;
}
*string = tmp;
}
(*string)[index++] = ch;
}
return *string;
}
How would I go about doing this? Should I do the splitting first or allocate the space required for the array first?
You can count words without splitting the sentence, here is an example :
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
// Change this to change the separator characters
static inline char isSeparator(char ch) { return isspace(ch) || ispunct(ch); }
char * jumpSeparator(char *string) {
while(string[0] && isSeparator(string[0])) string++;
return string;
}
char * findEndOfWord(char *string) {
while (string[0] && !isSeparator(string[0])) string++;
return string;
}
int countWords(char *string) {
char * ptr = jumpSeparator(string);
if (strlen(ptr) == 0) return 0;
int count = 1;
while((ptr = findEndOfWord(ptr)) && ptr[0]) {
ptr = jumpSeparator(ptr);
if (!ptr) break;
count++;
}
return count;
}
int main() {
char * sentence = "This is,a function... to||count words";
int count = countWords(sentence);
printf("%d\n", count); //====> 7
}
EDIT : Reusing the same functions here is another example that allocates substrings dynamically :
int main() {
char * sentence = "This is,a function... to||split words";
int count = countWords(sentence);
char * ptr = sentence, *start, *end;
char ** substrings = malloc(count * sizeof(char *));
int i=0;
while((ptr = jumpSeparator(ptr)) && ptr[0]) {
start = ptr;
ptr = findEndOfWord(ptr);
end = ptr;
int len = end-start;
char * newString = malloc(len + 1);
memcpy(newString, start, len);
newString[len] = 0;
substrings[i++] = newString;
}
// Prints the result
for(int i=0; i<count; i++) printf("%s\n", substrings[i]);
// Frees the allocated memory
for(int i=0; i<count; i++) free(substrings[i]);
free(substrings);
return 0;
}
Output :
This
is
a
function
to
split
words

Allocating memory for a string in C using malloc

I am trying to allocate memory for an array of strings using malloc. The size of each string is not known before the input from the user, so this is how I tried to allocate memory for each element in the array.
I have some errors with the code, but can't figure them out or can't understand them. I am getting an error regarding the allocation. Can anyone tell me what's wrong about this?
bool read_strings(char * strings[], int n)
{
int i = 0;
while (i<n)
{
char string[MAX_LENGTH];
if (scanf("%s", string)!=1)
return false;
char* memory= (char*)malloc(sizeof(char)*strlen(string));
if (memory == NULL)
return false;
memory = string;
strings[i] = memory;
i++;
}
return true;
}
Thanks a lot!
At least you have to replace
char* memory= (char*)malloc(sizeof(char)*strlen(string));
if (memory == NULL)
return false;
memory = string;
strings[i] = memory;
by
strings[i] = strdup(string)
Note that using scanf("%s", string) the separator between the read string is the space
The problem is right here:
char* memory = (char*)malloc(sizeof(char)*strlen(string));
memory = string; <<<
strings[i] = memory;
You will lose memory if you assign strings to pointers like this.
Either:
a) Copy the string into the newly allocated memory using strcpy() or strncpy(), also make sure you have enough space for the NULL character \0
strings[i] = (char*)malloc(sizeof(char) * (strlen(string) + 1));
strcpy(strings[i], string);
b) Use strdup(), which is like a mix between strcpy() and malloc(), it creates just enough space for your string and copies it into a new memory location
strings[i] = strdup(string);
You have many mistakes
(char*)malloc(sizeof(char)*(strlen(string) **+ 1**)) . You have to reserve memory to '\0'
Very wrong
memory = string;
To copy strings you have to usr strcpy (the correct function nowadays is strncpy is more safe)
To have a truly unlimited buffer in C, (or limited by the amount of memory and a size_t,) you could build up the memory allocation incrementally.
#include <stdlib.h> /* realloc free */
#include <stdio.h> /* stdin fgets printf */
#include <string.h> /* strcpy */
#include <assert.h> /* assert */
#include <stdint.h> /* C99 SIZE_MAX */
#include <stdbool.h> /* C99 bool */
/* Returns an entire line or a null pointer, in which case eof or errno may be
set. If not-null, it must be freed. */
static char *line(void) {
char temp[1024] = "", *str = 0, *str_new;
size_t temp_len, str_len = 0;
while(fgets(temp, sizeof temp, stdin)) {
/* Count the chars in temp. */
temp_len = strlen(temp);
assert(temp_len > 0 && temp_len < sizeof temp);
/* Allocate bigger buffer. */
if(!(str_new = realloc(str, str_len + temp_len + 1)))
{ free(str); return 0; }
str = str_new;
/* Copy the chars into str. */
strcpy(str + str_len, temp);
assert(str_len < SIZE_MAX - temp_len); /* SIZE_MAX >= 65535 */
str_len += temp_len;
/* If on end of line. */
if(temp_len < sizeof temp - 1 || str[str_len - 1] == '\n') break;
}
return str;
}
static bool read_strings(char * strings[], int n) {
char *a;
int i = 0;
while(i < n) {
if(!(a = line())) return false;
strings[i++] = a;
}
return true;
}
int main(void) {
char *strings[4] = { 0 }; /* C99 */
size_t i;
bool success = false;
do {
if(!read_strings(strings, sizeof strings / sizeof *strings)) break;
for(i = 0; i < sizeof strings / sizeof *strings; i++)
printf("%lu: <%s>\n", (unsigned long)i, strings[i]);
success = true;
} while(0); {
for(i = 0; i < sizeof strings / sizeof *strings; i++)
free(strings[i]);
}
return success ? EXIT_SUCCESS : (perror("stdin"), EXIT_FAILURE);
}
I think that's right-ish. However, this should bring pause; what if they never hit enter? If one has a MAX_LENGTH, then consider allocating statically, depending on your situation.
Edit: It also has a worst-case running time that may not be desirable; if entering really arbitrarily large lines, use a geometric progression to allocate space.
I think this is what you wanted to do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int read_strings(char * strings[], int n)
{
int i = 0;
char buffer[256] ={0}; /*a temp buffer of fixed max size for input */
if(NULL == strings)
{
return 0 ;
}
for (i= 0; i<n; ++i)
{
if (fgets(buffer, 256,stdin)== NULL) /*safer then scanf - read input into the buffer*/
return 0;
strings[i]= malloc(sizeof(char)*(strlen(buffer)+1)); /* the char poiner in he i place will now point to the newly allocated memory*/
strcpy(strings[i], buffer); /*copy the new string into the allocated memory now string[i] is pointing to a string*/
}
return 1;
}
static void printStringsArray(const char* strArr[], size_t size)
{
int i = 0;
if(NULL == strArr)
{
return;
}
for(i = 0; i< size; ++i)
{
printf("%s", strArr[i]);
}
}
int main(void)
{
char * arr[3]; /*array of (char*) each will point to a string after sending it to the function */
read_strings(arr,3);
printStringsArray(arr,3);
return 0;
}

Why is my Program is Returning (null) and Gibberish rather than the Intended Output

Windows NT crashed.
I am the Blue Screen of Death.
No one hears your screams.
I am writing a program in which I have to read in haikus from a file, after which I must store all of the five syllable lines into one array and all of the seven syllable lines into another. I have to use an array of char* pointers to store each set of lines. I also can only allocate enough space at each pointer for the actual length of the string. As a result, for the haiku above, I should malloc 20, 31, and 28 bytes for those three lines, respectively (including the \0).
Once all the lines have been read in from the file and stored into the appropriate arrays, I must use a random number generator to pick a set of three lines to print to the screen.
#include <stdio.h>
#include <ctype.h>
#include <math.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
int main(int argc, char **argv)
{
if (argc != 2)
{
printf("This number of arguments is incorrect.");
return 1;
}
FILE *oldfile, *newfile;
char const *newfilename = "myhaiku.txt";
int linecntr = 0, fivesyllines = 0, svnsyllines = 0;
int freearr, newhaiku;
// char **fivesylptrs = malloc(200 * sizeof(char *));
// char **svnsylptrs = malloc(200 * sizeof(char *));
char *fivesylptrs[200], *svnsylptrs[100];
char line[129];
srand(time(NULL));
/* for (fivesyllines = 0; fivesyllines < 200; fivesyllines++)
{
fivesylptrs[fivesyllines] = (char *) malloc(129 * sizeof(char));
}
for (svnsyllines = 0; svnsyllines < 200; svnsyllines++)
{
svnsylptrs[svnsyllines] = (char *) malloc(129 * sizeof(char));
} */
oldfile = fopen(argv[1], "r");
newfile = fopen(newfilename, "w");
if (oldfile)
{
while (fgets(line, 129, oldfile) != NULL)
{
linecntr++;
if ((linecntr % 2) != 0)
{
// printf("%s", line);
fivesylptrs[fivesyllines] = (char *)malloc(129 * sizeof(char));
if (fivesylptrs[fivesyllines] == NULL)
{
printf("The memory for the five syllable strings wasn't allocated properly.\n");
}
strcpy(fivesylptrs[fivesyllines], line);
// printf("%s", fivesylptrs[fivesyllines]);
fivesylptrs[fivesyllines] = fivesylptrs[fivesyllines + 1];
// fivesyllines++;
}
else if ((linecntr % 2) == 0 && line[0] != '\n')
{
// printf("%s", line);
svnsylptrs[svnsyllines] = (char *)malloc(129 * sizeof(char));
if (svnsylptrs[svnsyllines] == NULL)
{
printf("The memory for the seven syllable strings wasn't allocated properly.\n");
}
strcpy(svnsylptrs[svnsyllines], line);
// printf("%s", svnsylptrs[svnsyllines]);
svnsylptrs[svnsyllines] = svnsylptrs[svnsyllines + 1];
// svnsyllines++;
}
}
}
else
{
printf("This file could not be opened, please try again.\n");
}
for (newhaiku = 0; newhaiku < 10; newhaiku++)
{
printf("%s", fivesylptrs[(rand() % 200)]);
printf("%s", svnsylptrs[(rand() % 100)]);
printf("%s", fivesylptrs[(rand() % 200)]);
}
fclose(oldfile);
fclose(newfile);
for (freearr = 0; freearr < 200; freearr++)
{
free(fivesylptrs[freearr]);
}
for (freearr = 0; freearr < 100; freearr++)
{
free(svnsylptrs[freearr]);
}
return 0;
}
I have tried quite a few different techniques to resolve the errors that I'm pretty sure I'm having with malloc, but I can't figure out exactly what I'm doing incorrectly. I would really appreciate any and all help!
svnsyllines and fivesyllines are not incremented so you allocate svnsylptrs and fivesylptrs at index 0 multiple times (leak). The rest of the array is never allocated even though you access it.
Two issues here:
Array indices for the arrays svnsylptrs and fivesylptrs are not incremented in the respective blocks.
The size of the array svnsylptrs is 200. But the for loop at the end only frees the first 100 elements. This will cause a memory leak.
for (freearr = 0; freearr < 100; freearr++) //This should check upto 200
{
free(svnsylptrs[freearr]);
}

why this snipped code triggers breakpoint at second iteration at this line "*(str_arr + i) = (char*)calloc((strlen(temp_str) + 1), sizeof(char));"

Just trying to scan N strings to an array and dynamically allocate them
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3
#define MAX_STR_LEN 20
void main(void) {
char* str_arr[N] = { '\0' };
char temp_str[MAX_STR_LEN] = { '\0' };
for (size_t i = 0; i < N; i++)
{
//scan string
fgets(temp_str, MAX_STR_LEN, stdin);
strtok(temp_str, "\n");
// next line of code triggers breakpoint at second iteration
*(str_arr + i) = (char*)calloc((strlen(temp_str) + 1), sizeof(char));
if (!(str_arr + i))
{
printf("Could not allocate memory\n");
return 1;
}
//copy temporary string to dedicated string
strcpy_s(*(str_arr + i), sizeof(temp_str), temp_str);
}
printf("\n");
system("PAUSE");
}
A few observations:
You didn't allocate sizeof(temp_str). You allocated
strlen(temp_str)+1. So the first strcpy_s is going to cause a buffer overrun, which is why things go bad on the second iteration through the loop.
The statement if (!(str_arr +i)) doesn't check the pointer that you
just allocated
*(str_arr + i) is the same as str_arr[i] and the latter is easier
for someone reading the code
In the line char* str_arr[N] = { '\0' };, you declare an array of pointers, but you initialize with a character constant (\0) which is an int. The initializer should be a NULL pointer.
This answer shows a better way to remove the newline from the buffer. The problem with strtok is that it won't remove the newline if/when the user enters a blank line.
With that in mind, here's my recommended fix:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 3
#define MAX_STR_LEN 20
int main(void) {
char* str_arr[N] = { NULL };
char temp_str[MAX_STR_LEN] = { '\0' };
for (size_t i = 0; i < N; i++)
{
//scan string
if (fgets(temp_str, MAX_STR_LEN, stdin) == NULL)
{
printf("Not enough lines of input\n");
return 1;
}
temp_str[strcspn(temp_str, "\n")] = '\0';
size_t length = strlen(temp_str) + 1;
str_arr[i] = (char *)calloc(length, 1);
if (!str_arr[i])
{
printf("Could not allocate memory\n");
return 1;
}
//copy temporary string to dedicated string
strcpy_s(str_arr[i], length, temp_str);
}
printf("\n");
system("PAUSE");
}

Resources