Segmentation Fault caused by realloc? - c

Hei, I was trying to solve this school exercise..
Write a program that keeps reading in character strings and concatenates them (adds them to a single character string). the concatenation should take place in a function that returns 1 if successful or 0 if it fails. for memory allocation use only realloc!
I don't receive any error while debugging the program, but when I try to run the program, after I insert the string the only thing that appears is "Segmentation Fault", what could it be? This is the code:
#include<stdlib.h>
#include<stdio.h>
#include<string.h>
int cat(char **, char *);
int main(void)
{
char string[51];
char *output=NULL;
char choice;
do
{
printf("Please enter a string [<50 chars]: ");
fgets(string,50,stdin);
if(string[strlen(string)-1]=='\n') /* if newline was read as well */
string[strlen(string)-1]=0; /* discard it */
if(cat(&output,string))
printf("\n\nThe string now contains:\n%s\n",output);
else
{
printf("error: memory (re-)allocation failed!\n\n");
return 1; /* exit with error */
}
printf("Continue? (y/n) - ");
fgets(string,3,stdin); /* read input from keyboard - leave a safety buffer to account for read newline */
choice=string[0]; /* use the first character from the previous read as the choice */
} while(choice=='y' || choice=='Y');
free(output);
return 0;
}
int cat(char **dest, char *src)
{
int i;
int length1=strlen(src);
int length2=strlen(*dest);
int length3=length1+length2;
*dest=(char*)realloc(NULL,sizeof(*src));
printf("%p", *dest);
if(*dest==NULL) return 0; /* if allocation failed */
for(i=0;i<=length3;i++)
{
if(i<=length1)
(*dest)[i]=(*dest)[i];
else
(*dest)[i]=(src)[i];
}
free(src);
return 1;
}

There are at least 5 issues with your code:
1) You should free only what you allocated yourself on the heap. Don't free(src) because what you pass in src points to stack memory (char string[51]; is freed automatically).
2) you probably meant to reallocate dest, and 3) you meant to allocate memory the size of length3 (+1 null-terminator).
*dest=(char*)realloc(*dest, length3 + 1);
4) strlen(*dest) will crash when *dest is NULL initially.
int length2=(*dest)?strlen(*dest):0;
5) I don't think your for-loop is correct. It won't concatenate the strings, your offset calculation is off.

The initial value of the pointer output is NULL. However inside the function there is no check that the pointer is equal to NULL. So applying the function strlen to the pointer results in undefined behavior.
Also you need to reserve one more character for the terminating zero.
The memory is not reallocated correctly in the function. Moreover sizeof( *src ) is equal to one byte.
This statement
if(i<=length1)
(*dest)[i]=(*dest)[i];
does not make great sense. The reallocated memory provided that it was reallocated correctly already contains the original string.
You shall not free the pointer src because it does not point to a dynamically allocated memory.
The function can look the following way as it is shown in the demonstrative program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int cat( char **dest, const char *src )
{
size_t n = strlen( src ) + ( *dest == NULL ? 0 : strlen( *dest ) );
char *tmp = realloc( *dest, n + 1 );
int success = tmp != NULL;
if ( success )
{
if ( *dest == NULL ) *tmp = '\0';
*dest = tmp;
while ( *tmp ) ++tmp;
while ( ( *tmp++ = *src++ ) );
}
return success;
}
#define N 50
int main(void)
{
char *output = NULL;
char choice = 'n';
do
{
char string[N];
printf( "Please enter a string [<%d chars]: ", N );
fgets( string, sizeof( string ),stdin );
string[strcspn( string, "\n" )] = '\0';
if ( cat( &output, string ) )
{
printf( "\nThe string now contains:\n\"%s\"\n\n", output );
}
else
{
printf( "error: memory (re-)allocation failed!\n\n" );
return 1; /* exit with error */
}
printf( "Continue? (y/n) - " );
fgets( string, 3, stdin ); /* read input from keyboard - leave a safety buffer to account for read newline */
choice = string[0]; /* use the first character from the previous read as the choice */
} while ( choice == 'y' || choice == 'Y' );
free( output );
return 0;
}
Its output might look like
Please enter a string [<50 chars]: Hi Stefano Feltre
The string now contains:
"Hi Stefano Feltre"
Continue? (y/n) - y
Please enter a string [<50 chars]:
The string now contains:
"Hi Stefano Feltre "
Continue? (y/n) - y
Please enter a string [<50 chars]: Let's learn C
The string now contains:
"Hi Stefano Feltre Let's learn C"
Continue? (y/n) - n

