fscanf on string causes segmentation fault - c

I have a three row input file.
First row is an int, the second row is ints with space, the third row is a string.
I have to scan them than manipulate the string based on the ints.
My problem is that I can scan the ints, but scanning the string causes segmentation fault at fclose.
My code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE* in = fopen("be.txt", "r");
FILE* out = fopen("ki.txt", "w");
if(in==NULL){
printf("Error opening in!\n");
return -1;
}
if(out==NULL){
printf("Error opening out!\n");
return -1;
}
int brknglength, i;
fscanf(in, "%d", &brknglength);
printf("%d\n", brknglength);
int* seed[brknglength];
seed[brknglength] = malloc(sizeof(int[brknglength]));
for(i = 0; i < brknglength; i++){
if (fscanf(in, "%d", &seed[i]) != 1) {
printf("%d", i);
}
printf("%d.: %d \n", i, seed[i]);
}
char string;
fscanf(in, "%s", &string);
free(seed[brknglength]);
fclose(in);
fclose(out);
return 0;
}
What causes the segmentation fault?

Your first Problem appears here:
int* seed[brknglength];
This defines an array of int pointers on the stack.
seed[brknglength] = malloc(sizeof(int[brknglength]));
This initializes the element behind the array and overwrites your stack.
To fix it, use either:
int seed[brknglength]; /* use without free(seed) */
or:
int *seed = malloc(sizeof(int[brknglength]));
/* ... */
free(seed);
The latter also works for compilers, which do not support variable length arrays.
Your second problem is reading a string into a single char variable, which also overwrites the stack. Try something like:
char string[100];
fscanf(in, "%99s", &string);
Be aware, that "%s" stops at whitespace. Use something like "%99[^\t\n]" to define your own separators, or "%99c" for a fixed length string.
The GNU Compiler offers the modifier "m" (=allocate memory) as a convenient non-standard extension for all these cases:
char *string;
fscanf(in, "%ms", &string);
/* ... */
free(string);

int* seed[brknglength];
seed[brknglength] = malloc(sizeof(int[brknglength]));
It looks like what you tried to do here is make seed a pointer to an array of int and allocate space for it. However, that is the wrong syntax. Because [ ] has higher precedence than *, int* seed[brknglength]; defines an array of pointers to int. Also, the name of the object is seed, not seed[brknglength], so you would assign a value to it with seed = …, not with seed[brknglength] = ….
To make a pointer to an array and allocate space for it, use:
int (*seed)[brknglength];
seed = malloc(sizeof *seed);
Those can be combined (which is not a violation of the above note about using seed = for assignment—initialization has a special syntax):
int (*seed)[brknglength] = malloc(sizeof *seed);
However, you probably do not want that. If size is a pointer to an array, then you have to use *seed wherever you want to refer to the array. So fscanf(in, "%d", &seed[i]) would have to be fscanf(in, "%d", &(*seed)[i]).
Instead of making seed a pointer to an array, just make it a pointer to an int, and allocate space for as many int as you want:
int *seed = malloc(brknglength * sizeof *seed);
Then you can use seed[i] for element i of the array instead of having to use (*seed)[i].
char string;
That defines string to be a single char. But fscanf(in, "%s", &string); reads as many characters as the input has until a white-space character. So you need to pass fscanf a pointer to the first of many char. You can either declare string to be an array:
char string[100];
or a pointer to space that is allocated:
char *string = malloc(100 * sizeof *string);
Then you can use fscanf(in, "%s", string);. Note that you do not want to pass &string. That is the address of the array or of the pointer, depending on how you defined string. You want to pass the address of the first character, which is &string[0], or, equivalently, string. (If string is an array, it is automatically converted in this expression to a pointer to its first element, so it is equivalent to &string[0].)
Note that fscanf will read as many character as the input contains until a white-space character appears. That can exceed whatever size you provide for string. So you need to ensure the input does not have too many characters or tell fscanf to limit how much it reads, which you can do with:
fscanf(in, "%99s", string);
or:
int n = 99;
fscanf(in, "%*s", n, string);
Note that fscanf should be told to read at most one character less than the space in string because it needs to add a terminating null character.
To free these objects, use:
free(seed);
free(string); // (If defined as a pointer, not an array.)

