Allocation memory for array of strings - c

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 ) );

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.

Allocating memory to user input using malloc [closed]

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"

Assert is failing in a string array comparison [duplicate]

This question already has answers here:
How do I properly compare strings in C?
(10 answers)
Why isn't the size of an array parameter the same as within main?
(13 answers)
Closed 3 years ago.
In this part of my code I remove white spaces of string1 and copy the result to string2.
char * remove_blank_spaces(char * string1) {
char * string2 = malloc(sizeof(string1));
int index = 0;
for (int i = 0; string1[i] != 0; i++) {
if(string1[i] != ' ') {
//printf("i: %d\n", i);
//printf("c2: %c\n", string1[i]);
string2[index] = string1[i];
index++;
}
}
string2[index] = '\0';
printf("string2: %s\n", string2);
return string2;
}
I check the result with:
assert(remove_blank_spaces("a b") == "ab"); // Edit: here is the error!
I got an error: Assertion failed! and Expression: remove_blank_spaces("a b") == "ab"
I compared the strings in Virtual-C and they look the same.
Why the assertion is failing?
Your code has a bug: malloc allocates insufficient space, and this results in undefined behaviour when trying to access unallocated memory.
The assertion is also failing because you are comparing pointers via ==, instead of C strings via strcmp.
Furthermore, I suggest making two changes:
Don’t mix computation and output. Return the value, don’t printf it inside the function.
Use descriptive and correct names. This requires taking context into account. For instance, index can generally be a good name, but in your case it’s unclear which index you’re referring to, and this invites errors, where index is used to index into the wrong variable. As for “correct” names, what you call “blank space” is more conventionally known as “whitespace”.
To improve the second point, I suggest actually changing the implementation and, instead of having a second index variable, to iterate over the output using a pointer. There are other possibilities, but this one has the advantage that accidentally indexing using the wrong variable is impossible.
Taking this together, we get
char *remove_whitespace(const char *str) {
char *result = malloc(strlen(str) + 1);
char *out = result;
for (size_t i = 0; str[i] != '\0'; i++) {
if (str[i] != ' ') {
*out++ = str[i];
}
}
*out = '\0';
return result;
}
We could additionally do away with the i loop counter. Unfortunately the result is less readable, not more, because we would need to increment str at the end of the loop, and this would leave us with an unsightly for (; *str != '\0'; str++) loop construct.
For starters this function declaration
char * remove_blank_spaces(char * string1) {
is incorrect and only confuses users of the function. If within the function you are creating a new character array then the parameter shall have the qualifier const.
char * remove_blank_spaces( const char * string1) {
Otherwise the function should change the original string "in-place".
This call
char * string2 = malloc(sizeof(string1));
also is incorrect. I think you mean
char * string2 = malloc( strlen( string1 ) + 1 );
But even this call is not very good because the result string can be much less than the original string.
So at first you should count the numb er of characters in the result string and only then allocate the memory.
This assert is also incorrect
assert(remove_blank_spaces("a b") == "ab");
In this expression there are compared addresses of two string: the first one is the string returned by the function and the second one is the string literal.
Even if you will write an expression like this
assert( "ab" == "ab");
the value of the expression can be equal either to logical true or false depending on the compiler option that specifies whether equal string literals are stored as one string literal or occupy different extents of memory.
You should write instead
assert( strcmp( remove_blank_spaces("a b"), "ab" ) == 0 );
Take into account that it is reasonable also to consider trhe tab character '\t' in the if statement like
if(string1[i] != ' ' && string1[i] != '\t') {
Or you could use the standard function isblank.
Here is a demonstrative program
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( const char *s )
{
size_t n = 0;
for ( size_t i = 0; s[i] != '\0'; i++ )
{
if ( !isblank( ( unsigned char )s[i] ) ) ++n;
}
char *result = malloc( n + sizeof( ( char )'\0' ) );
char *p = result;
do
{
if ( !isblank( ( unsigned char )*s ) )
{
*p++ = *s;
}
} while ( *s++ != '\0' );
return result;
}
int main(void)
{
const char *s1 = "a b";
char *s2 = remove_blank_spaces( s1 );
assert( strcmp( s1, s2 ) == 0 );
puts( s2 );
free( s2 );
return 0;
}
The program output is
ab
Pay attention to that instead of the type int as it is shown in other answers you should use the type size_t for the variables index and i because it is the type that is used with string lengths and indices and by the function malloc. The type int is not large enough to store size of strings.
If you indeed want to declare the function like
char * remove_blank_spaces( char *s )
that is when the parameter does not have the qualifier const then you shall not allocate dynamically a new character array within the function and the function itself can look much simpler.
Here is a demonstrative program.
#include <stdio.h>
#include <assert.h>
#include <string.h>
char * remove_blank_spaces( char *s )
{
char *destination = s;
char *source = s;
do
{
if ( *source != ' ' && *source != '\t' )
{
*destination++ = *source;
}
} while ( *source++ != '\0' );
return s;
}
int main(void)
{
char s[] = "a b";
remove_blank_spaces( s );
assert( strcmp( s, "ab" ) == 0 );
puts( s );
return 0;
}
Its output is
ab

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;
}

C Program that stores an unknown number of strings of unknown sizes from user - Heap block at 00558068 modified at 00558096 past requested size of 26

I'm trying to write a program in C that takes an unknown number of strings (each of unknown size) from the user as input and stores them then prints them when the user has finished entering strings.
First I use a pointer that points to character pointers (char** strings) and allocate 1 char* sized block of memory to it with malloc. I then allocate 1 char sized block to the pointer that strings is pointing to ( (strings) ) with malloc also. From there I take a string input from the user using a user-defined function called get_String() and place it into the char pointer that char* string is pointing to. I then use a for loop to continue allocating an extra char* of memory to char** strings and 1 char of memory to the new pointer.
However, I keep experiencing an error on the 2nd iteration of the for loop, on the line strings = (char**) realloc (strings, index+1); and I receive the error message: Heap block at 00558068 modified at 00558096 past requested size of 26. It seems like I am writing past the allocated memory to char** strings, but I don't know where or how I am doing this.
Here is my entire code:
#include "stdio.h"
#include "string.h"
#include "stdlib.h"
void get_strings( char* strings); // function to take in a string of an unknown size
int main( void )
{
char** strings;
int index, count;
(strings) = (char**) malloc( sizeof(char*)); // this is the pointer that holds the addresses of all the strings
*strings = (char*) malloc( sizeof(char)); // the address of the first string
printf( "Enter a list of stringss. Quit by pressing enter without entering anything\n" );
get_strings( *strings );
for( index = 1; strlen(*(strings+index-1))!=1; index++) // stores strings from the user until they enter a blank line which is detected when string length is 1 for the \n
{
strings = (char**) realloc (strings, index+1); // adds an extra character pointer for another string
*(strings + index) = (char*) malloc(sizeof(char)); // allocates memory to the new character pointer
get_strings( *(strings + index) ); // stores the string from the user
}
printf( "You entered:\n" );
for( count = 0; strlen(*(strings + count)) != 1; count++ ) //prints every string entered by the user except for the terminating blank line
{
printf( "%s", *(strings + count ) );
free( *(strings + count ));
}
free( strings );
system( "PAUSE" );
return 0;
}
void get_strings( char* strings )
{
fgets( strings, 1, stdin );
while( strings[ strlen( strings ) - 1 ] != '\n' )
{
strings = (char*) realloc( strings, strlen(strings)+2 );
fgets( strings + strlen(strings), 2, stdin );
}
}
As stated before, heap block occurs on the second iteration of the for loop while executing the line: strings = (char**) realloc (strings, index+1);
for( index = 1; strlen(*(strings+index-1))!=1; index++) // stores strings from the user until they enter a blank line which is detected when string length is 1 for the \n
{
strings = (char**) realloc (strings, index+1); // error occurs here
*(strings + index) = (char*) malloc(sizeof(char)); // allocates memory to the new character pointer
I would very much appreciate it if someone could explain to me the cause of this error and a direction to fix it. Thank you.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *get_strings(void);
int main( void ){
char **strings = NULL;
char *string;
int index, count;
printf( "Enter a list of stringss. Quit by pressing enter without entering anything\n" );
for(index = 0; string = get_strings(); ++index){
strings = (char**) realloc (strings, (index+1)*sizeof(*strings));
strings[index] = string;
}
printf( "You entered:\n" );
for( count = 0; count < index; ++count ){
printf("%s\n", strings[count]);
free( strings[count] );
}
free( strings );
system( "PAUSE" );
return 0;
}
char *get_strings(void){
char *string = NULL;//or calloc(1,sizeof(char)) for return "" (test by caller: *string == '\0')
int ch;
size_t len = 0;
while(EOF != (ch=fgetc(stdin)) && ch != '\n' ) {
string = (char*)realloc( string, len + 2);//To realloc to each character is inefficient
string[len++] = ch;
}
if(string)
string[len] = '\0';
return string;//The value is NULL when there is no input substantial.
}

Resources