Where's my pointer error? - c

I've walked through the following code, but I can't find what's wrong. The function getsxnremem() gets a string up to len chars using fgets(), overwrites the newline (if there is one) with a null-terminator, then re-sizes the memory to fit the string. That's the idea anyway.
The following code sometimes works and sometimes crashes. I've had this happen plenty of times in the past and I usually find the problem, but this time it's taking me too long.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
unsigned getsxnremem(char **str, unsigned len){
unsigned l, flag = 1;
free(*str);
char *buff;
if ((*str = malloc(len)) == NULL) return 0;
if(fgets(*str, len, stdin) == NULL) { free(*str); return 0; }
l = strlen(*str);
if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }
if ((buff = realloc(*str, l + flag)) == NULL){ free(*str); return 0; }
*str = buff;
return (l - 1);
}
int main(void){
char *buff = NULL;
unsigned l = getsxnremem(&buff, 256);
printf("%s\n%u chars long.", buff, l);
}

The problem is, you failed to collect the return value of realloc() there.
As per the C11 standard, chapter §7.22.3.5
#include <stdlib.h>
void *realloc(void *ptr, size_t size);
The realloc function deallocates the old object pointed to by ptr and returns a
pointer to a new object that has the size specified by size. [...]
realloc() resizes the memory and returns a pointer to the new memory. The old memory is to be free()d, considering realloc() is successful.
So,
You need to collect and check the return value of realloc() and test it against NULL to ensure success. Then, reassign it to *str.
NOTE: Please do not use a form like p = realloc(p, newsize); because, then, if realloc() fails, you'll end up losing the actual pointer, too.
If realloc() is successful, you must not free() the old pointer. Calling free() on already free()-d memory invokes undefined behavior.
After that, as rightly mentioned in the other answer by dbush, the usage
{ *(str)[l-1] = '\0'; flag = 0; }
is also wrong. Your required string is represented by *str, not str. As per the operator precedence, The Array subscripting operator ([]) has higher precedence over the dereference (*) operator, so essentially your code looks like
{ * ((str)[l-1]) = '\0'; flag = 0; }
Which is not what you want. So, to honor the operator precedence, you should modify it like
{ (*str)[l-1] = '\0'; flag = 0; }
That said, you should also check for the return value of fgets() to ensure the success before you make use of the destination buffer. As malloc() returns unitialized memory, and in case fgets() fails, you'll end up reading from unitialized memory which will again cause UB.

For your most recent update, you've got your parenthesis in the wrong place.
This:
if (l && ((*str)[l-1] == '\n')) { *(str)[l-1] = '\0'; flag = 0; }
Should be:
if (l && ((*str)[l-1] == '\n')) { (*str)[l-1] = '\0'; flag = 0; }
^---- here

Related

Function that reads a string from a file of unknown length

I've been trying to piece together a function that allows me to create a string out of a given file of unknown length.
What it's supposed to do, is set the size of the output string to a single character, then for each character besides EOF, increment the size of the string by 1 and add the newly read character to it.
void readstring(FILE *f, char *s[])
{
int size = 1;
int c = 0, i = 0;
s = malloc(size*sizeof(char));
while(c != -1)
{
c = fgetc(f);
s[i] = (char)c;
i++;
if(i == size)
{
size++;
s = realloc(s, size*sizeof(char));
}
}
s[i] = '\0';
}
int main()
{
char *in = malloc(2*sizeof(char));
FILE *IN;
IN = fopen("in.txt", "r");
readstring(IN, in);
printf("%s",&in);
fclose(IN);
free(in);
return 0;
}
If you are on a POSIX compliant system (any modern Linux), don't try to reinvent the wheel. Just use getline(). It will do the tricky stuff of managing a growing buffer for you, and return a suitably malloc()'ed string.
You are assigning the result of malloc()/realloc() to s instead of *s. s is purely local to readstring(), *s is the pointer variable that s points to. Pass in the adress of the pointer in main. Correct code looks like this:
void foo(char** out_string) {
*out_string = malloc(...);
}
int main() {
char* string;
foo(&string);
}
Without the address taking & and pointer dereferenciation *, your main() cannot know where readstring() has stored the characters.
You also need to dereference the double pointer when you set the characters of the string: Use (*s)[i] instead of s[i], as s[i] denotes a pointer, not a character.
Also, try running your program with valgrind, and see if you can learn from the errors that it will spew out at you. It's a great tool for debugging memory related problems.

