Allocating memory to user input using malloc [closed] - c

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 years ago.
Improve this question
I am trying to dynamically allocate memory for this C program I have constructed which takes a single string input and reverses it then prints the reversed output. I am very new to C and am unsure on how to use malloc to allocate memory for specified user input. This is the current code I have, we are running these programs in linux, using gcc code.c -o code to compile. Where the file is named code.c
int main(void)
{
char input[256];
char temp;
int i = 0, j = 0;
scanf("%s", input);
j = strlen(input) - 1;
while (i < j)
{
temp = input[j];
input[j] = input[i];
input[i] = temp;
i++;
j--;
}
printf("Reverse = %s", input);
return 0;
}

For a malloc implementation you just need to replace
char input[256];
with
char* input = malloc(256);
if(input == NULL){
//uppon bad allocation NULL will be returned by malloc
perror("malloc");
return EXIT_FAILURE;
}
Since malloc needs the allocated space in bytes normally you would multiply the needed space by the type of variable:
char *input = malloc(256 * sizeof *input);
but, since the size of a char is always 1 byte, in this case you won't need it.
The rest of the code can be the same, then after you use input you can/should free the allocated memory:
free(input);
You will need to #include <stdlib.h>.
Side note:
To avoid buffer overflow it's recommmended that you define a maximum size for scanf specifier which sould be at most the size of the buffer:
scanf("%255s", input);
or to also parse spaces:
scanf("%255[^\n]", input);
Notice that there is one less space, which is reserved for the null terminator, automatically added by scanf and mandatory for the char array to be treated as a string.

Once you get a string from user, you can allocate memory like this.
malloc() returns a pointer to allocated memory, or NULL if it failed.
size_t input_len = strlen(input);
char* mem_data = malloc(input_len + 1); // +1 for null-terminate
if(!mem_data)
{
// failed to allocate memory. do dome error process
}
Following code is copying input reversely to the allocated memory.
int i;
for (i = 0; i < input_len; ++i)
{
mem_data[i] = input[input_len - i - 1];
}
mem_data[input_len] = 0; // null terminate
printf("Reverse = %s", mem_data);
Remember, dynamically allocated memory should be free()d after use.
free(mem_data); // never forget to free memory when you finished using.

To dynamically allocate space in c you need to know beforehand exactly how much space you want to allocate. So, allocate space exactly same size as input string, you need to take length of the string as input.
Sample code will be like below:
int n;
char* input;
int main(){
scanf("%d",&n);
input= (char*)malloc((n+1)*sizeof(char));
scanf("%s",input);
return 0;
}
You can read about dynamic memory allocation here https://www.geeksforgeeks.org/dynamic-memory-allocation-in-c-using-malloc-calloc-free-and-realloc/

The approach that is more often used in books on C for beginners is writing a function that reads character by character from the input stream and reallocates memory dynamically when a new character is read.
Something like the function shown in this demonstrative program below
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * getline( size_t n )
{
char *s = NULL;
if ( n != 0 )
{
s = malloc( sizeof( char ) );
size_t i = 0;
for ( int c; i < n - 1 && ( c = getchar() ) != EOF && c != '\n'; ++i )
{
s = realloc( s, i + 2 );
if ( s == NULL ) break;
s[i] = c;
}
if ( s ) s[i] = '\0';
}
return s;
}
char * reverse( char *s )
{
if ( *s != '\0' )
{
for ( size_t i = 0, j = strlen( s ); i < --j; i++ )
{
char c = s[i];
s[i] = s[j];
s[j] = c;
}
}
return s;
}
int main(void)
{
const size_t N = 256;
printf( "Enter a string (no more than %zu symbols): ", N );
char *s = getline( N );
if ( s ) printf( "The reversed string is \"%s\"\n", reverse( s ) );
free( s );
return 0;
}
The program output might look like
Enter a string (no more than 256 symbols): Hello Mgert33
The reversed string is "33tregM olleH"

Related

How can realloc be used effectively in loops?

