0

I am making a function to get the maximum value of an array of NMEMB members each one of size SIZ, comparing each member with memcmp(). The problem is that when comparing signed integers the result is incorrect but at the same time correct. Here is an example:

void *
getmax(const void *data, size_t nmemb, size_t siz){

    const uint8_t *bytes = (const uint8_t *)data;
    void *max = malloc(siz);

    if (!max){
        errno = ENOMEM;
        return NULL;
    }
    
    memcpy(max, bytes, siz);
    while (nmemb > 0){

        hexdump(bytes, siz);

        if (memcmp(max, bytes, siz) < 0)
            memcpy(max, bytes, siz);

        bytes += siz;
        --nmemb;
    }

    return max;
}

int
main(int argc, char **argv){
    int v[] = {5, 1, 3, 1, 34, 198, -12, -11, -0x111118};
    size_t nmemb = sizeof(v)/sizeof(v[0]);
    int *maximum = getmax(v, nmemb, sizeof(v[0]));
    printf("%d\n", *maximum);
    return 0;
}
  • hexdump() is just a debugging function, doesn't alter the program.

When compiling and executing the output is the following:

05 00 00 00 // hexdump() output
01 00 00 00
03 00 00 00
01 00 00 00
22 00 00 00
c6 00 00 00
f4 ff ff ff
f5 ff ff ff
e8 ee ee ff
-11  // "maximum" value

Which is correct since memcmp() compares an string of bytes and doesn't care about types or sign so -11 = 0xfffffff5 is the maximum string of bytes in the array v[] but at the same time is incorrect since -11 is not the maximum integer in the array.

Is there any way of getting the maximum integer of an array using this function?

dlb04
  • 27
  • 1
  • 7
  • I suppose it's doable, but what's the point? – Havenard Feb 27 '21 at 16:16
  • Why is `getmax` working with `void*` and using `memcmp` and not using `int*` and regular `int` comparison? – Werner Henze Feb 27 '21 at 16:19
  • Looks like a goofy homework assignment. If you're forced to use `memcmp` then you'll need a special case when the highest bit is `1` to deal with the two's complement representation. – Chris O Feb 27 '21 at 16:20
  • I don't understand the question. What do you mean with _when comparing signed integers the result is incorrect but at the same time correct._ How can be sometihg correct and incorrect at the same time. Is this some kind of paradox? – Luis Colorado Feb 27 '21 at 16:24
  • 2
    That's not the only problem with this code, when using `memcmp()` to compare integers, `1` is bigger than `256` because of little-endianess. You can't actually compare integers without respecting the nuances of their type, specially when there's sign involved. `memcmp()` is unsuitable for this task. – Havenard Feb 27 '21 at 17:09

3 Answers3

3

memcmp compares the locations and does not care about the sign. so for it -11 means 0xFFFFFFF5 and -12 means 0xFFFFFFF4 and the biggest number in the array 198 means 0x000000C6, so out of all these, -11 is the biggest unsigned number and it is returned for you. You should not use memcmp to compare the signed numbers.

ram914
  • 331
  • 4
  • 19
2

Go down the qsort route and require a custom comparator. Note that you absolutely don't need dynamic memory allocation in a function this simple:

#include <stdio.h>

void const *getmax(void const *data, size_t const count, size_t const elm_sz,
                   int (*cmp)(void const *, void const *)) {
  char const *begin = data;
  char const *end = begin + count * elm_sz;
  char const *max = begin;
  while (begin != end) {
    if (cmp(max, begin) < 0) max = begin;
    begin += elm_sz;
  }

  return max;
}

int int_cmp(void const *e1, void const *e2) {
  int const i1 = *(int const *)e1;
  int const i2 = *(int const *)e2;

  if (i1 > i2) return 1;
  if (i1 < i2) return -1;
  return 0;
}

int main() {
  int v[] = {5, 1, 3, 1, 34, 198, -12, -11, -0x111118};
  int const *maximum = getmax(v, sizeof(v) / sizeof(*v), sizeof(*v), int_cmp);
  printf("%d\n", *maximum);
}
Aykhan Hagverdili
  • 28,141
  • 6
  • 41
  • 93
1

All memory comparisons made by memcmp are unsigned and based on char sized array elements. When you feed this with a signed int array of cells, different size, your result can only be used to test equality of binary representations, meaning that a result of 0 or different than 0 means equality or unequality, but the sign on a different of zero result means comparing the individual bytes of the array of integeres, which, descomposed as bytes (in the machine endianness architecture), makes some of the bytes to be signed and compared as unsigned and others be signed and compared as unsigned. In addition, the significance of the different bytes in an integer will probably affect the sorting order, as the bytes are compared from lower addresses to higher addresses, that would match with the architecture endianness only in the case that the integers where stored as unsigned and (very important) stored in memory in big endian order. If probably you are using intel architecture, then this is just the opposite to be able to use that.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31