Values displayed after realloc are weird - c

I called malloc and then I want to cut size in half using realloc when i display values of relocated malloc few numbers shows as 0, is that normal or I messed something up?
double main()
{
double *test;
int size = 10;
int sizes;
int i = 0;
double value;
test = (double*)malloc(size*sizeof(double));
if(test == NULL)
{
printf("ERROR");
return 0;
}
else
{
printf("\nInsert values : \n");
for(i=0;i<=size;)
{
scanf("%lf",&*(test+i));
if(*(test+i)==0.0)
{
break;
}
if(i>=size)
{
size=size+2;
test = realloc(test, size*sizeof(double));
}
i++;
}
}
sizes=size/2;
test = realloc(test, sizes*sizeof(double));
printf("\nSaved values : \n");
for(i=0;i<size;i++)
{
printf("%lf\n",*(test+i));
}
}
I want to learn some coding so please tell me if and what I am doing wrong.

If I well understand you stop to read value at EOF or when the read value is 0, and 0 is not memorized, and when the read is done you want to reduce the allocates vector to the minimal size, in that case :
#include <stdlib.h>
#include <stdio.h>
int main()
{
int size = 10;
double *test = malloc(size*sizeof(double));
if(test == NULL)
{
printf("ERROR");
return 0;
}
else
{
int i = 0;
double value;
printf("\nInsert values : \n");
while ((scanf("%lf",&value) == 1) && (value != 0))
{
if (i >= size) /* == is enough */
{
size += 2; /* I add 2 entries as you */
test = realloc(test, size*sizeof(double));
}
test[i++] = value;
}
/* remove possible extra entry at the end */
if (i != size)
test = realloc(test, i*sizeof(double));
size = i;
printf("\nSaved values : \n");
for(i=0 ;i<size; ++i)
{
printf("%lf\n",test[i]);
}
free(test);
}
return 0;
}

Two issues here:
This
for(i=0;i<=size;)
makes this
scanf("%lf",&*(test+i));
scan into memory one element beyond the allocated memory on the last iteration before reallocating. Doing so invokes undefined behaviour. Already from now on anything may happen.
Those two lines
sizes=size/2;
test = realloc(test, sizes*sizeof(double));
shrink the valid memory to the half of its size, and then this loop
for(i=0;i<size;i++)
{
printf("%lf\n",*(test+i));
}
tries to read out the second half's value which just had been shrinked away. This half's memory is not valid any more. Trying to read it invokes undefined behaviour as well.
So to answer your question:
or i messed something up
Yes you did, namely by invoking undefined behaviour. Do not do that.

Related

last number in a function array