Calling free() after malloc causes unexpected behaviour

Hi I read that I should call free() as soon as I could do that to free the memory but when I call free in this way my code stops working correctly. what's the problem?
I want to call free() in every iteration and when an error occurs.
int read_words(char *words[], int size, int max_str_len) {
int i, j;
char *ExtendedWord = NULL;
for (i = 0; i < size && size != -1; ++i) {
char tmp[1], ch, *word = tmp;
for (j = 0; j < max_str_len; ++j) {
if (scanf("%c", &ch) == EOF || ch == 'R') {
size = -1;
break;
}
if (ch == ' ')
break;
word[j] = ch;
ExtendedWord = malloc((i + 2) * sizeof(char));
if (ExtendedWord == NULL)
return -1;
strcpy(ExtendedWord, word);
word = ExtendedWord;
free(ExtendedWord);
}
word[j] = '\0';
words[i] = word;
}
return i;
}
strcpy(ExtendedWord,word);
strcpy() expects as 2nd parameter the address of the 1st character of a "C"-string, which in fact is a char-array with at least one element being equal to '\0'.
The memory word points to does not meet such requirements.
Due to this the infamous undefined behaviour is invoked, probably messing up the program's memory management, which in turn causes free() to fail.
There are multiple problems in your code:
you free the newly allocated block instead of the previous one.
you so not null terminate the string before passing it to strcpy
word should be initialized as NULL or to a block of allocated memory, not to point to a local array which you cannot pass to free().
you should reallocate the array before copying the new character at its end.
Here is a modified version:
int read_words(char *words[], int size, int max_str_len) {
int i, j;
for (i = 0; i < size; i++) {
char *word = malloc(1);
if (word == NULL)
return -1;
for (j = 0; j < max_str_len; ++j) {
int ch;
char *ExtendedWord;
if ((ch = getchar()) == EOF || ch == 'R') {
size = -1;
break;
}
if (ch == ' ' || c == '\n')
break;
/* reallocate array for one more character and a null terminator */
ExtendedWord = malloc(i + 2);
if (ExtendedWord == NULL)
return -1;
memcpy(ExtendedWord, word, i);
free(word);
word = ExtendedWord;
word[j] = ch;
}
if (size == -1) {
free(word);
break;
}
word[j] = '\0';
words[i] = word;
}
return i;
}
I Read that I should call free() as soon as I could do that to free the memory
That description is a little ambiguous. It is reasonable only if you interpret "as soon as I could do that" to mean the same as "as soon as I no longer need the allocated memory".
but when I call free in this way my code stops working correctly. what's the problem?
The problem with respect to free is that you free the memory before you are done with it. Subsequently attempting to access that memory produces undefined behavior.
There are other problems with the code, too, discussed in other answers, but this is how the free fits into the picture.
I want to call free in every iteration and when an error occurs.
Inasmuch as it appears that your function intends to provide pointers to the allocated memory to its caller via the words array, you must not free that memory anywhere within the scope of the function, because the caller (it must be presumed) intends to use it. Therefore the caller must assume the responsibility for freeing it. The function's documentation should clearly describe that responsibility.
Perhaps the confusion arises here:
word=ExtendedWord;
It is essential to understand that the assignment copies the pointer, not the space to which it points. Afterward, word points to the same (dynamically allocated) space that ExtendedWord does, so that freeing ExtendedWord invalidates both copies of the pointer.

How to correctly malloc a struct in C

