Problem compiling K&R example - c

I'm having trouble compiling the example program presented in section 5.11 of the book. I have removed most of the code and left only the relevant stuff.
#define MAXLINES 5000
char *lineptr[MAXLINES];
void qsort1(void *lineptr[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
main(int argc, char *argv[]) {
int numeric = 1;
/* ... */
qsort1((void**) lineptr, 0, 100, (int (*)(void*, void*))(numeric ? numcmp : strcmp));
}
void qsort1(void *v[], int left, int right, int (*comp)(void *, void *)) {
/* ... */
}
int numcmp(char *s1, char *s2) {
/* ... */
}
The problem is that the code doesn't compile (I'm using Digital Mars compiler). The error I get is this:
qsort1((void**) lineptr, 0, nlines - 1, (int (*)(void*, void*))(numeric
? numcmp : strcmp));
^
go.c(19) : Error: need explicit cast to convert
from: int (*C func)(char const *,char const *)
to : int (*C func)(char *,char *)
--- errorlevel 1
There must be something wrong with the declarations although I pasted the code from the book correctly. I don't know enough to make the right changes (the section about the function pointers could certainly have been written more extensively).
EDIT: I should have mentioned that I'm reading the ANSI version of the book.

I think the error comes from the fact that old C did not know const yet: strcmp there took two pointers to non-const characters (char *) i think (which could be the reason why it compiled back then, but not with your compiler). However, nowadays strcmp takes char const* (const char* is the same thing). Change your function prototype to this:
int numcmp(char const*, char const*);

That's a common problem :)
The following line tells qsort to expect a pointer to a function with two void* parameters. Unfortunately, strcmp takes two non-modifiable strings hence it's signature is
int (*comp)(const char*, const char*)
instead of what you have:
int (*comp)(void *, void *)
Change the signature of both qsort1 and numeric:
qsort1(void *v[], int left, int right, int (*comp)(const void *, const void *))
and:
int numcmp(const char*, const char*)

The standard function pointer expected by qsort() or bsearch() has the prototype:
int comparator(const void *v1, const void *v2);
The qsort1() defined in the code expects:
int comparator(void *v1, void *v2);
The comparator functions defined in the code do not have that prototype, and there is no automatic conversion between different function pointer types.
So, fixes for qsort1() are either:
Introduce a cast: (int (*)(void *, void *)), or
Rewrite the comparators:
int numcmp(void *v1, void *v2)
{
char *s1 = v1;
char *s2 = v2;
...
}
int str_cmp(void *v1, void *v2) // Note new function name!
{
return(strcmp(v1, v2));
}
Obviously, the call to qsort1() would reference str_cmp instead of strcmp. The authors sought to avoid an intermediate function, but run foul of the (legitimately) fussier compilers in use nowadays.
The standard version of qsort() would require a bunch of const qualifiers, as in the first version of this answer.

Note that strcmp takes two const arguments, whereas your numcmp does not. Therefore, the two functions' types do not match, and the ? : operator will complain.
Do one of:
change numcmp to match the strcmp prototype in terms of constness
push the (int (*)(void*, void*)) cast inside the ? :, e.g.
numeric ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp

Its been awhile since I have done any pure C programming, I'm not certain on the new standard.
However casting to void ** creates a pointer to a pointer where the function requires a pointer to an array. Sure, they are the same thing internally, but strong typechecking will catch that as an error.
rewrite the qsort to accept ** instead of *[] and you should be ok.

Related

Warning when trying to run Anagram(John Bentley-Programming Pearls)-C

completely new to C. just trying to get the hang of linux and C programming by getting John Bentley's Anagram (column 2 I believe)program to run. Pretty sure ive copied this code verbatim(had to add headers, etc) but im receiving a warning, which when compiled and run with my squash.c program gives an undesired output. ill admit, i dont even know how this charcomp function behaves, or what it even does. (some enlightenment there would also be nice).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int charcomp(char *x, char *y) {return *x - *y;}
#define WORD_MAX 100
int main(void)
{
char word[WORD_MAX], sig[WORD_MAX];
while (scanf("%s", word) != EOF) {
strcpy(sig, word);
qsort(sig, strlen(sig), sizeof(char), charcomp);
printf("%s %s\n", sig, word);
}
return 0;
}
Here's the warning.
sign.c:13:41: warning: incompatible pointer types passing 'int (char *, char *)'
to parameter of type '__compar_fn_t' (aka 'int (*)(const void *, const
void *)') [-Wincompatible-pointer-types]
qsort(sig, strlen(sig), sizeof(char), charcomp);
^~~~~~~~
/usr/include/stdlib.h:766:20: note: passing argument to parameter '__compar'
here
__compar_fn_t __compar) __nonnull ((1, 4));
^
The qsort() function takes a comparison function as a fourth argument, with the following signature:
int (*compar)(const void *, const void *)
Therefore, to avoid compiler warnings, you have to modify you charcomp() function in the following way, to fit that signature:
int charcomp(const void *x, const void *y) { return *(char *)x - *(char *)y; }
Your charcomp function just takes two char* pointers to and compares first their first characters.

