Preprocessor error when defining = - c

I was trying some awkward preprocessing and came up with something like this:
#include <stdio.h>
#define SIX =6
int main(void)
{
int x=6;
int y=2;
if(x=SIX)
printf("X == 6\n");
if(y=SIX)
printf("Y==6\n");
return 0;
}
gcc gives me the errors:
test.c: In function ‘main’:
test.c:10:8: error: expected expression before ‘=’ token
test.c:12:8: error: expected expression before ‘=’ token
Why is that?

The == is a single token, it cannot be split in half. You should run gcc -E on your code
From GCC manual pages:
-E Stop after the preprocessing stage; do not run the compiler proper. The output is in
the form of preprocessed source code, which is sent to the standard output.
Input files that don't require preprocessing are ignored.
For your code gcc -E gives the following output
if(x= =6)
printf("X == 6\n");
if(y= =6)
printf("Y==6\n");
The second = is what causes the error message expected expression before ‘=’ token

The preprocessor doesn't work at the character level, it operates at the token level. So when it performs the substitution, you get something equivalent to:
if (x = = 6)
rather than your desired:
if (x==6)
There are some specific exceptions to this, like the # stringification operator.

if(x=SIX)
is parsed as
if (x= =6).
So you get the error.

What toolchain are you using? If you are using GCC, you can add the -save-temps option and check the test.i intermediate result to troubleshoot your problem.
I suspect you have a space between the x= and the =6.

Related

Clang parameter -Wno-conditional-type-mismatch and GCC

The following simple C example program correctly emits a compile warning:
#include <stdio.h>
int main(int argc, char *argv[])
{
int *p = NULL;
int q = 1;
if(1 == 2 ? p : q) { printf("Info\n"); }
return(0);
}
The warning emitted is "warning: pointer/integer type mismatch in conditional expression".
In clang, the parameter "-Wno-conditional-type-mismatch" works in such a way that no warnings appear for the example code.
In GCC, I was looking for a similar option. The best I could find was the parameter "-fcond-mismatch", which would allow the example code. From the man page:
Allow conditional expressions with mismatched types in the second and third arguments. The
value of such an expression is void. This option is not supported for C++.
But when using this option, GCC keeps complaining:
# gcc -fcond-mismatch -c example.c
example.c: In function 'main':
example.c:8:17: warning: pointer/integer type mismatch in conditional expression
8 | if(1 == 2 ? p : q) { printf("Info\n"); }
Using GCC 10.3.0, would there be a GCC option or compiler flag to suppress the warning, in a similar way as clang does?
I don't think there is any such flag. GCC's manual says, under -Werror=, "The warning message for each controllable warning includes the option that controls the warning."
Since this message does not mention such an option, this suggests it is not controllable.

Why does including a header using the full path lead to better error messages?