Related

Why I can't print first/last character right?

My output string needs to be palindrom of the input string. It works almost perfect but I am having problem with first character of the input string, my new string won't print it (in essence my new string won't print last character). Also strrev() does not work on Ubuntu so i need to do this without using that function.
#include <stdio.h>
#include <string.h>
int main(void){
int i,j=0;
char input_str[10];
char new_str[10];
printf("Enter characters:\n");
gets(input_str);
for(i=strlen(input_str)-1;i>0;i--){
new_str[j]=input_str[i];
j++;
}
printf("Output:\n");
printf("%s", new_str);
return 0;
}
For starters the function gets is unsafe function and is not supported by the C Standard.
You should use the standard C function fgets.
There are two problems with your code. The first one is in this loop
for(i=strlen(input_str)-1;i>0;i--){
new_str[j]=input_str[i];
j++;
}
the index i equal to 0 is skipped from using it to copy the corresponding character of the source string.
The second one is the destination array is not appended with the terminating zero character.
Here is a demonstrative program that shows how a function that makes a reversed copy of a source string can be implemented.
#include <stdio.h>
#include <string.h>
char * reverse_copy( char *dsn, const char *src )
{
size_t n = strlen( src );
size_t i = 0;
for ( ; i < n; i++ )
{
dsn[i] = src[n - i - 1];
}
dsn[i] = '\0';
return dsn;
}
int main(void)
{
enum { N = 10 };
char input_str[N] = "";
char new_str[N];
printf( "Enter a string (less than %zu symbols): ", ( size_t )N );
fgets( input_str, N, stdin );
input_str[ strcspn( input_str, "\n" ) ] = '\0';
printf( "\"%s\"\n", input_str );
printf( "\"%s\"\n", reverse_copy( new_str, input_str ) );
return 0;
}
The program output might look for example the following way
Enter a string (less than 10 symbols): Hello
"Hello"
"olleH"
i is never zero. That is why the first character (input_str[0]) is ignored.

Getting "Segmentation fault Core Dumped Error " while reversing a string

