What's the cause of my segmentation fault? - c

I'm trying to write a program that reads in entries from a file into a dynamically allocated array of structures using input redirection. My program compiles fine but I'm getting a segmentation fault and I'm having trouble finding the cause.
Here's my Program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct StudentData
{
char* name;
char* major;
double gpa;
} Student;
int main()
{
Student* data = (Student*)malloc(sizeof(Student)*5);
int i;
for(i = 0; i < 5; i++)
{
// allocate memory for name and read input
data[i].name = malloc(50);
*(data+i)->name == scanf("%s", (char*)&data[i].name);
// allocate memory for major and read input
data[i].major = malloc(30);
*(data+i)->major == scanf("%s", (char*)&data[i].major);
// read input for gpa
(data+i)->gpa == scanf("%lf", &data[i].gpa);
//print array
printf("%s\n%s\n%f\n", data[i].name, data[i].major, data[i].gpa);
}
}
Any clues? If it seems obvious it's because I'm relatively new to C!

This line:
*(data+i)->name == scanf("%s", (char*)&data[i].name);
Ignoring the weird and void == for a moment, &data[i].name is wrong since you're taking an address of a pointer. data[i].name would be sufficient here, since the name field is already an address scanf can write into.
And the cast to (char*) is what probably shuts the compiler up about it - did you enter it for this purpose :-) ? Because &data[i].name has the type char**, which scanf wouldn't accept, unless you forcefully casted it to char*.
As a general advice, try to avoid scanf - it leads to very unsafe code (as you've just seen!) Instead, use fgets to read a line (from the standard input too) and then break this line into its constituents. This may initially take a bit more code to implement, but leads to much safer and more predictable code.

*(data+i)->name == scanf("%s", (char*)&data[i].name);
What are you comparing the return value of scanf for? Just remove the first part. Also, data[i].name is already a pointer, so you shouldn't take the address once again. It should just be:
scanf("%s", data[i].name); // no & because name is already a pointer
And similarly:
scanf("%s", data[i].major);
scanf("%lf", &data[i].gpa); // & here because gpa is just a double

There is some unnecessary code being used with scanf, like *(data+i)->name ==. That doesn't do anything useful (and is probably causing the segfault). If it weren't causing access errors, it would compare the return value of scanf with the pointer and then ignore the result of the comparison. (A decent compiler would have warned about this.)
After getting rid of the excess code, it will be technically okay, except there is nothing to prevent buffer overrun. That's done either by controlling the input data, or adding limits to the lengths of the strings, like with scanf("%50s", data[i].name);

&data[i].name and &data[i].major are of type char **, so you cannot safely cast it to char *.
Losing the ampersand will correct your error.
There are also other logical errors with the use of scanf(), but that's probably overwhelming - it'd be nice if you revisited this code once you entered a name of more than 50 characters.

Related

How to return a string to main function?

I am trying to write code to implement strchr function in c. But, I'm not able to return the string.
I have seen discussions on how to return string but I'm not getting desired output
const char* stchr(const char *,char);
int main()
{
char *string[50],*p;
char ch;
printf("Enter a sentence\n");
gets(string);
printf("Enter the character from which sentence should be printed\n");
scanf("%c",&ch);
p=stchr(string,ch);
printf("\nThe sentence from %c is %s",ch,p);
}
const char* stchr(const char *string,char ch)
{
int i=0,count=0;
while(string[i]!='\0'&&count==0)
{
if(string[i++]==ch)
count++;
}
if(count!=0)
{
char *temp[50];
int size=(strlen(string)-i+1);
strncpy(temp,string+i-1,size);
temp[strlen(temp)+1]='\0';
printf("%s",temp);
return (char*)temp;
}
else
return 0;
}
I should get the output similar to strchr function but output is as follows
Enter a sentence
i love cooking
Enter the character from which sentence should be printed
l
The sentence from l is (null)
There are basically only two real errors in your code, plus one line that, IMHO, should certainly be changed. Here are the errors, with the solutions:
(1) As noted in the comments, the line:
char *string[50],*p;
is declaring string as an array of 50 character pointers, whereas you just want an array of 50 characters. Use this, instead:
char string[50], *p;
(2) There are two problems with the line:
char *temp[50];
First, as noted in (1), your are declaring an array of character pointers, not an array of characters. Second, as this is a locally-defined ('automatic') variable, it will be deleted when the function exits, so your p variable in main will point to some memory that has been deleted. To fix this, you can declare the (local) variable as static, which means it will remain fixed in memory (but see the added footnote on the use of static variables):
static char temp[50];
Lastly, again as mentioned in the comments, you should not be using the gets function, as this is now obsolete (although some compilers still support it). Instead, you should use the fgets function, and use stdin as the 'source file':
fgets(string, 49, stdin);/// gets() has been removed! Here, 2nd argument is max length.
Another minor issue is your use of the strlen and strncpy functions. The former actually returns a value of type size_t (always an unsigned integral type) not int (always signed); the latter uses such a size_t type as its final argument. So, you should have this line, instead of what you currently have:
size_t size = (strlen(string) - i + 1);
Feel free to ask for further clarification and/or explanation.
EDIT: Potential Problem when using the static Solution
As noted in the comments by Basya, the use of static data can cause issues that can be hard to track down when developing programs that have multiple threads: if two different threads try to access the data at the same time, you will get (at best) a "data race" and, more likely, difficult-to-trace unexpected behaviour. A better way, in such circumstances, is to dynamically allocate memory for the variable from the "heap," using the standard malloc function (defined in <stdlib.h> - be sure to #include this header):
char* temp = malloc(50);
If you use this approach, be sure to release the memory when you're done with it, using the free() function. In your example, this would be at the end of main:
free(p);

How to read a sentence of char pointer with scanf()?

I have a char pointer: char *sentences; and I read it like this:
#include <stdio.h>
int main() {
char *sentences;
sentences="We test coders. Give us a try?";
printf("%s", sentences);
return 0;
}
but I want to read with scanf() function in c.
scanf("%[^\n]s",S); or scanf("%s",S); didn't work.
How can I do that?
Are you declaring the variable char *sentences; and immediately trying to write to it with scanf? That's not going to work.
Unless a char * pointer is pointing to an existing string, or to memory allocated with malloc-family functions, assigning to it with scanf or similar is undefined behavior:
char *sentences;
scanf("%s", sentences); // sentences is a dangling pointer - UB
Since you haven't actually shared your code that uses scanf and doesn't work, I can only assume that's the problem.
If you want to assign a user-supplied value to a string, what you can do is declare it as an array of fixed length and then read it with a suitable input function. scanf will work if used correctly, but fgets is simpler:
char sentence[200];
fgets(sentence, 200, stdin);
// (User inputs "We test coders. Give us a try")
printf("%s", sentence);
// Output: We test coders. Give us a try.
Also, never, ever use gets.

I don't know what's going wrong with this code?

I am writing a simple code which accepts a string from the user of any length and just displays it. But my code is not doing it correctly as it accepts the string but not prints it correctly.
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
main()
{
int i,len;
static int n=5;
char a[20];
char **s;
s=malloc(5*sizeof(char));
char *p;
for(i=0;i<n;i++)
{
scanf("%s",a);
if(*a=='1') /*to exit from loop*/
{
break;
}
len=strlen(a);
p=malloc((len+1)*sizeof(char));
strcpy(p,a);
s[i]=p;
if(i==n-1)
{
s=realloc(s,(5+i*5)*sizeof(char));
n=5+i;
}
}
for(i=0;i<n-1;i++)
{
printf("%s ",s[i]);
}
free(p);
p=NULL;
return 0;
}
There are multiple issues, but at first look, the most prominent one is,
s=malloc(5*sizeof(char));
is wrong. s is of type char **, so you'd need to allocate memory worth of char * there. In other words, you expect s to point to a char * element, so, you need to allocate memory accordingly.
To avoid these sort of mistakes, never rely on hardcoded data types, rather, use the form
s = malloc( 5 * sizeof *s); // same as s=malloc( 5 * sizeof (*s))
where, the size oid essentially determined from the type of the variable. Two advantages
You avoid mistakes like above.
The code becomes more resilient, you don;t need to change the malloc() statement in case you choose to change the data type
That said, scanf("%s",a); is also potentially dangerous and cause buffer overflow by longer-than-expected-input. You should always limit the input scanning length, using the maximum field width, like
scanf("%19s",a); // a is array of dimension 20, one for terminating null
That said, to advice about the logic, when you don't know or don't dictate the length of the input string beforehand, you cannot use a string type to scan the input. The basic way of getting this done would be
Allocate a moderate length buffer, dynamically, using allocator functions like malloc().
Keep reading the input stream one by one, fgetc() or alike.
If the read is complete (for example, return of EOF), you've read the complete input.
If the allocated memory has run out, re-allocate the original buffer and continue to step 3.
and, don't forget to free() the memory.
Otherwise, you may use fgets() to read chunks of memory and keep realloacting as mentioned above.

Char arrays and scanf function in C

I expected to get errors in following code, but I did not. I did not use & sign. Also I am editing array of chars.
#include <stdio.h>
int main()
{
char name[10] ="yasser";
printf("%s\n",name);
// there is no error ,
// trying to edit array of chars,
// also did not use & sign.
scanf("%s",name);
// did not use strcpy function also.
printf("%s\n",name);
return 0;
}
I expected to get errors in following code, but I did not.I did not use & sign.
scanf("%s",name);
That's totally ok as name is already the address of the character array.
It sounds like you have several questions:
calling scanf("%s", name) should have given an error, since %s expects a pointer and name is an array? But as others have explained, when you use an array in an expression like this, what you always get (automatically) is a pointer to the array's first element, just as if you had written scanf("%s", &name[0]).
Having scanf write into name should have given an error, since name was initialized with a string constant? Well, that's how it was initialized, but name really is an array, so you're free to write to it (as long as you don't write more than 10 characters into it, of course). See more on this below.
Characters got copied around, even though you didn't call strcpy? No real surprise, there. Again, scanf just wrote into your array.
Let's take a slightly closer look at what you did write, and what you didn't write.
When you declare and initialize an array of char, it's completely different than when you declare and initialize a pointer to char. When you wrote
char name[10] = "yasser";
what the compiler did for you was sort of as if you had written
char name[10];
strcpy(name, "yasser");
That is, the compiler arranges to initialize the contents of the array with the characters from the string constant, but what you get is an ordinary, writable array (not an unwritable, constant string constant).
If, on the other hand, you had written
char *namep = "yasser";
scanf("%s", namep);
you would have gotten the problems you expected. In this case, namep is a pointer, not an array. It's initialized to point to the string constant "yasser", which is not writable. When scanf tried to write to this memory, you probably would have gotten an error.
When you pass arrays to functions in C, they decay to pointers to the first item.
Therefore for:
char name[] ="yasser";
scanf("%s", name) is the same as scanf("%s", &name[0]) and either of those invocations should send shivers down your spine, because unless you control what's on your stdin (which you usually don't), you're reading a potentially very long string into a limited buffer, which is a segmentation fault waiting to happen (or worse, undefined behavior).
#include <stdlib.h>
#include <stdio.h>
int main(int argc, char **argv, char **envp) {
char *myName = (char *) calloc(10, sizeof(char));
*(myName)='K'; *(myName+1)='h'; *(myName+2)='a'; *(myName+3)='l'; *(myName+4)='i'; *(myName+5)='d';
printf("%s\n",myName);
scanf("%s",myName);
printf("%s\n",myName);
return (EXIT_SUCCESS);
}
#include <stdio.h>
#include <string.h>
int main()//fonction principale
{
char name[10] ="yasser";
int longeur=0;
printf("%s\n",name);
scanf("%s",name);
longeur = strlen(name);
for (int i=0;i<longeur;i++) {
printf("%c",*(name+i));
}
return 0;}

Malloc and scanf

I'm fairly competent in a few scripting languages, but I'm finally forcing myself to learn raw C. I'm just playing around with some basic stuff (I/O right now). How can I allocate heap memory, store a string in the allocated memory, and then spit it back out out? This is what I have right now, how can I make it work correctly?
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *toParseStr = (char*)malloc(10);
scanf("Enter a string",&toParseStr);
printf("%s",toParseStr);
return 0;
}
Currently I'm getting weird output like '8'\'.
char *toParseStr = (char*)malloc(10);
printf("Enter string here: ");
scanf("%s",toParseStr);
printf("%s",toParseStr);
free(toParseStr);
Firstly, the string in scanf is specifies the input it's going to receive. In order to display a string before accepting keyboard input, use printf as shown.
Secondly, you don't need to dereference toParseStr since it's pointing to a character array of size 10 as you allocated with malloc. If you were using a function which would point it to another memory location, then &toParseStr is required.
For example, suppose you wanted to write a function to allocate memory. Then you'd need &toParseStr since you're changing the contents of the pointer variable (which is an address in memory --- you can see for yourself by printing its contents).
void AllocateString(char ** ptr_string, const int n)
{
*ptr_string = (char*)malloc(sizeof(char) * n);
}
As you can see, it accepts char ** ptr_string which reads as a pointer which stores the memory location of a pointer which will store the memory address (after the malloc operation) of the first byte of an allocated block of n bytes (right now it has some garbage memory address since it is uninitialized).
int main(int argc, char *argv[])
{
char *toParseStr;
const int n = 10;
printf("Garbage: %p\n",toParseStr);
AllocateString(&toParseStr,n);
printf("Address of the first element of a contiguous array of %d bytes: %p\n",n,toParseStr);
printf("Enter string here: ");
scanf("%s",toParseStr);
printf("%s\n",toParseStr);
free(toParseStr);
return 0;
}
Thirdly, it is recommended to free memory you allocate. Even though this is your whole program, and this memory will be deallocated when the program quits, it's still good practice.
You need to give scanf a conversion format so it knows you want to read a string -- right now, you're just displaying whatever garbage happened to be in the memory you allocated. Rather than try to describe all the problems, here's some code that should at least be close to working:
char *toParseStr = malloc(10);
printf("Enter a string: ");
scanf("%9s", toParseStr);
printf("\n%s\n", toParsestr);
/* Edit, added: */
free(toParseStr);
return 0;
Edit: In this case, freeing the string doesn't make any real difference, but as others have pointed out, it is a good habit to cultivate nonetheless.
Using scanf() (or fscanf() on data you don't control) with a standard "%s" specifier is a near-certain way to get yourself into trouble with buffer overflows.
The classic example is that it I enter the string "This string is way more than 10 characters" into your program, chaos will ensue, cats and dogs will begin sleeping together and a naked singularity may well appear and consume the Earth (most people just state "undefined behaviour" but I think my description is better).
I actively discourage the use of functions that cannot provide protection. I would urge you (especially as a newcomer to C) to use fgets() to read your input since you can control buffer overflows with it a lot easier, and it's more suited to simple line input than scanf().
Once you have a line, you can then call sscanf() on it to your heart's content which, by the way, you don't need to do in this particular case since you're only getting a raw string anyway.
I would use:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define BUFFSZ 10
int main(int argc, char *argv[]) {
char *toParseStr = malloc(BUFFSZ+2);
if (toParseStr == NULL) {
printf ("Could not allocate memory!\n");
return 1;
}
printf ("Enter a string: ");
if (fgets (toParseStr, BUFFSZ+2, stdin) == NULL) {
printf ("\nGot end of file!\n");
return 1;
}
printf("Your string was: %s",toParseStr);
if (toParseStr[strlen (toParseStr) - 1] != '\n') {
printf ("\nIn addition, your string was too long!\n");
}
free (toParseStr);
return 0;
}
You don't need an & before toParseStr in scanf as it is already a pointer
also call free(toParseStr) afterwards
First, the errors that was keeping your program from working: scanf(3) takes a format-string, just like printf(3), not a string to print for the user. Second, you were passing the address of the pointer toParseStr, rather than the pointer toParseStr.
I also removed the needless cast from your call to malloc(3).
An improvement that your program still needs is to use scanf(3)'s a option to allocate memory for you -- so that some joker putting ten characters into your string doesn't start stomping on unrelated memory. (Yes, C will let someone overwrite almost the entire address space with this program, as written. Giant security flaw. :)
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
char *toParseStr = malloc(10);
printf("Enter a short string: ");
scanf("%s",toParseStr);
printf("%s\n",toParseStr);
return 0;
}

Resources