I am trying to create a C function I can call in small programs I write, to accept user input:
char GetStringMine()
{
int i = 0;
char ch;
char * tmpstring = (char *) malloc(2048 * sizeof(char));
while(ch != '\n')
{
ch = getchar();
tmpstring[i++] = ch;
}
tmpstring[i] = '\0';
return * tmpstring;
free(tmpstring);
}
But it does not compile.
Please can you tell me what I am doing wrong, and what I can do better?
First thing:
In your code,
while(ch != '\n')
invokes undefined behavior, as ch is an automatic local variable and unless initialized explicitly, it contains an indeterminate value. Trying to read an indeterminate value is UB.
Second thing,
free(tmpstring);
after the return statement has no effect, at all. Just remove it.
Third thing:
Please see this discussion on why not to cast the return value of malloc() and family in C..
Fourth thing:
As per your return statement, the function return type should be of char *, instead of a char.
Finally
But it does not compile
cannot be answered in current form. You need to provide more information in your question to clarify "what" and "how".
You are trying to return a char pointer(char*) but the return type is a char. Also you should consider other comments too.
char* GetStringMine()
{
int i = 0;
char ch;
char * tmpstring = (char *) malloc(2048 * sizeof(char));
while(ch != '\n')
{
ch = getchar();
tmpstring[i++] = ch;
}
tmpstring[i] = '\0';
return tmpstring;
}
The worst thing about this code is that you're trying return a pointer to a local variable. The variable tmpstring is destroyed after the execution of your function is complete (i.e. on your return statement).
To correct this, you should ask a char* as a parameter, and store the read characters in it (careful with the overflows if you're going with this solution).
Or, you could declare tmpstring as a static char tmpstring[2048] = {0};. static means it won't be destroyed after the function is over. Although I've seen that kind of things in the standard library sometimes, I wouldn't recommend it since the contents will be erased when the function is called again.
For the other problems, see the previous answers.
Try this...
static char tmpstring[2048] = {0};
char* GetStringMine()
{
int i = 0;
char ch = 0;
while(ch != '\n')
{
ch = getchar();
tmpstring[i++] = ch;
}
return tmpstring;
}
You cannot free the allocated memory then return a pointer to it, the data will be gone
No need to use malloc, as you are not dynamically allocating the array(1)
Return a char*, not a char
(1) In your example you effectively are fixing the size of memory to 2048. Think about protecting against a buffer overrun - what will happen if a user enters more than 2048 characters, and how will you protect your code against this.
In a real world application you would need to reallocate if going over the allotted size, or restrict the amount of input for the memory allocated.
Related
First of all Thanks for visiting my question... :)
I am interested in competitive programming, so I daily do some amount of problem-solving, however, I only know C language at a decent level, and I often face problems while dynamically allocating something as usual, especially for strings and 2D arrays.
But I somehow manage to find ways (thanks to StackOverflow), for example, I wanted to create a function that scans string dynamically until the user enters space or new line, so I came up with the solution below and it works perfectly:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
// scanf("%[^\n]%*c", str);
char *create_string(char *ptr)
{
ptr = (char *)malloc(0 * sizeof(char));
unsigned int size = 0;
char c = 0;
while (1)
{
scanf("%c", &c);
if (c == 32 || c == 10)
{
break;
}
size++;
ptr = (char *)realloc(ptr, size * sizeof(char));
ptr[size - 1] = c;
}
ptr = (char *)realloc(ptr, (size + 1) * sizeof(char));
ptr[size] = '\0';
return ptr;
}
int main()
{
char *str;
str = create_string(str);
printf("%s", str);
printf("\n%lu", strlen(str));
return 0;
}
And now for curiosity purposes, I want to know how can I do this same thing using the void function?, something like:
char *str;
create_string(&str);
should start storing everything in the dynamic memory which is pointed by str.
Also, please if you have more knowledge to show in DMA for 2D array, then please show me it, feel free to give examples with different problems.
And also How can I stop scanning the string (which was allocated dynamically) with specific string ending? for example, scanning(any kind of scanning, i.e. int, bool, custom structures etc...) should stop if user enters string "STOP", Please feel free to give pictorial examples.
Because I am sure that this question is burning like a fire in beginner's and intermediate C programmers' minds.
As C passes arguments by value, to return something via an out parameter, you need to pass in a pointer to it. So to return a char * it would:
void create_string(char **s) {
*s = malloc(42);
}
Here is your refactored code. I changed the following:
Eliminate return value of update caller.
Initialize *ptr = malloc(1) for the trailing '\0'. It eliminates an unnecessary and implementation defined malloc(0). This also eliminates the (*ptr)[size] = ... which looks wrong as the last index is expected to be size - 1. Alternatively initialize it to NULL.
Use character constants instead of magic values (32, 10).
sizeof(char) is defined as 1 so leave it out.
Reduced scope of variable c.
free() memory allocated.
(cosmetic) Use size_t size instead of unsigned int size.
(cosmetic) Avoid the noise of casting casting void *.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void create_string(char **ptr) {
*ptr = malloc(1);
size_t size = 1;
for(;;) {
char c;
scanf("%c", &c);
if (c == ' ' || c == '\n') break;
(*ptr)[size-1] = c;
size++;
*ptr = realloc(*ptr, size);
}
(*ptr)[size-1] = '\0';
}
int main() {
char *str;
create_string(&str);
printf("%s\n", str);
printf("%zu\n", strlen(str));
free(str);
}
I didn't fix these issue:
Check return value of malloc(), realloc().
v = realloc(v, ...) is unsafe and will leak memory if realloc() fails. You need to do char *tmp = realloc(v,...); if(!tmp) { // err }; v = tmp;.
Check return value of scanf() otherwise you may be operating on uninitialized data.
Use scanf("%s", ..) instead of for(;;) { scanf("%c", ...). It's more efficient to allocate a chunk at a time instead of per byte.
If user enters ctrl-d (EOF) the program will go into an infinite loop.
It's good idea to separate i/o from logic (i.e. let caller do the scanf(). That way create_string() is much more reusable.
Hi I wrote this code and it worked but in the end, "the program has stopped working"
#include <stdio.h>
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
void main()
{
char *s;
s=(char*)malloc(sizeof(char));
printf("Enter a string:\n");
gets(s);
while (*s)
{
if (*s>= 65 && *s<=90)
printf("%c",*s+32);
else if(*s>=97 && *s<=122)
printf("%c",*s-32);
else
printf("%c",*s);
*s++;
}
free(s);
}
That code does not work, in fact it has undefined behavior.
This:
s = (char *) malloc(sizeof(char));
allocates 1 byte of storage, into which you then scan a string, thus very likely leading to buffer overflow. The buffer can only hold a single string, i.e. string of 0 characters before the terminator character at the end.
You meant:
s = malloc(128);
or something like that.
There's no need to cast, and sizeof (char) is always 1 so that doesn't add anything.
Also, as more of a code review, magic numbers in code is generally considered a bad idea, instead write:
if (*s >= 'A' && *s <= 'Z')
or even better
if (isupper((unsigned int) *s))
to not hard-code a depdency on ASCII.
UPDATE Oh, and as pointed out in a comment, you can't change the value of s and then pass the changed value to free(), that is undefined behavior also. The address passed to free() must be the same as the one you got back from malloc().
Use a separate variable for the iteration:
char *p = s;
and only free() the original s.
Firstly, by s=(char*)malloc(sizeof(char)); you are allocating only 1 byte of memory for buffer. Allocate enough memory to store the input. Also avoid typecasting malloc() result. Better version looks like
s = malloc(MAX * sizeof(*s));/* MAX is num of bytes you need to define */
Secondly don't use gets() use fgets() instead. Read man 3 gets or check https://linux.die.net/man/3/gets
Finally use int main(void) { } instead of just main(){ }
And more importately when you do free(s) at that time s doesn't point to memory which was earlier allocated to it because of s++ so it may result error like
free(): invalid pointer:
So don't change s use s[row] while iterating OR you can assign s to other pointer and then you can do free(s).
Complete code
int main() {
char *s = NULL;
int size = MAX*sizeof(*s);/*define MAX value, it is no of bytes need*/
s = malloc(size);/* this is generic
sizeof(*s) works for any data type */
printf("Enter a string:\n");
fgets(s,size,stdin);/* use fgets() instead of gets() */
int row = 0;
while (s[row]!='\0') {
if ( *(s+row) >= 65 && *(s+row) <= 90)
printf("%c",*(s+row) + 32);
else if( *(s+row) >=97 && *(s+row) <= 122)
printf("%c",*(s+row) - 32);
else
printf("%c",*(s+row));
row++;
}
free(s);/* s still points to same location */
return 0;
}
Also you can use isupper() instead of comparing each char ASCII value.
This is wrong.
s = (char*)malloc(sizeof(char));
printf("Enter a string:\n");
gets(s);
s = (char*)malloc(sizeof(char)); allocates 1 byte of memory. And then with
gets(s); you get a string, which will be Undefined Behavior.
You have to changed it to
s = malloc(MAX_LENGTH * sizeof(char)); //MAX_LENGTH is user defined
Additionally, you must check if malloc() returned anything. If it returns NULL then it means no memory is allocated and all of the existing program will invoke undefined behavior.
Also, there is no need to cast the malloc result, so to further improve your code, you need to change it to,
s = malloc(MAX_LENGTH * sizeof(char));
if(s == NULL)
{
// Add error handling here
}
Also,
void main()
isn't by the standard anymore, see This post which explains why. If you want to know what C11 standard states about it, then refer the standard here: Section 5.1.2.2.1 Program startup
So change it to,
int main(void)
You should make sure that you call free(s); only if it was allocated. As one of the comments below rightly indicates that free(NULL); is NOT a problem, but it also have no effect, so why call it anyway.
Make s point to NULL again, but its irrelevant in this piece of code.
I created a function designed to get user input. It requires that memory be allocated to the variable holding the user input; however, that variable is returned at the end of the function. What is the proper method to free the allocated memory/return the value of the variable?
Here is the code:
char *input = malloc(MAX_SIZE*sizeof(char*));
int i = 0;
char c;
while((c = getchar()) != '\n' && c != EOF) {
input[i++] = c;
}
return input;
Should I return the address of input and free it after it is used?
Curious as to the most proper method to free the input variable.
It's quite simple, as long as you pass to free() the same pointer returned by malloc() it's fine.
For example
char *readInput(size_t size)
{
char *input;
int chr;
input = malloc(size + 1);
if (input == NULL)
return NULL;
while ((i < size) && ((chr = getchar()) != '\n') && (chr != EOF))
input[i++] = chr;
input[size] = '\0'; /* nul terminate the array, so it can be a string */
return input;
}
int main(void)
{
char *input;
input = readInput(100);
if (input == NULL)
return -1;
printf("input: %s\n", input);
/* now you can free it */
free(input);
return 0;
}
What you should never do is something like
free(input + n);
because input + n is not the pointer return by malloc().
But your code, has other issues you should take care of
You are allocating space for MAX_SIZE chars so you should multiply by sizeof(char) which is 1, instead of sizeof(char *) which would allocate MAX_SIZE pointers, and also you could make MAX_SIZE a function parameter instead, because if you are allocating a fixed buffer, you could define an array in main() with size MAX_SIZE like char input[MAX_SIZE], and pass it to readInput() as a parameter, thus avoiding malloc() and free().
You are allocating that much space but you don't prevent overflow in your while loop, you should verify that i < MAX_SIZE.
You could write a function with return type char*, return input, and ask the user to call free once their done with the data.
You could also ask the user to pass in a properly sized buffer themselves, together with a buffer size limit, and return how many characters were written to the buffer.
This is a classic c case. A function mallocs memory for its result, the caller must free the returned value. You are now walking onto the thin ice of c memory leaks. 2 reasons
First ; there is no way for you to communicate the free requirement in an enforceable way (ie the compiler or runtime can't help you - contrast with specifying what the argument types are ). You just have to document it somewhere and hope that the caller has read your docs
Second: even if the caller knows to free the result he might make a mistake, some error path gets taken that doesnt free the memory. This doesnt cause an immediate error, things seem to work, but after running for 3 weeks your app crashes after running out of memory
This is why so many 'modern' languages focus on this topic, c++ smart pointers, Java, C#, etc garbage collection,...
I am trying to develop a basic OS using assembly and C and I am kind of stuck in developing the keyboard driver.
I use the following two functions to get a line from the user through the keyboard
char getchar() {
char c;
int i;
while(char_count == 0);
c = buffer[0];
for(i=0 ; i<KEYBOARD_BUFFER_SIZE ; i++) {
buffer[i] = buffer[i+1];
}
char_count--;
push_index--;
return c;
}
char* getline(unsigned char password_mode) {
char* line;
char c = 0x00;
int index = 0;
while(c != '\n') {
if(c) {
if(!password_mode)
printch(c);
else
printch('*');
if(c == '\b')
index--;
else
line[index++] = c;
}
c = getchar();
}
line[index] = '\0';
return line;
}
However, when I try to get the username and the password from the user, I ask him to enter the username first and then enter the password, the problem is that the password overwrites the username which leads to incorrect data. How can the password be written over the username ? and does that mean that the char* arr allocated in getline() has the same address each time the function is accessed ?
thanks for you help
Your first problem is you're not allocating any memory to read your input into. char* line; declares line as a pointer to char pointing to a random bit of memory. So dereferencing line via line[index++] = c takes you straight into undefined behaviour - you're writing to memory that you don't own. It's pretty much pure fluke that subsequent calls to getline are hitting the same bit of memory and overwriting what went before.
So you need to change getline to actually read into memory you own.
One option is to change the declaration of line to a static char array; something like static char line[100];. This would give you some actual memory to work with. However this guarantees that each call to getline will definitely write to the same area of memory. It would then be up to the caller to copy the input somewhere else if it needs to be preserved beyond the next call to getline.
Another option would be to change getline to accept a buffer from the caller, something like getline(unsigned char password_mode, char *line, size_t line_size). That way you can pass different arrays to each call.
Whichever option you choose you'd also want to make sure you're not in danger of overrunning the buffer inside getline by reading more characters than you have room for.
I need a function/method that will take in a char array and set it to a string read from stdin. It needs to return the last character read as its return type, so I can determine if it reached the end of a line or the end of file marker.
here is what I have so far, and I kind of based it off of code from here
UPDATE: I changed it, but now it just crashes upon hitting enter after text. I know this way is inefficient, and char is not the best for EOF check, but for now I am just trying to get it to return the string. I need it to do it in this fashion and no other fashion. I need the string to be the exact length of the line, and to return a value that is either the newline or EOF int which I believe can still be used in a char value.
This program is in C not C++
char getLine(char **line);
int main(int argc, char *argv[])
{
char *line;
char returnVal = 0;
returnVal = getLine(&line);
printf("%s", line);
free(line);
system("pause");
return 0;
}
char getLine(char **line) {
unsigned int lengthAdder = 1, counter = 0, size = 0;
char charRead = 0;
*line = malloc(lengthAdder);
while((charRead = getc(stdin)) != EOF && charRead != '\n')
{
*line[counter++] = charRead;
*line = realloc(*line, counter);
}
*line[counter] = '\0';
return charRead;
}
Thank you for any help in advance!
You're assigning the result of malloc() to a local copy of line, so after the getLine() function returns it's not modified (albeit you think it is). What you have to do is either return it (as opposed to use an output parameter) or pass its address (pass it 'by reference'):
void getLine(char **line)
{
*line = malloc(length);
// etc.
}
and call it like this:
char *line;
getLine(&line);
Your key problem is that line pointer value does not propagate out of the getLine() function. The solution is to pass pointer to the line pointer to the function as a parameter instead - calling it like getLine(&line); while the function would be defined as taking parameter char **line. In the function, on all places where you now work with line, you would work with *line instead, i.e. dereferencing the pointer to a pointer and working with the value of the variable in main() where the pointer leads. Hope this is not too confusing. :-) Try to draw it on a piece of paper.
(A tricky part - you must change line[counter] to (*line)[counter] because you first need to dereference the pointer to the string, and only then to access a specific character in the string.)
There is a couple of other problems with your code:
You use char as the type for charRead. However, the EOF constant cannot be represented using char, you need to use int - both as the type of charRead and return value of getLine(), so that you can actually distringuish between a newline and end of file.
You forgot to return the last char read from your getLine() function. :-)
You are reallocating the buffer after each character addition. This is not terribly efficient and therefore is a rather ugly programming practice. It is not too difficult to use another variable to track the amount of space allocated and then (i) start with allocating a reasonable chunk of memory, e.g. 64 bytes, so that ideally you will never reallocate (ii) enlarge the allocation only if you need to based on comparing the counter and your allocation size tracker. Two reallocation strategies are common - either doubling the size of the allocation or increasing the allocation by a fixed step.
The way you use realloc is not correct. If it returns NULL then the memory block will be lost.
It is better to use realloc in this way:
char *tmp;
...
tmp = realloc(line, counter);
if(tmp == NULL)
ERROR, TRY TO SOLVE IT
line = tmp;