Related

How to pass a 1D array of 500 pointers to a function in C

The programm is compiling ok but once the user inputs something to the array it crashes. Any help will be appreciated :)
#include <stdio.h>
#include <stdlib.h>
void Register(char *arr[],char arr2[]);
int main() {
char *Username[500];
char table1[20];
Register(Username,table1);
return 0;
}
void Register(char *Username[],char table1[]){
int i;
for(i = 0; i < 500; i++){
scanf("%s",&table1);
Username[i] = table1;
printf("for i = %d username[%d] is %s\n\n",i,i,Username[i]);
}
}
The problem is that when you pass your arrays they decay to pointers to their first element. For "array" arguments something like e.g. char table1[] is actually char *table1. And this is a problem when you try to use the address-of operator & in scanf.
When you use &table1 in the scanf call you get a pointer to the pointer, which has the type char **, not the expected char *. This mismatch between the format %s and the expected type leads to undefined behavior and probably your crash.
The solution to this crash is to never use the address-of operator for reading strings (with e.g. the %s format), as it's even wrong when you have an actual array:
scanf("%s",table1);
As for the problem of making all elements of Username point to the single string in table1, I recommend that you use arrays of arrays for Username instead:
char Username[500][20];
This array decays to a pointer to an array, with the type of char (*)[20], which needs to be part of the Register function declaration:
void Register(char (*Username)[20]);
Then you can use this directly in the call to scanf:
scanf("%19s", Username[i]);
Also note how I limited the length of the input string, so you can't read more than the arrays can handle (and it's 19 because the array need to fit the string null-terminator as well).
In this statement
scanf("%s",&table1);
instead of reading a string into the array pointed to by the pointer table1 the string is read into the pointer itself.
You have to write
scanf("%s",table1);
Also the function Register does not make great sense because all elements of the array Username will point to the first character of the array table1. That is all elements of the array will point to the first character of the last string that was read.
You need to allocate memory dynamically. Also the second parameter of the function is redundant.
The function cam look the following way
size_t Register( char *Username[], size_t n )
{
char record[20];
int i = 0;
for ( ; i < n && scanf( "%19s", record ) == 1; i++ )
{
Username[i] = malloc( strlen( record ) + 1 );
strcpy( Username[i], record );
printf( "for i = %zu username[%zu] is %s\n\n", i, i, Username[i] );
}
return i;
}
And in main the function can be called like
size_t n = Register( Username, 500 );
\Of course you will need to free all allocated memory.

Why do my string values get overwritten?