Here is my full code, it looks like to work, but it's not working very well.
I would accept any code, that is working like this.
Firstly, the code works, but when I want to add the third name to the struct, it crashes.
Is there any other way to do this?
I need struct, because in the future, I want to add some other params, like age, average, gender, etc.
Please, help me out.
//The student table
typedef struct students {
char name[50];
} students;
//Global params
int scount = 0;
students *s;
//Basic functions
void addNewStudent();
int main()
{
int loop = 1;
char in;
int ch;
printf("Willkommen.\n Wahlen Sie bitte von die folgenden Optionen:\n");
while (loop)
{
printf("\t[1] Neue Student eingeben\n");
printf("\t[9] Programm beenden\n");
scanf(" %c", &in);
while ((ch = getchar()) != '\n');
switch (in)
{
case '1':
addNewStudent();
break;
case '9':
loop = 0;
break;
default: printf("------\nOption nicht gefunden.\n------\n");
break;
}
}
free(s);
return 0;
}
void addNewStudent()
{
int index = 0;
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
realloc(s, sizeof(students) * scount);
}
printf("Geben Sie Bitte die Name:\n");
fgets(s[scount].name, sizeof(s[scount].name), stdin);
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
scount++;
}
I'm using Visual Studio.
Thanks for help!
students *mynew= realloc(s, sizeof(students)* (scount+1));
if( mynew != NULL )
s=mynew;
Otehrwise you are having a memory leak. You didn't use the return value of realloc.
Don't cast the return type of malloc.
As per standard §7.22.2.35
void *realloc(void *ptr, size_t size)
The realloc function deallocates the old object pointed to by ptr and
returns a pointer to a new object that has the size specified by size.
It is good not to use the same pointer variable on which you are calling malloc because in case it fails you will lose reference to the old one too (unless it is stored by other means).
Also you didn't check the return value of malloc.
s = malloc(sizeof(students));
if( s == NULL ){
frpntf(stderr,"%s","Memory allocation failed");
exit(1);
}
Also you should check the return value of fgets().
if( fgets(s[scount].name, sizeof(s[scount].name), stdin) == NULL){
fprintf(stderr,"%s","Error in input");
exit(1);
}
Also trying to compile your code it showed this
warning: ignoring return value of ‘realloc’, declared with attribute warn_unused_result [-Wunused-result]
realloc(s, sizeof(students) * scount);
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
When compiling try not to ignore any warning messages. It showed the problem you had.
Important point: (why scount+1 in realloc?)
When reallocating the general idea is increase the number of students. And for that you need to have extra memory allocated for an student. That's why the scount+1 in the code.(realloc).
Some other points:
while (s[scount].name[index] != '\n')
{
index++;
}
s[scount].name[index] = '\0';
You can do it like this also
size_t len = strlen(s[scount].name);
if(len){
s[scount].name[len-1]='\0';
}
To understand why from standard §7.21.7.2
char *fgets(char * restrict s, int n,FILE * restrict stream)
The fgets function reads at most one less than the number of
characters specified by n from the stream pointed to by stream into
the array pointed to by s. No additional characters are read after a
new-line character (which is retained) or after end-of-file. A null
character is written immediately after the last character read into
the array.
\0 character was there already in the inputted string. You can get the length of it but you know that the one before the \0 is the \n character 1 that you entered by pressing the Enter key. We are overwriting it with the \0.
1. This is the usual case but not the only one. There are two cases where this might not be the right way to look at the thing.
The input line has n-1 or more characters before the '\n'. The the one before \0 will not be the \n rather it will be some character inputted by the user.
The last line is a stream which may not have a '\n'. (stdin closed). In that case also the input doesn't contain the \n.
So in these cases the idea of removing \n would fail.Discussed in comment. (chux)
A better and safe solution than overwriting this way:
s[scount].name[strcspn(s[scount].name, "\n")] = '\0';
The explanation from the link is that if a \0 is given as input then we will basically write to s[scount].name[SIZE_MAX] which is not desired.
From the standard §7.24.5.3
size_t strcspn(const char *s1, const char *s2)
The strcspn function computes the length of the maximum initial
segment of the string pointed to by s1 which consists entirely of
characters not from the string pointed to by s2.
How to correctly malloc a struct in C ?
p = malloc(sizeof *p);
if (p == NULL) Handle_OutOfMemory();
How to correctly re-allocate a struct in C ?
void *t = realloc(p, sizeof *p * number_of_elements);
if (t == NULL && number_of_elements > 0) {
Handle_OutOfMemory();
} else {
p = t;
}
p points to some struct. Notice no coding of that type in above.
OP' primary problem is not using the return value of realloc() and allocating 1-too-small
// realloc(s, sizeof(students) * scount);
s = realloc(s, sizeof *s * (scount+1)); // or use above code with check for out-of-memory.
realloc returns a new pointer that you need to keep:
students* snew = realloc(s, sizeof(students) * (scount + 1));
if (!snew) {
free(s); // If there is not enough memory, the old memory block is not freed
// handle out of memory
} else {
s = snew;
}
You are not allocating it back! Take a look at how realloc works. You need to assign the pointer back after making the re-allocation like this.
if (scount == 0)
{
s = (students*)malloc(sizeof(students));
}
else
{
students *temp = realloc(s, sizeof(students) * (scount+1));
if(temp == NULL){
free(s);
}
else{
s = temp;
}
}
By Definition, realloc returns a void pointer but you aren't collecting it.
void *realloc(void *ptr, size_t size);
realloc returns a NULL if there's not enough space. So you can re-assign it when you are sure that it is not NULL
Just make a small change above and your code works like a charm!
Cheers!