Passing a void value function as part of a function signature in C

First off I'm primarily a Java programmer, but I've been tasked with doing some network stuff in C. I've got a function with the following signature:
foo(int, void (*) (int, char *, int))
It's the void (*) that's throwing me for a loop. This is supposed to call another function (static)
bar(int, char *, int)
Now am I right in thinking that foo wants a pointer to bar with whatever variables I need at the time?
Calling
foo(1,myfunction(1,&anCharArray,10));
fails with a number of errors.
If anyone has any links to good articles on pointers that would also help.
foo(1,myfunction(1,&anCharArray,10))
fails with a number of errors.
Try instead:
foo(1, myfunction)
The second parameter of foo function is a function pointer but you were passing the return value of a function call.
The void (*)(int, char *, int) is an anonymous parameter of type 'pointer to function returning void and taking three arguments: an int, a char * and another int'. You need to pass the name of such a function (in your case, bar) as the second argument to the function foo. Personally, I'd prefer to see the declaration of foo written with a return type and names for parameters, and the declaration of bar() should also have a return type and names for the parameters. The names do not have to match between function declaration and definition, but it is not usually regarded as good style to vary the names between them.
void foo(int num, void (*func)(int num, char *str, int len));
static void bar(int num, char *str, int len);
You can then call:
foo(10, bar);
Inside foo(), you will have code such as:
void foo(int num, void (*func)(int num, char *str, int len))
{
char str[] = "Supercalifragilisticexpialidocious";
(*func)(num, str, strlen(str));
}
Or, if you're new school (not an archaic relic like me), then:
void foo(int num, void (*func)(int num, char *str, int len))
{
char str[] = "Supercalifragilisticexpialidocious";
func(num, str, strlen(str));
}
These are equivalent. I still prefer the explicit "I'm calling a function via a pointer to function" notation, but it isn't necessary and modern style tends to avoid it.

"programming pearls": Strings of Pearls

In column 15.3, the author introduced how to generate text randomly from an input document. The author also gave the source code.
qsort(word, nword, sizeof(word[0]), sortcmp);
int sortcmp(char **p, char **q)
{ return wordncmp(*p, *q);
}
I've been confused by the above lines in the source code.
The last argument of qsort is:
int comparator ( const void * elem1, const void * elem2 ).
But the definition of sortcmp is different. Actually, the source code cannot compiled in my VS2010.
It seems this code was originally compiled with a more forgiving (or less standard-compliant) compiler. The idea seems to be that the canonical void * arguments of the comparator function are interpreted as char ** so that wordncmp(), which is an implementation of lexicographical comparison of up to length n, can be applied to them.
Declaring the function as expected (i.e. taking two const void * arguments) and making the type casts explicit appears to solve the problem (tested with GCC 4.7.0):
int sortcmp(const void *p, const void *q) {
return wordncmp(*(const char **)p, *(const char **)q);
}
I also had to modify the declaration of the wordncmp() function:
int wordncmp(const char *p, const char* q)
{
/*.. Definition unchanged.. */
}

Casting a Function Pointer