I am trying to learn C, I am getting this error while reversing a string. I am not well versed with memory allocation stuffs, can you please point out where I am doing wrong.
#include<stdio.h>
#include<string.h>
char* strrev(char *str);
int main()
{
char str[1000];
char *str1;
printf("Please enter a String\n");
gets(str);
str1=strrev(str);
puts(str1);
}
char* strrev(char *str)
{
char *str1;
int i,length,c;
length=strlen(str);
for (i=length-1;i<=0;i--)
{
*(str1+c) = *(str+i);
c++;
}
*(str1+c) ='\0';
return str1;
}
for (i=length-1;i<=0;i--)
This will never (unless string is 0 or 1 character long) run due to i<=0, should be i>=0 probably.
Also in general you need to make pointer point to some valid memory in order to be able to dereference it. In your case you should probably use malloc for allocating sufficient number of bytes, and assign its result to str1. Then you can write to it as you are doing.
Inside your strrev() function, str1 is not allocated memory. Hence, *(str1+c) = *(str+i); is UB.
Then, c is an automatic local variable which is not initialized before use. Also UB.
Next, as Giorgi mentioned, correct your for loop.
That said, don't use gets(), it suffers from buffer overrun issues. Use fgets() instead.
What you are trying to do is not reversing a string. Neither string is reversed in your program. You are trying to copy one string in another string in the reverse order.
So the sring where you are going to copy the original string in the reverse order shall have enough space to store the copied characters.
However in your function
char* strrev(char *str)
{
char *str1
//...
pointer str1 was not initialized and does not point to an extent of memory of the appropriate size.
So your program has undefined behaviour.
Take also in account that function gets is unsafe and is not supported by the C standard any more. Use function fgets instead.
If your compiler supports Variable Length Arrays (VLA) then the program can look the following way
#include <stdio.h>
#include <string.h>
char * reverse_copy( char *s2, const char *s1 )
{
size_t n = strlen( s1 );
size_t i = 0;
for ( ; i < n; i++ ) s2[i] = s1[n-i-1];
s2[i] = '\0';
return s2;
}
int main( void )
{
char s1[1000];
printf( "Please enter a String: " );
fgets( s1, sizeof( s1 ), stdin );
size_t n = strlen( s1 );
if ( n != 0 && s1[n-1] == '\n' ) s1[n-1] = '\0';
char s2[n + 1];
puts( s1 );
puts( reverse_copy( s2, s1 ) );
return 0;
}
If to enter for example
Hello Asfakul Islam
then the output will look like
Please enter a String: Hello Asfakul Islam
Hello Asfakul Islam
malsI lukafsA olleH
Otherwise if your compiler does not support VLA(s) you need to dynamically allocate an array of the appropriate size. In this case the program can look for example the following way
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char * reverse_copy( char *s2, const char *s1 )
{
size_t n = strlen( s1 );
size_t i = 0;
for ( ; i < n; i++ ) s2[i] = s1[n-i-1];
s2[i] = '\0';
return s2;
}
int main( void )
{
char s1[1000];
printf( "Please enter a String: " );
fgets( s1, sizeof( s1 ), stdin );
size_t n = strlen( s1 );
if ( n != 0 && s1[n-1] == '\n' ) s1[n-1] = '\0';
char *s2 = malloc( ( n + 1 ) * sizeof( char ) );
puts( s1 );
puts( reverse_copy( s2, s1 ) );
free( s2 );
return 0;
}
You don't initialize str1 in strrev()
If you enter string whose length is 2 characters or more, i<=0 is false and the block inside the for loop won't be executed.
*(str1+c) ='\0'; will cause the crash because the value of str1 is indeterminate and you have few chance that str1 points some valid place.
UPD: c in strrev() is also uninitialized, and it will cause some trouble.
In your function, you did not initialize c
int i,length,c;
and are using it inside the for loop
*(str1+c) = *(str+i);
Plus other problems are there..
1) str1 inside the function is not allocated memory.
2) This loop will never get executed, as the condition in for (i=length-1;i<=0;i--) is never true (unless string is 0 or 1 character long).
3) Do not use gets(), it is deprecated. Instead use fgets()

What will cause fgets() to continuously wait for input?

