2

I have an array and I have to find the largest element in it, tell how many times it is in and in what position. I have to use pointers and dynamic memory allocation.

It doesn't work when the array is filled randomly, but works fine when filled manually.

This code works properly:

#include <stdio.h>
#include <stdlib.h>

void max_elem (int n, float* vett) {

  int i=0, *pos, p=0, n_ele_pos;
  float max=*(vett);

  pos=(int*)malloc(sizeof(int));
  if (pos==NULL) {printf("BUM\n" ); exit (0);}
  *(pos)=0;
  for (i=1; i<n; i++) {
    if (*(vett+i)>max) {
      max=*(vett+i);
      p=0;
      *(pos)=i;
    }
    else  if (*(vett+i)==max) {
      p++;
      *(pos+p)=i;
    }
  }
  if (p==0) {
    printf("\nmax element is %f, in position %d  ", max, *pos+1);
  }
  else {
    printf("\n max element %f, %d times in position\n", max, p+1);
    for (i=0; i<p+1; i++) {
    printf("%d  ", *(pos+i)+1);
    }
  }
  printf("\n\n");
  free(pos);
}

int main()
{
    int i,n;
    float *element;
    printf("\n\n Pointer : Find the largest element using Dynamic Memory Allocation :\n");
    printf("-------------------------------------------------------------------------\n");
    printf(" Input total number of elements(1 to 100): ");
    scanf("%d",&n);
    element=(float*)calloc(n,sizeof(float));  // Memory is allocated for 'n' elements
    if(element==NULL)
    {
        printf(" No memory is allocated.");
        exit(0);
    }
    printf("\n");
    for(i=0;i<n;++i)
    {
       printf(" Number %d: ",i+1);
       scanf("%f",element+i);
    }

    max_elem (n, element);
    return 0;
}

but the next one doesn't. I have to fill the array with random numbers in a specific range decided by me, even from decimal to decimal extrems for integers random numbers (yes, I know it doesn't make much sense practically). Search when filled with decimal numbers works fine sometimes, with integers I would have just 0s.

Here the code:

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

float rand_float (float *, float *);
int rand_int (float *, float *);
void min_max(float *, float *, float *, float *);
int num_intero(float);
void max_elem (int, float*);

int main () {

  int n, i, t;
  float x1, x2;

  printf ("array size:\t");
  scanf ("%d", &n);
  printf("\ninterval extremes:\t");
  scanf ("%f %f", &x1, &x2);
  do {
    printf("1 for decimal random numbers, 2 for integers:\t");
    scanf("%d", &t);
  } while (t!=1 && t!=2);
  srand(time(NULL));
  switch (t) {

    case 1 :  {

      float *vett;

      vett=(float*)calloc(n, sizeof(float));
      if (vett==NULL) {printf("BUM\n" ); exit (0);}
      for (i=0;i<n;i++) {
        *(vett+i)=rand_float(&x1, &x2);
        printf("%d__\t%10f\n", i, *(vett+i));
      }
      max_elem (n, vett);
      free(vett);

      break;
    }
    case 2 :  {

      int *vett;

      vett=(int*)calloc(n, sizeof(int));
      if (vett==NULL) {printf("BUM\n" ); exit (0);}
      for (i=0; i<n; i++) {
        *(vett+i)=rand_int(&x1, &x2);
        printf("%d__\t%10d\n", i, *(vett+i));
      }
      max_elem (n, (float*)vett);
      free (vett);

      break;
    }
  }

  return 0;
}

void min_max (float*x1, float *x2, float *min, float *max) {

  if (*x1<*x2) {
    *min=*x1;
    *max=*x2;
  }
  else {
    *min=*x2;
    *max=*x1;
  }
}

float rand_float (float *x1, float *x2) {

  float min, max;

  min_max(x1, x2, &min, &max);
  return ((max-min)*(float)rand()/RAND_MAX)+min;
}

int num_intero (float min) {

  if (min/(int)min==1)
    return 1; //e' intero
  else return 0;
}

int rand_int (float *x1, float *x2) {

  float min, max;

  min_max(x1, x2, &min, &max);
  if ((int)min==(int)max) {
    return (int)min;
  }
  else  if (min==0) {
          return (rand()%((int)(max)+1));
        }
        else  if (max==0) {
                return (rand()%((int)(-min)+1)+min);  //funziona anche con ((int)(min)-1)+min
              }
              else if ((int)min!=(int)max) {
                      if (num_intero (min)==1) {
                        return (rand()%((int)(max-min)+1)+min);
                      }
                      else  if (num_intero (max)==0) {
                              return (rand()%((int)(max-min))+min+1);
                            }
                            else return (rand()%((int)(max-min)+1)+min+1);
              }
}

