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().
Related
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.)
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)
My code is suppose to get the names of students and the student grade. After that I try to print the student names from the structure that I made and I can only get the grade to print. I get an error when trying to print the string using
printf("%s", test[0].names);
and the error says,
Unhandled exception at 0x0fe113af (msvcr100d.dll) in StudentNamesAndGrades.exe: 0xC0000005: Access violation reading location 0x65736f4a.
But when I use
printf("%d", test[0].studentScores);
It prints out the score of the first student. Here is the entire code because it might be something other than the way I'm trying to print it out.
#include <stdio.h>
#include <string>
/*
this program will get a name of students
and then you will enter the grade for each one :)
*/
struct students
{
char *names;
int studentScores;
};
int main(void)
{
int numStudents = 0;
students *test;
int i;
printf("Enter the number of students in your class: ");
scanf("%d", &numStudents);
test = (students*)malloc(numStudents * sizeof(students));
printf("Enter the names of the %d students\n", numStudents);
for (i = 0; i < numStudents; i++)
{
printf("Enter the name of student %d: ", i + 1);
scanf("%s", &test[i].names);
printf("Enter the students score: ");
scanf("%d", &test[i].studentScores);
}
printf("%d", test[0].studentScores);
printf("%s", test[0].names); // This is where I get a problem :/
return 0;
}
You did not allocate memory for char *names; while taking input at all.
Your struct could be like:
typedef struct students
{
char names[30];
int studentScores;
}students;
Also using fgets is safer than using scanf.
You need to allocate space for the names field, but I would recommend a different approach
struct students
{
char names[100];
int studentScores;
};
and then change the scanf() to
scanf("%99s", test[i].names);
there is another mistake in your first scanf() your are passing the address to the pointer, instead of the pointer.
You should use the address of & operator for the integer, because you need to pass a pointer to an integer for the "%d" specifier, but your names field variable was already a pointer, so no neet to take it's address.
Some other tips you might be interested in
Don't cast the result of malloc, although it seems that you are erroneously using a c++ compiler to compile c code, and in c++ you do need the cast, in c you don't, it makes your code harder to read and other problems which you can read with the most popular c question on Stack Overflow.
Check the return value from malloc, it doesn't matter how unlikely it could fail, since it could theoretically fail, you must check it's return value, which is NULL on failure.
struct employee {
int employeeId;
double payrate;
char *inputname;
};
int main (void){
//remember the struct before employee
struct employee davidB;
fgets(davidB.inputname, 20, stdin);
davidB.employeeId = 1;
davidB.payrate = 45020.50;
printf("The employees name is %s\n The employees id num is %d\n The employees payrate is %f\n", davidB.inputname, davidB.employeeId, davidB.payrate);
struct employee ericG;
printf("Please enter employee name, id number, and payrate\n");
scanf("%s", ericG.inputname);
scanf("%d", &ericG.employeeId);
scanf("%lf", &ericG.payrate);
printf("The employees name is %s\n The employees id num is %d\n The employees payrate is %f\n", ericG.inputname, ericG.employeeId, ericG.payrate);
return 0;
}
My questions involve:
fgets(davidB.inputname, 20, stdin);
scanf("%s", ericG.inputname);
Why does the first one work and why does the second one cause stack overflow? Furthermore, when can I directly assign a "string" to a pointer, and when does it need to be a pre-defined char array?
for example:
const char *string = "Hey"; //works? so can't I do the same with the scanf above?
You need to allocate memory in order to use those functions, otherwise you get stack overflow since this pointer (davidB.inputname) is pointing to garbage.
One option would be to define it like char inputname[21]; but this will only work for the fgets function since you can limit the number of chars your read.
But with scanf there is nothing you can do unless you can be 100% on the size of the string you're about to read so it's better avoid it otherwise.
It is just random luck that one way works, but the other does not. Both methods are incorrect, as you are not allocating any memory for inputname to point to. So, the functions are just inputting data to some random memory location.
The const char *string = "Hey" works because this is static data, and the compiler is allocating space for it somewhere.
You haven't allocated memory for `inputname. So both invoke undefined behaviour. So both don't work, not in legal C anyway.
But when you directly assign like this:
const char *string = "Hey";
string merely points to a string literal. So there's no need for memory allocation there.
So in this way, you can directly assign to the struct member:
struct employee davidB;
davidB.inputname = "Eric"; // Thiis is valid.
davidB.employeeId = 1;
davidB.payrate = 45020.50;
Otherwise, you have to dynamically allocate memory for inputname:
struct employee davidB;
davidB.inputname = malloc(256);
fgets(davidB.inputname, 256, stdin);
davidB.employeeId = 1;
davidB.payrate = 45020.50;
Trying to scan from a file into a C array but I'm getting an error. It worked for me when I was using a file with only numbers on each line like:
1.2
3.4
5.2
But now I have this file:
0001:Satriani:Joe:6:38.0
0002:Vai:Steve:1:44.5
0003:Morse:Steve:10:50.0
0004:Van Halen:Eddie:3:25.75
0005:Petrucci:John:8:42.25
0006:Beck:Jeff:3:62.0
Here is how I'm trying to scan it into an array, but I get a segmentation fault!
FILE *employeesTXT;
int empID[100];
char lastName[100];
char firstName[100];
int payGroup[100];
double hoursWorked[100];
employeesTXT = fopen("employees.txt", "r");
if (employeesTXT == NULL)
{
printf("Error: file cannot be opened.\n");
} else {
while (fscanf(employeesTXT, "%[^:]:%[^:]:%[^:]:%d:%lf\n", &empID[i], lastName[i], firstName[i], &payGroup[i], &hoursWorked[i]) != EOF)
{
i++;
}
fclose(employeesTXT);
numRecords = i;
for(i = 0; i < numRecords; i++){
printf("%d - %s - %s - %d - %.2lf\n", empID[i], lastName[i], firstName[i], payGroup[i], hoursWorked[i]);
}
}
It has to be something in this line... %[^:]:%[^:]:%[^:]:%d:%lf\n
Your argument parameters are wrong for the strings:
fscanf(employeesTXT, "%[^:]:%[^:]:%[^:]:%d:%lf\n",
&empID[i], lastName[i], firstName[i], &payGroup[i], &hoursWorked[i])
lastName and firstName are declared as an array of 100 char. You want those to be strings so you need to define them to be an array of 100 "buffers".
Try changing the declarations to:
char lastName[100][50]; /* 50 or whatever the max length you'd expect + 1 */
char firstName[100][50];
I believe that should work just like that.
You also have a different issue with empID, you're reading the value as a string and not an integer. In the format, it should be %d for integers if these are indeed integers in your input.
As pointed out, for the names you are assigning each new employees' information into an one dimensional array, thereby overwriting the previous employee's information (except the first character). This eventually leads to your error when you try to assign a long enough name to an index near the end of your array which causing it to write over the last index. You can use a two dimensional array as suggested for the names:
char lastname[number_of_employees][maximum_length_of_name];
and each lastname[i] will be a null terminated string.
But, this is what structures are made for.
// Define it before main()
struct employee_type{
int empID;
char *lastname;
char *firstname;
int paygroup;
double hoursworked;
}
// In main()
struct employee_type employees[100]; // Declare an array of employee_type structures.
Change
char lastName[100];
char firstName[100];
to
char lastName[100][100];
char firstName[100][100];
Also while reading empID[i] don't use %[^:], use %d instead.
You need to have addresses as parameters for fscanf, this won't work as parameter as it is only a character:
lastName[i]