So usually I would declare any function pointer like this:
typedef size_t (*hash_function)(const int *);
and then later use it in another function
HashTable *hash_table_create(const hash_function hash)
so for any function which fulfills the hash_function definition like
size_t hash_modulo(const int *parameters)
size_t hash_universal(const int *parameters)
...
I can use them as a parameter
hash_table_create(hash_modulo)
The problem is: My IDE (Clion) complains that the parameters in this case do not match (the code works tho). Specifically it doesn't seem to accept passing hash_function as a parameter type, but will accept if I use size_t (*hash_function)(const int *) instead. What am I missing here?
Is my code right and my IDE wrong or vice versa?
Thanks in advance!
Edit 1: The exact error message is: Types 'hash_function' and size_t(const int *)' are not compatible
Edit 2: This seems to be a Clion Bug
CLion seems to have a bug (possibly). The function names are of the type size_t(const int *). Now, since functions are implicitly convertible to function pointers, your code is perfectly valid C.
The CLion syntax checker probably doesn't take implicit conversions into account. If you obtain a function pointer explicitly from the function name the error should go away:
hash_table_create(&hash_modulo); // Note the ampersand
I think the problem is that you typedef the function as const
HashTable *hash_table_create(const hash_function hash)
and the other functions you want to put in as parameters aren't declared const
size_t hash_modulo(const int *parameters)
size_t hash_universal(const int *parameters)
Edit:
This works fine in CodeBlocks
Change this:
size_t hash_modulo(const int *parameters)
size_t hash_universal(const int *parameters)
into this:
hash_function hash_modulo;
hash_function hash_universal;
and then this work fine:
hash_table_create(hash_modulo);
hash_table_create(hash_universal);
Explanation in the comment below.
Related
I'm sorting an array of strings by using the qsort function.
char treeName[100][31];
By trial and error, I figured out that it should be done like this:
qsort(&treeName[0], count, sizeof(*treeName), Comparar_nome);
However, I am not entirely sure why sizeof(*treeName). Shouldn't it be sizeof(char) * 31, or something like that?
If treeName is indeed defined as char treeName[100][31]; in the scope of the function calling qsort, your code is correct:
qsort(&treeName[0], count, sizeof(*treeName), Comparar_nome);
You could write this equivalently:
qsort(treeName, count, sizeof treeName[0], Comparar_nome);
Note however that Comparar_nome must have the correct prototype:
int Comparar_nome(const void *a, const void *b);
A simple implementation being:
#include <stdio.h>
int Comparar_nome(const void *a, const void *b) {
return strcmp(a, b);
}
Passing strcmp instead of Comparar_nome would be a type mismatch invoking undefined behavior that would go unnoticed in many architectures but is nevertheless incorrect.
qsort(&treeName[0], count, sizeof(*treeName),Comparar_nome);
Can be broken down as follows:
qsort
The function call.
&treeName[0]
Address of the start of the array. Can be simplified to treeName.
count
The number of entries in treeName actually used.
sizeof(*treeName)
The size of an array element. I would have written this is sizeof(treename[0]) but there's no difference. sizeof(char)*31 really should have worked. Are you sure you didn't have something else broken when you tried it? We were unable to find a real compiler for which this would not work. sizeof(*treename) is better anyway for readability and should that 31 ever change.
Comparar_nome
Address of the function that compares tree nodes. You wrote it correctly; &Comparar_nome is archaic style.
when I had a look on the sources of opensc, especial the libpkcs11.c file, I found a type declaration combined with a function call which I just don't understand:
CK_RV rv, (*c_get_function_list)(CK_FUNCTION_LIST_PTR_PTR);
c_get_function_list = (CK_RV (*)(CK_FUNCTION_LIST_PTR_PTR)) sc_dlsym(mod->handle, "C_GetFunctionList");
I know that:
CK_RV is a typedef for unsigned long
CK_FUNCTION_LIST_PTR_PTR is a typedef for something like **ck_function_list where ck_function_list is a struct
rv is a variable
c_get_function_list is a variable
But if I split the first line and substitute the typedefs with their original values , I get something like:
unsigned long rv;
unsigned long (*c_get_function_list)(**ck_function_list);
So, what does the (**ck_function_list) in the second line mean ?
Thanks in advance,
Robert
unsigned long (*c_get_function_list)(<param>);
is a function pointer to a function whose prototype is.
unsigned long func(<param>);
The pointer here is c_get_function_list
c_get_function_list is pointer to function. When assigned, you can call the pointed function like normal function call: c_get_function_list( param ).
I want to implement a function like a c++ generic. Apart from integers, the parameters should also support char type, and any other types as well.
void function t(int a[],int len);
How to implement this in C language?
Since you don't have templates in C like you do C++ you have a few different options, which may be suitable depending on the task at hand:
Using the pre-processor and creating a basic macro, such as your basic MIN/MAX type macro that simply wraps a few < and > operations.
Writing different versions of the function for specific data types - function_int(int...), function_char(char...), etc.
Writing a generic function which takes in data as a void*, not knowing the implementation details, and a function pointer which is called by the function which acts on the data and does know how to operate on this data.
In this case, look at a function like bsearch, where the search key and array are of type void *, and a comparison function is passed for a specific type of data:
void *bsearch(const void *key, const void *base, size_t nmemb, size_t size,
int (*compar)(const void *, const void *));
The comparison function is the only piece of code that needs to know the type of the data, so you only need to write the generic part of your function once. However, you do still need to write a function for each data type that you want to operate on, as with the point above.
This is a technique that I believe was described in the book C Unleashed. The book probably has something a little more elegant than what I am able to reproduce from memory.
You can have an include file like this:
/* function.tmpl */
#ifndef FUNCTION
#define FUNCTION_PASTE(X,Y) X ## _ ## Y
#define FUNCTION(X) FUNCTION_PASTE(function, X)
#endif
void FUNCTION(TYPE) (TYPE a[], int len) {
/* do something */
}
Then you can include it like this:
#define TYPE int
#include "function.tmpl"
#undef TYPE
#define TYPE double
#include "function.tmpl"
#undef TYPE
And call functions like this:
int a[4];
FUNCTION(int)(a, 4);
double b[5];
FUNCTION(double)(b, 5);
The idiomatic way to do this in C is to pass a void*. But then you need an extra information to actually do anything with it.
You could try something like this:
enum data_type { CHAR_TYPE, INT_TYPE };
void function t(void* buf, size_t len, enum data_type type) {
int* int_ary;
char* char_ary;
if (type == CHAR_TYPE) {
char_ary* = buf;
...
} else if (type == INT_TYPE) {
int_ary* = buf;
...
}
}
It's hackish and error-prone, but it could work. It's not exactly like C++ in that it's one unified function instead of a separate version for each type.
I have the following code parts:
typedef struct Board* BoardP;
typedef struct Board {
int _rows;
int _cols;
char *_board;
} Board;
char* static allocateBoard(BoardP boardP, int row, int col) {
boardP->_rows = row;
boardP->_cols = col;
boardP->_board = malloc(row * col * sizeof(char));
return boardP->_board;
}
i can't seem to figure out why it gives the error
expected identifier or ‘(’ before ‘static’
it gives the error after i changed return type to be char*. when it was void no error was given.
and one more question: i was taught that cast is needed when using malloc, however, this seems to be working ok without a cast. is it needed in this case?
thanks
Change your function to
static char* allocateBoard(BoardP boardP, int row, int col):
The return value of malloc is a void*, and in C (unlike C++), a void* is implicittly convertible to any
other pointer type - except function pointers. so you don't need a cast.
Your function prototype needs to be:
static char* allocateBoard(BoardP boardP, int row, int col)
No cast is needed on malloc() in C; however, it is in C++.
In C, Casting for malloc is needed before ANSI C, as there was no void* type (perhaps as extension on some C compilers), but after ANSI C there is no need for casting at all, if you do that then you are suppressing some useful diagnostics by the compiler which is more harmful to your program. Never do casting on malloc() in C.
Although the casting here in this case works for you. It is suggested that you make it a habit to cast things, before you try to assign them to a different type. It saves a lot of pain later. Also when incorporating this code into C++ it saves a lot of time.
I have a function:
int get_symbol(tab *tp, FILE *fp, char delim)
and I call it like this:
get_symbol(tp, fp, ';')
I always have it declared in the header as:
int get_symbol(tab *, FILE *, char);
No this all works fine, I can execute the code in the function and the delim is set.
But if I try to add one more char to the function's signature like:
int get_symbol(tab *tp, FILE *fp, char delim1, char delim2)
The function stops executing. Why would that be?
You should have :
int get_symbol(tab *tp, FILE *fp, char delim1, char delim2)
{
blah blah;
return 1;
}
...
...
get_symbol(tp, fp, ';','?')
do You?
OK, there's not enough information here, so I'm going to make a wild stab at an answer.
You're using a C++ compiler, and don't have warning levels set very high. You've changed the prototype for the function, but you've not changed the arguments when you call it. The C++ compiler is treating these as different functions due to overloading, and so is not calling the right one.
This may be way off what's happening. If it is, give us something more to go on….
As a guess at what "stops executing" could mean, did you update the signature in the header file as well?