void max_elem (int n, float* vett) {

  int i=0, *pos, p=0, n_ele_pos;
  float max=*(vett);

  pos=(int*)malloc(sizeof(int));
  if (pos==NULL) {printf("BUM\n" ); exit (0);}
  *(pos)=0;
  for (i=1; i<n; i++) {
    if (*(vett+i)>max) {
      max=*(vett+i);
      p=0;
      *(pos)=i;
    }
    else  if (*(vett+i)==max) {
      p++;
      *(pos+p)=i;
    }
  }
  if (p==0) {
    printf("\nmax element is %f, in position %d  ", max, *pos+1);
  }
  else {
    printf("\n max element %f, %d times in position\n", max, p+1);
    for (i=0; i<p+1; i++) {
    printf("%d  ", *(pos+i)+1);
    }
  }
  printf("\n\n");
  free(pos);
}

In addition, when the array is big I have runtime error like this, that I don't have with the first code: error

Example: first code, second code and integers error(bottom)

I'm on Ubuntu 16.04 (gnome) with gcc and atom editor. Thanks and sorry.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
accr0
  • 23
  • 5
  • 3
    `pos` is only allocated to the size of one `int`. If `p` is ever > 0 then `*(pos+p)=i;` is undefined behavior – yano Jun 27 '17 at 19:23
  • `max_elem` seem a bit oversized to me to find a max element in an array... Why do you need to *dynamically* allocate local variables like `pos`? To add more possible bugs? – Eugene Sh. Jun 27 '17 at 19:25
  • @yano So I have to use calloc? Sorry, I'm a newbie and I did't understant well what you've said. – accr0 Jun 27 '17 at 19:27
  • I suggest you learn some debugging skills. First write down step-by-step what you expect your code to do. Then use a debugger or cout statements to verify what your code actually does. Finally, figure out why what you expect differs from the actual results. – Code-Apprentice Jun 27 '17 at 19:28
  • @EugeneSh. I've used dynamic allocation because I didn't want to create an n-size static array – accr0 Jun 27 '17 at 19:31
  • @Code-Apprentice I've debugged manually the code many times but I can't figure out the errors, that's why I'm asking here – accr0 Jun 27 '17 at 19:32
  • What? `pos` is a pointer to a *single* integer. At least this is how you allocate it. But I see now, this is exactly what @yano is talking about... – Eugene Sh. Jun 27 '17 at 19:34
  • @EugeneSh. so how have I to allocate pos to use it like a dynamic array (because I don't prior know how many times 'max' will show)? I tought you could do it with malloc. Anyway thanks so much for replying! – accr0 Jun 27 '17 at 19:40
  • How many `int`s do you want to allocate? Either you'll know that at compile time (in which case you should just create a static array in automatic storage), or you'll figure that out during runtime. Currently, you're only allocating 1, so any access beyond 1 is undefined behavior. AFAIK `calloc` is identical to `malloc` except `calloc` initializes the memory to 0 as well. That won't address your UB problem. – yano Jun 27 '17 at 19:42
  • @yano yes, I could use a static array, but I want to use just the memory I need: I don't know how many 'max' rand() will create, so I want that p stores how many time 'max' occurred and pos stores the positions. Was I clear enough? Many thanks! – accr0 Jun 27 '17 at 19:50
  • I think I understand. So you want `pos` to store the positions of all the max values? The simplest (although not very efficient) is to iterate through the array 3 times. First time through, simply find the max value. Second time through, find how many occurrences of the max value there are and store that to `p`, then do `pos = malloc(sizeof(int)*p);`. Finally, during the third time through, you can store the position of each max value in `pos`. I'm sure there are more efficient ways of doing this than looping through the array 3 times. – yano Jun 27 '17 at 20:19
  • @yano yes, that's the easiest way, but you can also use a static array to store the position and my code, modified, should work well. The matter is that I want a dynamic array, and I thought I could do it using malloc in that way. – accr0 Jun 27 '17 at 20:47
  • you can, that's what I described. Perhaps I don't understand what you're asking. With dynamic memory allocation, you still have to tell it how much memory you want. "Dynamic" doesn't mean your memory will dynamically grow as necessary; it means you're allocating it during runtime because you won't know how much you need until runtime, but it's still a fixed size. If `malloc(sizeof(int))` doesn't return `NULL`, the only guarantee you have is a memory chunk that can store one and only one int. Anything beyond that and you're writing into memory you don't own. – yano Jun 27 '17 at 20:53
  • @yano ok, that's the point then. So is there any way to make "memory dynamically grow as necessary"? And what about this: https://i.stack.imgur.com/H5dON.png ? Thanks a lot for the replies! – accr0 Jun 27 '17 at 21:00
  • nope, not in `c` :),, it's the lowest of the high-level languages. In `c++`, a `std::vector` will dynamically grow under the hood. None of that happens for free, however. If you want to change the size of dynamically allocated memory during runtime in `c`, use [`realloc`](https://linux.die.net/man/3/realloc), just make sure you use it correctly. Core dump looks like you seg faulted... that's the kind of thing that can happen when you access memory out of bounds. – yano Jun 27 '17 at 21:06
  • @yano ok, thanks! :) – accr0 Jun 27 '17 at 23:13