I'm new to c so sorry if the question might seem basic. I'm trying to read a string with unknown size from the user and store it in a dynamic array.
This program is going to make accounts for users but I can't successfully scan the username or the password.
Please note that no arrays with fixed size can be used.
Any solutions using files can be used and also be really helpful.
void main(){
user *dummyUser = (user *) malloc(sizeof(user));
dummyUser->next=NULL;
char* password=(char* )malloc(sizeof(char));
char* name=(char* )malloc(sizeof(char));
int i=0;
char c;
char ptr;
while((c=getchar())!=' '){
name[i]=c;
ptr=(char *)realloc(name,sizeof(char));
if(ptr==NULL){
printf("realloc failed");
exit( EXIT_FAILURE );
}
i++;
}
i=0;
while((c=getchar())!='\n'){
password[i]=c;
password=(char *)realloc(password,sizeof(char));
i++;
}
}
realloc seem to work for 1 time, but the next times it returns a null pointer and makes the program crash.
note: I know I have to check if realloc returns a null pointer, I checked it in my watchlist so I didn't write the related code.Thanks for helping.
This isn't doing what you want:
ptr=(char *)realloc(name,sizeof(char));
The second argument of realloc specifies the total number of bytes you want allocated, not just how many more bytes to allocate. You're not extending the array by 1 each time, you're just reallocating the same 1-byte space (sizeof (char) is 1 by definition).
Remember that to store an N-character string, you need to allocate at least N+1 bytes to account for the string terminator.
The typical way to do this is to double the size of the buffer each time - you'll need to keep track of the total buffer size as well as the number of bytes used, like so:
#include <ctype.h> // for the isspace call below:
...
size_t size = 2; // 2 bytes can store a 1-character string
char *name = malloc( size ); // no need for a cast
size_t i = 0;
int c;
...
/**
* You'll probably want to break on EOF and *any* whitespace
* character
*/
while( (c = getchar()) != EOF && !isspace( c ) )
{
if ( i == size )
{
/**
* Double the size of the buffer; again, no need for a cast
*
* Since realloc will return NULL if it can't extend the
* buffer, we want to assign the result to a temporary;
* otherwise we risk overwriting the pointer value in name
* with a NULL pointer and losing our access to the memory
* that's been previously allocated.
*/
char *tmp = realloc( name, size * 2 );
if ( tmp )
{
name = tmp;
size *= 2;
}
else
{
/**
* We were unable to extend the buffer; you can treat this
* as a fatal error and exit immediately, or you can
* try to proceed with the data you have. The previous
* buffer is still allocated at this point.
*
* For this particular program, you'd probably just want
* to exit with an error message at this point, like so:
*/
fputs( "Failed to extend name buffer, exiting...", stderr );
free( name ); // clean up what we've already allocated
exit( 0 );
}
}
name[i++] = c;
}
name[i] = 0; // terminate the string
For convenience's sake, you'll want to move the buffer extension into its own function:
char *extend( char **buf, size_t *size )
{
char *tmp = realloc( *buf, *size * 2 );
if ( tmp )
{
*buf = tmp;
*size *= 2;
}
return tmp;
}
and you'd call it as
while( (c = getchar()) != EOF && !isspace( c ) )
{
if ( i == size )
{
if ( !extend( &name, &size ) )
{
// handle realloc failure
}
}
name[i++] = c;
}
name[i] = 0; // terminate the string
ptr=(char *)realloc(name,sizeof(char));
Please note that this is allocating a single byte. You have done this several times. A C string is an array of characters with a null terminator ('\0') byte at the end. An array that can only hold one byte can only ever hold an empty C string, or lack that null terminator, resulting in undefined behavior.
You likely want to start by allocating a more useful amount of space, and then grow exponentially as your input grows to avoid excessive calls to realloc.
E.g.
size_t allocated_bytes = 64;
char *name = malloc(allocated_bytes);
size_t read_bytes = 0;
char ch;
while ((ch = getchar()) != ' ') {
if (read_bytes >= allocated_bytes - 1) {
allocated_bytes *= 2;
name = realloc(name, allocated_bytes);
}
name[read_bytes++] = ch;
}
name[read_bytes] = '\0';
Checking the return of realloc and for EOF I leave as an exercise for the OP.

C loop to read lines of input