Why Does my program have Memory Leaks?

I am trying to create a small c program that will read a string with arbitrary size, without having any memory leaks.
According to my research, the function malloc can be used to allocate a number of bytes for whatever data we want to store.
In my program, I start by allocating space for 0 characters, and I make the pointer word point to it. Then whenever I read a single character, I make a pointer oldWord that points to word, which frees the old memory location once I allocate a larger memory location for the new character.
My research shows that the function free can be used to free an old memory location that is no longer needed. However, I am not sure where I am going wrong. Below you can see my code.
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *word = malloc(0);
printf("Enter name: ");
readWord(word);
printf("Your name is: %s\n", word);
free(word);
word = realloc(0);
printf("Enter name: ");
readWord(word);
printf("Your name is: %s\n", word);
free(word);
return 0;
}
void readWord(char *word){
int i = 0;
char *oldWord, c = getchar();
while(c != ' ' && c != '\n'){
oldWord = word;
word = realloc(word, i + 1);
free(oldWord);
word[i++] = c;
c = getchar();
}
oldWord = word;
word = realloc(word, i + 1);
free(oldWord);
word[i] = '\0';
}
The problem as I see it here is with
free(oldWord);
without checking the failure of realloc(). In case realloc() is success, passing the same pointer to free() causes undefined behavior.
That said, some more notes
a syntax like
word = realloc(word, i + 1);
is dangerous, in case realloc() fails, you'll lose the actual pointer, too. You should use a temporary pointer to hold the return value of realloc(), check for success and only then, assign it back to the original pointer, if you need.
In your code, c is of char type, which may not be sufficient to hold all the possible values returned by getchar(), for example, EOF. You should use an int type, that is what getchar() returns.
There are multiple problems in your code:
you free the pointer you passed to realloc(). This is incorrect as realloc() will have freed the memory already if the block was moved.
Otherwise the pointer is freed twice.
The pointer reallocated bu readWord() is never passed back to the caller.
Allocating a 0 sized block has unspecified behavior: it may return NULL or a valid pointer that should not be dereferenced but can be passed to free() or realloc().
You do not test for end of file: there is an infinite loop if the file does not have a space nor a linefeed in it, for example if the file is empty.
you do not have a prototype for readWord() before it is called.
Here is an improved yet simplistic version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
char *readWord(void);
int main(void) {
char *word;
printf("Enter name: ");
word = readWord();
if (word == NULL) {
printf("Unexpected end of file\n");
else
printf("Your name is: %s\n", word);
free(word);
return 0;
}
char *readWord(void) {
int c;
size_t i = 0;
char *word = NULL;
while ((c = getchar()) != EOF && !isspace(c)) {
word = realloc(word, i + 2);
if (word == NULL)
break;
word[i++] = c;
word[i] = '\0';
}
return word;
}

reading an unbounded line from the console with scanf

I need to read a finite yet unbounded-in-length string.
We learned only about scanf so I guess I cannot use fgets.
Anyway, I've ran this code on a an input with length larger than 5.
char arr[5];
scanf("%s", arr);
char *s = arr;
while (*s != '\0')
printf("%c", *s++);
scanf keeps scanning and writing the overflowed part, but it seems like an hack. Is that a good practice? If not, how should I read it?
Note: We have learned about the alloc functions family.
Buffer overflows are a plague, of the most famous and yet most elusive bugs. So you should definitely not rely on them.
Since you've learned about malloc() and friends, I suppose you're expected to make use of them.
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
// Array growing step size
#define CHUNK_SIZE 8
int main(void) {
size_t arrSize = CHUNK_SIZE;
char *arr = malloc(arrSize);
if(!arr) {
fprintf(stderr, "Initial allocation failed.\n");
goto failure;
}
// One past the end of the array
// (next insertion position)
size_t arrEnd = 0u;
for(char c = '\0'; c != '\n';) {
if(scanf("%c", &c) != 1) {
fprintf(stderr, "Reading character %zu failed.\n", arrEnd);
goto failure;
}
// No more room, grow the array
// (-1) takes into account the
// nul terminator.
if(arrEnd == arrSize - 1) {
arrSize += CHUNK_SIZE;
char *newArr = realloc(arr, arrSize);
if(!newArr) {
fprintf(stderr, "Reallocation failed.\n");
goto failure;
}
arr = newArr;
// Debug output
arr[arrEnd] = '\0';
printf("> %s\n", arr);
// Debug output
}
// Append the character and
// advance the end index
arr[arrEnd++] = c;
}
// Nul-terminate the array
arr[arrEnd++] = '\0';
// Done !
printf("%s", arr);
free(arr);
return 0;
failure:
free(arr);
return 1;
}
%as or %ms(POSIX) can be used for such purpose If you are using gcc with glibc.(not C standard)
#include <stdio.h>
#include <stdlib.h>
int main(void){
char *s;
scanf("%as", &s);
printf("%s\n", s);
free(s);
return 0;
}
scanf is the wrong tool for this job (as for most jobs). If you are required to use this function, read one char at a time with scanf("%c", &c).
You code misuses scanf(): you are passing arr, the address of an array of pointers to char instead of an array of char.
You should allocate an array of char with malloc, read characters into it and use realloc to extend it when it is too small, until you get a '\n' or EOF.
If you can rewind stdin, you can first compute the number of chars to read with scanf("%*s%n", &n);, then allocate the destination array to n+1 bytes, rewind(stdin); and re-read the string into the buffer with scanf("%s", buf);.
It is risky business as some streams such as console input cannot be rewinded.
For example:
fpos_t pos;
int n = 0;
char *buf;
fgetpos(stdin, &pos);
scanf("%*[^\n]%n", &n);
fsetpos(stdin, &pos);
buf = calloc(n+1, 1);
scanf("%[^\n]", buf);
Since you are supposed to know just some basic C, I doubt this solution is what is expected from you, but I cannot think of any other way to read an unbounded string in one step using standard C.
If you are using the glibc and may use extensions, you can do this:
scanf("%a[^\n]", &buf);
PS: all error checking and handling is purposely ignored, but should be handled in you actual assignment.
Try limiting the amount of characters accepted:
scanf("%4s", arr);
It's just that you're writing beyond arr[5]. "Hopefully" you're keeping writing on allocated memory of the process, but if you go beyond you'll end up with a segmentation fault.
Consider
1) malloc() on many systems only allocates memory, not uses it. It isn't until the memory is assigned that the underlining physical memory usage occurs. See Why is malloc not "using up" the memory on my computer?
2) Unbounded user input is not realistic. Given that some upper bound should be employed to prevent hackers and nefarious users, simple use a large buffer.
If you system can work with these two ideas:
char *buf = malloc(1000000);
if (buf == NULL) return NULL; // Out_of_memory
if (scanf("%999999s", buf) != 1) { free(buf); return NULL; } //EOF
// Now right-size buffer
size_t size = strlen(buf) + 1;
char *tmp = realloc(buf, size);
if (tmp == NULL) { free(buf); return NULL; } // Out_of_memory
return tmp;
Fixed up per #chqrlie comments.

Resources