typedef fixed length array - c

I have to define a 24-bit data type.I am using char[3] to represent the type. Can I typedef char[3] to type24? I tried it in a code sample. I put typedef char[3] type24; in my header file. The compiler did not complain about it. But when I defined a function void foo(type24 val) {} in my C file, it did complain. I would like to be able to define functions like type24_to_int32(type24 val) instead of type24_to_int32(char value[3]).

The typedef would be
typedef char type24[3];
However, this is probably a very bad idea, because the resulting type is an array type, but users of it won't see that it's an array type. If used as a function argument, it will be passed by reference, not by value, and the sizeof for it will then be wrong.
A better solution would be
typedef struct type24 { char x[3]; } type24;
You probably also want to be using unsigned char instead of char, since the latter has implementation-defined signedness.

You want
typedef char type24[3];
C type declarations are strange that way. You put the type exactly where the variable name would go if you were declaring a variable of that type.

From R..'s answer:
However, this is probably a very bad idea, because the resulting type
is an array type, but users of it won't see that it's an array type.
If used as a function argument, it will be passed by reference, not by
value, and the sizeof for it will then be wrong.
Users who don't see that it's an array will most likely write something like this (which fails):
#include <stdio.h>
typedef int twoInts[2];
void print(twoInts *twoIntsPtr);
void intermediate (twoInts twoIntsAppearsByValue);
int main () {
twoInts a;
a[0] = 0;
a[1] = 1;
print(&a);
intermediate(a);
return 0;
}
void intermediate(twoInts b) {
print(&b);
}
void print(twoInts *c){
printf("%d\n%d\n", (*c)[0], (*c)[1]);
}
It will compile with the following warnings:
In function ‘intermediate’:
warning: passing argument 1 of ‘print’ from incompatible pointer type [enabled by default]
print(&b);
^
note: expected ‘int (*)[2]’ but argument is of type ‘int **’
void print(twoInts *twoIntsPtr);
^
And produces the following output:
0
1
-453308976
32767

Arrays can't be passed as function parameters by value in C.
You can put the array in a struct:
typedef struct type24 {
char byte[3];
} type24;
and then pass that by value, but of course then it's less convenient to use: x.byte[0] instead of x[0].
Your function type24_to_int32(char value[3]) actually passes by pointer, not by value. It's exactly equivalent to type24_to_int32(char *value), and the 3 is ignored.
If you're happy passing by pointer, you could stick with the array and do:
type24_to_int32(const type24 *value);
This will pass a pointer-to-array, not pointer-to-first-element, so you use it as:
(*value)[0]
I'm not sure that's really a gain, since if you accidentally write value[1] then something stupid happens.

To use the array type properly as a function argument or template parameter, make a struct instead of a typedef, then add an operator[] to the struct so you can keep the array like functionality like so:
typedef struct type24 {
char& operator[](int i) { return byte[i]; }
char byte[3];
} type24;
type24 x;
x[2] = 'r';
char c = x[2];

Here's a short example of why typedef array can be confusingly inconsistent. The other answers provide a workaround.
#include <stdio.h>
typedef char type24[3];
int func(type24 a) {
type24 b;
printf("sizeof(a) is %zu\n",sizeof(a));
printf("sizeof(b) is %zu\n",sizeof(b));
return 0;
}
int main(void) {
type24 a;
return func(a);
}
This produces the output
sizeof(a) is 8
sizeof(b) is 3
because type24 as a parameter is a pointer. (In C, arrays are always passed as pointers.) The gcc8 compiler will issue a warning by default, thankfully.

Building off the accepted answer, a multi-dimensional array type, that is a fixed-length array of fixed-length arrays, can't be declared with
typedef char[M] T[N]; // wrong!
instead, the intermediate 1D array type can be declared and used as in the accepted answer:
typedef char T_t[M];
typedef T_t T[N];
or, T can be declared in a single (arguably confusing) statement:
typedef char T[N][M];
which defines a type of N arrays of M chars (be careful about the order, here).

Related

How to user define array of function pointers which have same prototype as main function?

I have an assignment to typedef function pointers which can point to main function. I tried something like this but I'm not sure if it's viable.
typedef mainPtr(*f[])(int, char**);
The thing that bothers me is that array size is not defined. How would you do this?
The type of main (in the form you want) is int main(int, char **).
A pointer to that is int (*main)(int, char **).
An array of those is int (*main[])(int, char **).
A typedef of that is typedef int (*mainPtr[])(int, char **);.
Whether you need a size for the array depends on how you will use the type. If you define and initialize an object of this type, its array size will be completed by counting the initializers. For example:
mainPtr p = { main, NULL };
will create an array with two elements.
In other uses, such as declaring a function parameter with this type, you may not need the array to be complete. An array function parameter is automatically adjusted to be a pointer, so the size is discarded anyway. However, if you wish, you could include the size in the typedef.
The syntax is easier if you typedef function itself:
typedef int mainfunc(int, char **);
Then you can use the "normal" pointer syntax:
/* definition of the function pointer*/
mainfunc *mainptr;
/* definitions of the function pointer arrays*/
mainfunc *mainptrarray[5];
mainfunc *mainptrarray1[] = {foo, bar, NULL};