I want to create a program in C that takes an arbitrary number of lines of arbitrary length as input and then prints to console the last line that was inputted. For example:
input:
hi
my name is
david
output: david
I figured the best way to do this would be to have a loop that takes each line as input and stores it in a char array, so at the end of the loop the last line ends up being what is stored in the char array and we can just print that.
I have only had one lecture in C so far so I think I just keep setting things up wrong with my Java/C++ mindset since I have more experience in those languages.
Here is what I have so far but I know that it's nowhere near correct:
#include <stdio.h>
int main()
{
printf("Enter some lines of strings: \n");
char line[50];
for(int i = 0; i < 10; i++){
line = getline(); //I know this is inproper syntax but I want to do something like this
}
printf("%s",line);
}
I also have i < 10 in the loop because I don't know how to find the total number of lines in the input which, would be the proper amount of times to loop this. Also, the input is being put in all at once from the
./program < test.txt
command in Unix shell, where test.txt has the input.
Use fgets():
while (fgets(line, sizeof line, stdin)) {
// don't need to do anything here
}
printf("%s", line);
You don't need a limit on the number of iterations. At the end of the file, fgets() returns NULL and doesn't modify the buffer, so line will still hold the last line that was read.
I'm assuming you know the maximum length of the input line.
This one here will surely do the job for you
static char *getLine( char * const b , size_t bsz ) {
return fgets(b, bsz, stdin) );
}
But remember fgets also puts a '\n' character at the end of buffer so perhaps something like this
static char *getLine( char * const b , size_t bsz ) {
if( fgets(b, bsz, stdin) ){
/* Optional code to strip NextLine */
size_t size = strlen(b);
if( size > 0 && b[size-1] == '\n' ) {
b[--size] = '\0';
}
/* End of Optional Code */
return b;
}
return NULL;
}
and your code needs to be altered a bit while calling the getline
#define BUF_SIZE 256
char line[BUF_SIZE];
for(int i = 0; i < 10; i++){
if( getLine(line, BUF_SIZE ) ) {
fprintf(stdout, "line : '%s'\n", line);
}
}
Now it is how ever quite possible to create function like
char *getLine();
but then one needs to define the behavior of that function for instance if the function getLine() allocates memory dynamically then you probably need use a free to de-allocate the pointer returned by getLine()
in which case the function may look like
char *getLine( size_t bsz ) {
char *b = malloc( bsz );
if( b && fgets(b, bsz, stdin) ){
return b;
}
return NULL;
}
depending on how small your function is you can entertain thoughts about making it inline perhaps that's a little off topic for now.
In order to have dynamic number of input of dynamic length, you have to keep on reallocating your buffer when the input is of greater length. In order to store the last line, you have to take another pointer to keep track of it and to stop the input from the terminal you have to press EOF key(ctrl+k). This should do your job.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *get_last_line(FILE* fp, size_t size){
//The size is extended by the input with the value of the provisional
char *str, *last_str = NULL;
int ch;
size_t len = 0, last_len = 0;
str = realloc(NULL, sizeof(char)*size);//size is start size
if(!str)return str;
while(ch=fgetc(fp)){
if(ch == EOF){
break;
}
if(ch == '\n'){
str[len]='\0';
last_len = len;
last_str = realloc(last_str,sizeof(char)*last_len);
last_str[last_len]='\0';
//storing the last line
memcpy(last_str,str,sizeof(char)*last_len);
str = realloc(NULL, sizeof(char)*size);//size is start size
len = 0;
}
else {
str[len++]=ch;
if(len==size){
str = realloc(str, sizeof(char)*(size+=16));
if(!str)return str;
}
}
}
free(str);
return last_str;
}
int main(void){
char *m;
printf("input strings : ");
m = get_last_line(stdin, 10);
printf("last string :");
printf("%s\n", m);
free(m);
return 0;
}

garbage output after whitespace removing

