I haven't used C in a long time and apparently I've forgotten more than I thought. While attempting to use malloc() to allocate a string, I keep getting the old data for that string, including it's old, longer length when the requested space is shorter. The circumstance do include the pointer to the string being free()'d and set to NULL. Here is a sample run of what I see in my terminal:
yes, quit, or other (<-message from program)
oooo (<-user input; this will be put to upper case and token'd)
------uIT LENGTH:4 (<-debug message showing length of userInputToken)
preC--tmp: (<-contents of tmp variable)
pstC--tmp:OOOO (<-contents of temp variable)
bad input (<-program response)
yes, quit, or other
yes
------uIT LENGTH:3
preC--tmp:OOOO (<-: tmp = malloc(sizeof(char)*(strlen(userInputToken)-1)); )
pstC--tmp:YESO (<-: strncpy(tmp,userInputToken,strlen(userInputToken)-1); )
bad input
yes, quit, or other
yes
------uIT LENGTH:3
preC--tmp:YESO
pstC--tmp:YESO
bad input
yes, quit, or other
quit
------uIT LENGTH:4
preC--tmp:YESO
pstC--tmp:QUIT (<-: Successful quit because I only did 4 chars; if 5 were used, this would have failed)
As you can see, strlen(userInputToken) gets the correct length and it is used to get the correct number of characters copied – but either free() or malloc() doesn't seem to care about it. I can't figure out what's going on here! Is this a punishment for leaving C for Python?
What's more, the tmp variable should be cleared regardless of free() because it is limited by its scope. Here is the code where everything goes down:
In main.c:
void run() {
outputFlagContainer *outputFlags = malloc(sizeof(outputFlagContainer));
while(true) {
puts("yes, quit, or other");
outputFlags = getUserInput(outputFlags);
if (outputFlags->YES) {
puts("It was a yes!");
} else if (outputFlags->QUIT) {
break;
} else {
puts("bad input");
}
}
free(outputFlags);
}
In messsageParserPieces.h:
outputFlagContainer *getUserInput(outputFlagContainer *outputFlags) {
outputFlags = resetOutputFlags(outputFlags);
char *userInput = NULL;
char user_input[MAX_INPUT];
char *userInputToken = NULL;
char *tmp = NULL;
char *finalCharacterCheck = NULL;
// Tokens to search for:
char QUIT[] = "QUIT";
char YES[] = "YES";
userInput = fgets(user_input, MAX_INPUT-1, stdin);
int i = 0;
while(userInput[i]) {
userInput[i] = toupper(userInput[i]);
i++;
}
userInputToken = strtok(userInput, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
int MEOW = strlen(userInputToken)-1; // DEBUG LINE
printf("\n------uIT LENGTH:%d\n", MEOW); // DEBUG LINE
// The problem appears to happen here and under the circumstances that
// userInput is (for example) 4 characters and then after getUserInput()
// is called again, userInput is 3 characters long.
tmp = malloc(sizeof(char)*(strlen(userInputToken)-1));
if (tmp == NULL) {
exit(1);
}
printf("\npreC--tmp:%s\n", tmp); // This shows that the malloc DOES NOT use the given length.
strncpy(tmp,userInputToken,strlen(userInputToken)-1);
printf("\npstC--tmp:%s\n", tmp); // Copies in the correct number of characters.
userInputToken = tmp;
free(tmp);
tmp = NULL;
}
}
while (userInputToken != NULL) { // NULL = NO (more) tokens.
if (0 == strcmp(userInputToken, YES)) {
outputFlags->YES = true;
} else if (0 == strcmp(userInputToken, QUIT)) {
outputFlags->QUIT = true;
}
userInputToken = strtok(NULL, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
tmp = malloc(sizeof(char)*(strlen(userInputToken)-1));
if (tmp == NULL) {
exit(1);
}
strncpy(tmp,userInputToken,strlen(userInputToken)-1);
userInputToken = tmp;
free(tmp);
tmp = NULL;
}
}
}
return outputFlags;
}
I'm assuming this is some kind of obvious error, but I've tried googling it for about 2 hours tonight. I can't think of how to search this that doesn't bring up a malloc() tutorial – and I have looked at a couple already.
Any insight at all would be greatly appreciated!
tmp = malloc(sizeof(char)*(strlen(userInputToken)-1));
if (tmp == NULL) {
exit(1);
}
printf("\npreC--tmp:%s\n", tmp); // This shows that the malloc DOES NOT use the given length.
strncpy(tmp,userInputToken,strlen(userInputToken)-1);
printf("\npstC--tmp:%s\n", tmp); // Copies in the correct number of characters.
This snippet shows that you expect tmp to be initialised with something. This is not true. You must initialise your memory after allocating it. That's what you do with strncpy.
There's also a problem because you are not allocating enough bytes to hold the string, therefore you cannot display it with a plain %s format specifier. You are allocating strlen(userInputToken)-1 bytes and copying that same number. That means there's no room for a null character, and strncpy will consequently not terminate your string. You should always add one more byte, and if the NULL character will not be copied by strncpy then you must set it yourself:
size_t length = strlen(userInputToken)-1;
tmp = malloc(length + 1);
strncpy(tmp, userInputToken, length);
tmp[length] = 0;
So, just to be clear, you have three issues:
You display the newly allocated 'string' before you initialise it;
You do not allocate enough memory to hold the string
You do not terminate the string (and neither does strncpy because it did not encounter a string terminator within the allowed number of bytes).
I just spotted something else in your while (userInputToken != NULL) loop... You always to a string compare using userInputToken at the beginning of the loop, but inside the loop (and also in the part above the loop) you do this:
userInputToken = tmp;
free(tmp);
That means userInputToken is a dangling pointer. It points to memory that has been freed, and you must NOT use it. You will have to rethink your approach, and allow it to live until it's no longer needed.
You should probably use calloc. You should also not use uninitialized memory like this. Malloc allocates memory in chunks. When you a free a chunk it may be reused. You don't get an exact size and until you mset the memory with malloc there is not guarantee as to what the value of its bytes will be. All you know is that you have a chunk of memory to use that is at least as big as the size you requested. So in this example, you are printing the old contents of the memory chunks before you write to it.
Your validation of allocated length with this line is not correct:
printf("\npreC--tmp:%s\n", tmp); // This shows that the malloc DOES NOT use the given length.
malloc will allocated requested number of bytes, but it does not initialize the allocated memory. So when you try to print it as character string which should be terminating with '\0', it will try to print all characters until it finds '\0' in the memory. The terminating character may not be from the same memory block. Presence of '\0' is non-deterministic.
I hope this helps
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define MAX_INPUT 128
#define true 1
#define false 0
typedef struct _outputFlagContainer{
int YES, QUIT;
}outputFlagContainer;
void run();
outputFlagContainer *getUserInput(outputFlagContainer *outputFlags);
outputFlagContainer *resetOutputFlags(outputFlagContainer *outputFlags);
int main(int argc, char *argv[]){
run();
return 0;
}
void run() {
outputFlagContainer *outputFlags = malloc(sizeof(outputFlagContainer));
while(true) {
puts("yes, quit, or other");
outputFlags = getUserInput(outputFlags);
if (outputFlags->YES)
{
puts("It was a yes!");
}
else if (outputFlags->QUIT)
{
break;
}
else
{
puts("bad input");
}
}
free(outputFlags);
}
outputFlagContainer *resetOutputFlags(outputFlagContainer *outputFlags) {
if(outputFlags!= NULL){
outputFlags->YES = false;
outputFlags->QUIT = false;
}
return outputFlags;
}
outputFlagContainer *getUserInput(outputFlagContainer *outputFlags) {
int len;
char user_input[MAX_INPUT]={0}; // Zero Initialization
char *userInput = NULL;
char *userInputToken = NULL;
char *tmp = NULL;
char *finalCharacterCheck = NULL;
// Tokens to search for: // Immutable Strings
char *QUIT = "QUIT";
char *YES = "YES";
// Reset The Structure
outputFlags = resetOutputFlags(outputFlags);
userInput = fgets(user_input, MAX_INPUT, stdin); // it copies one less than MAX_INPUT
// Converting to Upper Case
int i = 0;
while(userInput[i]) {
userInput[i] = toupper(userInput[i]);
i++;
}
userInputToken = strtok(userInput, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
len = strlen(userInputToken);
printf("\n------uIT LENGTH:%d\n", len); // DEBUG LINE
tmp = malloc(sizeof(char)*(len+1));
if (tmp == NULL)
exit(1);
strncpy(tmp,userInputToken,len);
tmp[len]='\0';
printf("\npstC--tmp:%s\n", tmp); // Copies in the correct number of characters.
strcpy(user_input,tmp);
userInputToken = user_input;
free(tmp);
tmp = NULL;
}
}
while (userInputToken != NULL) { // NULL = NO (more) tokens.
if (0 == strcmp(userInputToken, YES)) {
outputFlags->YES = true;
}
else if (0 == strcmp(userInputToken, QUIT)) {
outputFlags->QUIT = true;
}
userInputToken = strtok(NULL, " ");
if (userInputToken) {
finalCharacterCheck = strchr(userInputToken, '\n');
if (finalCharacterCheck) {
len = strlen(userInputToken);
tmp = malloc(sizeof(char)*(len+1));
if (tmp == NULL) {
exit(1);
}
strncpy(tmp,userInputToken,len);
tmp[len]='\0';
strcpy(user_input,tmp);
userInputToken = user_input;
free(tmp);
tmp = NULL;
}
}
}
return outputFlags;
}
Related
I would like to know what would be the equivalent for filling an array like we do in Python, but in C.
Python example code:
arrayname=[0]*10
randomtextvar="test_text"
for i in range(10):
arrayname[i]=randomtextvar
for k in range(10):
print(arrayname[k]+" "+str(k)+ " position")
I haven't find an solution for this, I don't really understand how to set value to a string (char) array position (in C).
EDIT:
#include <stdio.h>
#include <string.h>
int main() {
int c = 0;
char arr[256];
fgets(arr, 256, stdin);
char spl[] = " ";
char *ptr = strtok(arr, spl);
while (ptr != NULL) {
ptr = strtok(NULL, spl);
// HERE I WANT TO ADD THE ptr value to a new array of strings
c++;
}
return 0;
}
You are doing strtok before the while loop and immediately at the start. So, you are trashing the first token on the line.
kaylum pointed out a simple way to save the strings into a fixed array using strdup.
But, I suspect you'd like something as flexible as what python is doing. So, the array of strings can be dynamically grown as you process many input lines using realloc.
Also, in addition, it's sometimes nice to have the last array element be NULL [just like argv].
Here's some refactored code. I've annotated it to explain what is going on:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char *ptr;
char *bp;
const char *spl = " \n";
char buf[256];
char **arr = NULL;
size_t arrcnt = 0;
size_t arrmax = 0;
// read in all input lines
while (1) {
// get next input line -- stop on EOF
ptr = fgets(buf,sizeof(buf),stdin);
if (ptr == NULL)
break;
// parse the current line
bp = buf;
while (1) {
// get next token on the current line
ptr = strtok(bp,spl);
bp = NULL;
// stop current line if no more tokens on the line
if (ptr == NULL)
break;
// grow the string array [periodically]
// NOTE: using arrmax cuts down on the number of realloc calls
if (arrcnt >= arrmax) {
arrmax += 100;
arr = realloc(arr,sizeof(*arr) * (arrmax + 1));
if (arr == NULL) {
perror("realloc/grow");
exit(1);
}
}
// add current string token to array
// we _must_ use strdup because when the next line is read any
// token data we had previously would get overwritten
arr[arrcnt++] = strdup(ptr);
// add null terminator just like argv -- optional
arr[arrcnt] = NULL;
}
}
// trim the array to the exact number of elements used
arr = realloc(arr,sizeof(*arr) * (arrcnt + 1));
if (arr == NULL) {
perror("realloc/trim");
exit(1);
}
// print the array
for (char **av = arr; *av != NULL; ++av)
printf("%s\n",*av);
// free the array elements
for (char **av = arr; *av != NULL; ++av)
free(*av);
// free the array
free(arr);
// reset counts and pointer
arrmax = 0;
arrcnt = 0;
arr = NULL;
return 0;
}
My str_split function returns (or at least I think it does) a char** - so a list of strings essentially. It takes a string parameter, a char delimiter to split the string on, and a pointer to an int to place the number of strings detected.
The way I did it, which may be highly inefficient, is to make a buffer of x length (x = length of string), then copy element of string until we reach delimiter, or '\0' character. Then it copies the buffer to the char**, which is what we are returning (and has been malloced earlier, and can be freed from main()), then clears the buffer and repeats.
Although the algorithm may be iffy, the logic is definitely sound as my debug code (the _D) shows it's being copied correctly. The part I'm stuck on is when I make a char** in main, set it equal to my function. It doesn't return null, crash the program, or throw any errors, but it doesn't quite seem to work either. I'm assuming this is what is meant be the term Undefined Behavior.
Anyhow, after a lot of thinking (I'm new to all this) I tried something else, which you will see in the code, currently commented out. When I use malloc to copy the buffer to a new string, and pass that copy to aforementioned char**, it seems to work perfectly. HOWEVER, this creates an obvious memory leak as I can't free it later... so I'm lost.
When I did some research I found this post, which follows the idea of my code almost exactly and works, meaning there isn't an inherent problem with the format (return value, parameters, etc) of my str_split function. YET his only has 1 malloc, for the char**, and works just fine.
Below is my code. I've been trying to figure this out and it's scrambling my brain, so I'd really appreciate help!! Sorry in advance for the 'i', 'b', 'c' it's a bit convoluted I know.
Edit: should mention that with the following code,
ret[c] = buffer;
printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
it does indeed print correctly. It's only when I call the function from main that it gets weird. I'm guessing it's because it's out of scope ?
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#define DEBUG
#ifdef DEBUG
#define _D if (1)
#else
#define _D if (0)
#endif
char **str_split(char[], char, int*);
int count_char(char[], char);
int main(void) {
int num_strings = 0;
char **result = str_split("Helo_World_poopy_pants", '_', &num_strings);
if (result == NULL) {
printf("result is NULL\n");
return 0;
}
if (num_strings > 0) {
for (int i = 0; i < num_strings; i++) {
printf("\"%s\" \n", result[i]);
}
}
free(result);
return 0;
}
char **str_split(char string[], char delim, int *num_strings) {
int num_delim = count_char(string, delim);
*num_strings = num_delim + 1;
if (*num_strings < 2) {
return NULL;
}
//return value
char **ret = malloc((*num_strings) * sizeof(char*));
if (ret == NULL) {
_D printf("ret is null.\n");
return NULL;
}
int slen = strlen(string);
char buffer[slen];
/* b is the buffer index, c is the index for **ret */
int b = 0, c = 0;
for (int i = 0; i < slen + 1; i++) {
char cur = string[i];
if (cur == delim || cur == '\0') {
_D printf("Copying content of buffer to ret[%i]\n", c);
//char *tmp = malloc(sizeof(char) * slen + 1);
//strcpy(tmp, buffer);
//ret[c] = tmp;
ret[c] = buffer;
_D printf("Content of ret[%i] = \"%s\" \n", c, ret[c]);
//free(tmp);
c++;
b = 0;
continue;
}
//otherwise
_D printf("{%i} Copying char[%c] to index [%i] of buffer\n", c, cur, b);
buffer[b] = cur;
buffer[b+1] = '\0'; /* extend the null char */
b++;
_D printf("Buffer is now equal to: \"%s\"\n", buffer);
}
return ret;
}
int count_char(char base[], char c) {
int count = 0;
int i = 0;
while (base[i] != '\0') {
if (base[i++] == c) {
count++;
}
}
_D printf("Found %i occurence(s) of '%c'\n", count, c);
return count;
}
You are storing pointers to a buffer that exists on the stack. Using those pointers after returning from the function results in undefined behavior.
To get around this requires one of the following:
Allow the function to modify the input string (i.e. replace delimiters with null-terminator characters) and return pointers into it. The caller must be aware that this can happen. Note that supplying a string literal as you are doing here is illegal in C, so you would instead need to do:
char my_string[] = "Helo_World_poopy_pants";
char **result = str_split(my_string, '_', &num_strings);
In this case, the function should also make it clear that a string literal is not acceptable input, and define its first parameter as const char* string (instead of char string[]).
Allow the function to make a copy of the string and then modify the copy. You have expressed concerns about leaking this memory, but that concern is mostly to do with your program's design rather than a necessity.
It's perfectly valid to duplicate each string individually and then clean them all up later. The main issue is that it's inconvenient, and also slightly pointless.
Let's address the second point. You have several options, but if you insist that the result be easily cleaned-up with a call to free, then try this strategy:
When you allocate the pointer array, also make it large enough to hold a copy of the string:
// Allocate storage for `num_strings` pointers, plus a copy of the original string,
// then copy the string into memory immediately following the pointer storage.
char **ret = malloc((*num_strings) * sizeof(char*) + strlen(string) + 1);
char *buffer = (char*)&ret[*num_strings];
strcpy(buffer, string);
Now, do all your string operations on buffer. For example:
// Extract all delimited substrings. Here, buffer will always point at the
// current substring, and p will search for the delimiter. Once found,
// the substring is terminated, its pointer appended to the substring array,
// and then buffer is pointed at the next substring, if any.
int c = 0;
for(char *p = buffer; *buffer; ++p)
{
if (*p == delim || !*p) {
char *next = p;
if (*p) {
*p = '\0';
++next;
}
ret[c++] = buffer;
buffer = next;
}
}
When you need to clean up, it's just a single call to free, because everything was stored together.
The string pointers you store into the res with ret[c] = buffer; array point to an automatic array that goes out of scope when the function returns. The code subsequently has undefined behavior. You should allocate these strings with strdup().
Note also that it might not be appropriate to return NULL when the string does not contain a separator. Why not return an array with a single string?
Here is a simpler implementation:
#include <stdlib.h>
char **str_split(const char *string, char delim, int *num_strings) {
int i, n, from, to;
char **res;
for (n = 1, i = 0; string[i]; i++)
n += (string[i] == delim);
*num_strings = 0;
res = malloc(sizeof(*res) * n);
if (res == NULL)
return NULL;
for (i = from = to = 0;; from = to + 1) {
for (to = from; string[to] != delim && string[to] != '\0'; to++)
continue;
res[i] = malloc(to - from + 1);
if (res[i] == NULL) {
/* allocation failure: free memory allocated so far */
while (i > 0)
free(res[--i]);
free(res);
return NULL;
}
memcpy(res[i], string + from, to - from);
res[i][to - from] = '\0';
i++;
if (string[to] == '\0')
break;
}
*num_strings = n;
return res;
}
I want to dynamically allocate only a portion of a character array.
So part of an array of size 100 is concrete. Say 10 is permanent memory, the other 90 is dynamic memory.
I made some attempt to read character by character until I decided to give up and take a shortcut idea I thought would work. However I end up getting an error that is
incorrect checksum for freed object - object was probably modified
after being freed
I use this method in a while loop in main and I pretty much free everything after the while loop processes. Because, I have the declaration outside of the while loop. I wanted to read an object in a while loop session since these objects end up being added into a list of objects. However the scope of the while loop causes segmentation problems, it cannot remember anything about the object. (I digress).
Here is my attempt.
Object* read(char* str)
{
Object* object = (Object*)malloc(sizeof(*object));
object->identity[0] = 0;
int capacity = (100 + 1) - (10);
object->name = (char*)malloc(capacity * sizeof(*object->name));
object->value = 0.0;
int length = strlen(str);
if (length > capacity)
object->name = (char*)realloc(object->name, (capacity * 2) * sizeof(*object->name));
int arguments = sscanf(str, "%" STRING_SPACE "s %lf %[^\n]s",
object->identity,
&object->value,
object->name);
if (arguments == MATCHER) {
return object;
} else {
return NULL;
}
return object;
}
In this case, an object has a variable sized name but a fixed amount of space allocated for its identity.
I tried something else with sscanf but realized it will never work because I read the string too late to assign memory to name. See;
/*
int len = 0;
for (char* itemObserve = item->name; *itemObserve; itemObserve++) {
if (len == sizeof(item->name)) {
capacity *= MULTIPLIER;
item->name = (char*)realloc(item->name, capacity * sizeof(*item->name));
}
len++;
}
*/
Here is the code in main, everything undefined is probably irrelevant to the bug:
int main()
{
FILE* stream;
Object* object;
ObjectList* list = initList();
while (true) {
char* line;
char cmd[15] = {0};
char* arg;
char* rest;
printf("> ");
line = getline(stdin);
arg = (char*)malloc(35 * sizeof(*arg));
rest = (char*)malloc(35 * sizeof(*rest));
int arguments = sscanf(line, "%s %s %[^\n]", cmd, arg, rest);
free(line);
line = NULL;
printf("\n");
if (strcmp(cmd, "add") == 0) {
arg = (char*)realloc(arg, (35 * 2) * sizeof(*arg));
sprintf(arg, "%s %s", arg, rest);
if ((object = read(arg)) == NULL) {
continue;
}
objectListAdd(list, object);
} else {
free(rest);
free(arg);
exit(EXIT_SUCCESS);
}
free(rest);
free(arg);
}
freeObject(object);
freeObjectList(list);
return EXIT_SUCCESS;
}
Separate getline function in main file
char* getline(FILE* stream)
{
int capacity = LINE_MAX + 1;
char* buffer = (char*)malloc(capacity * sizeof(*buffer));
int len = 0;
int ch;
while ((ch = fgetc(stream)) != '\n' && (ch != EOF)) {
if (len == capacity) {
capacity *= MULTIPLIER;
buffer = (char*)realloc(buffer, capacity * sizeof(*buffer));
}
buffer[len++] = ch;
}
if (ch == EOF) {
return NULL;
}
buffer[len] = '\0';
if (buffer == NULL)
return NULL;
return buffer;
}
There are other conditionals which work as a kind of command switch but they are irrelevant to the errors my program is exhibiting. This much I have narrowed the problem down to.
I have a problem with my code. I want to load a dictionary which works fine with a small one. But when i try to load the larger version, my while loop stops at the 701th word which is " acclimatization" and then the programs continues. I searched a lot on forums and tried a lot of things, but i just can't find the reason this is caused. Does anyone have an idea of how this occurs?
Dictionary.c
bool load(const char* dictionary)
{
// reserve space for word
char* word = malloc(sizeof(char*));
// open file
FILE* dict = fopen(dictionary, "r");
if (dict == NULL)
{
fclose(dict);
fprintf(dict, "Could not load %s.\n", dictionary);
return 1;
}
root = (struct node *) malloc(sizeof(struct node));
root->is_word = false;
//Loops over word aslong the EOF is not reached
while (fgets(word,LENGTH,dict) != NULL)
{
printf("word = %s\n", word);
int word_length = strlen(word) -1;
node* current = root;
word_count++;
//Loops over letters
for (int i = 0; i < word_length; i++)
{
int index;
node *next_node;
// checks if letter isnt a apostrophe
if(word[i] == 39)
{
index = MAX_CHARS - 1;
}
// gets nummeric value of letter
else
{
index = tolower(word[i]) - 'a';
}
next_node = current->children[index];
// creates new node if letter didnt exists before
if(next_node == NULL)
{
next_node = malloc(sizeof(node));
current->children[index] = next_node;
current->is_word = false;
printf("new letter: %c\n", word[i]);
}
else
{
printf("letter: %c\n", word[i]);
}
// checks for end of the word
if(i == word_length - 1)
{
next_node->is_word = true;
}
current = next_node;
}
}
return true;
}
The node is defined by:
// node
typedef struct node
{
bool is_word;
struct node* children[27];
}
node;
char* word = malloc(sizeof(char*));
Depending on platform it can be 4 or 8 . You need to allocate more memory.
char* word;
word = malloc(LENGTH); // LENGTH as you use it here while (fgets(word,LENGTH,dict) != NULL)
if(word!=NULL){ // and checking if malloc is successful
// your code
free(word); // freeing allocated memory
return true;
}
else { // executed only if malloc fails
//handle error
}
You can give any desired size.
Note - Using function free() , you need to free every time you allocate memory.
You allocate very little space for word, it's probably 8 or 4 bytes depending on your platform.
You are allocating space for 1 char pointer, so when you read from the file LENGTH characters you can be storing bytes beyond the limits of the allocated buffer. The problem is, that the behavior is undefined thus the program might work or it might stop or anything can happen.
You don't need to allocate it dynamically, just like this it's ok
char word[100];
while (fgets(word, sizeof(word), file) != NULL) ...
/* ^ this only works with arrays, */
/* the benefit is that you can */
/* change the definition of word */
/* and resize it without changing */
/* this part. */
/* */
/* It will NOT work if you use `malloc()' */
Also, you would have a memory leak if fopen() failes, every malloc() requires a corresponding free().
Suggestion:
for (int i = 0; i < word_length; i++)
can be written like this too
for (int i = 0; ((word[i] != '\n') && (word[i] != '\0')); i++)
and you avoid calling strlen() which will also iterate through the characters.
I want to read input from user using C program. I don't want to use array like,
char names[50];
because if the user gives string of length 10, then the remaining spaces are wasted.
If I use character pointer like,
char *names;
then I need to allocate memory for that in such a way of,
names = (char *)malloc(20 * sizeof(char));
In this case also, there is a possibility of memory wastage.
So, what I need is to dynamically allocate memory for a string which is of exactly same as the length of the string.
Lets assume,
If the user input is "stackoverflow", then the memory allocated should be of 14 (i.e. Length of the string = 13 and 1 additional space for '\0').
How could I achieve this?
Read one character at a time (using getc(stdin)) and grow the string (realloc) as you go.
Here's a function I wrote some time ago. Note it's intended only for text input.
char *getln()
{
char *line = NULL, *tmp = NULL;
size_t size = 0, index = 0;
int ch = EOF;
while (ch) {
ch = getc(stdin);
/* Check if we need to stop. */
if (ch == EOF || ch == '\n')
ch = 0;
/* Check if we need to expand. */
if (size <= index) {
size += CHUNK;
tmp = realloc(line, size);
if (!tmp) {
free(line);
line = NULL;
break;
}
line = tmp;
}
/* Actually store the thing. */
line[index++] = ch;
}
return line;
}
You could have an array that starts out with 10 elements. Read input character by character. If it goes over, realloc another 5 more. Not the best, but then you can free the other space later.
You can also use a regular expression, for instance the following piece of code:
char *names
scanf("%m[^\n]", &names)
will get the whole line from stdin, allocating dynamically the amount of space that it takes. After that, of course, you have to free names.
If you ought to spare memory, read char by char and realloc each time. Performance will die, but you'll spare this 10 bytes.
Another good tradeoff is to read in a function (using a local variable) then copying. So the big buffer will be function scoped.
Below is the code for creating dynamic string :
void main()
{
char *str, c;
int i = 0, j = 1;
str = (char*)malloc(sizeof(char));
printf("Enter String : ");
while (c != '\n') {
// read the input from keyboard standard input
c = getc(stdin);
// re-allocate (resize) memory for character read to be stored
str = (char*)realloc(str, j * sizeof(char));
// store read character by making pointer point to c
str[i] = c;
i++;
j++;
}
str[i] = '\0'; // at the end append null character to mark end of string
printf("\nThe entered string is : %s", str);
free(str); // important step the pointer declared must be made free
}
First, define a new function to read the input (according to the structure of your input) and store the string, which means the memory in stack used. Set the length of string to be enough for your input.
Second, use strlen to measure the exact used length of string stored before, and malloc to allocate memory in heap, whose length is defined by strlen. The code is shown below.
int strLength = strlen(strInStack);
if (strLength == 0) {
printf("\"strInStack\" is empty.\n");
}
else {
char *strInHeap = (char *)malloc((strLength+1) * sizeof(char));
strcpy(strInHeap, strInStack);
}
return strInHeap;
Finally, copy the value of strInStack to strInHeap using strcpy, and return the pointer to strInHeap. The strInStack will be freed automatically because it only exits in this sub-function.
This is a function snippet I wrote to scan the user input for a string and then store that string on an array of the same size as the user input. Note that I initialize j to the value of 2 to be able to store the '\0' character.
char* dynamicstring() {
char *str = NULL;
int i = 0, j = 2, c;
str = (char*)malloc(sizeof(char));
//error checking
if (str == NULL) {
printf("Error allocating memory\n");
exit(EXIT_FAILURE);
}
while((c = getc(stdin)) && c != '\n')
{
str[i] = c;
str = realloc(str,j*sizeof(char));
//error checking
if (str == NULL) {
printf("Error allocating memory\n");
free(str);
exit(EXIT_FAILURE);
}
i++;
j++;
}
str[i] = '\0';
return str;
}
In main(), you can declare another char* variable to store the return value of dynamicstring() and then free that char* variable when you're done using it.
Here's a snippet which I wrote which performs the same functionality.
This code is similar to the one written by Kunal Wadhwa.
char *dynamicCharString()
{
char *str, c;
int i = 0;
str = (char*)malloc(1*sizeof(char));
while(c = getc(stdin),c!='\n')
{
str[i] = c;
i++;
realloc(str,i*sizeof(char));
}
str[i] = '\0';
return str;
}
char* load_string()
{
char* string = (char*) malloc(sizeof(char));
*string = '\0';
int key;
int sizer = 2;
char sup[2] = {'\0'};
while( (key = getc(stdin)) != '\n')
{
string = realloc(string,sizer * sizeof(char));
sup[0] = (char) key;
strcat(string,sup);
sizer++
}
return string;
}
int main()
{
char* str;
str = load_string();
return 0;
}
realloc is a pretty expensive action...
here's my way of receiving a string, the realloc ratio is not 1:1 :
char* getAString()
{
//define two indexes, one for logical size, other for physical
int logSize = 0, phySize = 1;
char *res, c;
res = (char *)malloc(sizeof(char));
//get a char from user, first time outside the loop
c = getchar();
//define the condition to stop receiving data
while(c != '\n')
{
if(logSize == phySize)
{
phySize *= 2;
res = (char *)realloc(res, sizeof(char) * phySize);
}
res[logSize++] = c;
c = getchar();
}
//here we diminish string to actual logical size, plus one for \0
res = (char *)realloc(res, sizeof(char *) * (logSize + 1));
res[logSize] = '\0';
return res;
}