I want to write a function where I have a given array and number N. The last occurrence of this number I want to return address. If said number cannot be found I want to use a NULL-pointer
Start of the code I've made:
int main(void) {
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
for (int i = 0; i <= 6; i++) {
if (ary[i] == 3) {
printf("%u\n", ary[i]);
}
}
return 0;
}
result in command prompt:
3
3
The biggest trouble I'm having is:
it prints all occurrences, but not the last occurrence as I want
I haven't used pointers much, so I don't understand how to use the NULL-pointer
I see many minor problems in your program:
If you want to make a function, make a function so your parameters and return types are explicit, instead of coding directly in the main.
C arrays, like in most languages, start the indexing at 0 so if there are N element the first has index 0, then the second has 1, etc... So the very last element (the Nth) has index N-1, so in your for loops, always have condition "i < size", not "i <= size" or ( "i <= size-1" if y'r a weirdo)
If you want to act only on the last occurence of something, don't act on every. Just save every new occurence to the same variable and then, when you're sure it was the last, act on it.
A final version of the function you describe would be:
int* lastOccurence(int n, int* arr, int size){
int* pos = NULL;
for(int i = 0; i < size; i++){
if(arr[i] == n){
pos = &arr[i]; //Should be equal to arr + i*sizeof(int)
}
}
return pos;
}
int main(void){
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
printf("%p\n", lastOccurence(3, ary, 6);
return 0;
}
Then I'll add that the NULL pointer is just 0, I mean there is literally the line "#define NULL 0" inside the runtime headers. It is just a convention that the memory address 0 doesn't exist and we use NULL instead of 0 for clarity, but it's exactly the same.
Bugs:
i <= 6 accesses the array out of bounds, change to i < 6.
printf("%u\n", ary[i]); prints the value, not the index.
You don't actually compare the value against n but against a hard-coded 3.
I think that you are looking for something like this:
#include <stdio.h>
int main(void)
{
int n = 3;
int ary[6] = { 1,3,7,8,3,9 };
int* last_index = NULL;
for (int i = 0; i < 6; i++) {
if (ary[i] == n) {
last_index = &ary[i];
}
}
if(last_index == NULL) {
printf("Number not found\n");
}
else {
printf("Last index: %d\n", (int)(last_index - ary));
}
return 0;
}
The pointer last_index points at the last found item, if any. By subtracting the array's base address last_index - ary we do pointer arithmetic and get the array item.
The cast to int is necessary to avoid a quirk where subtracting pointers in C actually gives the result in a large integer type called ptrdiff_t - beginners need not worry about that one, so just cast.
First of all, you will read from out of array range, since your array last element is 5, and you read up to 6, which can lead in segmentation faults. #Ludin is right saying that you should change
for (int i = 0; i <= 6; i++) // reads from 0 to 6 range! It is roughly equal to for (int i = 0; i == 6; i++)
to:
for (int i = 0; i < 6; i++) // reads from 0 to 5
The last occurrence of this number I want to return as address.
You are printing only value of 3, not address. To do so, you need to use & operator.
If said number cannot be found I want to use a NULL-pointer
I don't understand, where do you want to return nullpointer? Main function can't return nullpointer, it is contradictory to its definition. To do so, you need to place it in separate function, and then return NULL.
If you want to return last occurence, then I would iterate from the end of this array:
for (int i = 5; i > -1; i--) {
if (ary[i] == 3) {
printf("place in array: %u\n", i); // to print iterator
printf("place in memory: %p\n", &(ary[i])); // to print pointer
break; // if you want to print only last occurence in array and don't read ruther
}
else if (i == 0) {
printf("None occurences found");
}
}
If you want to return an address you need yo use a function instead of writing code in main
As you want to return the address of the last occurence, you should iterate the array from last element towards the first element instead of iterating from first towards last elements.
Below are 2 different implementations of such a function.
#include <stdio.h>
#include <assert.h>
int* f(int n, size_t sz, int a[])
{
assert(sz > 0 && a != NULL);
// Iterate the array from last element towards first element
int* p = a + sz;
do
{
--p;
if (*p == n) return p;
} while(p != a);
return NULL;
}
int* g(int n, size_t sz, int a[])
{
assert(sz > 0 && a != NULL);
// Iterate the array from last element towards first element
size_t i = sz;
do
{
--i;
if (a[i] == n) return &a[i];
} while (i > 0);
return NULL;
}
int main(void)
{
int n = 3;
int ary[] = { 1,3,7,8,3,9 };
size_t elements = sizeof ary / sizeof ary[0];
int* p;
p = g(n, elements, ary); // or p = f(n, elements, ary);
if (p != NULL)
{
printf("Found at address %p - value %d\n", (void*)p, *p);
}
else
{
printf("Not found. The function returned %p\n", (void*)p);
}
return 0;
}
Working on the specified requirements in your question (i.e. a function that searches for the number and returns the address of its last occurrence, or NULL), the code below gives one way of fulfilling those. The comments included are intended to be self-explanatory.
#include <stdio.h>
// Note that an array, passed as an argument, is converted to a pointer (to the
// first element). We can change this in our function, because that pointer is
// passed BY VALUE (i.e. it's a copy), so it won't change the original
int* FindLast(int* arr, size_t length, int find)
{
int* answer = NULL; // The result pointer: set to NULL to start off with
for (size_t i = 0; i < length; ++i) { // Note the use of < rather than <=
if (*arr == find) {
answer = arr; // Found, so set our pointer to the ADDRESS of this element
// Note that, if multiple occurrences exist, the LAST one will be the answer
}
++arr; // Move on to the next element's address
}
return answer;
}
int main(void)
{
int num = 3; // Number to find
int ary[6] = { 1,3,7,8,3,9 }; // array to search
size_t arrlen = sizeof(ary) / sizeof(ary[0]); // Classic way to get length of an array
int* result = FindLast(ary, arrlen, num); // Call the function!
if (result == NULL) { // No match was found ...
printf("No match was found in the array!\n");
}
else {
printf("The address of the last match found is %p.\n", (void*)result); // Show the address
printf("The element at that address is: %d\n", *result); // Just for a verification/check!
}
return 0;
}
Lots of answers so far. All very good answers, too, so I won't repeat the same commentary about array bounds, etc.
I will, however, take a different approach and state, "I want to use a NULL-pointer" is a silly prerequisite for this task serving only to muddle and complicate a very simple problem. "I want to use ..." is chopping off your nose to spite your face.
The KISS principle is to "Keep It Simple, St....!!" Those who will read/modify your code will appreciate your efforts far more than admiring you for making wrong decisions that makes their day worse.
Arrays are easy to conceive of in terms of indexing to reach each element. If you want to train in the use of pointers and NULL pointers, I suggest you explore "linked lists" and/or "binary trees". Those data structures are founded on the utility of pointers.
int main( void ) {
const int n = 3, ary[] = { 1, 3, 7, 8, 3, 9 };
size_t sz = sizeof ary/sizeof ary[0];
// search for the LAST match by starting at the end, not the beginning.
while( sz-- )
if( ary[ sz ] == n ) {
printf( "ary[ %sz ] = %d\n", sz, n );
return 0;
}
puts( "not found" );
return 1; // failed to find it.
}
Consider that the array to be searched is many megabytes. To find the LAST match, it makes sense to start at the tail, not the head of the array.
Simple...

Can someone explain to me how can i access a void* item that is inside a void** array, taking in account void** belongs to a struct

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct student{
int grade;
int enrollCode;
}student;
typedef struct colVoidStar{
int capacity;
int num_itens_curr;
void **arr;
int current_pos;
}colVoidStar;
colVoidStar *colCreate(int capacity){
if(capacity > 0){
colVoidStar *c = malloc(sizeof(colVoidStar));
if(c != NULL){
c->arr = (void**)malloc(sizeof(void*)*capacity);
if( c->arr != NULL){
c->num_itens_curr = 0;
c->capacity = capacity;
return c;
}
free(c->arr);
}
free(c);
}
return NULL;
}
int colInsert(colVoidStar *c, void *item){
if(c != NULL){
if(c->num_itens_curr < c->capacity){
c->arr[c->num_itens_curr] = (student*)item;
c->num_itens_curr++;
return 1;
}
}
return 0;
}
void *colRemove(colVoidStar *c, void *key, int compar1(void* a, void* b)){
int(*ptrCompar)(void*, void*) = compar1;
student* eleRemoved;
if(c != NULL){
if(c->num_itens_curr > 0){
int i = 0;
for(i; i < c->num_itens_curr; i++){
if(ptrCompar((void*)key, (void*)c->arr[i]) == 0){
eleRemoved = (student*)c->arr[i];
for(int j = i; j < c->num_itens_curr; j++){
c->arr[i] = c->arr[i + 1];
c->arr[i + 1] = 0;
}
return (void*)eleRemoved;
}
return NULL;
}
}
}
return NULL;
}
int compar1(void *a, void*b){
int key;
student *item;
key = *(int*)a;
item = (student*)b;
return (int)(key - item->enrollCode);
}
int main(){
int finishProgram = 0, choose, capacity, returnInsert, removeEnroll;
colVoidStar *c;
student *a, *studentRemoved;
while(finishProgram != 9){
printf("-----------------panel-----------------------\n");
printf("Type: \n");
printf("[1] to create a collection;\n");
printf("[2] to insert a student;\n");
printf("[3] to remove some student of collection;\n");
printf("--------------------------------------------------------\n");
scanf("%d", &choose);
switch(choose){
case 1:
printf("Type the maximum of students the collection will have: \n");
scanf("%d", &capacity);
c = (colVoidStar*)colCreate(capacity);
if(c == NULL){
printf("Error in create collection!\n");
}
break;
case 2:
if(c->num_itens_curr < capacity){
a = (student*)malloc(sizeof(student));
printf("%d student:(type the Grade and the Enroll code, back-to-back)\n", c->num_itens_curr + 1);
scanf("%d %d", &a->grade, &a->enrollCode);
returnInsert = colInsert(c, (void*)a);
if(returnInsert == 1){
for(int i = 0; i < c->num_itens_curr; i++){
printf("The student added has grade = %d e enrollCode = %d \n", (((student*)c->arr[i])->grade), ((student*)c->arr[i])->enrollCode);
}
}else{
printf("the student wasn't added in the collection\n");
}
}else{
printf("it's not possible to add more students to the colletion, since the limit of elements was reached!");
}
break;
case 3:
printf("Type an enrollcode to remove the student attached to it:\n");
scanf("%d", &removeEnroll);
studentRemoved = (student*)colRemove(c, &removeEnroll, compar1(&removeEnroll, c->arr[0]));
if(studentRemoved != NULL)
printf("the student removed has grade = %d and enrollcode %d.", studentRemoved->grade, studentRemoved->enrollCode);
else
printf("the number typed wasn't found");
break;
}
}
return 0;
}
---> As you can realize, what I'm trying to do, at least at this point, is access and remove an item(student* that initially will assume a void* type) of a student's collection(void** arr) using a sort of enrollment code. However, I'm having problems with Segmentation Fault and can't understand why and how can solve them, hence my question up there. Debugging the code I found out the errors lies at: if(ptrCompar((void)key, (void**)*c->arr[i]) == 0) inside of Remove function and return (int)(key - item->matricula) inside of Compar1.
Besides, if you can point me out some articles/documentations/whatever that helps me to understand how to cope with problems like that, I'll appreciate it a lot.
Here are the problems I see in colRemove:
(Not really a problem, just a matter of style) Although the function parameter int compar1(void* a, void* b) is OK, it is more conventional to use the syntax int (*compar1)(void* a, void* b).
(Not really a problem) Having both compar1 and ptrCompar pointing to the same function is redundant. It is probably better to name the parameter ptrCompar to avoid reader's confusion with the compar1 function defined elsewhere in the code.
The function is supposed to be general-purpose and shouldn't be using student* for the eleRemoved variable. Perhaps that was just for debugging? It should be void*.
After the element to be removed has been found, the remaining code is all wrong:
c->num_itens_curr has not been decremented to reduce the number of items.
The code is accessing c->arr[i] and c->arr[i + 1] instead of c->arr[j] and c->arr[j + 1].
c->arr[j + 1] may be accessing beyond the last element because the loop termination condition is off by 1. This may be because c->num_itens_curr was not decremented.
The assignment c->arr[j + 1] = 0; is not really needed because all but the last element will be overwritten on the next iteration, and the value of the old last element does not matter because the number of items should be reduced by 1.
(Not really a problem) There is unnecessary use of type cast operations in the function (e.g. casting void * to void *).
Here is a corrected and maybe improved version of the function (using fewer variables):
void *colRemove(colVoidStar *c, void *key, int (*ptrCompar)(void* a, void* b)){
void* eleRemoved = NULL;
if(c != NULL){
int i;
/* Look for item to be removed. */
for(i = 0; i < c->num_itens_curr; i++){
if(ptrCompar(key, c->arr[i]) == 0){
/* Found it. */
eleRemoved = c->arr[i];
c->num_itens_curr--; /* There is now one less item. */
break;
}
}
/* Close the gap. */
for(; i < c->num_itens_curr; i++){
c->arr[i] = c->arr[i + 1];
}
}
return eleRemoved;
}
In addition, this call of colRemove from main is incorrect:
studentRemoved = (student*)colRemove(c, &removeEnroll, compar1(&removeEnroll, c->arr[0]));
The final argument should be a pointer to the compar1 function, but the code is actually passing the result of a call to the compar1 function which is of type int. It should be changed to this:
studentRemoved = (student*)colRemove(c, &removeEnroll, compar1);
or, removing the unnecessary type cast of the the void* to student*:
studentRemoved = colRemove(c, &removeEnroll, compar1);
The colInsert function is also supposed to be general-purpose so should not use this inappropriate type cast to student*:
c->arr[c->num_itens_curr] = (student*)item;
Perhaps that was also for debugging purposes, but it should just be using item as-is:
c->arr[c->num_itens_curr] = item;
As pointed out by #chux in the comments on the question, the expression key - item->enrollCode in the return statement of compar1 may overflow. I recommend changing it to something like this:
return key < item->enroleCode ? -1 : key > item->enrolCode ? 1 : 0;
or changing it to use this sneaky trick:
return (key > item->enroleCode) - (key < item->enroleCode);

A function in C runs for a set of values but gives Segmentation Fault: 11 for another

I am trying to find unique non-zero intersection between two sets. I have written a program which works for some set of arrays but gives segmentation fault for some. I have been trying to figure out why but have failed, any help will be greatly valued. The thing is the functions defined (NoRep and ComEle) are working fine but are unable to return the value to the assigned pointer in the case when Seg Fault is shown. Below is the code:
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
int* ComEle(int ar_1[], int size_ar1, int ar_2[], int size_ar2);
int* NoRep(int a[], int l1);
int main ()
{
// Case 1: Gives segmentation fault
int A[10] = {1,1,0,2,2,0,1,1,1,0};
int B[10] = {1,1,1,1,0,1,1,0,4,0};
int *C = ComEle(A,10,B,10); printf("check complete\n");
// //Case 2: Does not give segmentation fault
// int A[4] = {2,3,4,5};
// int B[4] = {1,2,3,4};
// int *C = ComEle(A,4,B,4); printf("check complete\n");
}
//---------------- Local Functions --------------------//
int* ComEle(int ar_1[], int size_ar1, int ar_2[], int size_ar2) {
// sort of intersection of two arrays but only for nonzero elements.
int i=0, j=0, cnt1 = 0;
int temp1 = size_ar1+size_ar2;
int CE1[temp1]; for(i=0;i<temp1;i++) {CE1[i] = 0;}
/* Size of CE1 is knowingly made big enough to accommodate repeating
common elements which can expand the size of resultant array to
values bigger than those for the individual arrays themselves! */
for(i=0;i<size_ar1;i++) {
j = 0;
while(j<size_ar2) {
if(ar_1[i]==ar_2[j] && ar_1[i]!=0) {
CE1[cnt1] = ar_1[i];
cnt1++;
}
j++;
}
}
// Have to remove repeating elements.
int *CE = NoRep(CE1, cnt1);
for(i=0;i<(CE[0]+1);i++) {printf("CE:\t%d\n", CE[i]);}
printf("ComEle: %p\n",CE);
return(CE);
}
int* NoRep(int a[], int l1) {
int cnt = 0, i = 0, j =0;
int *NR; NR = (int*)calloc((l1), sizeof(int));
//int NR[l1]; for(i=0;i<l1;i++) {NR[i] = 0;}
for(i=0;i<l1;i++) {
j = 0;
while(j<i) {
if(a[i]==a[j]) {break;}
j++;
}
if(j == i) {
cnt++;
NR[cnt] = a[i];
}
}
NR[0] = cnt; // First element: # of relevant elements.
printf("NoRep: %p\n",NR);
return(NR);
}
Thanks again for your help!
Take a look at this code:
int temp1 = size_ar1+size_ar2;
int CE1[temp1]; for(i=0;i<temp1;i++) {CE1[i] = 0;}
/* Size of CE1 is knowingly made big enough to accommodate repeating
common elements which can expand the size of resultant array to
values bigger than those for the individual arrays themselves! */
for(i=0;i<size_ar1;i++) {
j = 0;
while(j<size_ar2) {
if(ar_1[i]==ar_2[j] && ar_1[i]!=0) {
CE1[cnt1] = ar_1[i];
cnt1++;
}
j++;
}
}
Here you have nested loops, i.e. a for-loop with a while-loop inside. So - in worst case - how many times can cnt1 be incremented?
The answer is size_ar1 * size_ar2
But your code only reserve size_ar1 + size_ar2 element for CE1. So you may end up writing outside the array.
You can see this very easy by printing cnt1 inside the loop.
In other words - your CE1 is too small. It should be:
int temp1 = size_ar1*size_ar2; // NOTICE: * instead of +
int CE1[temp1]; for(i=0;i<temp1;i++) {CE1[i] = 0;}
But be careful here - if the input arrays are big, the VLA gets huge and you may run in to stack overflow. Consider dynamic memory allocation instead of an array.
Besides the accepted answer: I have been missing a break statement in the while loop in ComEle function. It was not giving me the expected value of cnt1. The following will be the correct way to do it:
for(i=0;i<size_ar1;i++) {
j = 0;
while(j<size_ar2) {
if(ar_1[i]==ar_2[j] && ar_1[i]!=0) {
CE1[cnt1] = ar_1[i];
cnt1++;
break;
}
j++;
}
}
This will also do away with the requirement for a bigger array or dynamic allocation as suggested (and rightly so) by #4386427

Segfault in c program

I have a segmentation fault upon executing this program and all I know is that segfault is happening after two of the gets commands and after the next scanf command. I know gets isn't good command to use in here but my university is using it so... Here is the code of my main function and functions that program uses and I would appreciate if someone could said to me where the problem persists. The code is written in croatian but nothing really particular isnt said in there so don't mind that
My main program
#include <stdio.h>
#include "funkcije.h"
#define MAX 300
int main (void) {
char niz1[MAX+1], podniz[6], niz2[MAX+1];
int dulj_pod = 0, kontrola, duljina1 = 0, duljina2 = 0, kraj, brojac = 0, i;
float slicnost;
printf("Ucitaj prvi niz > ");
gets(niz1);
printf("Ucitaj drugi niz > ");
gets(niz2);
while (niz1[duljina1] != '\0')
duljina1++;
while (niz2[duljina2] != '\0')
duljina2++;
printf("Ucitaj duljinu podniza : ");
do {
scanf("%d", &dulj_pod);
} while ((dulj_pod < 2 || dulj_pod > 5) || (dulj_pod > duljina1 || dulj_pod > duljina2));
kraj = duljina1 - dulj_pod;
for (i=0; i<=kraj; i++) {
genPodniz (niz1, podniz, i, dulj_pod);
kontrola = sadrziPodniz (niz2, podniz);
if (kontrola == 1)
brojac++;
}
slicnost = (float)brojac / (kraj+1);
printf("Prvi niz : %s\nDrugi niz : %s\nDuljina podniza za usporedbu nizova : %d\n", niz1, niz2, dulj_pod);
printf("Slicnost nizova '%s' i '%s' iznosi %f.\n", niz1, niz2, slicnost);
return 0;
}'
And functions
#include "funkcije.h"
void genPodniz (char *niz, char *podNiz, int pocPozicija, int duljPodniz) {
int i, j=0;
for (i=0; i<duljPodniz; i++, j++) {
*(podNiz+j) = *(niz+pocPozicija+i);
}
*(podNiz+j) = '\0';
}
int sadrziPodniz (char *niz, char *podNiz) {
int kontrola, i = 0, j = 0, duljina1 = 0, duljina2 = 0, poz = 0;
while (*(niz+duljina1) != '\0')
duljina1++;
while (*(niz+duljina2) != '\0')
duljina2++;
while (j < duljina2) {
if (*(niz+i) == *(podNiz+j)) {
kontrola = 1;
i++;
j++;
}
else {
kontrola = 0;
j = 0;
++poz;
i = poz;
}
}
return kontrola;
}
First - find out how to run this code under a debugger. Then it'll just stop on the line where the segfault occurs, which should make it more-obvious what the problem is. Given that you're saying "segfault", it's probably safe to assume you're running some unix-variant, in which case, "gdb" is probably your debugger.
Second - a segfault is, maybe 90% of the time, due to overrunning the bounds of allocated memory. Since your program isn't allocating any memory with "malloc", that means you're writing past the end of one of your arrays. Chances are, one of your loops is running way too many times, and accessing past the end of an array.

Input line freezing, segmentation fault

I'm working on a project for a class and have been stuck for quite a while. When I unit tested the input earlier, it accepted the values for numOfDataSets and createDataSets without error. Now, however, after typing in any set of values for createDataSets, the code freezes after the first input until I enter any character (such as 1 or a), then errors with a segmentation fault. I am not sure what went wrong, and I would appreciate any help.
#include <stdio.h>
#include <stdlib.h>
// Function to return the number of data sets the user wants.
int numOfDataSets(void) {
int ret;
printf("Enter number of data sets: ");
scanf("%d", &ret);
return ret;
}
// Function that creates the data sets in the input arrays.
void createDataSets(float **inputArr, int inputLength, int *lengths) {
int i = 0, j, k;
float value, *currentSet;
// For every element in inputArr...
while (i < inputLength) {
printf("Enter the number of values in this data set, followed by the values: ");
scanf("%d", &j);
*(lengths + i) = j;
currentSet = (float*)calloc(j, sizeof(float));
k = 0;
while (k < j-1) {
scanf("%f", &value);
*(currentSet + k) = value;
k++;
}
scanf("%f", &value);
*(currentSet + j - 1) = value;
*(inputArr + i) = (float*)&currentSet;
i++;
}
}
// Function to get int value of data set to choose.
int chooseDataSet(void) {
int ret;
printf("Enter the number of the data set on which you wish to do calculations: ");
scanf("%d", &ret);
ret = ret - 1;
return ret;
}
// Gets the number option of the operation that the user wants to do.
int getOption(void) {
int ret;
printf("Enter one of the following numbers:\n");
printf("1. Find the minimum value.\n");
printf("2. Find the maximum value.\n");
printf("3. Calculate the sum of all the values.\n");
printf("4. Calculate the average of all the values.\n");
printf("5. Sort the values in ascending order (i.e., from smallest to largest).\n");
printf("6. Select a different data set.\n");
printf("7. Exit the program.\n");
scanf("%d", &ret);
return ret;
}
// Function to find the minimum value of a dataset.
void minimum(float *ptr, int length) {
int i = 1;
float min;
min = *(ptr);
while (i < length) {
if (*(ptr + i) < min) {
min = *(ptr + i);
}
i++;
}
printf("The minimum value of the set is: %d\n", min);
}
// Function to find the maximum value of a dataset.
void maximum(float *ptr, int length) {
int i = 1;
float max;
max = *(ptr);
while (i < length) {
if (*(ptr + i) > max) {
max = *(ptr + i);
}
i++;
}
printf("The maximum value of the set is: %d\n", max);
}
// Function to find the sum of the values of a dataset.
void sum(float *ptr, int length) {
int i = 1;
float sum;
sum = *(ptr);
while (i < length) {
sum = sum + *(ptr + i);
i++;
}
printf("The sum of the set is: %d\n", sum);
}
// Function to find the average of the values of a dataset.
void average(float *ptr, int length) {
int i = 1;
float sum;
sum = *(ptr);
while (i < length) {
sum = sum + *(ptr + i);
i++;
}
sum = sum / length;
printf("The average of the set is: %d\n", sum);
}
// Function to sort the values of a dataset.
void sort(float *ptr, int length) {
int i = 1, j;
float temp;
while (i < length) {
j = i;
while ((j > 0) && (*(ptr + j - 1) > *(ptr + j))) {
temp = *(ptr + j);
*(ptr + j) = *(ptr + j - 1);
*(ptr + j - 1) = temp;
j--;
}
i++;
}
printf("The sorted array is: ");
i = 0;
while (i < length) {
printf("%f\t", *(ptr + i));
i++;
}
printf("\n");
}
// Main method...
int main(void) {
int *lengths, outerLength, userChoiceSet = 0, userChoiceOption = 0, breakOutterLoop = 0;
float **outer;
outerLength = numOfDataSets();
outer = (float**)calloc(outerLength, sizeof(float*));
lengths = (int*)calloc(outerLength, sizeof(int));
createDataSets(outer, outerLength, lengths);
while (breakOutterLoop == 0) {
userChoiceSet = chooseDataSet();
while ((userChoiceOption != 6) || (userChoiceOption != 7)) {
userChoiceOption = getOption();
switch (userChoiceOption)
{
case 1:
minimum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
break;
case 2:
maximum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
break;
case 3:
sum(*(outer + userChoiceSet), *(lengths + userChoiceSet));
break;
case 4:
average(*(outer + userChoiceSet), *(lengths + userChoiceSet));
break;
case 5:
sort(*(outer + userChoiceSet), *(lengths + userChoiceSet));
break;
case 7:
breakOutterLoop = 1;
default:
break;
}
}
}
return (0);
}
The type of input to expect from the user would be something like:
2
3 1.2 2.3 3.4
4 4.5 5.6 6.7 7.8
Your main problem is this, in createDataSets():
*(inputArr + i) = (float*)&currentSet;
What this actually does is assign the address of currentSet to each element of inputArr. This address doesn't change on each iteration, so each element of inputArr gets set to the exact same value. Moreover, this address refers to a variable local to createDataSets() which will be destroyed when that function returns, so the address will be invalid. All the arrays you're dynamically creating are just being discarded, because you're not storing the addresses.
What you should have is:
inputArr[i] = currentSet;
As you mention in the comments, your compiler warned you about this, because what you were doing was trying to store a float ** in a float *, which is rarely a good idea. By adding the cast you silenced the warning, but you didn't fix the problem it was warning you about. The number of occasions in C where a cast is actually what you want to do are relatively few. None of the casts in your program are either necessary, or wise.
A few other points...
You use the wrong format specifier in many of your printf() calls. The %d here:
printf("The minimum value of the set is: %d\n", min);
for instance, should be an %f, because min is a float.
You are overusing pointer notation which makes your code very difficult to follow. That includes very difficult for you, too. For instance, your minimum() function could be much better written as so:
void minimum(float *ptr, int length) {
float min = ptr[0];
for ( int i = 0; i < length; ++i ) {
if ( ptr[i] < min ) {
min = ptr[i];
}
}
printf("The minimum value of the set is: %f\n", min);
}
Similarly, in your switch statement, something like:
average(*(outer + userChoiceSet), *(lengths + userChoiceSet));
is much more clearly written as:
average(outer[userChoiceSet], lengths[userChoiceSet]);
You are missing a call to fflush(stdout) in a few places, where you prompt for input but do not end the prompt with an '\n'. When I ran this code on my system, the prompt did not show before it sat to wait for the input. Interactive output is line-buffered by default, in C, and if you want things to be predictable, then you need to output a '\n' or call fflush(stdout) when output needs to be displayed.
You would benefit from defining your variables closer to the time of use. Restricting the scope of your variables to the minimum feasible is generally good. For instance, in your main() function, your variable userChoiceSet is never used outside of the outer while loop, so define it inside with:
while (breakOutterLoop == 0) {
int userChoiceSet = chooseDataSet();
You don't check the return from calloc() anywhere - you must do this, because the allocation might fail. malloc() and friends return NULL on failure. There's also no real point using calloc(), here - malloc() would be more normal.
You seem to use while loops in places where for loops would be much more natural.
You haven't done too bad a job with this one, but you'll find writing larger programs easier if you make each function do just one thing. For instance, your minimum() function should just calculate the minimum, but right now it calculates it and prints it. Particularly when it comes to dealing with input in the wrong format (see point 9 below) wrapping this up in a separate function will make the functions that use that input much less cluttered, and it's easy to get a function correct and to visually debug it if it's not doing a bunch of different things at once. Also, your opportunity for reusing code goes up when you do this (e.g. right now you couldn't use that minimum() function at any place where you wanted to calculate the minimum without also printing it).
Overall, having one array for your values, and a second for their lengths, is not a good approach. Far better would be to have an array of structs, each struct having a member for the array, and a member for the length, so the two related pieces of data are packaged together.
Also, your use of scanf() is potentially troublesome. If you enter input that's not expected, your program will not fail gracefully. For instance, if you enter anything other than a number in your main menu, then you'll go into an infinite loop. Generally better is to use fgets() to read in an entire line, and use sscanf() to parse its contents. At a minimum, you should check the return from scanf() to see if it successfully read a value, and if it did not, take appropriate remedial action (like reading all the characters in the input buffer and going back to ask for more input).
Overall, bearing all of the above in mind except for the last two points, your createDataSets() function would be better looking something like this:
void createDataSets(float **inputArr, const int inputLength, int *lengths) {
for ( int i = 0; i < inputLength; ++i ) {
printf("Enter the number of values in this data set, "
"followed by the values: ");
fflush(stdout);
scanf("%d", &lengths[i]);
float * currentSet = malloc(lengths[i] * sizeof *currentSet);
if ( !currentSet ) {
perror("Couldn't allocate memory in createDataSets()");
exit(EXIT_FAILURE);
}
for ( int j = 0; j < lengths[i]; ++j ) {
scanf("%f", &currentSet[j]);
}
inputArr[i] = currentSet;
}
}
Much easier to debug, easier to follow, and easier to not get wrong in the first place.
Since I've got a bit of time on my hands, here's how I'd figure it:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <ctype.h>
/* Maximum length of input buffer */
#define MAX_LINE 1024
/* Dataset structure */
struct dataset {
float * data;
size_t length;
};
/* Gets a single integer from user */
int getInteger(const char * prompt)
{
int value;
bool first_try = true;
char buffer[MAX_LINE];
do {
printf("%s%s: ", first_try ? "" : "Try again - ", prompt);
fflush(stdout);
fgets(buffer, MAX_LINE, stdin);
first_try = false;
} while ( sscanf(buffer, "%d", &value) != 1 );
return value;
}
/* Gets a bounded integer from user */
int getBoundedInteger(const char * prompt, const int min, const int max)
{
bool bad_input;
int value;
do {
bad_input = false;
value = getInteger(prompt);
if ( value < min ) {
printf("Too low, try again - ");
bad_input = true;
}
else if ( value > max ) {
printf("Too high, try again - ");
bad_input = true;
}
} while ( bad_input );
return value;
}
/* Gets a list of floats from user - caller must free */
float * getFloats(const char * prompt, const int num)
{
float * values = malloc(num * sizeof *values);
if ( !values ) {
perror("Couldn't allocate memory in getFloats()");
exit(EXIT_FAILURE);
}
bool bad_input = false;
do {
printf("%s%s: ", bad_input ? "Try again - " : "", prompt);
fflush(stdout);
char buffer[MAX_LINE];
fgets(buffer, MAX_LINE, stdin);
char * ptr = buffer;
int num_read = 0;
bad_input = false;
while ( *ptr && num_read < num ) {
/* Skip leading whitespace */
while ( *ptr && isspace(*ptr) ) {
++ptr;
}
/* Get and check input */
char * endptr;
float val = strtof(ptr, &endptr);
if ( ptr == endptr ) {
bad_input = true;
break;
}
/* Advance ptr and store input if good */
ptr = endptr;
values[num_read++] = val;
}
if ( num_read < num ) {
bad_input = true;
}
} while ( bad_input );
return values;
}
/* Returns the number of data sets the user wants. */
int numOfDataSets(void)
{
return getInteger("Enter number of data sets");
}
/* Creates the data sets */
void createDataSets(struct dataset ** sets, const int set_length)
{
for ( int i = 0; i < set_length; ++i ) {
struct dataset * new_set = malloc(sizeof *new_set);
if ( !new_set ) {
perror("Couldn't allocate memory for dataset");
exit(EXIT_FAILURE);
}
new_set->length = getInteger("Enter number of values in set");
new_set->data = getFloats("Enter values", new_set->length);
sets[i] = new_set;
}
}
/* Gets the number of data set to choose */
int chooseDataSet(const int min, const int max)
{
return getBoundedInteger("Choose data set", min, max) - 1;
}
/* Gets a menu choice from the user */
int getOption(void)
{
printf("Enter one of the following numbers:\n");
printf("1. Find the minimum value\n");
printf("2. Find the maximum value\n");
printf("3. Calculate the sum of all the values\n");
printf("4. Calculate the average of all the values\n");
printf("5. Sort the values in ascending order\n");
printf("6. Output the data set\n");
printf("7. Select a different data set\n");
printf("8. Exit the program\n");
return getInteger("Choose option");
}
/* Returns the minimum value in a data set */
float minimum(const struct dataset * set)
{
float min = set->data[0];
for ( size_t i = 0; i < set->length; ++i ) {
if ( set->data[i] < min ) {
min = set->data[i];
}
}
return min;
}
/* Returns the maximum value in a data set */
float maximum(const struct dataset * set)
{
float max = set->data[0];
for ( size_t i = 0; i < set->length; ++i ) {
if ( set->data[i] > max ) {
max = set->data[i];
}
}
return max;
}
/* Returns the sum of the data in a dataset */
float sum(const struct dataset * set)
{
float sum = 0;
for ( size_t i = 0; i < set->length; ++i) {
sum += set->data[i];
}
return sum;
}
/* Returns the arithmetic average of the data in a dataset */
float average(const struct dataset * set)
{
float sum = 0;
for ( size_t i = 0; i < set->length; ++i ) {
sum += set->data[i];
}
return set->length > 0 ? sum / set->length : sum;
}
/* Sorts the elements of a dataset in place */
void sort(struct dataset * set)
{
for ( size_t i = 0; i < set->length; ++i ) {
for ( size_t j = i; j && set->data[j-1] > set->data[j]; --j ) {
float temp = set->data[j];
set->data[j] = set->data[j-1];
set->data[j-1] = temp;
}
}
}
/* Prints a dataset */
void print_set(const struct dataset * set) {
for ( size_t i = 0; i < set->length; ++i ) {
printf("%.4f ", set->data[i]);
}
putchar('\n');
}
/* Main function */
int main(void)
{
/* Get and initialize sets */
const int num_sets = numOfDataSets();
struct dataset ** sets = malloc(num_sets * sizeof *sets);
if ( !sets ) {
perror("Couldn't allocate memory for sets");
return EXIT_FAILURE;
}
createDataSets(sets, num_sets);
/* Main menu */
int chosen_set = chooseDataSet(1, num_sets);
bool keep_going = true;
while ( keep_going ) {
switch ( getOption() )
{
case 1:
printf("Minimum value is %f\n\n",
minimum(sets[chosen_set]));
break;
case 2:
printf("Maximum value is %f\n\n",
maximum(sets[chosen_set]));
break;
case 3:
printf("Sum of values is %f\n\n",
sum(sets[chosen_set]));
break;
case 4:
printf("Average of values is %f\n\n",
average(sets[chosen_set]));
break;
case 5:
sort(sets[chosen_set]);
break;
case 6:
print_set(sets[chosen_set]);
break;
case 7:
chosen_set = chooseDataSet(1, num_sets);
break;
case 8:
keep_going = false;
break;
default:
break;
}
}
/* Free memory for sets */
for ( int i = 0; i < num_sets; ++i ) {
free(sets[i]->data);
free(sets[i]);
}
free(sets);
return 0;
}

Resources