I want to generate an array initializer with arbitrary logic that unfortunately requires some looping.
#define RANDOM_ARRAY(n) \
...
double array[] = RANDOM_ARRAY(10);
Suppose the code above generates an initializer for a 10-element array. Is it possible to define such a macro (with a loop) in C99 ?
NB: it doesn't have to be a macro if a function call could suffice (but it has to be possible to call it among global initializers, not in a second function);
Unfortunately, it is not possible to create a recursive (or loop) macrofunction in C. Nevertheless, if you have a reasonable maximum length for your initializer, you can use this type of construct :
#define INITIALIZER(N) { INITIALIZER_ ## N }
#define INITIALIZER_1 1
#define INITIALIZER_2 INITIALIZER_1, 2
#define INITIALIZER_3 INITIALIZER_2, 3
int
main(void)
{
int tab[3] = INITIALIZER(3);
return 0;
}
The C preprocessor doesn't support loops, so what you want is not (easily) possible.
I added the '(easily)' because there are ways to get loop-like behavior using something like boost's ITERATE. This uses recursive file inclusion to emulate a loop. But I'm not sure if you want to go that far.
Since you're working in C99, you can of course create a macro that does the initialization, but you won't be able to make it look like an initializer:
#define INCREMENTING_ARRAY(t,a,n) t a[n]; do {\
for(size_t i = 0; i < n; ++i)\
a[i] = i;\
} while(0);
This creates an array whose elements are initialized to be incrementing, as an example.
Usage:
int main(void)
{
INCREMENTING_ARRAY(int, dozen, 12);
int i;
for(i = 0; i < sizeof dozen / sizeof *dozen; ++i)
printf("array at %d = %d\n", i, dozen[i]);
return 0;
}
This works since in C99 you can freely mix declarations and code, so the int i; after the macro usage is fine. In C89, it wouldn't have worked.
Related
I would like to pass as the second argument to function mem the "correct" size of its first argument, which is an address to void.
I thought of using a macro, MEM, which uses the operator sizeof. This macro is supposed to "infer" at pre-processing time the "correct" size by looking at the text, i.e. how the first argument is written in code.
Except my macro it does not work well when the argument is an array that is passed with the name of the array (arr). (Note that the macro works well if I pass &arr, as suggested in the comments)
#include <stdio.h>
#define MEM(addr) mem(addr,sizeof *addr) // <-- does not work for arrays
void mem(const void* , int );
int main()
{
int t=3;;
int arr[10];
int* p=&t;
MEM(&t); // --> mem(&t,sizeof *&t) <- OK!
MEM(p); // --> mem(p,sizeof *p <- OK!
MEM(arr); // --> mem(arr,sizeof *arr) <- NO! I WANT THE SIZE OF THE ALL ARRAY!
// SO I WANT --> mem(arr,sizeof arr)
return 0;
}
void mem(const void* pp, int bytestoread) // prints bytestoread bytes starting from address pp
{
int i;
unsigned char* p=(unsigned char*)pp;
for(i=0;i<bytestoread;i++)
{
if(i%8==0)
{
if(i!=0) printf("\n");
printf("%p: ",p);
}
printf("%02x ",*p);
p++;
}
printf("\n\n");
}
If there's a different solution or approach to my problem other then a macro, even better. But arrays decay when passed as parameters to a function, so I don't see how I can use a function to accomplish what I want.
I would be happy even if there was some hack with the C preprocessor, like using the stringizing preprocessor operator # on the name of the address written in code and selectively compile MEM accordingly, but I couldn't do that. (maybe using also the #if #else #endif preprocessor directives ...? )
FOLLOW-UP:
The following would be a tentative solution that exploits the fact that for an array arr (as opposed to a regular pointer) &arr==arr...
#define SIZE(add) ( #add[0]=='&' ?
sizeof *add :
((void*)add==(void*)&add ? sizeof add : sizeof *add) )
#define MEM(address) mem( address, SIZE(address) )
...but of course this, as is, doesn't compile when address is written as &t in the code (where t is an int for example), because &&t is not syntactically correct, even if the check #add[0]=='&' makes sure it would never be executed.
1) Is there a way to make it work?
2) Or should I write a parser that outputs the code I want to be compiled based on the text I pass to the parser?
3) Why is the C pre-processor so strict regarding what I can use as a condition in #if directives? (I cannot even use string literals nor # operator! Only const expressions like 2>1 or other defined macro are allowed)
It seems that there is no obvious solution with MEM macro, although there might be a solution with preprocessor condition expressions where according to the type of the operand we decide how to calculate the size.
The common practice in C is creating functions with an extra argument that takes the size of the data container. So you would write something like this, without macro usage at all:
int t = 3;
int arr[10] = {0};
int *p = &t;
mem(&t, sizeof(t));
mem(p, sizeof(*p));
mem(arr, sizeof(arr) / sizeof(arr[0])); // <- 40/4 = 10
This is quite verbose but in this case, you could rely on this and be sure that you're getting the expected behavior.
I want to replace array subscript of a C code with a call to a hash function. Is it possible to do this using a C macro?
This is an old code and it is very difficult to make changes to it. It will be very helpful, if I could just use a macro and a hash function instead of changing the actual code.
int array[1000];
// I want to define a macro such that array[idx] will be replaced with
// array[hash(idx)] and then define a function called hash(idx) which
// will return an integer within the bounds of the array (0..999)
int main()
{
int input;
scanf("%d", &input);
array[input] = 1;
return 0;
}
I've tried to check around, but I can't seem to find an answer.
I'm writing some C code, and I want to figure out if given int i, is that defined as a macro somewhere. For instance, running a for loop checking if the counter is a macro. I've come up with the following, but it give me warnings when I compile, so I'm assuming its bad code
for(int i = 1; i < 25; i++){
#define DEFINED 1
#ifndef i
#define DEFINED 0
#endif
int a = DEFINED;
if(a){
bla bla
}
}
Thank you so much.
Short answer: You can't use the value of i as a condition in a preprocessor expression. You can't even check if it's defined.
Longer answer: preprocessing is one of the early translation stages. It happens even before the code is compiled. The value of i is available only during run time. Meaning after the program has been compiled, linked, and then executed. The two stages are as far apart as they can be.
You can't check if an i variable is defined either, since the symbol i is known as a variable only during the compilation stage (again, after the preprocessor has finished its run).
It's true that the preprocessor allows you to conditionally compile code, but you cannot base those conditions on things which are known only at later translation stages.
The reason you're getting a warning is because you redefine DEFINED in the event the macro i is not defined. You need to define this macro entirely within an #if.
#ifndef i
#define DEFINED 0
#else
#define DEFINED 1
#endif
The preprocessor doesn't "know" C. Barring special directives like #error and #pragma, it is much just a text-substitution system.
#if((n)def) only works for macros declared with the preprocessor with the #define keyword; C variables like int i are not macros.
To give you a better idea:
#include <stdio.h>
#define MYFORLOOP for(i = 0; i < 25; i++)
int main()
{
int i;
int data[25];
MYFORLOOP
{
data[i] = i;
}
MYFORLOOP
{
printf("%d\n", data[i]);
}
return 0;
}
The code is then preprocessed before it is actually compiled, so what the compiler actually "sees" is:
// < The contents of stdio.h, including any headers it includes itself >
int main()
{
int i;
int data[25];
for(i = 0; i < 25; i++)
{
data[i] = i;
}
for(i = 0; i < 25; i++)
{
printf("%d\n", data[i]);
}
return 0;
}
The reason #ifdef i would fail is because the variable i is not a #define macro.
I have a set of arrays :msg1[] msg2[] .... msgn[] . And I need to use the values in a while loop. as msgi[]. When I define it as #define MSG(a) msg##a
and put it in a loop and increment i, it expands it to msgi?
You can't do it that way. Instead you could create a new array, that contains pointers to the actual arrays:
int array1[...];
int array2[...];
int *all_arrays[] = { array1, array2 };
build your c code with gcc -E myfile.c and you will see the reason
this called preprocessor code. the prprocessor code is the code generated by your compilator before the compilation. in this code the compilator replace the macros in your origin code with the content of the macro.
your origin code:
for (i=0; i<10; i++) {
for (j=0; j<10; j++)
MSG(i)[j] = 3;
}
preprocessr code generated from the origin code (could be seen with gcc -E):
for (i=0; i<10; i++) {
for (j=0; j<10; j++)
msgi[j] = 3;
}
You can use 2D array instead
int msg[5][5];
It can't be done cause macros are replaced at compilation time not runtime, so it will be replaced once...
what you could do is use 2D array if there are in the same size or use array of arrays if there are in different sizes:
//once in your code you need to do this:
int array0[];
int array1[];
//...
int arrayX[]; //X should be replaced with a real number...
int **arrayOfArrays = new (int*)[X+1];
arrayOfArrays[0] = array0;
arrayOfArrays[1] = array1;
//...
arrayOfArrays[X] = arrayX;
//now you could use it like that anytime in your code:
int i;
for(i = 0; i < X; ++i)
{
//Do whatever you want to do, like:
arrayOfArrays[i][0] = 1234;
}
When I define it as #define MSG(a) msg##a and put it in a loop and
increment i, it expands it to msgi?
No, it will not work that way, because the macro gets expanded before compilation, not after. You'll need a different approach, such as the 2D array suggested by #zakinster.
No, unfortunately it won't. C does not support runtime name lookups. Instead, you should use a two dimensional array of the form:
void** msg;
This will allow the arrays to be of different sizes and types, although you will have to cast to whatever type the array is.
I just came to C from C# and was looking for a way to define generic functions like those in C#. I came across this post but when I tried to compile it I get a bunch of errors ("`n' undeclared here (not in a function)", " syntax error before "array" ", etc.)
Here's my code:
#include<conio.h>
#include<stdlib.h>
#define MAKE_PRINTEACH(TYPE)\
void printeach_##TYPE (TYPE[n] array, int n, void(*f)(TYPE)){\
int i;\
for(i = 0; i < n; i++) {\
f(array[i]);\
}\
}
MAKE_PRINTEACH(int)
MAKE_PRINTEACH(float)
void printInt(int x)
{
printf("got %d\n",x);
}
void printFloat(float x)
{
printf("got %f\n",x);
}
int main()
{
int[5] ia = {34,61,3,6,76};
float[6] fa = {2.4,0.5,55.2,22.0,6.76,3.14159265};
printeach_int(ia, 5, printInt);
printeach_float(fa,6,printFloat);
getch();
}
What am I doing wrong here?
I am using DevC++ if that makes a difference.
A correct version would look like this
#define MAKE_PRINTEACH(TYPE) \
void printeach_##TYPE (size_t n, TYPE array[n], void(*f)(TYPE)){ \
for(size_t i = 0; i < n; i++) { \
f(array[i]); \
} \
}
to summarize what went wrong with your version:
n must be declared before it is used
the array bounds come after the identifier
the semantically correct type for array sizes and things like that is size_t
C since C99 also has local variables for for loops.
You might try this variation:
#define MAKE_PRINTEACH(TYPE)\
void printeach_##TYPE (TYPE * array, int n, void(*f)(TYPE)){\
int i;\
for(i = 0; i < n; i++) {\
f(array[i]);\
}\
}
The TYPE[n] array implies the compiler supports VLA (Variable Length Array) and I do not know whether your compiler does.
For gcc adding the command line option -std=c99 would make the original code compile.
Update:
Corrections applied as by Jens's comment.
The solution I propose is to simply pass a pointer to a variable of the type which the array (as proposed in the OP) would have contained. Doing so, is the way arrays are passed to a function. They are passed by reference.
Also Jens mentions several other warnings/errors. As there are:
1 conio.h is not a standard C include, stdio.h whould be appropriate here
2 Arrays are declared by adding the array's size to the variable name, not to the type. It has to be: int ia[5]not int[5] ia
3 main() returns int, the OP does not return anything.
4 The prototype for getch() is missing. One might like to include curses.h