1 Answers1

1

Let simplify the problem a bit.

I have an array and I have to find the largest element in it, tell how many times it is in and in what position. I have to use pointers and dynamic memory allocation.

So, in the worst case when your array contains same elements, you'll have same number of positions as elements. If you don't need to optimize your code to memory usage, then you can allocate two arrays with same lengths (one for numbers and one for positions).

The next simplification option is to find the maximum while you filling your array. (No matter the fillig method uses random numbers of reads user input, you can determine the maximum while inserting elements one-by-one into your array.)

Maybe you can find more simplification options but with these you can leave your max_elem function behind:

#include <stdio.h>
#include <stdlib.h>
#include <float.h> /* for FLT_MIN */

void assert_alloc(void* ptr) {
    if(ptr == NULL) {
        printf("No memory is allocated.\n");
        exit(0);
    }
}

int main() {
    int i, n, k = 0, *positions;
    float max = FLT_MIN, *elements; /* max is now the smallest possible float */

    printf("Input total number of elements(1 to 100): ");
    scanf("%d", &n);

    /* allocate array of same size for numbers and for positions */
    elements = (float*)calloc(n, sizeof(float));
    assert_alloc(elements);

    positions = (int*)calloc(n, sizeof(int));
    assert_alloc(positions);

    /* fill the array and find the maximum meanwhile */
    for(i = 0; i < n; ++i) {
        printf("Number %d: ", i + 1);
        scanf("%f", elements + i);

        if(max < *(elements + i))
            max = *(elements + i);
    }
    printf("\nmax element is %f, ", max); /* max is already known */

    /* find the positions of the maximal number */
    for(i = 0; i < n; ++i) {
        if(max == *(elements + i)) {
            *(positions + k) = i;
            k++;
        }
    }

    /* print the positions of the maximal number */
    if(k > 1) {
        printf("%d times in position\n", k);
        for(i = 0; i < k; i++)
            printf("%d  ", *(positions + i) + 1);
    }
    else {
        printf("in position %d", *positions + 1);
    }    
    printf("\n");

    free(elements);
    free(positions);
    return 0;
}

I checked your code and it has two issues. The main issue is inside your max_elem function where you have to use either calloc or multiply the sizeof(int) by n:

pos = (int*)malloc(n * sizeof(int));

After doing this, your second example will work fine with float types. The reason why it won't work for int types is that your max_elem accepts only float pointers. You pass your int pointer to that function as max_elem(n, (float*)vett); which is wrong due to pointer arithmetics and because the int and float represented in the memory in different way. To solve this issue, declare your vett pointer as float* regardless of whether you'll fill it with int or float values.

Live Demo

Akira
  • 4,385
  • 3
  • 24
  • 46
  • Thanks! Anyway, this is another way to code it. Can you please help me to fix my code, the "way" I've thought it? – accr0 Jun 27 '17 at 20:54
  • I edited my answer and I pointed out the main issues of your second example. – Akira Jun 27 '17 at 22:01
  • I think it's not over yet. I've made a lot of tests (all of them with same input) and seems that both with [yours](http://ideone.com/j9zkIT) and [mine](https://ideone.com/N0qnM1) when there are more than 1 occurrence of 'max' we get wrong or incomplete output. Anyhow now I can use big arrays. – accr0 Jun 28 '17 at 00:31
  • I tested it too and for me it works very well. As I see, [your](https://ideone.com/N0qnM1) example fails because the [precision](http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers) of `float`. You may see the same number on your output because the number of printed digits but in comparison they can be different numbers. – Akira Jun 28 '17 at 06:39
  • To round your `float` values to specific number of digits after the decimal point, use [`roundf`](https://linux.die.net/man/3/round). – Akira Jun 28 '17 at 07:02
  • Ok, I've edited with "*(vett+i)=(roundf(rand_float(&x1, &x2)*100)/100);" for a precision of two decimals. But "max" is still a 22 decimals (if I've counted it right ahah) number. I want that the number will be stored in "vett" with two decimals, and not just print (at this point I could have used %.2f in printf). Am I right? Thanks – accr0 Jun 28 '17 at 10:14
  • [Floating point numbers](https://en.wikipedia.org/wiki/Single-precision_floating-point_format) are stored in very different way than and `int`. You can print as many decimals as you like even if your `float` is rounded to two decimals. – Akira Jun 28 '17 at 11:00
  • Ok, the code should work fine then. Thanks so much!! – accr0 Jun 28 '17 at 11:32