-3
#include<stdio.h>
#include<stdlib.h>
#define N 5
typedef  enum { FALSE, TRUE } BOOL;
BOOL Int_Sum(void*, void*, void*);
BOOL Sum(BOOL(*f)(void*, void*, void*), void* p_num, void* number) {
    int i, j;
    for (i = 0; i < N; i++) {
        for (j = 1; j < N; j++) {
            if (f(p_num+i, p_num+j, number))
                return TRUE;
        }
    }
    return FALSE;
}

int main()
{
    int num[] = { 3,5,23,5,6 }, i, value;

    if (Sum(Int_Sum, num, 5) == TRUE)
        printf("There is such sum\n");
    else
        printf("There is no such sum\n"); 

    getch();
    return 0;
}

BOOL Int_Sum(void* a, void* b, void* c)
{
    if (*(int*)a + *(int*)b == *(int*)c)
        return TRUE;
    return FALSE;
}

it only works when i use void**, anyone can explain it to me? i thought it doesn't matter since im passing the address of the first element in the array?

Edit: the original code was like this and it works, the one above i change so i can test things out, but the one below i still don't quite it

   typedef  enum { FALSE, TRUE } BOOL;
    BOOL Int_Sum(void*, void*, void*);
    BOOL Sum(BOOL(*f)(void*, void*, void*), void** p_num, void* number) {
        int i,j;
        for (i = 0; i < N; i++) {
            for (j = 1; j < N; j++) {
                if(f(p_num[i], p_num[j], number))//f is int_sum
                return TRUE;
            }
        }
        return FALSE;
    }

    int main()
    {
        int num[] = { 3,5,23,5,6 }, i, value;
        void* p_num[N];

        for (i = 0; i < N; i++)
            p_num[i] = &num[i];
        printf("\nPlease enter an integer number ");
        scanf("%d", &value);
        if (Sum(Int_Sum, num, &value) == TRUE)
            printf("There is such sum\n");
        else
            printf("There is no such sum\n");
  • Which "void**" do you mean? Explain your program by adding comments to appropriate parts of your code first; tell us what you expect of your constructs, and where they don't meet your expectation. – DannyNiu Feb 07 '20 at 12:51
  • @DannyNiu obviously im talking about the array as i mentioned, so its the same parameter im passing the array to which is p_num, and void * p_num doesn't work except i change it to void ** p_num – Mustafa Shama Feb 07 '20 at 12:58
  • Note the Standard C had `` and type `bool` (and `_Bool`), and values `true` and `false`. It has done all of this millennium. – Jonathan Leffler Feb 07 '20 at 14:36
  • Please don't write stone age C like this. Generic programming in modern C should rather look like this https://godbolt.org/z/aMrMDJ. Where you may optionally add type safety macros such as `#define TYPECHECK(n) _Generic((n), int: 1, float: 2, default: 0)` to be used with static asserts like `_Static_assert(TYPECHECK(a), "Wrong type: " #a);` or `_Static_assert(TYPECHECK(a)==TYPECHECK(b)==TYPECHECK(c), "Mixing several types");` – Lundin Feb 07 '20 at 14:58
  • How does `BOOL Sum(, , void* number);` match with `Sum(Int_Sum, num, 5)`? Doesn't your compailer complain about parameter #3? – Gerhardh Feb 07 '20 at 15:16

2 Answers2

1

The problem with void * is here:

            if (f(p_num+i, p_num+j, number))

, in function Sum. That code is non-conforming because p_num has type void *, and pointer arithmetic is undefined for pointers to incomplete types such as void. This arises naturally from the fact that pointer arithmetic is defined in terms of the size of the pointed-to type, and the size of an object of incomplete type is unknown.

That doesn't mean your compiler must reject the code. On the contrary, it is permitted to do anything whatever with non-conforming code. But if you cannot find a way to get it at least to emit a warning about that (maybe by turning up the warning level) then you ought to look for a better compiler.

Note that void ** is not correct either. It is a complete type, so pointer arithmetic can be done with it, but it is not compatible with the int * arguments you are passing (whereas void * is compatible). If your compiler is not warning about that by default then you probably ought to seek a better one.

A working approach with the present function signatures would be to cast appropriately for the pointer arithmetic:

            if (f(((int *)p_num)+i, ((int *)p_num)+j, number))

But of course, that calls into question the value of defining Sum's parameters the way this code does. Since its behavior is well-defined only when its p_num parameter is in fact an int *, it just obfuscates to declare it as anything else.

Alternatively, if you want something generic then you need to tell the function at least how big the pointed-to type is, so that you could do something along these lines:

BOOL Sum(BOOL(*f)(void*, void*, void*), void* p_num, void* number, size_t num_size) {
    char *p_bytes = p_num;

    // ...
            if (f(p_bytes + i * num_size, p_bytes + j * num_size, number))
    // ...
}

Addendum:

As @P__J__ observed in his answer, the code presented has another significant problem, in that the main program passes the integer 5 as the third argument of Sum(), whereas the corresponding function parameter has type void *. You should also be able to get your compiler to warn about this, because although it is permitted to convert integers to pointers and vice versa, a conforming C program must do so explicitly, via a cast.

Such a warning would help you recognize that when Sum() passes the argument on to function Int_Sum(), the latter assumes it is a valid pointer to an int, which it is not. It seems unlikely (albeit not impossible) for the resulting undefined behavior to manifest as seeming to work as expected. To fix this, main() and Int_Sum() need to agree on how the argument is meant to be interpreted, and then both must handle it appropriately. There are two main alternatives:

  1. main() casts the argument to type void *, and Int_Sum() casts it back. Although this will almost surely produce the desired behavior in that regard, it may elicit compiler warnings on some systems, and you do not seem to have enough experience to judge which warnings you may safely ignore.

  2. main() passes a bona fide pointer to an int variable containing the desired number (and Int_Sum() does not need to change).

John Bollinger
  • 160,171
  • 8
  • 81
  • 157
  • 1
    I afraid it not the only problem here, there is one even more severe – 0___________ Feb 07 '20 at 13:34
  • You're quite right, @P__J__, thanks. I have added comments directed at that. – John Bollinger Feb 07 '20 at 14:49
  • @JohnBollinger Would you look at the new code i added and elaborate it for me please? thank you in advance :) – Mustafa Shama Feb 08 '20 at 11:24
  • @MustafaShama, please do not make substantive changes to your questions after you have received answers. Moreover, requests for advice on improving existing, working code are off topic on SO, but they are the bread & butter of [Code Review SE](https://codereview.stackexchange.com/). – John Bollinger Feb 08 '20 at 14:55
1

The are plenty of problems there - the most important:

  1. void pointer arithmetic. It is not portable as it is a gcc extension. It works on the char size and not any other type size. So you need to calculate it yourself.
  2. Sum(Int_Sum, num, 5) - you do not pass the numner 5 you pass the 5 converted to the pointer. Then this pointer is dereferenced - and it is an UB. You need a separate variable for it and pass the reference to this variable

https://godbolt.org/z/3Ju9e4



#define N 5

typedef  enum { FALSE, TRUE } BOOL;

BOOL Int_Sum(void*, void*, void*);

BOOL Sum(BOOL(*f)(void*, void*, void*), void* p_num, void* number, size_t size) {
    int i, j;
    for (i = 0; i < N; i++) {
        for (j = 1; j < N; j++) {
            if (f(((char *)p_num) + i * size, ((char *)p_num) + j * size, number))
                return TRUE;
        }
    }
    return FALSE;
}

int main()
{
    int num[] = { 3,5,23,5,6 }, i, value;
    int anothernum = 5;

    if (Sum(Int_Sum, num, &anothernum, sizeof(num[0])) == TRUE)
        printf("There is such sum\n");
    else
        printf("There is no such sum\n"); 

    return 0;
}

BOOL Int_Sum(void* a, void* b, void* c)
{
    if (*(int*)a + *(int*)b == *(int*)c)
        return TRUE;
    return FALSE;
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
0___________
  • 60,014
  • 4
  • 34
  • 74
  • 1
    "you pass the 5 converted to the pointer" That part will not even compile with strict standard C compilers. Parameter passing follows the same conversion rules as simple assignment, which is this: https://stackoverflow.com/questions/52186834/pointer-from-integer-integer-from-pointer-without-a-cast-issues – Lundin Feb 07 '20 at 15:01
  • @Lundin which one you mean? (the strict compiler)\ – 0___________ Feb 07 '20 at 15:30
  • gcc, clang or icc configured to be C language compilers with `-std=c17 -pedantic-errors`, alternatively pretty much every embedded system compiler out there. Pretty much everything out there except VC but that one hardly counts. – Lundin Feb 07 '20 at 19:19
  • I don't know why these compilers chose to be lax about constraint violations by default. It's not "any warning", it's a C language violation. When porting the code to a C compiler, it will not compile. – Lundin Feb 08 '20 at 20:49
  • @Lundin it is UB. US are allowed by the language. If the code does not compile, it is not C compiler any more. C++ disallows it, C allows. – 0___________ Feb 09 '20 at 01:06
  • It is UB just like cutting the power supply of your computer is UB - it's not covered by the standard. Just read the link... – Lundin Feb 09 '20 at 13:21