I am trying to put together a program that will ask the user to enter song titles for a set list to be printed in a random order. The program uses fgets() to take in the song titles. It also uses memory allocation to put each song in. It is similar to:
argv[0] = song1, argv[1] = song2, argv[2] = song3 (etc.)
The problem I am running into is when the program is executed fgets() waits continuously for input, when it is only a total of five songs to be entered. I want to know what will cause fgets() to continuously wait for input? Thanks for your help.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <string.h>
int main( void ){
printf("\tPlease enter the songs you want to play!\n");
printf("\tI will create the set list and randomize it for you!\n");
printf("\t(enter all songs with a space in between each and then press 'enter')\n");
int s = 5; //Here we declare number of songs to be entered
int set = 5;//This is set list size
char input[100];
char *argv[ s ];
char *token;
/* get the input and perform memory allocation */
fgets(input, s, stdin);
token = strtok(input, " ");
int i=0;
while( token != NULL ) {
argv[i] = malloc(strlen(token) + 1);
memcpy(argv[i], token, strlen(token)+1);
i++;
token = strtok(NULL, " ");
}
argv[i] = NULL; //argv ends with NULL
unsigned int setList[ s ];
memset( setList, 0, s*sizeof(unsigned int) );//Must manually initalize array
srand( time( NULL ) ); // seed random-number generator
/*Begin Randomize Code*/
size_t column;
size_t c;
size_t c1;
size_t column1;
for ( c = 1; c <= set; ++c ) {
do {
column = rand() % s;
} while( setList[ column ] != 0 );
setList[ column ] = c;
}//end of for
/*End Randomize Code*/
/*Begin Code to Print SetList*/
for ( c1 = 1; c1 <= set; ++c1 ) {
for ( column1 = 0; column1 < s; ++column1 ) {
if ( setList[ column1 ] == c1 ) {
printf( "%s\n", argv[ column1 ]);
}//end of for (oops if)
}//end of for
}//end of if (oops for)
/*End Code to Print SetList*/
}//end of main
Actually, the problem is right here:
fgets(input, s, stdin); <-- you tell fgets to only read 5 characters (actually only 4, the fifth character is the null terminator); this means that your tokenization will fail and not allocate memory for all five elements of argv, causing an access violation in your printf call later.
Change it to:
fgets(input, sizeof(input), stdin); and you get this:
Some other problems with the code:
argv is, by convention, the name of the tokenized command line string passed to your main function, so don't name your variables like this, it's confusing
instead of using a variable for things like the maximum size of something, use #define, e.g. #define MAX_SONGS 5
it is very buggy and will crash if bad input is given, you should validate it (for instance, if I enter more than 5 songs, you'll overflow your buffer and most likely crash)

Segmentation fault caused by fgets() in C

This is a problem I don't understand - I am using fgets() in main and it works. I use it (I think) in exactly the same way in a function and I get an error [Segmentation fault core dumped -- exit code 139).
This code is based on a sample program in book Ivor Horton's "Beginning C" (it's a old tile but I'm just wanting to learn the basics from it).
My program is as follows. I am working on *nix using Geany (basically, compiling with GCC). You can see that fgets works in main (output is the string you enter). But it doesn't work in the function str_in(). It gets as far as the second printf() statement to enter a string, no further. Note that in the book, Horton uses gets(). I am trying to implement a safer string input function here, but no joy.
By the way the program is supposed to sort strings stored in an array of string pointers.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define MAX_NUM_STRINGS 50
int str_in(char **); /*Pointer to a string pointer*/
void str_sort(char *[], int n); /*Array of pointers to strings, number of strings in array*/
void str_out (char *[], int n); /*Array of pointers to strings, number of strings in array*/
int main(){
char *pS[MAX_NUM_STRINGS] = { NULL }; /*Array of pointers to strings stored in str_space*/
int numStrings = 0; /*Count of strings*/
char buffer[BUFSIZ];
printf("Enter a string\n");
fgets(buffer, BUFSIZ, stdin);
printf("%s", buffer);
printf("fgets works here\n\n");
/* get string input from user - a pointer to each string is saved in pS */
while ( str_in(&pS[numStrings]) && numStrings < MAX_NUM_STRINGS)
numStrings++;
if ( numStrings > 0 ){
str_sort(pS, numStrings);
str_out(pS, numStrings);
}
return 0;
}
int str_in(char** pString){
char buffer[BUFSIZ];
char *p;
printf ("Enter string:\n");
fgets(buffer, 60, stdin);
printf("fgets doesn't work here!!\n");
if( buffer != NULL ){
printf("here");
if ((p = strchr(buffer, '\n')) != NULL)
*p = '\0'; /*replace newline with null character*/
else
return FALSE;
if ( strlen(buffer) > 0 ){
strcpy(*pString, buffer);
return TRUE;
}
else
return FALSE; /*blank line - end of input*/
}
else
return FALSE;
}
void str_sort(char* pStrings[], int n){
/*sort strings by manipulating array of string pointers*/
char *temp;
int sorted = FALSE;
int i = 0;
while (!sorted){
sorted = TRUE;
for(i = 0; i < n - 1; i++){
temp = pStrings[i];
if ( strcmp(temp, pStrings[i+1]) > 1 ){
pStrings[i] = pStrings[i+1];
pStrings[i+1] = temp;
sorted = FALSE;
break;
}
}
}
}
void str_out(char* pStrings[], int n){
/*print strings to standard output. Free memory as each string is printed */
int i = 0;
printf("Sorted strings:\n");
for(i = 0; i < n; i++){
printf("%s", pStrings[i]);
free(pStrings[i]);
}
}
The segmentation fault is not caused by fgets(), but by strcpy():
strcpy(*pString, buffer);
You try to write to *pString, but you never allocate memory for it. pS in main() is just an array of null pointers.
Another thing is with the test with if( buffer != NULL ), that would never be true since buffer is an array, not a pointer.
You must check for the return value of fgets to see if you have successfully received something, if not then you should never use your buffer as a string as you are not NUL terminating the buffer.
/* Checking for buffer != NULL is of no use */
/* as buffer will always be not NULL since */
/* since you have allocated it as char buffer[BUFSIZ] */
if (fgets(buffer, BUFSIZ, stdin) == NULL) {
/* buffer may not be a valid string */
}
So what you can do it to initialize the buffer to a NUL string, as soon as you enter the function (after your declarations are done
buffer[0] = 0; /* initialize to NUL string */
now you can use buffer as a string anywhere.
Also note than if BUFSIZ is too big greater than a couple of KB, then your might get seg fault due to stack overflow. If they are too big you could make buffer as "static char" instead of "char".

Building a basic shell, more specifically using execvp()

In my program I am taking user input and parsing it into a 2d char array. The array is declared as:
char parsedText[10][255] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
and I am using fgets to grab the user input and parsing it with sscanf. This all works as I think it should.
After this I want to pass parsedText into execvp, parsedText[0] should contain the path and if any arguments are supplied then they should be in parsedText[1] thru parsedText[10].
What is wrong with execvp(parsedText[0], parsedText[1])?
One thing probably worth mentioning is that if I only supply a command such as "ls" without any arguments it appears to work just fine.
Here is my code:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "308shell.h"
int main( int argc, char *argv[] )
{
char prompt[40] = "308sh";
char text[40] = "";
char parsedText[10][40] = {{""},{""},{""},{""},{""},
{""},{""},{""},{""},{""}};
// Check for arguments to change the prompt.
if(argc >= 3){
if(!(strcmp(argv[1], "-p"))){
strcpy(prompt, argv[2]);
}
}
strcat(prompt, "> ");
while(1){
// Display the prompt.
fputs(prompt, stdout);
fflush(stdout);
// Grab user input and parse it into parsedText.
mygetline(text, sizeof text);
parseInput(text, parsedText);
// Check if the user wants to exit.
if(!(strcmp(parsedText[0], "exit"))){
break;
}
execvp(parsedText[0], parsedText[1]);
printf("%s\n%s\n", parsedText[0], parsedText[1]);
}
return 0;
}
char *mygetline(char *line, int size)
{
if ( fgets(line, size, stdin) )
{
char *newline = strchr(line, '\n'); /* check for trailing '\n' */
if ( newline )
{
*newline = '\0'; /* overwrite the '\n' with a terminating null */
}
}
return line;
}
char *parseInput(char *text, char parsedText[][40]){
char *ptr = text;
char field [ 40 ];
int n;
int count = 0;
while (*ptr != '\0') {
int items_read = sscanf(ptr, "%s%n", field, &n);
strcpy(parsedText[count++], field);
field[0]='\0';
if (items_read == 1)
ptr += n; /* advance the pointer by the number of characters read */
if ( *ptr != ' ' ) {
strcpy(parsedText[count], field);
break; /* didn't find an expected delimiter, done? */
}
++ptr; /* skip the delimiter */
}
}
execvp takes a pointer to a pointer (char **), not a pointer to an array. It's supposed to be a pointer to the first element of an array of char * pointers, terminated by a null pointer.
Edit: Here's one (not very good) way to make an array of pointers suitable for execvp:
char argbuf[10][256] = {{0}};
char *args[10] = { argbuf[0], argbuf[1], argbuf[2], /* ... */ };
Of course in the real world your arguments probably come from a command line string the user entered, and they probably have at least one character (e.g. a space) between them, so a much better approach would be to either modify the original string in-place, or make a duplicate of it and then modify the duplicate, adding null terminators after each argument and setting up args[i] to point to the right offset into the string.
You could instead do a lot of dynamic allocation (malloc) every step of the way, but then you have to write code to handle every possible point of failure. :-)

Resources