I am trying to accept some values from the user and store them in a char pointer array like so:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name));
names[i] = &name;
// strcpy(names[i], name);
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
}
However, for the following input, I get the following output
Input:
Mark Drew Andrew
Output:
Andrew Andrew Andrew
Why is this happening? When I use the strcpy function I have commented out instead, it seems to work fine.
names[i] = &name; is assigning every element of names to the same character buffer, so only the final version of what's in name will persist in the output.
You need to use strcpy (better still, strncpy) to copy the contents of name to names[i].
And don't forget to call free on every element of names when you're done.
You have memory leak in the code and also assigning the wrong assignment type . correct type assignment would be names[i]=name but still that won't solve the problem. Then also it wont work. You need to use strcpy to store different names. Here you have assigned to names[i] to the same variable - that's why the same output you got.
Note that &name is of type char(*)[20] which you assigned to char*(Compiler warned about this). And for all of the 3 input you got the pointer to array of char which is always the same - so it is pointing to the same array. And now the last value it contained is the input "Andrew". That's what it printed.
So the thing would be
strcpy(names[i],name);
Also scanf should be
if( scanf("%19s",name)!=1 ){
fprintf(stderr,"Error in input\n");
exit(EXIT_FAILURE);
}
malloc's return value should be checked and there is no need for casting.Because void* to char* conversion is implicitly done.
Another easy way would be to use strdup to duplicate the strings.
names[i]=strdup(name);
Also don't forget to free (using free() - this you will have to do in case of strdup also) the dynamically allocated memory when you are done working with it.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
char *names[3];
char name[20];
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("Enter your name\n");
if(scanf("%19s", name)!= 1){
fprintf(stderr, "%s\n","Error in input" );
exit(EXIT_FAILURE);
}
names[i] = malloc(strlen(name)+1);
if( names[i] == NULL){
perror("malloc");
exit(EXIT_FAILURE);
}
strcpy(names[i],name);
}
printf("Printing the names\n");
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++) {
printf("%s\n", names[i]);
}
for(size_t i = 0; i < sizeof(names)/sizeof(names[0]); i++)
free(names[i]);
return 0;
}
names[0], names[1] and names[2] are pointing to same memory location name, for i=0 Mark will be stored in name for i=1 Drew will be stored and for i=2 Andrew will be stored, so by the end of the loop your array is pointing to name whose value is Andrew
C strings are simple arrays of characters, terminated by a trailing null character. You cannot assign arrays to other arrays, but you can assign them to pointers.
Now by doing names[i] = &name;, you are doing such a pointer assignment. Other than Java or C++ strings, just the address is copied to the pointer, there is no copying of string contents involved (by the way, &name is of bad type: char(*)[20], i. e. a pointer to array of length 20, you need a pointer to char, which you get by simply assigning name directly: names[i] = name;; name decays to a pointer automatically in this case).
The result is that all your string pointers in names point to one and the same character array name, overwriting the pointers to the arrays created by malloc (these are then lost completely, so you cannot free them again either, i. e. you have a memory leak!).
Instead, you have to copy the strings explicitly. However, to not forget the trailing null character:
int len = strlen(name) + 1;
// trailing null char(!): ^^^
names[i] = malloc(len);
memcpy(names[i], name, len);
Notice: using memcpy. Alternatives would have been strcpy or strncpy, but as length (including the trailing null character!) is known anyway, memcpy is most efficient...
Alternative could have been:
names[i] = malloc(20);
scanf("%19s", names[i]);
You spare copying for the price of the arrays potentially being too long. Have a close look on the format string: By adding a maximal length (you need to leave space for the terminating null character again, thus one less!) you prevent the user from writing beyond your buffer (which would be undefined behaviour and potentially lead to crash). If you do not return the array anywhere, even nicer:
char names[3][20];
Edit: Nice alternative, too: strdup (see coderredoc's answer); Two other important points:
always check the result of malloc for being null (memory allocation might have failed! - again see coderredoc's answer).
avoid (further) memory leaks by freeing the created strings again (not with my very last alternative)!
You use names[i] = &name; in every loop iteration, however name gets overwritten with different name, meaning at the end of each iteration, you have all your names[i] (up to the number of iterations so far) all point to name which obviously holds one specific name (which is "Andrew" in your case).
Consider how the memory changes while your code runs:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--points to name, i.e. points to "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--points to name, i.e. points to "Drew "--|,|--points to name, i.e. points to "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--|,|--points to name, i.e. points to "Andrew"--| (3rd iteration)
Fix this by using strcpy as you mentioned, which will result in:
|--name--| ... |--names[0]--|,|--names[1]--|,|--names[2]--| (loop starting)
|--"Mark"--|...|--copied the value of name, i.e. holds "Mark"--|,|--names[1]--|,|--names[2]--| (1st iteration)
|--"Drew "--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--names[2]--| (2nd iteration)
|--"Andrew"--|...|--copied the value of name, i.e. holds "Mark"--|,|--copied the value of name, i.e. holds "Drew "--|,|--copied the value of name, i.e. holds "Andrew"| (3rd iteration)
Basically, you pointed to variable that changed over time (name) instead of saving it's value (using strcpy)
#note: names[i] = (char *)malloc(strlen(name));should benames[i] = (char *)malloc(strlen(name) + 1);
The line names[i]=&name; doesn't make sense.
You should be getting a compilation error and if you aren't turn on all warnings and errors on your compiler.
It doesn't make sense because names[i] is a pointer to character (char*) and &name is a pointer to a pointer to character (char** or more accurately char(*)[20]).
But changing that to names[i]=name; won't help. It makes names[i] point to the start of the array name. There is only one instance of name. So all elements of names will point to the same location (name) at the end of the loop.
Here's a version with the basic problems fixed:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
char *names[3];
char name[20];
int i;
for(i = 0; i < 3; i++) {
printf("Enter your name\n");
scanf("%s", name);
names[i] = (char *)malloc(strlen(name)+1);//+1 to include NUL terminator.
strcpy(names[i], name);//Copy name into the space allocated.
}
printf("Printing the names\n");
for(i = 0; i < 3; i++) {
printf("%s\n", names[i]);
free(names[i]);//Release malloc'ed memory after use.
}
}
There's still a security issue that the user can exceed the name buffer by entering more than 19 characters. You should impose a limit on scanf and/or use scanf_s if available using scanf("%19s",name).
The mistake is in the following line ,
names[i] = &name;
it only contact the address of the name string , but it points the last value what the user is input which means it presistant with the last value only .
You must use string copy 'strcpy()' function to copy the input 'name' string in the array of pointer string 'name'.
strcpy(name[i],&name)

pointer being assigned values with a loop

I am trying to make an array with 10 pointers which will receive the input from scanf within a for loop. Then I want to call that list. However, I get a runtime error for memory:
#include <stdio.h>
int main(void) {
int i;
char * string[10];
//printf("%s", *string[0]);
for(i = 0; i<3; i++)
{
printf("what is string");
scanf("%s", string[i]);
printf("%s", string[i]);
}
return 0;
}
You need to think about the data structure with which you are attempting to store your strings.
char * string[10];
This declares an array of 10 elements of type "char *" or "pointer to a char".
Now think about what you are doing with this operation.
scanf("%s", string[i]);
You are using scanf to attempt to read user input. Does your function call make sense though?
The first parameter is your format string. "%s" makes sense because you want the user to provide you a string. Let's ignore the fact that you have not put any bounds on the length of that input for the moment.
Your second parameter needs to be be the variable where you want to store the input. You have provided "string[i]". Remember that in C, strings are just char arrays. However, your variable "string[i]" has the type "char *". This doesn't make sense. How can you store data of type "char array[]" in variable of type "char * array[]"? The types are different.
You need to create a buffer within which you can store your input string. Something like:
char buffer[256];
Then you call scanf like:
scanf("%s", buffer);
Keep in mind that this isn't the best idea because you don't know how many characters the user might enter. This will take the input character array and store them in a variable that correctly represents that data.
Note that this call would be safer:
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
fgets will read a fixed number of characters from the stream. In the above call I define that number by just taking the length of my buffer variable. I divide by sizeof(char) just to make sure that any strange representation issues where a char != 1 byte are taken care of. Also note that you will end up with the carraige return in your buffer (fgets will grab it).
Once you have your string in an appropriate variable, you can save it and get a pointer to a copy of that string with:
char * ptr = strdup(buffer);
This will create a new copy of the string stored in buffer and return a pointer to that new copy location. This pointer can then go into your array.
Note that you cannot just do:
string[i] = buffer
Because string[i] is just a pointer to the buffer variable...which will be overwritten during each iteration! You would end up with an array of pointers to the same variable, which would all end up giving you to the same string (the last input).
Put it all together in something like this:
char buffer[256];
printf("What is your string? ");
fgets(buffer, sizeof(buffer)/sizeof(char), stdin);
string[i] = strdup(buffer);
Because strdup() uses a malloc under the hood, you will need to free all your strings at the end of your program with:
int i = 0;
for(; i < sizeof(string)/sizeof(char *); i++) {
if(string[i]) free(string[i]);
}
You need to either allocate memeory dynamically or make your 2-d array:
char string[10][100];
You can do something like this to allocate memory -
for(i=0;i<10;i++){
string[i]=malloc(sizeof(char)*10);
}
So that your program does not give error.
But after this you have to free the memory allocated.

Why would scanf crash while reading a string?

This is just a small program I wrote to find a problem with a larger one. Everything changes when I add the line with scanf. I know it is not safe, I read other threads concerning printf errors that suggest other functions. Anything but cin is fine. Btw, I didn't choose the type definitions of the 'messages', that came from my teachers, so I cannot change them.
#include <stdio.h>
#include <string.h>
char message1 [] = "amfdalkfaklmdklfamd.";
char message2 [] = "fnmakajkkjlkjs.";
char initializer [] = ".";
char* com;
char* word;
int main()
{
com = initializer;
int i = 1;
while (i !=4)
{
printf ("%s \n", com);
scanf("%s",word);
i++;
};
return 0;
}
The problem: after a single iteration the program exits, nothing is printed.
The reason the scanf will crash is buffer is not initialized: word has not been assigned a value, so it is pointing nowhere.
You can fix it by allocating some memory to your buffer, and limiting scanf to a certain number of characters, like this:
char word[20];
...
scanf("%19s", word);
Note that the number between % and s, which signifies the maximum number of characters in a string, is less by 1 than the length of the actual buffer. This is because of null terminator, which is required for C strings.
com is a pointer whose value is the address of the literal string initializer. Literal strings are contained within read-only memory areas, but the scanf function will attempt to write into the address given to it, this is an access-violation and causes the OS to kill your process, hence the crash you're seeing.
Change your scanf code to resemble this, note the addition of width limit in the %s placeholder, as well as the use of the scanf_s version to ensure there is no buffer overflow.
static int const BufferLength = 2048; // 2KiB should be sufficient
char* buffer = calloc( BufferLength , 1 );
if( buffer == null ) exit(1);
int fieldCount = scanf_s("%2047s", buffer, BufferLength );
if( fieldCount == 1 ) {
// do stuff with `buffer`
}
free( buffer );
Note that calloc zeroes memory before returning, which means that buffer can serve as a null-terminated string directly, whereas a string allocated with malloc cannot (unless you zero it yourself).
word has no memory associated with it.
char* word;
scanf("%s",word);
Could use
char word[100];
word[0] = '\0';
scanf("%99s",word);
If available, use getline().
Although not standard C, getline() will dynamicaly allocate memory for arbitrarily long user input.
char *line = NULL;
size_t len = 0;
ssize_t read;
while ((read = getline(&line, &len, stdin)) != -1) {
printf("%s", line);
}
free(line);
Linux Programmer's Manual GETLINE(3)

Assign value to char * from ScanF

Can someone please help me understand why when I try to print out the value of student_name, it only returns null? I'm implementing a basic hashtable in C to store the a students name, id, and 2 tests. Everything else is storing correctly, I just can't manage to save the the student_name no matter what I try. I have two structs, the hashtable itself and then record, the elements I intend to put inside of the table. The character string will never be longer than 18 characters.
int main(){
char op[1];
int stu_id[1];
int exam1[1];
int exam2[1];
char * student_name = (char*)malloc(18*sizeof(char));
struct hashtable * dictionary = malloc(sizeof(struct hashtable));
dictionary->size = 13;
dictionary->table = malloc(13*sizeof(struct record *));
if(dictionary==NULL||dictionary->table==NULL){
printf("Unable to allocate memory for the dictionary.\n");
return;
}
int i;
int s = 13;
while(i<s){
dictionary->table[i]=NULL;
i++;
}
while(scanf("%s %d %d %d %s", op, stu_id, exam1, exam2, student_name) !=EOF){
if(*op=='i'){
printf("Intializing %s\n", *student_name);
add_item(dictionary, stu_id[0], exam1[0], exam2[0], student_name);
}
free(dictionary);
free(student_name);
return 0;
}
Remember that a string always have to contain a special terminator character ('\0'). This means that a string of length one (like your op array) is actually two characters.
This means that when you read into op you are actually writing beyond the bounds of the array, leading to undefined behavior. You either need to increase the size of op (to at least two), or declare it as a single char (i.e. not an array) and use the '%c' format code to read a single character.
Also, don't declare the integer variables as arrays, use the address-of operator & when calling scanf instead:
char op;
int stu_id;
int exam1;
int exam2;
/* ... */
scanf("%c %d %d %d %s", &op, &stu_id, &exam1, &exam2, student_name)
You also should not check the return value of scanf against EOF, in case the input is not formatted correctly. Compare it agains the number of values you want scanned, five in your case.
I suppose you are allocating memory for student record inside add_item() and assigning them to the dictionary->table. From the code you have posted, you are allocation memory to hold pointers to struct student record and not for the records themselves.
You need to free memory allocated for "dictionary->table" at the end of main().

Resources