If I have a prototype that is declared as:
void foo(int (*fi)(void *, void *))
And I call the function with something like this:
foo(int (*)(void*, void*)(bool_expr ? func1 : func2));
Where func1 and func2 are defined as follows:
int func1(char *s1, char *s2);
int func2(int *i1, int *i2);
Is the function call casting one of the functions (func1 ^ func2) to the type of the function required by foo? Could I also make the prototype for foo look like:
void foo(int (*)(void *, void *))
According to C specification, casting function pointers results in unspecified behavior, but many compilers (e.g. gcc) do what you expect them to do, because function pointer is just an address in memory. Just to be safe, I would re-declare func1 and func2 to take void*, and then cast these pointers to char* and int* as required.
In GCC, this expression:
bool_expr ? func1 : func2
gives this warning:
warning: pointer type mismatch in conditional expression
even if you don't turn on special warnings.
What's more, GCC resolves this pointer-type mismatch by casting both expressions to void *; so then, when you try to cast it back (either explicitly, with (int (*)(void*, void*)), or implicitly, by passing it to foo), it gives this warning:
warning: ISO C forbids conversion of object pointer to function pointer type
if you enable pedantry. (The reason that ISO C forbids this is that a function pointer does not have to be implemented, internally, as a pointer to a memory location, so it may not have the same size and whatnot as a regular "object" pointer.)
That said, this:
foo((bool_expr ? (int (*)(void*, void*))func1 : (int (*)(void*, void*))func2));
should be relatively safe, provided that foo is passing valid char pointers to func1 or valid int pointers to func2.
And I would guess that, on a system where function pointers are truly incompatible with void *, a compiler wouldn't resolve the mismatch bool_expr ? func1 : func2 in favor of void * (though I haven't consulted the spec on this).
As commented the code as posted did not compile.
FWIW, this compiles and executes as you would expect with VC2010:
int func1(char *s1, char *s2) { printf("func1\n"); return 0; }
int func2(int *i1, int *i2) { printf("func2\n"); return 0; }
void foo(int (*)(void *, void *));
int main(int argc, char** argv)
{
foo(2 == argc ? (int(*)(void*,void*))func1 :
(int(*)(void*,void*))func2);
return 0;
}
void foo(int (*a)(void *, void *))
{
a((void*)0, (void*)0);
}

pointer from integer w/o cast warning when calling lfind

I'm writing a vector in C. The CVectorSearch function uses bsearch if it's sorted, and lfind if it's unsorted. Why am I getting the warning "assignment makes pointer from integer without a cast" when I'm calling lfind? It seems to work fine even when lfind is being used.
typedef struct
{
void *elements;
int logicalLength;
int allocatedLength;
int elementSize;
} CVector;
typedef void (*CVectorFreeElemFn)(void *elemAddr);
int CVectorSearch(const CVector *v, const void *key,
CVectorCmpElemFn comparefn,
int startIndex, bool isSorted)
{
void * found;
int elemSize = v->elementSize;
int length = v->logicalLength;
void *startAddress = (char*)v->elements + startIndex*elemSize;
if(isSorted)
found = bsearch(key, startAddress, length, elemSize, comparefn);
else
found = lfind(key, startAddress, &length, elemSize, comparefn);
if(found)
return ((char*)found - (char*)v->elements) / elemSize;
else
return -1;
}
edit: Now that I've included search.h I'm getting:
warning: passing argument 3 of 'lfind' from incompatible pointer type
The program is still working correctly, though.
Have you included <search.h> which defines lfind? If a function is called without a prototype, your compiler may assume it returns int.
The third argument to lfind() is a pointer to size_t not int as you are passing. The size_t type may be of a different size than int on some architectures (particularly x86-64) and it is also unsigned. You have to change the type of the length variable.
I don't think the above questions really solve the issue as I had this problem. The true answer I believe is the distinction between bsearch prototype and lfind prototype. Let's takea look
void *bsearch(const void *key, const void *base, size_t nmemb,
size_t size, int (*compar)(const void *, const void *));
Versus
void *lfind(const void *key, const void *base, size_t *nmemb,
size_t size, int(*compar)(const void *, const void *));
If you'll notice that the third parameter of the lfind function is a pointer to a size_t type not (as in the bsearch function ) a direct copied value.
Just make sure you pass in the address of the size and it'll be fine.

Resources