i've this code:
int i =0;
char * str = "ar bitrary whitespace";
int whitespace=0,index;
for(index = 0;index < strlen(str);index++)
{
if(isspace(str[index]) != 0)
{
whitespace++;
}
}
char * tmp = (char *)calloc(strlen(str)-whitespace +1,sizeof(char));
memset(tmp,'\0',strlen(tmp)+1);
while(i < strlen(str))
{
if(isspace(str[i]) != 0)
{
i++;
continue;
}else if(isspace(str[i]) == 0)
{
strcat(tmp,&str[i]);
i++;
}
}
printf("\nnew string is: %s \n",tmp);
the problem is that the output is a string without the whitespace removed + some garbage character.
I've used memset to null terminate tmp,is there the problem?
The length of the source string could be calculated before this loop
for(index = 0;index < strlen(str);index++)
Otherwise if the code will not be optimized the function strlen will be called for each iteration of the loop. In fact using of the function is redundant for such a task.
This statement
memset(tmp,'\0',strlen(tmp)+1);
does not make sense because the call of calloc already initialized the memory with zeroes.
This statement
strcat(tmp,&str[i]);
also copies blanks from the source string after the position i. So it can write beyond the memory allocated for the array pointed to by the pointer tmp.
You can write a separate function that can look as it is shown in this demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
char * remove_blanks( const char *s )
{
size_t n = 0;
const char *p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) ++n;
} while ( *p++ );
char *t = malloc( n );
if ( t )
{
char *q = t;
p = s;
do
{
if ( !isspace( ( unsigned char )*p ) ) *q++ = *p;
} while ( *p++ );
}
return t;
}
int main(void)
{
char * str = "ar bitrary whitespace";
printf( "\"%s\"\n", str );
char *t = remove_blanks( str );
printf( "\"%s\"\n", t );
free( t );
}
The program output is
"ar bitrary whitespace"
"arbitrarywhitespace"
this is your problem
memset(tmp,'\0',strlen(tmp)+1);
strlen(tmp) works by looking for '\0' in tmp, you have a chicken and egg situation here.
You should not be doing a memset any way, just tack on a '\0' when you fnish copying
And dont use strcat, instead maintain a pointer to tmp and just do *p = str[i] then increment p
I will not read your question, you overwrite the '\0' terminator for sure.
Now that I read your question, it looks like you need to understand strings and arrays better,
Don't ever write while (i < strlen(str))
Don't use strcat() for adding a single character, you apparently did overwrite the '\0' there. Furthermore, don't ever use strcat() for concatenating more than to pieces of a string.
Also notable,
You memset() after calloc() which already initialized to 0. That means that you are enforcing something that is not necessary, and trying it twice as if it failed the first time which I can guarantee it didn't.
In fact, since you have used calloc() and all bytes pointed to by tmp are 0 then strlen(tmp) will return 0, thus your memset() is equivalent to
tmp[0] = '\0';
and you REALLY don't need initialize tmp except when you finally copy the actual bytes from str.
I always advice against calloc() for strings, because
You don't really need to initialize something twice.
You should be sure your code does take the terminating '\0' into account and not simply assume that it's there because you calloc()ed. That is a bug that you just hide with calloc() but it shows up at some point.
Try this and see if you can understand the reasons for my changes
#include <stdio.h>
#include <ctype.h>
#include <stdlib.h>
int main(void)
{
int whitespace;
int length;
char *str = "ar bitrary whitespace";
char *tmp;
whitespace = 0;
for (length = 0; str[length] != '\0'; ++length) {
if (isspace(str[length]) != 0) {
whitespace++;
}
}
tmp = malloc(length - whitespace + 1);
if (tmp == NULL)
return -1;
for (int i = 0, j = 0; str[i] != '\0'; ++i) {
if (isspace(str[i]) != 0)
continue;
tmp[j++] = str[i];
}
tmp[length - whitespace] = '\0';
printf("new string is: %s\n",tmp);
free(tmp);
return 0;
}

Allocation memory for array of strings

