I have a struct with a 2d char array that can be modified, I should be able to increase the number of rows in the 2d array, I managed to do that but I run into a problem where after the fourth row I can't allocate more space, I tried to change the length of the array to be smaller and in that case, the same problem accord after four times, and the return value of the realloc() isn't NULL and I can't exit the program.
This is the struct:
struct Buffer{
/*--buffer--*/
/*2d array that store the whole text */
char **text;
/* number of rows */
int count;
};
the program reads text and after 60 characters it should create a new row
/*initialize a buffer */
struct Buffer* buffer = (struct Buffer*)malloc(sizeof(struct Buffer));
buffer->text = (char**)malloc(sizeof(char*));
buffer->text[0] = (char*)malloc(sizeof(char)*61);
buffer->count = 1;
/*start reading the text*/
readText(1, buffer);
printf("\nFixed output: \n");
/*print the text*/
printText(1, buffer);
inside readText()
/* try to add more memory for the ADT */
buffer->text[buffer->count-1][60] = '\0';
if(!expandADT(1, buffer)) exit_error(0);
buffer->text[buffer->count-1][0] = c;
int expandADT(int option, void *ptr)
/* cast the pointer */
struct Buffer* buffer = (struct Buffer*) ptr;
struct Buffer* temp;
/* increase the number of rows */
buffer->count++;
/* try to make space for another row */
temp = (struct Buffer*)realloc(buffer, sizeof(*buffer->text) * buffer->count);
/* if the buffer is NULL it means that we don't have enough space and we return an error*/
if(temp == NULL) return 0;
buffer = temp;
buffer->text[buffer->count-1] = (char*)malloc(sizeof(char)*61);
if(buffer->text[buffer->count-1] == 0) return 0;
If the array runs out of space it should print a message and close the program, however the term
if(temp == NULL) return 0;
in expandADT() isn't correct and the program keeps running.
then it stops at the code line -
buffer->text[buffer->count-1][0] = c; /* c is an input character */
at readText() after the program tried to expand the array.
For input that is larger than 60*4.
There is no output whatsoever from the program and it closes.
Related
I am trying to solve a "suppose to be" a simple C question.
Q: Receive "infinite" input by user using int pointer until EOF received using malloc and realloc.
I defined a int pointer like this:
int *inputBuffer = NULL;
and initialized it using this method:
/* #define BUFFER_SIZE 8 - already defined at top of code, posted here for more info */
/* Creates buffer by using malloc -
if succeded returns true, otherwise false */
int createBuffer(int **inputBuffer)
{
*inputBuffer = (int*) calloc(BUFFER_SIZE, sizeof(char)); /* Allocate memory to get input from user */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
by calling createBuffer(&inputBuffer)
so far so good, memory is allocated successfully.
before starting to receive characters from user I defined the following properties:
int totalCharacters = 0;
int bufferExtendsCounter = 1;
int character;
Next step is to receive characters inputs from user like this:
/* #define MEMORY_SAFE_GAP 4 - already defined at top of code, posted here for more info */
while((character = getchar()) != EOF && inputBuffer != NULL)
{
/* Check if reallocate needs to be called since almost maxed out buffer */
if(totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
{
/* Add 1 to extends buffer times */
bufferExtendsCounter+=1;
if(!extendBuffer(&inputBuffer, totalCharacters))
printf("\nFailed to allocate more memory.");
}
/* Initialize buffer with character, this is safe since there is a memory safe gap */
inputBuffer[totalCharacters] = character;
totalCharacters++;
}
extend buffer looks like this:
/* Extends buffer size by using realloc
if succeded returns true, otherwise false */
int extendBuffer(int **inputBuffer, int minimumBufferSize)
{
/* Check how many times buffer needs to be multiple (at least) */
int multipleBufferNumber = (minimumBufferSize / BUFFER_SIZE) + 1;
int newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
while(newBufferSize < minimumBufferSize)
{
multipleBufferNumber+=1;
newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
}
/* Reallocate memory for next chunck of data */
*inputBuffer = realloc(*inputBuffer, newBufferSize);
/* Check if memory successfully allocated */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
It looks like I extend the buffer size enough for more input by user, but still gets error:
corrupted size vs. prev_size: 0x08f86010 ***
Example input:
TestProgramTest (Pressed Enter after last 't')
(DebugPrint: Received 13 characters)
(DebugPrint: Reallocating to size 16)
*** Error in `./test': corrupted size vs. prev_size: 0x08f86010 ***
EDIT (Due to lack of code parts):
The following part is right after while loop:
inputBuffer[totalCharacters] = '\0';
printf("\nInput by user:\n");
/* #define LINE_LENGTH 5 - already defined at top of code, posted here for more info */
printBuffer(inputBuffer, LINE_LENGTH, totalCharacters);
/* free memory */
free(inputBuffer);
and printBuffer looks like:
/* Prints the buffer data to pretty output */
void printBuffer(int *inputBuffer, int lineLength, int totalCharacters)
{
int i;
for(i = 0; i < totalCharacters; i++)
{
/* Print each char value */
printf("%c", inputBuffer[i]);
/* Check if got to line limit, if so enter new line */
if((i+1) % lineLength == 0)
printf("\n");
}
}
Second edit:
Changed all int pointer parts to char pointer.
Full code looks like:
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
#define LINE_LENGTH 5
#define BUFFER_SIZE 8
#define TRUE 1
#define FALSE 0
#define MEMORY_SAFE_GAP 4
int createBuffer(char **inputBuffer);
int extendBuffer(char **inputBuffer, int minimumBufferSize);
void printBuffer(char *inputBuffer, int lineLength, int totalCharacters);
int main(void)
{
char *inputBuffer = NULL;
if(!createBuffer(&inputBuffer))
{
printf("Memory cannot be allocated, program will exit now.");
exit(-1);
}
int totalCharacters = 0;
int bufferExtendsCounter = 1;
char character;
printf("Please enter a string:\n");
/* Loop till EOF received */
while((character = getchar()) != EOF && inputBuffer != NULL)
{
/* Check if reallocate needs to be called since almost maxed out buffer */
if(totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
{
/* Add 1 to extends buffer times */
bufferExtendsCounter+=1;
if(!extendBuffer(&inputBuffer, totalCharacters))
printf("\nFailed to allocate more memory.");
}
/* Initialize buffer with character, this is safe since there is a memory safe gap */
inputBuffer[totalCharacters] = character;
totalCharacters++;
}
inputBuffer[totalCharacters] = '\0';
printBuffer(inputBuffer, LINE_LENGTH, totalCharacters);
/* free memory */
free(inputBuffer);
return 0;
}
/* Creates buffer by using malloc
if succeded returns true, otherwise false */
int createBuffer(char **inputBuffer)
{
/* Allocate memory to get input from user */
*inputBuffer = (char*) calloc(BUFFER_SIZE, sizeof(char));
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
/* Extends buffer size by using realloc
if succeded returns true, otherwise false */
int extendBuffer(char **inputBuffer, int minimumBufferSize)
{
/* Check how many times buffer needs to be multiple (at least) */
int multipleBufferNumber = (minimumBufferSize / BUFFER_SIZE) + 1;
int newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
while(newBufferSize < minimumBufferSize)
{
multipleBufferNumber+=1;
newBufferSize = BUFFER_SIZE * multipleBufferNumber * sizeof(char);
}
/* Reallocate memory for next chunck of data */
*inputBuffer = realloc(*inputBuffer, newBufferSize);
/* Check if memory successfully allocated */
if(*inputBuffer == NULL)
return FALSE;
return TRUE;
}
/* Prints the buffer data to pretty output */
void printBuffer(char *inputBuffer, int lineLength, int totalCharacters)
{
printf("Printing buffer\n");
int i;
for(i = 0; i < totalCharacters; i++)
{
/* Print each char value */
printf("%c", inputBuffer[i]);
/* Check if got to line limit, if so enter new line */
if((i+1) % lineLength == 0)
printf("\n");
}
}
Any help would be great!
Thanks in advance.
Right here
*inputBuffer = (int*) calloc(BUFFER_SIZE, sizeof(char));
You reserve space for 8 chars but try to store 8 ints in it
Why isnt inputBuffer just a char*? since thats what you are storing
Now you have fixed that - look at this
if (totalCharacters - MEMORY_SAFE_GAP > (BUFFER_SIZE * bufferExtendsCounter))
I do not know what the intention of the 'MEMORY_SAFE_GAP' is but its wrong
Look at what happens when I input character number 8
if(8 - 4 > 8 * 1)
is false, so you do not extend the buffer.
This 'SAFE-GAP ensure that you always run off the end, your code no longer crashes if you just have
if (totalCharacters >= (BUFFER_SIZE * bufferExtendsCounter))
output is still a little garbled but you can probably fix that. I input 1234567890 and got
Please enter a string:
1234567890
^Z
Printing buffer
12345
678═0
Hi there, I am new to C Programing and I am trying to make a Program to Read and Print text from standard input (file or keyboard) as part of my studies.
The rules are:
use two function one for read and one for print.
read function should read into dynamic array or dynamic linked list of arrays.
read function should ignore enter character ('\n').
the user should be able to choose one of two data struct types option (array or linked list).
print function should print the text in lines of 60characters.
the first memory allocation should be at the main function.
I have been trying to solve this out for few weeks but getting stuck, any step will be helpful.
I am still having problems in both function readText and printText
There are some code that have not written yet because I could not check print function without make read function work out.
The First problem is that the read function failed to allocate new memory after 120 characters.
My Code:
/* This program will pick up text from the standard input store him at one of two data structure types and print it according to the following rules:
8. every printed line will be at the size of 60 characters.
9. if there is no more memory to allocate the program will print the text that stored until this moment.
*/
#include <stdio.h>
#include <stdlib.h>
#define LINE_SIZE 60
#define BUFFER 1
#define LINKED_LIST 2
/* function prototype */
void readText(void *pointer,int dataStructType); /* function to read text from standard input and store it in a data structure */
void printText(void *pointer,int dataStructType); /* function to print text from a given data structure */
struct linkedList
{
char data[LINE_SIZE];
struct linkedList * next;
};
int main()
{ /* start of main function */
void *pointer = NULL;
int dataStructType = 0;
/* message that asking the user to choose one of two functions to use */
printf("please insert the number of the data structure you want to use and press enter:\n1 - Buffer.\n2 - Linked List.\n");
scanf("%d",&dataStructType); /* collect the user input into dataStructType variable */
while(dataStructType != BUFFER && dataStructType != LINKED_LIST) /* while the user put in an invalid option */
{
printf("invalid input!\tplease try again.\n\n"); /* print error message to the user and return the process */
printf("please insert the number of the data structure you want to use and press enter:\n1 - Buffer.\n2 - Linked List.\n");
scanf("%d",&dataStructType);
}
if(dataStructType == BUFFER) /* if the user chose buffer as the data structure type */
{
printf("you have chosen option one - Buffer data structure type.\n");
pointer = (char*) calloc(LINE_SIZE, sizeof(char));
if(!pointer) /* if pointer is NULL */
{
printf("error!\t memory allocation failed.\t please try again later."); /* error message for the user */
return 0; /* exit main function and the program */
}else /* if the memory allocation succeeded */
{
readText(pointer,dataStructType); /* call readText function with BUFFER dataStructType */
printText(pointer,dataStructType); /* call printText function with BUFFER dataStructType */
}
}else /* if the user chose linked list as the data structure type */
{
printf("you have chosen option two - Linked List data structure type.\n");
struct linkedList * head = NULL;
head = calloc(1, sizeof(struct linkedList));
if(!head) /* if head is NULL */
{
printf("error!\t memory allocation failed.\t please try again later."); /* error message for the user */
return 0; /* exit main function and the program */
}else /* if the memory allocation succeeded */
{
pointer = head;
readText(&pointer,dataStructType); /* call readText function with linkedList dataStructType */
printText(pointer,dataStructType); /* call printText function with linkedList dataStructType */
}
}
return 0;
} /* end of main function */
/* readText function.
*
* mission: read text from standard input and store it in a data structure by the following rules:
* if there is not enough memory the function will try to find new memory for the structure.
* if the function cant find memory she will store the text until the memory is full and print him.
*
* parameters:
* pointer - points to the pointer that points to the start of the data structure.
* dataStructType - an integer that represent the data struct type.
*
* available data structures:
* 1 - Buffer - Array.
* 2 - linkedList - Linked List.
*/
void readText(void *pointer,int dataStructType)
{ /* start of readText function */
int cnt = 0; /* count the number of characters collected into the the array until the end of the specific line (limited for 60 characters for a line) */
int currentCell = 0; /* counter to count the collected characters **/
char c; /* character variable to collect characters from the input */
int lines = 1; /* lines counter */
char *holder = NULL; /* temporary pointer to a new line */
printf("please enter your text and in the end press cntrl+d\n");
if(dataStructType == BUFFER) /* if the structure type is a buffer. */
{
while((c = getchar()) != EOF)
{
if (c != '\n') /* if the character is '\n' skip the character */
{
if (cnt == LINE_SIZE - 1) /* if this is the sixty character (starts from index 0) */
{
cnt = 0; /* restart counter value */
lines++; /* add a "line" to calculate the memory size */
holder = (char *)realloc(pointer, (lines * LINE_SIZE) * sizeof (char)); /* reallocate new memory including memory for a new "line" */
if (holder == NULL) /* if the memory allocation failed */
{
printf("error!\t memory allocation failed.\n the program will now print the successfully collected text.\n"); /* message for the user */
printf("after printing the collected text the program will be finished\n"); /* message for the user */
pointer = (pointer - (lines * LINE_SIZE)); /* re-aim the pointer to the buffer start */
printText(pointer, BUFFER); /* call printText function to print the collected text */
} else /* if the memory allocation succeeded */
{
free(pointer); /* free the old array memory */
pointer = holder; /* re-aim the pointer to the start of the new allocated memory */
/* pointer = (pointer + ((lines - 1) * LINE_SIZE)); /* re-aim the pointer to the start of the "new line" on the new allocated memory */
}
} /* if we did not finished to collect text into a line and the character is not '\n' */
*(char*)(pointer + currentCell) = c; /* place the character in the current cell of the array */
cnt++; /* increment counter by one */
currentCell++; /* increment current cell counter by one */
}
} *(char*)(pointer + currentCell) = c; /* place the character in the current cell of the array */
}else /* if the structure type is a linked list. */
{
}
} /* end of readText function */
void printText(void *pointer,int dataStructType)
{
if (dataStructType == BUFFER) /* if the data structure is buffer */
{
register int cnt = 0; /* count the number of characters printed from a specific line (limited for 60 characters for a line) */
register int currentCell = 0; /* counter to count the collected characters */
while (*(char*)pointer != EOF) /* if its the end of the file finish the printing */
{
if (cnt == 59) /* if we printed 60 characters print an enter for a new line */
{
printf("\n");
cnt = 0;
} else /* if there is more characters to print until 60 characters */
{
printf("%c", *(char*)(pointer + currentCell)); /* print the character */
cnt++; /* count another character that was printed */
currentCell++; /* increment current cell counter by one */
}
}
} /* else if the data structure is a linked list */
}
Congratulations on working with C, once you're over the learning curve, your experience in it will be invaluable!
Some hints:
if you want to read into something dynamic, consider these options: provide a ** parameter, or return a * so that the caller can link to the memory returned by realloc
you're using free incorrectly, realloc handles freeing any memory
printf has length format specifiers which work with strings, and the function returns the number of bytes written
I made a program that works! (sorry excited a little bit).
Well this is the mistakes I found:
using free(); function in an unneeded spot. (following the reference of Yimin Rong).
Incorrect use of brackets while trying to get the pointer point value.
Incorrect use of printf(); function, instead I used putchar(); function to print the characters.
Added free methods by David C. Rankin advice.
Completed the linked list part in both functions.
Here is the completed code:
/* This program will pick up text from the standard input store him at one of two data structure types and print it according to the following rules:
* every printed line will be at the size of 60 characters.
* if there is no more memory to allocate the program will print the text that stored until this moment.
*/
#include <stdio.h>
#include <stdlib.h>
#define LINE_SIZE 60
#define BUFFER 1
#define LINKED_LIST 2
/* function prototype */
void readText(void *pointer,int dataStructType); /* function to read text from standard input and store it in a data structure */
void printText(void *pointer,int dataStructType); /* function to print text from a given data structure */
struct Node
{
char data[LINE_SIZE];
struct Node * next;
};
int main()
{ /* start of main function */
void *pointer = NULL;
struct Node * head = NULL;
int dataStructType = 0;
/* message that asking the user to choose one of two functions to use */
printf("please insert the number of the data structure you want to use and press enter:\n1 - Buffer.\n2 - Linked List.\n");
scanf("%d",&dataStructType); /* collect the user input into dataStructType variable */
while(dataStructType != BUFFER && dataStructType != LINKED_LIST) /* while the user put in an invalid option */
{
printf("invalid input!\tplease try again.\n\n"); /* print error message to the user and return the process */
printf("please insert the number of the data structure you want to use and press enter:\n1 - Buffer.\n2 - Linked List.\n");
scanf("%d",&dataStructType);
}
if(dataStructType == BUFFER) /* if the user chose buffer as the data structure type */
{
printf("you have chosen option one - Buffer data structure type.\n");
pointer = calloc(LINE_SIZE, sizeof(char));
if(!pointer) /* if pointer is NULL */
{
printf("error!\t memory allocation failed.\t please try again later."); /* error message for the user */
return 0; /* exit main function and the program */
}else /* if the memory allocation succeeded */
{
readText(pointer,dataStructType); /* call readText function with BUFFER dataStructType */
printText(pointer,dataStructType); /* call printText function with BUFFER dataStructType */
}
}else /* if the user chose linked list as the data structure type */
{
printf("you have chosen option two - Linked List data structure type.\n");
head = (struct Node*)calloc(1, sizeof(struct Node));
if(!head) /* if head is NULL */
{
printf("error!\t memory allocation failed.\t please try again later."); /* error message for the user */
return 0; /* exit main function and the program */
}else /* if the memory allocation succeeded */
{
pointer = head;
readText(pointer,dataStructType); /* call readText function with linkedList dataStructType */
printText(pointer,dataStructType); /* call printText function with linkedList dataStructType */
}
}
if(dataStructType == BUFFER) /* if the data struct type is buffer */
free(pointer); /* free the program memory */
else /* if data struct type is linked list */
{
pointer = head; /* re-aim the pointer to the first Node */
while(head->next != NULL) /* if we have more unfree memory that used by Nodes */
{
pointer = (head->next); /* set the pointer to the next Node */
free(head); /* free the node that pointed by head */
head = NULL; /* safety step */
head = pointer; /* re-aim head to the node that was not freed yet */
}
}
return 0;
} /* end of main function */
/* readText function.
*
* mission: read text from standard input and store it in a data structure by the following rules:
* if there is not enough memory the function will try to find new memory for the structure.
* if the function cant find memory she will store the text until the memory is full and print him.
*
* parameters:
* pointer - points to the start of the data structure.
* dataStructType - an integer that represent the data struct type.
*
* available data structures:
* 1 - Buffer - Array.
* 2 - linkedList - Linked List.
*/
void readText(void *pointer,int dataStructType)
{ /* start of readText function */
register int cnt = 0; /* count the number of characters collected into the the array until the end of the specific line (limited for 60 characters for a line) */
register int currentCell = 0; /* counter to count the collected characters **/
char c; /* character variable to collect characters from the input */
int lines = 1; /* lines counter */
void *holder = NULL; /* temporary pointer to a new line */
printf("please enter your text and in the end press cntrl+d\n");
if(dataStructType == BUFFER) /* if the structure type is a buffer. */
{
while((c = getchar()) != EOF)
{
if (c != '\n') /* if the character is '\n' skip the character */
{
if (cnt == LINE_SIZE - 1) /* if this is the sixty character (starts from index 0) */
{
cnt = 0; /* restart counter value */
lines++; /* add a "line" to calculate the memory size */
holder = realloc(pointer, ((lines * LINE_SIZE) * sizeof (char))); /* reallocate new memory including memory for a new "line" */
if (holder == NULL) /* if the memory allocation failed */
{
printf("error!\t memory allocation failed.\n the program will now print the successfully collected text.\n"); /* message for the user */
printf("after printing the collected text the program will be finished\n"); /* message for the user */
printText(pointer, BUFFER); /* call printText function to print the collected text */
} else /* if the memory allocation succeeded */
{
pointer = holder; /* re-aim the pointer to the start of the new allocated memory */
} /* if we did not finished to collect text into a line and the character is not '\n' */
(*(((char*)pointer) + currentCell)) = c; /* place the character in the current cell of the array */
cnt++; /* increment counter by one */
currentCell++; /* increment current cell counter by one */
}
} *(((char*)pointer) + currentCell) = c; /* place the EOF character to sine the end of the array */
}else /* if the structure type is a linked list. */
{
holder = pointer; /* save the address of the first Node */
while((c = getchar()) != EOF)
{
if (c != '\n') /* if the character is '\n' skip the character */
{
if(cnt == LINE_SIZE - 1) /* if this is the sixty character (starts from index 0) */
{
cnt = 0; /* restart counter value */
lines++; /* in this case adding a new Node to calculate the memory size */
(((struct Node *)pointer)->next) = (struct Node*) calloc(1,sizeof (struct Node)); /* allocate new memory for another new Node and point the "last" Node pointer to the new Node */
if ((((struct Node *)pointer)->next) == NULL) /* if the memory allocation failed */
{
printf("error!\t memory allocation failed.\n the program will now print the successfully collected text.\n"); /* message for the user */
printf("after printing the collected text the program will be finished\n"); /* message for the user */
printText(pointer, LINKED_LIST); /* call printText function to print the collected text */
} else /* if the memory allocation succeeded */
{
pointer = ((struct Node*)pointer)->next; /* re-aim the pointer to the new node that allocated */
}
} /* if we did not finished to collect text into a line and the character is not '\n' */
(((struct Node *) pointer)->data[cnt]) = c; /* place the character in the current cell of the array */
cnt++; /* increment counter by one */
}
} (((struct Node *) pointer)->data[cnt]) = c; /* place the EOF character to sine the end of the array */
pointer = holder; /* re-aim the pointer to the first Node */
}
} /* end of readText function */
/* printText function.
*
* mission: print the text that was collected by readText function to one of two data struct types:
* print until the end of the file (the end of the collected text).
* print sixty characters per a line.
*
* parameters:
* pointer - points to the start of the data structure.
* dataStructType - an integer that represent the data struct type.
*
* available data structures:
* 1 - Buffer - Array.
* 2 - linkedList - Linked List.
*/
void printText(void *pointer,int dataStructType)
{ /* start of printText function */
register int cnt = 0; /* count the number of characters printed from a specific line (limited for 60 characters for a line) */
register int currentCell = 0; /* counter to count the collected characters */
if (dataStructType == BUFFER) /* if the data structure is buffer */
{
while (*(((char*)pointer) + currentCell) != EOF) /* while we still have characters to print */
{
if (cnt == LINE_SIZE - 1) /* if we printed 60 characters print an enter for a new line */
{
printf("\n"); /* print a new line */
cnt = 0; /* restart the counter */
} else /* if there is more characters to print until 60 characters */
{
putchar(*(((char*)pointer) + currentCell)); /* print the character */
cnt++; /* count another character that was printed */
currentCell++; /* increment current cell counter by one */
}
}
} else /* else if the data structure is a linked list */
{
while((((struct Node*)pointer)->data[cnt]) != EOF) /* while we still have characters to print */
{
if (cnt == LINE_SIZE - 1) /* if we printed 60 characters print an enter for a new line */
{
printf("\n"); /* print a new line */
cnt = 0; /* restart the counter */
if(((struct Node*)pointer)->next == NULL) /* if there are no more Nodes in the linked list */
exit(EXIT_FAILURE); /* exit the program because we finished to print the text */
else /* if there are more nodes in the linked list */
pointer = ((struct Node*)pointer)->next; /* set the pointer to the next Node */
}else /* if there is more characters to print until 60 characters */
{
putchar(((struct Node*)pointer)->data[cnt]); /* print the character */
cnt++; /* count another character that was printed */
}
}
}
} /* end of printText function */
Thank to anyone that was trying to help, love that community
if I want to store string array in C program from stdin, whose array length is not known in advance and the string length is unfixed or unlimited. That means I can not define such thing as char buf[10][100]; in the program. Is there any good solution for this case?
The C standard doesn't have such a function but getline() which is POSIX does what you want. This may or may not be what you're looking for, depending on what OS you're planning to run this on.
You just do something like:
char *inf_line = NULL;
size_t n = 0;
ssize_t input = getline(&inf_line, &n, stdin);
Alternatively, you could try filling up an array with getchar() in some loop, dynamically reallocating memory as you reach the end of the array using malloc(), for example.
See the following code as an example how to read input until EOF is reached (in terminal, try Ctrl-Z or Ctrl-D to emulate an EOF, depending on your OS), by using fixed size chunks and creating a full string after the last chunk was read.
#define CHUNK_SIZE 4 // testing size
//#define CHUNK_SIZE 1024 // my suggested production size
struct node
{
char data[CHUNK_SIZE];
struct node* next;
};
int main()
{
// will be allocated and filled after reading all input
char* full_text = NULL;
// head node
struct node* start = NULL;
// iterator node
struct node* current = NULL;
// for tail allocation
struct node** next = &start;
// count the number of chunks (n-1 full and one partially filled)
size_t count = 0;
// size of the last read - will be the count of characters in the partially filled chunk
size_t last_size;
// will be initialized to the full text size (without trailing '\0' character)
size_t full_size;
while (!feof(stdin))
{
// casting malloc result is bad practice, but working with VS here and it's complaining otherwise
// also, you may want to check the result for NULL values.
*next = (struct node*)calloc(1, sizeof (struct node));
last_size = fread_s((*next)->data, CHUNK_SIZE, 1/* sizeof char */, CHUNK_SIZE, stdin);
next = &((*next)->next);
++count;
}
// calculate the full size and copy each chunk data into the combined text
if (count > 0)
{
full_size = CHUNK_SIZE * (count - 1) + last_size;
// one additional character for the null terminator character
full_text = (char*)malloc(full_size + 1);
full_text[full_size] = '\0';
count = 0;
current = start;
while (current && current->next)
{
memcpy(&full_text[count * CHUNK_SIZE], current->data, CHUNK_SIZE);
current = current->next;
++count;
}
if (current)
{
memcpy(&full_text[count * CHUNK_SIZE], current->data, last_size);
}
}
else
{
full_text = (char*)calloc(1, 1);
}
// full_text now contains all text
// TODO free the node structure
return 0;
}
side note: I use calloc instead of malloc so I get zero-initialized storage.
side note: I use the binary fread_s instead of fgets, which doesn't zero-terminate the read data (would need some different handling otherwise) and which may not play nice with non-ASCII input. So make sure you understand your input format when using this 1:1
Please excuse any mistakes made in this post, as this is my first post here, but do point them out.
I'm still quite new to C, but I'm trying to implement a dictionary from a txt file that has one word in each line through a hash table but whenever I try to search for a word it's never found, though it exists in the file.
Can you please help me figure out what I'm doing wrong?
Both following files:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define tab_size 29 /* table size */
#define word_len 24 /* max length of word */
.h file:
/* structure to be used for each word */
typedef struct list {
char *word;
struct list *next;
} WORD;
.c file:
/* create table */
WORD **create_tab(int size) {
int i = 0;
WORD **hash_tab;
hash_tab = malloc(sizeof(*hash_tab) * size); /*allocate memory */
for (; i<size; i++) /* initialize elements */
hash_tab[i]=NULL;
return hash_tab;
}
/* search for word in table; returns 1 if found, 0 otherwise */
int search(WORD **tb, char *w) {
WORD *htmp/*, *hprv*/;
unsigned long hash = (hash_f(w) % tab_size); /* hash_f is Dan Bernstein's hash function; modulo by tab_size to make sure it fits */
for (htmp = tb[hash]; (htmp != NULL) && (strcmp(htmp->word, w) != 0); / htmp = htmp->next) /* follow chained array of respective cell until word is found or until the end */
{
;
}
if (htmp == NULL) return 0;
return 1;
}
/* insert new WORD with word w at the beginning of the chained array of the respective cell */
void insert(WORD **ht, char *w) {
WORD *htmp;
unsigned long hash = (hash_f(w) % tab_size); /* hash_f is Dan Bernstein's hash function; modulo by tab_size to make sure it fits */
htmp = malloc( sizeof(*htmp) ); /* allocate memory for new WORD */
htmp->word = calloc(word_len+1,sizeof(char)); /* allocate memory for new word with max word length plus one character to make sure there's no buffer overflow in the next line*/
strncpy(htmp->word, w, word_len); /* copy w to word */
htmp->next = ht[hash]; /* new WORD now points to content of the respective table cell */
ht[hash] = htmp; /* table cell now points to new WORD */
}
/* receive empty table and create the whole dictionary in memory word by word */
WORD **make(WORD **dic;) {
char w[word_len];
FILE *dictionary;
dictionary = fopen("dictionary.txt","r");
while ((fgets( w, word_len, dictionary )) != NULL)
insert(dic, w);
fclose(dictionary);
return dic;
}
int main() {
WORD **dic;
char w[word_len];
dic = create_tab(tab_size); /* create the table */
dic = make(dic); /* insert all entrys of dictionary in table */
printf("Insert a word in lowercase: \n");
if ((scanf("%s",w)) == 0) return 0; /* if I didn't somehow verify the scanf it would return an error */
else if (search(dic, w) == 1) printf("The word %s exists in the dictionary \n",w);
else printf("The word %s does not exist in the dictionary",w);
return 0;
}
I've tried several aproaches, some based on the following links, but this always happens. This version is based on the last one.
Quick Way to Implement Dictionary in C
http://www.sparknotes.com/cs/searching/hashtables/section3.rhtml
http://www.seg.rmit.edu.au/code/zwh-ipl/hashtable.c
Problem 1
This is not right.
hash_tab = malloc(sizeof(WORD) * size); /*allocate memory */
It should be
hash_tab = malloc(sizeof(WORD*) * size); /*allocate memory */
To avoid errors like this, you should get in the habit of using
hash_tab = malloc(sizeof(*hash_tab) * size); /*allocate memory */
Problem 2
for (; i<tab_size; i++) /* initialize elements */
hash_tab[i]=NULL;
should be
for (; i<size; i++) /* initialize elements */
hash_tab[i]=NULL;
This is a potential problem. It may not be a real problem in your case if size and tab_size happens to be equal. But as a matter of style, you should use the second block of code.
I need to create program that takes input from stdin in this format:
abcde //number of characters in word = number of words => square shape
fghij
klmno
pqrst
uvwxy
// \n separates first half from second
word1word //any amount of characters, any amount of words
word
word2
sdf
// \n to end input
My code works, but only about 50% of the time. I have couple of example inputs, that I use for testing, but for some of them my readwords function fails.
Here is my function, that reads words. Since I have no idea how many words or how long they are going to be, I use dynamic arrays and getchar() function.
void readWords(char **p,int *n,int w) /* before calling: n = 50; w = 20; p = 50x20 char array */
{
int i = 0,j = 0,x;
char tmp,prevtmp;
while (1)
{
prevtmp = tmp;
tmp = getchar();
if ((prevtmp == '\n' && tmp == '\n') || feof(stdin))
break; /* no more words to read */
if (tmp == '\n') /* end of word */
{
p[i][j] = '\0'; /* add \0 to create string format */
i++;
j = 0;
if (i == *n) /* if there is more words than there is space for them, double the size */
if (realloc(p,*n*2) != NULL)
*n*=2;
continue;
}
p[i][j] = tmp;
j++;
if (j == w) /* if width of word is larger than allocated space, double it */
{
for (x = 0; x < *n;x++);
if(realloc (p[x],w*2) != NULL);
w=w*2;
}
}
*n = i;
}
This is example of input for which this works (note:this function only reads second half after line with only \n):
dsjellivhsanxrr
riemjudhgdffcfz
<skipping>
atnaltapsllcelo
ryedunuhyxhedfy
atlanta
saltlakecity
<skipping 15 words>
hartford
jeffersoncity
And this is input that my function doesn't read properly:
<skipping>
...oywdz.ykasm.pkfwb.zazqy...
....ynu...ftk...zlb...akn....
missouri
delaware
<skipping>
minnesota
southdakota
What my function reads from this input:
e
yoming
xas
florida
lvania
ana
ÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿÿ
There is no difference between those two inputs (except different words and different amount and length of words), the first half gets read properly no matter what, but only the second half bugs out. How do I fix this?
P.S. sorry for long post, in case you want to see full input without skipped bytes, here is pastebin: http://pastebin.com/hBGn2tej
realloc() returns the address of the newly allocated memory, it does not update the argument passed into it. So this (and the other use of realloc()) is incorrect:
if (realloc(p,*n*2) != NULL)
and will results in the code accessing memory incorrectly, causing undefined behaviour. Store the result of realloc() to a temporary variable and check for non-NULL before updating p. The argument to realloc() also indicates the number of bytes, not the number of elements so the size argument calculation is incorrect as p is an array of char* so it should be realloc(p, sizeof(char*) * (*n * 2));. However, the change to p would not be visible to the caller. Also note that the only legal arguments to realloc() are pointers obtained from a previous call to malloc(), realloc() or calloc(). The comment p = 50x20 char array in the code suggests this is not the case.
Here is a small example that allocates an array of char* which should be helpful:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void f(char*** p)
{
/* Allocate space for two 'char*' elements.
Add a NULL pointer element as sentinel value
so caller knows where to find end of list. */
*p = malloc(sizeof(**p) * 3);
/* Allocate space for the two strings
and populate. */
(*p)[0] = malloc(10);
(*p)[1] = malloc(10);
strcpy((*p)[0], "hello");
strcpy((*p)[1], "world");
(*p)[2] = NULL;
/* Add a third string. */
char** tmp = realloc(*p, sizeof(**p) * 4);
if (tmp)
{
*p = tmp;
(*p)[2] = malloc(10);
strcpy((*p)[2], "again");
(*p)[3] = NULL;
}
}
int main()
{
char** word_list = 0;
f(&word_list);
if (word_list)
{
for (int i = 0; word_list[i]; i++)
{
printf("%s\n", word_list[i]);
free(word_list[i]);
}
}
free(word_list);
return 0;
}
Additionally:
prevtmp has an unknown value upon its first use.
getchar() actually returns an int and not a char.