So im currently trying to get my program to read a user input ( a double), and store said input in the heap using pointers in C. I allocate 40 bytes to the heap, and the program runs fine up until the program prompts me to enter an input to be stored, At which point after providing the first input, I get the segmentation fault. I know it may come as an out of bounds error, or an error with improper dereferencing, but I can't quite find where I did that. Any help would be greatly appreciated.
Edit: It's become clear my question was a bit confusing, so some clarifications: The goal of the program is to take an unspecified number of grades, starting with an initial heap size 40 bytes (project requirement), and whenever the heap is full, it is copied into a new one of size 80 bytes, with the old 40 bytes being freed. This continues until all grades have been entered.
Placing count as a parameter to defAlloc was a mistake, not quite sure why I put that there to begin with but that his since been removed.
The allocation aspect of this has been fixed and now works properly, im mainly having an issue with accessing elements within the heap and altering them ( grade Scanner Method)
Sorry for the messy code or confusing wordings, im new to both pointer usage and arithmetic as well as posting on stack overflow. Thank you for the responses.
an example of input and output: Input: 81.0 43.0 25.0 73.5
Output: Allocated 40 Bytes to the heap at 0x8d8010 stored 81 in the heap at 0x8d8010
This Repeats for each value until full heap or reaching a sentinel value.
I don't want to give away a whole bunch of information regarding input/output and the project as this is a school assignment (For academic dishonesty purposes) but essentially the issue im having is coming up in the gradeScanner method where im trying to read input and assign it to ptr, ptr 1, etc. Whatever it may be something is causing a segmentation fault
void defAllocator(double **grades);
void gradeScanner(int gradeCount, int allocCount, double *grades, double myGrade);
int main() {
int allocCount;
int gradeCount;
double myGrade;
double *grades;
printf("Enter a list of grades below where each grade is seperated by a newline Character");
printf("After the last grade is entered, enter a negative value to end the list");
defAllocator(&grades);
if(grades == NULL) {
printf("null");
}
else
printf("hello");
gradeScanner(gradeCount, allocCount, grades, myGrade);
}
void defAllocator(double **grades) {
double *arr = (double*)malloc(40);
if(arr != NULL) {
printf("Allocated 40 bytes at the heap at %p\n", grades);
}
else
printf("failed to allocate array");
*grades = arr;
}
void gradeScanner(int gradeCount, int allocCount, double *grades, double myGrade) {
int i =0;
while(i != 5){
if(scanf("%f", myGrade) > 0 && gradeCount == 0) {
*grades = myGrade;
gradeCount ;
printf("%p\n", grades);
printf("%p\n", gradeCount);
i ;
}
else if(scanf("%f", myGrade) < 0) {
i = 5;
}
else if(scanf("%f", myGrade) > 0 && gradeCount > 0) {
*(grades gradeCount) = myGrade;
}
}
}
CodePudding user response:
Lets get some simple bits going first. The allocation of the grades array
You have
void defAllocator();
then
double myGrade;
double *grades = &myGrade;
defAllocator(grades);
and finally
void defAllocator(double *grades, int count) {
grades = (double*)malloc(40);
printf("Allocated 40 bytes at the heap at %p\n", grades);
}
there is almost nothing correct about any of these lines.
- the function declaration (first line) does not match the actual function
- I think you expect the function to update the 'grades' pointer passed to it somehow. Setting 'grades' to point to a random double beforehand is not useful. Just init it to NULL if you want to be neat
- you then call allocate with only one argument even though its supposed to take 2. Its clear you intended to work out how many you needed by asking the user and passing that in, but for some reason you gave up on that plan
- the allocate function at the moment does not check that malloc worked
- the allocate function discards the returned pointer. It places it in the grades argument but thats a local copy.
- the allocator chooses 40 bytes , with no relationship to the size of what it it allocating
first - what should the allocation function look like. I would make it return the pointer to the allocated memory
double *defAllocator(int count) {
double *arr = (double*)malloc(sizeof(double)*count);
if(arr != NULL)
printf("Allocated 40 bytes at the heap at %p\n", arr);
else
printf("failed to alllocate array");
return arr;
}
Still keeping the hard coded max size. But make it a constant so that you can use it elsewhere , for example to check that not too many results have been entered.
So now we get
const int GRADE_ARRAY_SIZE = 40;
double *defAllocator(int count);
....
double *gradeArray = defAllocate(GRADE_ARRAY_SIZE);
If you want, as an exercise say, to pass the pointer in and have it updated by the allocater, then you need this (its an example of c-style 'pass by reference')
void defAllocator(double **gradeArrayPtr, int count) {
double *arr = (double*)malloc(sizeof(double)*count);
if(arr != NULL)
printf("Allocated 40 bytes at the heap at %p\n", arr);
else
printf("failed to alllocate array");
*gradeArrayPtr = arr;
}
now do
const int GRADE_ARRAY_SIZE = 40;
void defAllocator(double **gradeArrayPtr, int count);
....
double *gradeArray = NULL;
defAllocate(&gradeArray, GRADE_ARRAY_SIZE);
others have pointed out errors in the scanner code, but without the allocater working nothing else will work either
CodePudding user response:
OK now the allocator is fixed lets look at the scanner code. We have
int allocCount;
int gradeCount;
double myGrade;
double *grades;
gradeScanner(gradeCount, allocCount, grades, myGrade);
and the actual function.
void gradeScanner(int gradeCount, int allocCount, double *grades, double myGrade) {
int i =0;
while(i != 5){
if(scanf("%f", myGrade) > 0 && gradeCount == 0) {
*grades = myGrade;
gradeCount ;
printf("%p\n", grades);
printf("%p\n", gradeCount);
i ;
}
else if(scanf("%f", myGrade) < 0) {
i = 5;
}
else if(scanf("%f", myGrade) > 0 && gradeCount > 0) {
*(grades gradeCount) = myGrade;
}
}
}
Note, you code invokes multiple lots of Undefined Behavior so its hard to know exactly what is happening, I am guessing, But in fact tryi\ing to reason about UB is a big mistake.
Lets look at this piece
if(scanf("%f", myGrade) > 0 && gradeCount == 0)
- gradeCount is not initialized so its almost certainly not 0 (UB)
- scanf returns the number of things it scanned not the value it found. So here its either one (%f) or 0 if there was invalid input. I suspect you are seeing if a value > 0 was entered. Maybe not, but its not clear
- you must pass a pointer to the value you want to read into, you are only passing in the value (myGrade) (UB - very strong likelyhood of dying here)
OK so thats always false (gradeCount != 0) so we move onto the next
else if(scanf("%f", myGrade) < 0) {
i = 5;
}
- this reads the next value from input, its not looking again at the same input, not clear if that what you want.
- scanf only returns 0,1 or EOF in the case. Maybe you are trying to detect EOF (that is usually -1)
- you must pass a pointer to the value you want to read into, you are only passing in the value (myGrade) (UB, very string likely hood of death again)
Anyway this is false because of the test for < 0 that only happens at EOF
So now we get to
else if(scanf("%f", myGrade) > 0 && gradeCount > 0) {
*(grades gradeCount) = myGrade;
}
this reads the next value from the input, did you mean that?
same errror with 'myGrade' instead of '&myGrade' (very bad UB)
but scanf might return 1 and gradeCount is almost certainly > 0 (50/50 chance given that its unitialized) .If this did test to true we do
*(grades gradeCount) = myGrade;
more normally this is written
grades[gradeCount] = myGrade;
well gradeCount is uninitialized so this is very bad UB. Note that if this had worked myGrade will be unitialized too (also UB)
So
- Initialize all you variables.
- pass gradeCount in by reference so that it will get updated
- read the manual page on scanf https://man7.org/linux/man-pages/man3/scanf.3.html
Plus
- why the 5
- do you intend to keep reareading when an input misses a test