There was a recent post on Ask Ubuntu, where OP was trying to compile a program which included term.h. When the code had #include <term.h>, the errors were:
In file included from clear_screen_UNIX.c:5:0:
clear_screen_UNIX.c:9:6: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
void clear_screen(void) {
^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
clear_screen();
^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
clear_screen();
The OP then included the full path to term.h (#include "/usr/include/term.h"), which led to the much more useful message:
In file included from clear_screen_UNIX.c:7:0:
/usr/include/term.h:125:21: error: expected ‘=’, ‘,’, ‘;’, ‘asm’ or ‘__attribute__’ before ‘->’ token
#define CUR cur_term->type.
^
/usr/include/term.h:202:40: note: in expansion of macro ‘CUR’
#define clear_screen CUR Strings[5]
^
clear_screen_UNIX.c:9:6: note: in expansion of macro ‘clear_screen’
void clear_screen(void) {
^
clear_screen_UNIX.c: In function ‘main’:
clear_screen_UNIX.c:23:14: error: called object is not a function or function pointer
clear_screen();
^
clear_screen_UNIX.c:26:14: error: called object is not a function or function pointer
clear_screen();
These messages clearly indicate the problem is due to a macro expansion.
I verified the results myself, too. I wonder why GCC produced much better errors when the full path was given. Can I make it produce similar messages with the system include syntax as well?
I am using GCC 4.9.2, and I suspect OP was using GCC 4.8.2 (given the version of Ubuntu).
Conclusion
The reason GCC gives different/better messages if the full path of the header is given is that the GCC preprocessor gives information to GCC's cc1 compiler that the included header is a system header file or local header file by some digits at the end of the comment line of the preprocessor-generated .i file.
Then the cc1 compiler will generate more helpful messages if the header file is an local header file, and will suppress some error message if the header file is a system header, according to the GCC documentation.
To make the normal version of the code output error messages just like the code which specified the full path of the header file, as you asked for, GCC needs to stop including all system directories by specifying option -nostdinc, and then explicitly tell GCC the directories it could search for header files while not treat the directory as system directory using -I flag.
For your code, the command line could be like below (GCC_INCLUDE_DIR is your default GCC include directory, for the system default GCC, it could be /usr/lib/gcc/x86_64-unknown-linux-gnu/4.9.2/include/) :
gcc -c t.c -nostdinc -I/usr/include/ -IGCC_INCLUDE_DIR
Source code
Moved the source code here from this original post to make this answer more helpful.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <term.h>
//#include "/usr/include/term.h"
void clear_screen(void) {
if (!cur_term) {
int result;
setupterm( NULL, STDOUT_FILENO, &result );
if (result <= 0)
return;
}
putp( tigetstr( "clear" ) );
}
int main(void) {
puts("I am going to clear the screen");
sleep(1);
clear_screen();
puts("Screen Cleared");
sleep(1);
clear_screen();
return 0;
}
The difference between Preprocessor Generated Files
You could use the following command line to ask GCC to output the preproceser generated code. This code will be fed into the actual compiler of GCC, cc1. If the preprocessor generated files are exactly the same, the cc1 compiler's behavior should be exactly the same. (Assuming the code is put into file t.c)
gcc -E t.c -o t.i
Following is the difference between the two gcc preprocessor generated .i files. The t.fullpath.i is the file generated with the full path header file, while t.i is the code without full path (Some of the diff output have been removed, since they are only filename differences.)
$ diff t.i t.fullpath.i
2920,2922c2920,2924
< # 1 "/usr/include/term.h" 1 3 4
< # 47 "/usr/include/term.h" 3 4
---
> # 1 "/usr/include/term.h" 1
> # 47 "/usr/include/term.h"
2924,2925c2926,2927
< # 48 "/usr/include/term.h" 2 3 4
< # 80 "/usr/include/term.h" 3 4
---
> # 48 "/usr/include/term.h" 2
> # 80 "/usr/include/term.h"
3007,3008c3009,3010
< # 81 "/usr/include/term.h" 2 3 4
< # 673 "/usr/include/term.h" 3 4
---
> # 81 "/usr/include/term.h" 2
> # 673 "/usr/include/term.h"
3041c3043
< # 729 "/usr/include/term.h" 3 4
---
> # 729 "/usr/include/term.h"
The different flag in the preprocessor generated code's comments makes the difference
GCC's cc1 compiler will take advantage of the preprocessor genrated information to generate the source code location of the error message, and also the debug information which will be used for gdb in the future.
For the following format:
# line-number "source-file" [flags]
The digits 3 and 4 of the flags mean:
3: Following text comes from a system header file (#include <> vs #include "")
4: Following text should be treated as being wrapped in an implicit extern "C" block.
For more information about different kind of these flags, please refer to this link.
Therefore, for the code without fully specified path, cc1 compiler will treat it as a system header file, and assume that system code is mostly correct, and then just output error message of the user's code. That is why the error message is shorter.

Error with undefined variable defined within an IF statement

When trying to compile the following snippet of code:
#include <stdio.h>
#include <time.h>
void change_a(int * a) {
*a = 1;
return;
}
void main(void) {
int a = 0;
change_a(&a);
if (a) {
time_t start = time(NULL);
}
/* do something that never mentions a */
if (a) {
time_t end = time(NULL);
printf("Time spent = %ld\n", (int) end - start);
}
}
GCC states that the start variable is undefined in the printf line:
$ gcc --version
gcc (Debian 4.8.2-1) 4.8.2
[SNIP]
$ gcc -o test.bin test.c
test.c: In function ‘main’:
test.c:24:44: error: ‘start’ undeclared (first use in this function)
printf("Time spent = %ld\n", (int) end - start);
On the other hand, it compiles and works OK when changing the main function to:
void main(void) {
int a = 0;
time_t start = 0;
change_a(&a);
if (a) {
start = time(NULL);
}
...
Question 1
Am I doing something wrong or is the compiler doing something funny I don't know about?
I think it may be the compiler being too smart and optimizing that piece of code out or having a failure in its heuristics or something. But every other time I've "found compiler bugs" it was always me missing some very obvious error.
So I'd rather intelligent people check this before I accuse other smart people of not being so smart. Specially when the issue also happens without optimization:
$ gcc -O0 -o test.bin test.c
test.c: In function ‘main’:
test.c:24:44: error: ‘start’ undeclared (first use in this function)
printf("Time spent = %ld\n", (int) end - start);
^
test.c:24:44: note: each undeclared identifier is reported only once for each function it appears in
Question 2
I'd also like to know if there is a better way to avoid the compiler error (if not the workaround in the last code snippet). This is kind of obvious if it is my code that is wrong (as the response would include the "correct" code), but I would also like to know how to avoid the error while someone fixes the (alleged) bug in GCC.
In your first example, the start variable is declared inside the scope of the if statement. The variable therefore goes out of scope at the end of the code block (the closing curly brace }). This is definitely not a compiler bug.
Your "workaround" is the correct solution.
See here for a more thorough description of how variable scope works in C/C++ (and many other languages which use C-style scoping).
maybe "start" scope is in
if (a) {
time_t start = time(NULL);
}
start is cannot refernce out of if block

How to create a macro string

I'm trying to define a string macro before compiling my C code. I've tried something like:
#include <stdio.h>
int main(void) {
printf("%s", AMEM);
return 0;
}
and I've tried to compile with:
gcc -D AMEM="Deus Abencoa" file.c
But I keep getting this message:
file.c:5:15: note: in expansion of macro ‘AMEM’
printf("%s", AMEM);
^
<command-line>:0:4: note: each undeclared identifier is reported only once for each function it appears in
file.c:5:15: note: in expansion of macro ‘AMEM’
printf("%s", AMEM);
Any idea of how to put it to work?
Your shell interprets (“eats up”) the double-quotes. Since they need to be part of the cpp macro (as the C compiler requires them to form a string), you must pass them to the compiler driver, which means escaping them from the shell. Try this:
gcc -D'AMEM="Deos Abencoa"' file.c
Or this (commonly seen with GNU autoconf):
gcc -DAMEM=\"Deos\ Abencoa\" file.c
Do note that there is no space after -D either.
gcc -D AMEM='"Deus Abencoa"' file.c
The shell removes the single quotes, leaving the double quotes visible to the compiler. Before, the shell removed the double quotes.

gcc c error: expected ')' before numeric constant

Hi I have been trying to port LWIP to a new arm device. When compiling the code i get the error message:
"lwip/lwip-1.4.0/src/include/lwip/memp_std.h:35:23: error: expected ')' before numeric constant"
When I go to this file this and below this several similar macros is what I find on that line:
LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
If I remove the need for this macro with a define to deactivate the RAW functionality the error moves to the next LWIP_MEMPOL() macro.
The define it seems to want to put a ')' in front of is defined as this:
#define MEMP_NUM_RAW_PCB 1
The RAW_PCB is not defined but is "combined with MEMP_" to create an element in an enum.
I have tried to complie the whole ting with the -E option to get human redable object files and see if i can find any open '(' around the areas where MEMP_RAW_PCB apears and the substitution of MEMP_NUM_RAW_PCB to 1 but I have not found any by manual inspection yet.
Are there any suggestions on what can be going on here or what more I can do or look for to find the cause of the error?
I should maybe add that so far I don't call on any of the LWIP code from main() or any of the functions used in main().
I solved it with:
#ifndef MEMP_STD_H_
#define MEMP_STD_H_
... // memp_std.h codes ...
#endif //#ifndef MEMP_STD_H_
The error suggests you have unbalanced parentheses. The code you have provided thus far does not indicate where this problem is, but since ) is expected, it probably means the error is actually in the lines of code preceding the one you have shown.
Examine the code preceding the line you have shown (perhaps after using gcc -E) to check to see if all the parentheses are balanced.
If you're defining it with the dash-D option, it will generate the 1 by default, e.g.:
gcc -D 'MAX(A,B) ((A) < (B)? (B) : (A))' ...
Generates:
#define MAX(A,B) ((A) < (B)? (B) : (A)) 1
And you get the error: expected ‘)’ before numeric constant message at the line where the substitution occurs because of that trailing 1, e.g.:
int maxval = MAX(i,j);
// generates: int maxval = ((i) < (j)? (j) : (i)) 1;
Conversely, if you use the assignment operator to explicitly define the value, it will generate it the way you expected. E.g.:
int maxval = MAX(i,j);
// generates: int maxval = ((i) < (j)? (j) : (i));

Resources