I wrote the following function in C but have 2 problems:
I am asked to free the dynamically allocated memory to prevent using too much RAM but the line free(word) just causes bugs to my program.
if I delete it everything works fine, why is that happening? I used free after I finished using word as suggested in many different articles.
I am requested to use malloc with the minimum needed space but how could I do that?
currently my code allocates max_str_len blocks of RAM but if the word was much shorter like a letter I don't want to allocate 200 blocks for that.
Any suggestions please?
int read_words(char *words[], int size, int max_str_len) {
char *word;
char ch;
int i;
for (i = 0; i < size; ++i) {
word = (char *)malloc((max_str_len + 1) * sizeof(char));
if (word == NULL)
return -1;
for (int j = 0; j < max_str_len; ++j) {
scanf("%c", &ch);
if (ch == '\n') break;
if (ch == EOF || ch == 'R') return i;
word[j] = ch;
}
words[i] = word;
free(word);
}
return i;
}
You put a malloc'd char* into a caller-provided words[i] and then you free it. That doesn't make sense. If you free it, the caller can't do anything with it.
If you want the malloc'd strings to be minimal you could realloc them or you could read into a large buffer (allocated perhaps at the start of the function) and then copy the result into a malloc'd buffer that's sized just right.
Note that you're also failing to check scanf for errors and that you're leaking memory if you get a memory failure in the middle of the function—by returning -1, you effectively lose info on how many elements of words have been filled with owning pointers. You might want to return that info (return i;) or to free all pointers allocated by the function before the malloc failure.
There are multiple problems in your code:
you free the memory allocated for each word, yet you return pointers to the freed blocks in the array supplied by the caller, causing undefined behavior when the caller will dereference these pointers.
your test for end of file is incorrect: scanf() will return EOF then, bu the character will not be set to EOF, which might not be appropriate for a char anyway. You should use getchar() and make ch an int.
you should set a null terminator at the end of the string read.
you could use realloc to shrink the block of memory once you know the string length.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
char *word, *p;
int i, j, ch;
for (i = 0; i < size;) {
word = (char *)malloc(max_str_len + 1);
if (word == NULL) {
/* free the words allocated so far and return a failure code */
while (i-- > 0)
free(words[i];
return -1;
}
for (j = 0; j < max_str_len; ++j) {
ch = getchar();
if (ch == '\n' || ch == EOF || ch == 'R') break;
word[j] = ch;
}
if (j == 0 && (ch == EOF || ch == 'R'))
break;
word[j] = '\0';
p = (char *)realloc(word, j + 1);
if (p != NULL)
word = p;
words[i++] = word;
if (ch == EOF || ch == 'R')
break;
}
return i;
}
Related
I wrote this code, but inserts garbage in the start of string:
void append(char *s, char c) {
int len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
char c, *s;
int i = 0;
s = malloc(sizeof(char));
while ((c = getchar()) != '\n') {
i++;
s = realloc(s, i * sizeof(char));
append(s, c);
}
printf("\n%s",s);
}
How can I do it?
There are multiple problems in your code:
you iterate until you read a newline ('\n') from the standard input stream. This will cause an endless loop if the end of file occurs before you read a newline, which would happen if you redirect standard input from an empty file.
c should be defined as int so you can test for EOF properly.
s should be null terminated at all times, you must set the first byte to '\0' after malloc() as this function does not initialize the memory it allocates.
i should be initialized to 1 so the first realloc() extends the array by 1 etc. As coded, your array is one byte too short to accommodate the extra character.
you should check for memory allocation failure.
for good style, you should free the allocated memory before exiting the program
main() should return an int, preferably 0 for success.
Here is a corrected version:
#include <stdio.h>
#include <stdlib.h>
/* append a character to a string, assuming s points to an array with enough space */
void append(char *s, char c) {
size_t len = strlen(s);
s[len] = c;
s[len + 1] = '\0';
}
int main(void) {
int c;
char *s;
size_t i = 1;
s = malloc(i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
*s = '\0';
while ((c = getchar()) != EOF && c != '\n') {
i++;
s = realloc(s, i * sizeof(char));
if (s == NULL) {
printf("memory allocation failure\n");
return 1;
}
append(s, c);
}
printf("%s\n", s);
free(s);
return 0;
}
when you call strlen it searches for a '\0' char to end the string. You don't have this char inside your string to the behavior of strlen is unpredictable.
Your append function is acually good.
Also, a minor thing, you need to add return 0; to your main function. And i should start from 1 instead if 0.
Here is how it should look:
int main(void){
char *s;
size_t i = 1;
s = malloc (i * sizeof(char));//Just for fun. The i is not needed.
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
s[0] = '\0';
for(char c = getchar(); c != '\n' && c != EOF; c = getchar()) {//it is not needed in this case to store the result as an int.
i++;
s = realloc (s,i * sizeof(char) );
if(s == NULL) {
fprintf(stderr, "Coul'd not allocate enough memory");
return 1;
}
append (s,c);
}
printf("%s\n",s);
return 0;
}
Thanks for the comments that helped me improve the code (and for my english). I am not perfect :)
The inner realloc needs to allocate one element more (for the trailing \0) and you have to initialize s[0] = '\0' before starting the loop.
Btw, you can replace your append by strcat() or write it like
size_t i = 0;
s = malloc(1);
/* TODO: check for s != NULL */
while ((c = getchar()) != '\n') {
s[i] = c;
i++;
s = realloc(s, i + 1);
/* TODO: check for s != NULL */
}
s[i] = '\0';
I'm trying to declare an array of char dynamically, what I need to do is adding the newest character to the string which works fine, the problem is that when I try to print it out, at the beginning of the string there are some unknown characters.
char add[2];
char str2[200];
char c;
int temp = -1;
int num = 0;
char *str3;
str3 = malloc( (size_t)count ); //str3 = malloc(sizeof(char)) not working
while((c= getch()) !='\r')
{
for (int i = 0;i<200;i++)
{
if (str2[i] =='\0')
{
num = i;
break;
}
}
//printf("Num: %d\n",num);
if ((temp == -32) || (temp == 0))
{
}
else
{
if(isalnum((char)c) == 0)
{
if((c == '\'') || (c == -118) || (c == -115) || (c == -107) || (c == -123) || (c == -105)|| (c == 32))
{
realloc(str3,sizeof(char)+2);
printf("true: %c\n",c);
//realloc(str2,sizeof(char)+1);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("%s\n",str2);
printf("%s\n",str3);
}
else if (c == 8)
{
printf("Deleting something...\n");
}
}
else
{
realloc(str3,sizeof(char)+2);
printf("true: %c\n",c);
//realloc(str2,sizeof(char)+1);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("%s\n",str2);
printf("%s\n",str3);
}
}
printf("ASCII Code: %d\n",c);
temp = c;
}
To get some memory to your string, you have to tell malloc how many bytes of memory you want. sizeof(char) returns 1, therefore, you'll only have 1 byte. In C, strings are terminated by the NULL byte (\0), and printf and others will print until they find that NULL terminator.
If you do something like this:
char *str = malloc(1);
*str = 'a';
printf("%s", str);
You will probably get a very strange output, since you have no NULL terminator.
When you use the unsigned x; str = malloc(x);, it's actually undefined how many bytes you have, since that x variable is not initialized.
Since your question is very unclear, what I can tell you (from what I think you're asking) is how to actually get space for a string of 63 characters plus the NULL terminating byte.
char *str = malloc(64);
strcpy(str, "Stack Overflow");
printf("%s", str);
That will do it.
Also note that the memory block returned by malloc will not be zeroed, therefore you can't possibly know what's in it (that could be the reason you're getting garbage when you're printing).
I recommend you read about memory allocation in a good C book or in Wikipedia...
After your edit and "MCVE"
I made some edits to what I think it is you want. The modifications are explained in the comments of the source. Let me know if you have any doubts.
#include <stdio.h> /* printf */
#include <stdlib.h> /* malloc, free, realloc */
#include <string.h> /* strcat */
#include <ctype.h> /* isalnum */
#include <conio.h> /* getch */
int main(void)
{
char add[2];
char str2[200];
char c;
int temp = -1;
int num = 0;
char *str3;
/* I just think 'count' is an int, since you didn't put it in the code,
* I also deduced that #count will be used as the length of #str3
*/
int count;
/* Here, count is not initialized, so you MUST initialize it in order
* to call malloc with it! Since it seems you want to add character by
* character using realloc, then we just malloc() 2 bytes - 1 for a
* character and one for the NULL terminator.
*/
count = 2;
str3 = malloc(count);
/* You will be using #strcat to append strings to #str3, so you need
* to put a NULL terminator in it, because strcat will look for that
* NULL byte to find where it should append
*/
*str3 = 0x0;
while((c = getch()) != '\r') {
for (int i = 0;i < 200; i++) {
if (str2[i] =='\0') {
num = i;
break;
}
}
if ((temp == -32) || (temp == 0)) {
/* empty */
} else {
if(isalnum((char)c) == 0)
{
if((c == '\'') || (c == -118) || (c == -115) || (c == -107) || (c == -123) || (c == -105)|| (c == 32))
{
/* this is not the optimal way of using realloc, because
* you should first check for errors, but will do for
* this example.
* You must assign the returned value of realloc to str3.
*
* Also, since #count contains the length
* of #str3, you need to increment it.
*/
str3 = realloc(str3, ++count);
printf("true: %c\n",c);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("str2: %s\n",str2);
printf("str3: %s\n",str3);
} else if (c == 8) {
printf("Deleting something...\n");
}
} else {
/* see notes above on realloc */
str3 = realloc(str3, ++count);
printf("true: %c\n",c);
add[1] = '\0';
add[0] = c;
strcat(str3,add);
strcat(str2,add);
printf("str2: %s\n",str2);
printf("str3: %s\n",str3);
}
}
printf("ASCII Code: %d\n",c);
temp = c;
}
return 0;
}
In the first two cases, you are only allocating enough space for a single char. If you attempt to write more than one to that block of memory, you'll write past the end of the memory that was allocated for you. Doing so invokes undefined behavior, which in this case manifests as printing strange characters.
In the third case, you allocate x bytes of memory, however x is uninitialized and has an indeterminate value. Reading an indeterminate value is also undefined behavior. In this case it happens to work because the indeterminate value happens to be a valid value and is large enough to hold the string you want, however you can't depend on that behavior.
You need to allocate a byte for every character that you'll need, plus 1 for the terminating null byte that ends a string in C.
Note that the first allocation, this one
str = malloc(sizeof(char));
is exactly equivalent to1
str = malloc(1);
so you don't have room except for one character which is a problem, because it only represents an empty string.
If you allocate this much space you will very likely access memory out of the allocated space, causing undefined and unpredictable behavior. You need to understand what a string in c is,
A string in c is a sequence of non-null characters followed by a null character, so for a string with N characters you need N + 1 array elements (for ascii this equals bytes)
According to that definition of string if you wanted to store the string "Hello" you would need at least the following code
char *str = malloc(6);
if (str != NULL) {
str[0] = 'H';
str[1] = 'e';
str[2] = 'l';
str[3] = 'l';
str[4] = 'o';
str[5] = '\0'; // Or equivalently str[5] = 0;
}
as you can see, the last character being '\0' or 0 — which is the same — is very important.
All the functions in the standard library of c which expect a string parameter expect that there is the null terminator. For instance strlen() will count characters until it reaches the '\0', if it's not there then you can't predict where it is going to stop counting, this causing undefined behavior.
1sizeof(char) is as defined by the c standard always equal to one.
I am posed with a situation where my function does exactly what I want except handle higher amounts of input.
I initially thought to process each character one by one but was running into problems doing this. So fscanf not only does what I want it to do but it is essential in reading in only one line. I noticed, I cannot reallocate space for bigger array this way though. I have tried using format specifiers i.e. %*s to include a specific amount of buffer space before hand but this still does not work.
I have noticed also, I would have no way of knowing the size of the string I am reading in.
Here is my attempt and thoughts:
#define LINE_MAX 1000
char* getline(FILE* inputStream)
{
int capacity = LINE_MAX;
char* line = malloc(capacity * sizeof(char));
int ch;
/* if (sizeof(capacity) == sizeof(line)) { // Not a valid comparison? Too late?
capacity *= 2;
line = realloc(line, capacity * sizeof(line));
} */
if (fscanf(stream, "%[^\n]s", line) == 1) {
ch = fgetc(inputStream);
if (ch != '\n' && ch != EOF) {
fscanf(inputStream, "%*[^\n]");
fscanf(inputStream, "%*c");
}
free(line);
return line;
}
free(line);
return NULL;
}
I am new to memory allocation in general but I feel as though I had a good idea of what to do here. Turns out I was wrong.
Here is an example to read a line and store it in a Character array.
#include <stdio.h>
#include <stdlib.h>
int main(void) {
signed char *str;
int c;
int i;
int size = 10;
str = malloc(size*sizeof(char));
for(i=0;(c=getchar()) !='\n' && c != EOF;++i){
if( i == size){
size = 2*size;
str = realloc(str, size*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = c;
}
if(i == size){
str = realloc(str, (size+1)*sizeof(char));
if(str == NULL){
printf("Error Unable to Grow String! :(");
exit(-1);
}
}
str[i] = '\0';
printf("My String : %s", str);
return 0;
}
The array is resized to twice it's original size if current array can't hold the characters read from input.
I'm trying to read just a string until a space occurs from a file using fgetc. I could not use fscanf and other i/o functions because I have to know if a line feed is read. A function call leerString(string,f) should change the string variable content, it works if I print the string into the function, but at the outside its value is NONE.
I have got some errors like segmentation faults and others with the realloc function when I try to change the return value with the string variable or pass the "contador" variable like a reference parameter. This is driving me crazy. Sorry the bad english.
char leerString(char *string, FILE *archivo){
char caracter,caracter2 = 0;
char *nombre = malloc(sizeof(char)*30);//works like a buffer
int i = 0;
int j = 0;
while (1){
caracter = fgetc(archivo);
if ((caracter != 32) && (caracter != EOF) && (caracter != '\n')){
if (i < 30){
nombre[i] = caracter;
}
else{
nombre = realloc(nombre,30);
if (nombre != NULL){
nombre[i] = caracter;
}
else{
printf("Error de asignacion de memoria\n");
exit(-2);
}
}
i++;
}
else{
break;
}
}
char *nombre2=malloc(sizeof(char)*i);//allocating the real size of the str
for (j = 0; j<i; j++){ //cleaning the buffer
caracter2 = nombre[j];
nombre2[j] = caracter2;
}
strcpy(string,nombre2);
return caracter;
}
I can see a lot of problems with your code
You are using realloc() to re-allocate a buffer of the same size; it's meant for resizing the buffer.
Your caracter variable has type char, but fgetc() returns int.
You allocate memory for the destination string, you then use realloc() unnecessarily as explained above, and then you allocate space again, and do this
for (j = 0; j<i; j++){ //cleaning the buffer
caracter2 = nombre[j];
nombre2[j] = caracter2;
}
which does nothing more than,
memcpy(nombre2, nombre, i);
You did not allocate space for the terminating nul byte, so strcpy() will fail in your code, causing possible a segmentation fault or anything else, since it's Undefined Behavior.
You should also, be sure that string has enough space to copy the characters into it, or just return a pointer to the malloc()ed string in your function, which returns char and I don't know whether that is useful or not since you didn't post enough code to tell.
A better solution would be
char *
leerString(char *string, size_t size, FILE *archivo)
{
int chr;
size_t i;
i = 0;
chr = fgetc(archivo);
while ((chr != EOF) && (isspace(chr) == 0) && (i < size - 1))
{
string[i++] = chr;
chr = fgetc(archivo);
}
if (i == 0)
return NULL;
string[i] = '\0'; /* ensure it's `nul' terminated */
return string;
}
which you can use as follows
char string[100];
if (leerString(string, sizeof(string), archivo) != NULL)
printf("%s\n", string);
I am doing an exercise for fun from K and R C programming book. The program is for finding the longest line from a set of lines entered by the user and then prints it.
Inputs:
This is a test
This is another long test
this is another long testthis is another long test
Observation:
It runs fine for the first two inputs but fails for the larger string (3rd input)
Errors:
Error in `./longest': realloc(): invalid next size: 0x000000000246e010 ***
Error in `./longest': malloc(): memory corruption (fast): 0x000000000246e030 ***
My efforts:
I have been trying to debug this since 2 days (rubber duck debugging) but the logic seems fine. GDB points to the realloc call in the _getline function and shows a huge backtrace with glibc.so memory allocation calls at the top.
Here is what I have written (partially, some part is taken from the book directly):-
#include <stdio.h>
#include <stdlib.h>
int MAXLINE = 10;
int INCREMENT = 10;
char* line = NULL, *longest = NULL;
void _memcleanup(){
free(line);
free(longest);
}
void copy(char longest[], char line[]){
int i=0;
char* temp = realloc(longest,(MAXLINE)*sizeof(char));
if(temp == NULL){
printf("%s","Unable to allocate memory");
_memcleanup();
exit(1);
}
longest = temp;
while((longest[i] = line[i]) != '\0'){
++i;
}
}
int _getline(char s[]){
int i,c;
for(i=0; ((c=getchar())!=EOF && c!='\n'); i++){
if(i == MAXLINE - 1){
char* temp = realloc(s,(MAXLINE + INCREMENT)*sizeof(char));
if(temp == NULL){
printf("%s","Unable to allocate memory");
_memcleanup();
exit(1);
}
s= temp;
MAXLINE += INCREMENT;
}
s[i] = c;
}
if(c == '\n'){
s[i++] = c;
}
s[i]= '\0';
return i;
}
int main(){
int max=0, len;
line = malloc(MAXLINE*sizeof(char));
longest = malloc(MAXLINE*sizeof(char));
while((len = _getline(line)) > 0){
printf("%d%d", len, MAXLINE);
if(len > max){
max = len;
copy(longest, line);
}
}
if(max>0){
printf("%s",longest);
}
_memcleanup();
return 0;
}
You´re reallocating on copied addresses (because parameters).
A parameter in C is a copy of the original value everytime; in case of
a pointer it will point to the same location but the address itself is copied.
realloc resizes the buffer asociated with the address, everything fine so far.
But it can relocate the whole thing and assign a completely new address,
and this new address (if it happens) will be lost after the function returns to main.
Use a double pointer:
Pass a char **s instead of char *s (==char s[]) as formal parameter,
pass &xyz intead of xyz as actual value, and inside the function,
use *xyz and **xyz (or (*xyz)[index]) for address and value.
Other things:
Global variables are ugly (and confusing when named same as parameters),
multiplying with sizeof(char) is nonsense because it´s be 1 everytime,
and names in capitals should be used for #define´s rather than variables.
The double pointer alone, isn't the solution to your problems. You have 2 primary issues. You can see them by entering your strings as a string of characters and will notice you problem occurs when you pass the 20th character. (e.g. 01234567890123456789)
You have declared both line and longest globally. So while you can rewrite _getline (char **s), you can also simply update line at the end of _getline with memcpy (include string.h). For example:
memcpy (line, s, (size_t)i);
return i;
}
That cures your _getline issue. Issue two is fairly straight forward. You are not null-terminating longest in copy. (your choice of arguments with the same name as the globals presents challenges as well) Including the following fixes copy:
++i;
}
longest[i] = '\0';
}
If you incorporate both changes, then I believe you will find you routine works. You can then rewite _getline (char **s) and pass &line as another exercise. For example, you can rewrite _getline as:
int
_getline (char **s) {
int i, c;
for (i = 0; ((c = getchar ()) != EOF && c != '\n'); i++) {
if (i == MAXLINE - 1) {
char *temp = realloc (*s, (MAXLINE + INCREMENT) * sizeof (char));
if (temp == NULL) {
printf ("%s", "Unable to allocate memory");
_memcleanup ();
exit (1);
}
*s = temp;
MAXLINE += INCREMENT;
}
(*s)[i] = c;
}
if (c == '\n') {
(*s)[i++] = c;
}
(*s)[i] = '\0';
return i;
}
And then modify your call in main to:
while ((len = _getline (&line)) > 0) {