Why typedef for a function pointer is different from a regular typedef?

typedef regularly works like: typedef <type> <type_alias>. But typedefs for function pointers seems to have different structure: typedef int (*fn)(char *, char *); - there is no type alias, just a single function signature.
Here is the example code:
#include <stdio.h>
typedef void (*callback)(int);
void range(int start, int stop, callback cb) {
int i;
for (i = start; i < stop; i++) {
(*cb)(i);
}
}
void printer(int i) {
printf("%d\n", i);
}
main(int argc, int *argv[])
{
if (argc < 3) {
printf("Provide 2 arguments - start and stop!");
}
range(atoi(argv[1]), atoi(argv[2]), printer);
}
So - why typedefs for function pointers are different?
The syntax for using typedef to define a function pointer type follows the same syntax as you would use for defining function pointers.
int (*fn)(char *, char *);
Defines fn to be a pointer to a function ...
typedef int (*fn)(char *, char *);
Defines fn to be a type that is a pointer to a function ...
It works the same. You just have to look at it in a slightly different way. typedef defines your own type name by putting it in exactly the place where the identifier of your variable would go without the typedef. So
uint8_t foo;
becomes
typedef uint8_t footype;
footype foo;
edit: so "R Sahu" was a bit faster and see his example for the same principle applied to function pointers.
C declaration syntax is much more complicated than type identifier, with examples like
T (*ap)[N]; // ap is a pointer to an N-element array
T *(*f())(); // f is a function returning a pointer to
// a function returning a pointer to T
Syntactically, typedef is treated as a storage class specifier like static or extern. So you can add typedef to each of the above, giving
typedef T (*ap)[N]; // ap is an alias for type "pointer to N-element array
typedef T *(*f())(); // f is an alias for type "function returning
// pointer to function returning pointer to T"

Possible side effects of doing a typedef of a struct to an array of one element in C

I came across this code.
typedef __mpz_struct MP_INT;
typedef __mpz_struct mpz_t[1];
Here the struct __mpz_struct is a struct that is typedefed to an array of single element. I understand that this is a trick to pass by reference in C. Then mpz_t has been used as a type to declare variables and pass them to function as parameters. Also, There was one more comment
/*
MP_INT*, MP_RAT* and MP_FLOAT* are used because they don't have side-effects
of single-element arrays mp*_t
*/
What kind of side effects are they talking about?
Passing an array to a function let's the array decay to a pointer to it's 1st element.
One can achieve the same effect by applying the Address-Of operator & to a simple variable of the same type as the array's elements.
Examples:
struct S
{
int i;
float f;
};
This
void set_S(struct S * ps)
{
ps->i = 40;
ps->f = 2.;
}
is equivalent to
void set_S(struct S ps[1])
{
ps->i = 40;
ps->f = 2.;
}
is equivalent to
void set_S(struct S * ps)
{
ps[0].i = 40;
ps[0].f = 2.;
}
is equivalent to
void set_S(struct S ps[1])
{
ps[0].i = 40;
ps[0].f = 2.;
}
One-Element-Array approach:
typedef struct S Array_of_S_with_size_1[1];
int main(void)
{
Array_of_S_with_size_1 array_of_S_with_size_1;
array_of_S_with_size_1[0].i = 0;
array_of_S_with_size_1[0].f = 0.;
set_S(array_of_S_with_size_1);
...
}
The above main() provides the same functionality as the following:
int main(void)
{
struct S s;
s.i = 0;
s.f = 0.;
set_S(&s);
...
}
I do not see any gain using the "One-Element-Array" approach. An expection might be if the &-key is broken on ones keyboard ... ;-)
I see two parts to your question. The first part, how the typedef works for passing arguments to functions, would better be illustrated with an example. Without it, I'll have to guess a bit.
In C function declarations, an array parameter is equivalent to a pointer. That's why you see (for example) equivalently for the main function,
int main(int argc, char **argv)
and
int main(int argc, char *argv[])
Similarly, if a function in your program would be declared
int func(__mpz_struct *arg)
it would be equivalent to
int func(__mpz_struct arg[])
and hence to
int func(mpz_t arg)
Also, on the calling side, if you have a variable of type mpz_t, hence the array, and you pass it to a function, the "pointer decay" takes effect: in an expression, if you use (the name of) an array it "decays" into a pointer to its first element.
This way you can call the function:
mpz_t value;
func(value);
Of course, to modify these mpz_t objects outside of the API functions, you still have to be aware of their true nature.
The side effects that you mention, I would also have to guess about them. Possibly it is meant that you have to be aware you're working with pointers inside the functions. It might be considered better to make that explicit by using the pointer syntax.
You can assign an MP_INT to another but you can not assign an mpz_t to another since assignment is not defined for arrays. If you do not want your clients to assign variables other than by your methods (which might do memory managements and stuff) this is the trick for you.
Look at this sample code
typedef char type24[3];
Same as your, but well known data type 'char' insted of your struct __mpz_struct type.
above type def means, I am using above typedef to represent char[3].
So in your sample code,
typedef __mpz_struct mpz_t[1];
mpz_t should be __mpz_struct type.