How to allocate initial memory,and increase memory in each user input for a array of strings which that number of columns are known.
for example: ` char* type[20];
I want to allocate initial memory for it and increase space for each scanf("%s", type[i]); which is in the loop.
could you please help Me
You can use dynamic memory allocation for that
char *read_string(void)
{
int c;
int i = 0;
char *s = malloc(1);
printf("Enter a string: \t"); // It can be of any length
/* Read characters until found an EOF or newline character. */
while((c = getchar()) != '\n' && c != EOF)
{
s[i++] = c;
s = realloc(s, i+1); // Add memory space for another character to be read.
}
s[i] = '\0'; // Nul terminate the string
return s;
}
int main(void)
{
char* type[20];
for( size_t i = 0; i < sizeof(type); i++)
type[i] = read_string();
// Do some other stuff
}
Do not forget to free memory when you are done with it.
an array is a a contiguous block of memory, when you declare an array of a certain type, you can no longer increase or decrease its size.
one approach would be for you to declare a reasonable large sized array of char to hold your "string". like this:
const int size=256;
char input[size]; //declares an array of 256 chars
as for inputting actual "strings" in the arary of char, you COULD use scanf but I recommend you use fgets because it is much safer.
while(fgets(input, size, stdin)!=NULL)
{
//do something with input
}
This approach is susceptible to buffer overflow and is not necessarily safe, but is common approach nonetheless
as for your "array of string" problem, you want to allocate everything dynamically. ( if you dynamically allocate a char** and make it point to stack allocated char[] you are asking for big time trouble)
you should use a default size for your char** (say to take in 1000 char* ) and use a default allocation size for each of your char* ,for example 256 *sizeof(char) bytes.
this way you can use realloc every time you exhaust your space.
You can use standard function realloc declared in header <stdlib.h> each time when a new string has to be read.
Here a demonstrative program that instead of reading strings just copies a fixed number of them
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
int main( void )
{
char ( *s )[20] = NULL;
char ( *p )[20] = NULL;
size_t n = 0;
do
{
p = realloc( s, ( n + 1 ) * sizeof( *s ) );
if ( p )
{
s = p;
strcpy( s[n], ( char[] ){ 'A' + n, '\0'} );
++n;
}
} while ( p && n < 10 );
for ( size_t i = 0; i < n; i++ ) puts( s[i] );
free( s );
}
The program output is
A
B
C
D
E
F
G
H
I
J
Pay attention to how the function realloc is used.
If you know that the number of strings can not be less than some constant then you can initially allocate dynamically an array of the size that is equal to this constant.
For example
#define MIN_SIZE 10
//...
char ( *s )[20] = malloc( MIN_SIZE * sizeof( *s ) );

Allocating multidimensional char pointer in C

I am trying to read lines from a file and store them in a multidimensional char pointer. When I run my code, it runs without errors, however, the lines will not be printed correctly in my main() function, but prints correctly in the getline() function. Can anybody explain what is happening and how I correctly store them in my multidimensional pointer?
My code:
int main (int argc, const char * argv[]) {
char **s = (char**) malloc(sizeof(char*) * 1000);
int i = 0;
while (getline(s[i]) != -1){
printf("In array: %s \n", s[i]);
i++;
}
return 0;
}
int getline(char *s){
s = malloc(sizeof(char*) * 1000);
int c, i = 0;
while ((c = getchar()) != '\n' && c != EOF) {
s[i++] = c;
}
s[i] = '\0';
printf("String: %s \n", s);
if (c == EOF) {
return -1;
}
return 0;
}
and my output:
String: First line
<br>In array: (null)
<br>String: Second line
<br>In array: (null)
<br>String: Third line
<br>In array: (null)
You are passing by value. Even though you change the value of *s in getline(), main does not see it. You have to pass the address of s[i] so that getline() can change it:
int getline(char **s) {
* s= malloc( sizeof(char) * 1000) ;
...
}
Also, if you want to be a bit more efficient with memory, read the line into a local buffer (of size 1000) if you want. Then when you are done reading the line, allocate only the memory you need to store the actual string.
int getline( char ** s )
{
char tmpstr[1000] ;
...
tmpstr[i++]= c ;
}
tmpstr[i]= '\0' ;
* s= strdup( tmpstr) ;
return 0 ;
}
If you want to improve things even further, take a step back and thing about a few things. 1) allocating the two parts of the multi-dimensional array in two different functions is going to make it harder for others to understand. 2) passing in a temporary string from outside to getline() would allow it to be significantly simpler:
int main()
{
char ** s= (char **) malloc( 1000 * sizeof(char *)) ;
char tmpstr[1000] ;
int i ;
while ( -1 != getline( tmpstr))
{
s[i ++]= strdup( tmpstr) ;
}
return 0 ;
}
int getline( char * s)
{
int c, i = 0 ;
while (( '\n' != ( c= getchar())) && ( EOF != c )) { s[i ++]= c ; }
s[i]= '\0' ;
return ( EOF == c ) ? -1 : 0 ;
}
Now, getline is just about IO, and all the allocation of s is handled in one place, and thus easier to reason about.
The problem is that this line inside getline function
s = malloc(sizeof(char) * 1000); // Should be sizeof(char), not sizeof(char*)
has no effect on the s[i] pointer passed in. This is because pointers are passed by value.
You have two choices here:
Move your memory allocation into main, and keep passing the pointer, or
Keep your allocation in getline, but pass it a pointer to pointer from main.
Here is how you change main for the first option:
int main (int argc, const char * argv[]) {
char **s = malloc(sizeof(char*) * 1000);
int i = 0;
for ( ; ; ) {
s[i] = malloc(sizeof(char) * 1000);
if (getline(s[i]) == -1) break;
printf("In array: %s \n", s[i]);
i++;
}
return 0;
}
My advise: completely avoid writing your own getline() function, and avoid all fixed size buffers. In the POSIX.1-2008 standart, there is already a getline() function. So you can do this:
//Tell stdio.h that we want POSIX.1-2008 functions
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
int main() {
char** s = NULL;
int count = 0;
do {
count++;
s = realloc(s, count*sizeof(char*));
s[count-1] = NULL; //so that getline() will allocate the memory itself
} while(getline(&s[count-1], NULL, stdin));
for(int i = 0; i < count; i++) printf(s[i]);
}
Why do you use malloc in the first place. malloc is very very dangerous!!
Just use s[1000][1000] and pass &s[0][0] for the first line, &s[1][0] for the second line etc.
For printing printf("%s \n", s[i]); where i is the line you want.

Resources