C: Why aren't &(void *) and void** compatible?

I'm receiving an incompatible pointer type error warning although my code functions properly. First of all, here's a simplified version of my code:
typedef struct {
int field1;
int field2;
} my_struct;
void some_function(void **data){
// do something with *data
}
int main(int argc, char *argv[]){
my_struct *ptr = malloc(sizeof(my_struct));
ptr->field1 = 5;
ptr->field2 = 17;
some_function(&ptr);
}
The my_struct type is one example, but I'm actually using multiple types, so some_func must accept void**, rather than my_struct**. Each time I call some_func in the manner above, I recieve the following warning, and yet my code functions properly.
warning: passing argument 1 of ‘my_func’ from incompatible pointer type
I'm unsure of why these types aren't compatible, and would like to remove the warning in some way. For answers, feel free to explain why this is happening and, if possible, how to remove the warning (cpp directive, maybe?). Thanks!
Use a void * parameter type.
void ** is not a generic pointer type. void * is.
void* means (effectively) "a pointer to an untyped region of memory".
void** means (effectively) "a pointer to a region of memory, which contains pointers to untyped regions of memory".
Notice how the block in the middle has a type -- an array of void*. You're passing in effectively an array of mystruct*, which is a different type. So your compiler warns you.
Similar issues occur in C++ when trying to convert Derived** to Base**; but it applies here in C as well. (void* operates like the "base class" of all pointer types) Consider a piece of code like this:
void does_things(void** outParameter)
{
*outParameter = malloc(42); // Looks fine; writing void* to void* :)
}
int main()
{
my_struct* x = /*...*/;
does_things(&myStruct); // Ut oh, tries to make x point to
// something that is *not* a my_struct
}

Can I pass a structure into a function that takes void*?

Since this is a void* I should be able to pass a pointer of anytype right? Why is the compiler giving me errors?
int cmp_func(void *, void *));
typedef struct word_{
char key[WORD_SIZE];
int *frequency;
} word;
Phash_table new_hash(int size, int (*hash_func)(char *), int (*cmp_func)(void *\
, void *));
int comp_function(struct word_ *word1,struct word_ *word2){
if( word1->frequency < word2->frequency){
return -1;
}
if(word1->frequency < word2->frequency){
return 1;
}
if(word1->frequency == word2->frequency){
return 0;
}
}
project4_functions.c:47:3: warning: passing argument 3 of 'new_hash' from incompatible pointer type [enabled by default]
hash.h:38:13: note: expected 'int (*)(void *, void *)' but argument is of type 'int (*)(struct word_ *, struct word_ *)'
The key is to make your compare function take void pointers as well:
int comp_function(void *a, void *b){
struct word *word1 = a;
struct word *word2 = b;
// Use word1 and word2 as before.
}
Addendum, concerning why the compiler is giving you warnings:
To quote the c99 standard which I found here
A pointer to void may be converted to or from a pointer to any incomplete or object
type. A pointer to any incomplete or object type may be converted to a pointer to void
and back again; the result shall compare equal to the original pointer.
This means that you can have code like the following, and the compiler won't issue the warning you're seeing:
void *a = NULL;
int (*f)(int a, char *b) = NULL;
a = f;
f = a;
It's tempting to extrapolate that this means the following will also work (after all, we're just substituting "void*" with "struct foo*", right?)
int (*f1)(void*, void*);
int (*f2)(struct foo*, struct foo*);
f1 = f2;
However, this generates your warning since it is not trying to assign a pointer type to a pointer to void (or vice-versa) as is allowed by the standard. Instead it is trying to assign a value of type int (*)(struct foo*, struct foo*) to a variable of type int (*)(void*, void*).
Of course, you could try to make the compiler happy with an explicit cast, which convinces the compiler that you must know what you're doing. But in doing so you lose the privilege and safety of getting these warnings even when invoking "iffy" behavior.
Your question does not match your code. Your code does not pass a structure pointer as a void pointer. It is passing one function pointer as another. The function pointers are not compatible, hence the error.
It is legal to pass a structure pointer where a void pointer is expected because a structure pointer can be implicitly converted to a void pointer. It is not required to be representationally identical to a void pointer. (There are some machines where structure pointers are not the same size as a void pointer, for example.)
By analogy consider the case of passing an int when a long is expected. This is legal because there is an implicit conversion, but that doesn't meant that a function accepting int is interchangeable with function accepting long.
You need to cast the function pointer since your function prototype does not match the one the function expects:
typedef int (cmp_f)(void *, void *));
new_hash(..., ..., (cmp_f*)cmp_func_p);
Of course that typedef is not necessary, but it makes your code much more readable than without (you usually only do it without that typedef in exams where you are not allowed to use typedef for this purpose ;))

Resources