I found the following code on codegolf.stackexchange to print a code table for ASCII characters:
#include <stdio.h>
int main(){
char i;
for(i = 0; i < 256; i++){
printf("%3d 0x%2x: %c\n", i, i, i);
}
return 0;
}
Since chars store single bytes in them, they are always < 256 and the loop never terminates. I would like to detect this upon compilation.
Nicely, clang gives the following warning:
a.c:5:18: warning: comparison of constant 256 with expression of type 'char' is always true [-Wtautological-constant-out-of-range-compare]
for(i = 0; i < 256; i++){
~ ^ ~~~
However, neither gcc nor gcc -Wall give any warning of any sort. Is there any set of command line options I can give to turn on this kind of warnings? Or is it not possible in gcc?
-Wtype-limits (or -Wextra) should trigger this warning
Add -Wextra and -Wconversion. The first includes a warning for your actual probem, but the latter will warn about many other related problems.
But beware: -Wconversion will also warn about many other potential problems if your code is not well-written (signed/unsigned, etc). Best is to compile, see the warnings and carefully verify the listed lines, possibly adding casts (after thinking thrice if the code is correct!).
I compiled the posted code with gcc, on ubuntu 14.04 linux using:
-Wall -Wextra -pedantic -std=c99
and the compiler output this warning:
warning: comparison is always true due to limited range of data type [-Wtype-limits]
Just one more reason to always enable all the warnings when compiling
Related
Why doesn't this code defining and using a VLA (variable-length array) work reliably?
#include <stdio.h>
int main(void)
{
int n;
double vla[n];
if (scanf("%d", &n) != 1)
return 1;
for (int i = 0; i < n; i++)
{
if (scanf("%lf", &vla[i]) != 1)
return 1;
}
for (int i = 0; i < n; i++)
printf("[%d] = %.2f\n", i, vla[i]);
return 0;
}
Diagnosis
In the code in the question, the variable n is uninitialized when it is used in the definition of vla. Indeed, with GCC set fussy, the code shown produces a compilation error (it'll give a warning if you are careless enough to omit -Werror from your compilation options — don't do that!):
$ gcc -std=c11 -O3 -g -Wall -Wextra -Werror -Wstrict-prototypes -Wmissing-prototypes -Wshadow -pedantic-errors vla37.c -o vla37
vla37.c: In function ‘main’:
vla37.c:6:5: error: ‘n’ is used uninitialized [-Werror=uninitialized]
6 | double vla[n];
| ^~~~~~
vla37.c:5:9: note: ‘n’ declared here
5 | int n;
| ^
cc1: all warnings being treated as errors
$
(That's from GCC 11.2.0 on a machine running RedHat RHEL 7.4.)
The trouble is that the compiler must know the size of the array when it is declared, but the value in n is undefined (indeterminate) because it is uninitialized. It could be huge; it could be zero; it could be negative.
Prescription
The cure for the problem is simple — make sure the size is known and sane before it is used to declare the VLA:
#include <stdio.h>
int main(void)
{
int n;
if (scanf("%d", &n) != 1)
return 1;
double vla[n];
for (int i = 0; i < n; i++)
{
if (scanf("%lf", &vla[i]) != 1)
return 1;
}
for (int i = 0; i < n; i++)
printf("[%d] = %.2f\n", i, vla[i]);
return 0;
}
Now you can run the result:
$ vla41 <<<'9 2.34 3.45 6.12 8.12 99.60 -12.31 1 2 3'
[0] = 2.34
[1] = 3.45
[2] = 6.12
[3] = 8.12
[4] = 99.60
[5] = -12.31
[6] = 1.00
[7] = 2.00
[8] = 3.00
$
(That assumes your shell is Bash or compatible with Bash and supports 'here strings' (the <<<'…' notation.)
The code shown in the question and in this answer is barely adequate in handling I/O errors; it detects input problems but doesn't provide useful feedback to the user.
The code shown does not validate the value of n for plausibility. You should ensure that the size is larger than zero and less than some upper bound. The maximum size depends on the size of the data being stored in the VLA and the platform you're on.
If you're on a Unix-like machine, you probably have 8 MiB of stack; if you're on a Windows machine, you probably have 1 MiB of stack; if you're on an embedded system, you may have much less stack available to you. You need to leave some stack space for other code too, so you should probably check that the array size is not more than, for sake of discussion, 1024 — that would be 8 KiB of stack for an array of double, which is not huge at all but it provides plenty of space for most homework programs. Tweak the number larger to suit your purposes, but when the number grows, you should use malloc() et al to dynamically allocate the array instead of using an on-stack VLA. For example, on a Windows machine, if you use a VLA of type int, setting the size above 262,144 (256 * 1024) almost guarantees that your program will crash, and it may crash at somewhat smaller sizes than that.
Lessons to learn
Compile with stringent warning options.
Compile with -Werror or its equivalent so warnings are treated as errors.
Make sure the variable defining the size of a VLA is initialized before defining the array.
Not too small (not zero, not negative).
Not too big (not using more than 1 megabyte on Windows, 8 megabytes on Unix).
Leave a decent margin for other code to use as well.
Note that all compilers that support VLAs also support variables defined at arbitrary points within a function. Both features were added in C99. VLAs were made optional in C11 — and a compiler should define __STDC_NO_VLA__ if it does not support VLAs at all but claims conformance with C11 or later.
C++ and variable-length arrays
Standard C++ does not support C-style VLAs. However, GCC (g++) does support them by default as an extension. This can cause confusion.
Take a look at the following code:
int foo(void) {
for (int i = 0; i != 1; i = 1) {
return i;
}
}
GCC 10.2 with -Wall -Wextra -pedantic -std=c99 emits the following warning:
warning: control reaches end of non-void function [-Wreturn-type]
If I don't miss something, foo obviously terminates -- it will always return 0.
How to fix this warning? I can't use GCC diagnostic pragmas because the above for-loop is actually generated by a macro which must be a statement prefix, so I can't just _Pragma("GCC diagnostic pop") after a user-provided statement ({ return i; } in the above example).
Godbolt
You are correct in your assumption that the code in the function you have shown can never reach beyond the for loop and, thus, the "invalid return" cannot be executed. However, many (most) compilers won't accept this as a reason to not emit the warning. (Similar situations can arise when you have if and else if blocks that cover all possibilities, yet still omit a 'terminal' else block.
How to fix this warning?
Simply add a return 42; statement (the value of the integer literal can be anything you like) at the end of the function.
As a point of interest, the "clang-cl" code analysis tool gives two warnings: one that the for loop never runs past its first iteration, and one about control reaching the end of a non-void function (like your GCC warning) 😊:
warning : non-void function does not return a value in all control
paths [-Wreturn-type] warning : loop will run at most once (loop
increment never executed) [-Wunreachable-code-loop-increment]
These are things you have to learn to live with: compilers can't always reliably predict all possible outcomes, even though a human reader can see what's really happening.
In GCC the following C code compiles correctly-
int i = 7;
i = 5i;
printf("%d", i);
And prints- 0.
The statement i = 5i clearly makes no sense. Then why on earth the code does not give any compilation error? And why i becomes 0?
This is a GCC extension for representing the imaginary component of complex numbers.
The compiler complains if you compile with -pedantic and -Werror: http://ideone.com/PMlZr5.
I'm well aware of function prototypes, this error seems to be a function declaration error, which means I'm really bewildered as to why I'm see this warning and thus error.
It's almost like gcc completely ignores my function prototype. Is this a compiler bug?
In the interest of brevity, I did not declare this function in a separate header file, though it should make no difference.
gcc output:
$ gcc -Wall -std=c99 -pedantic primefactors.c
primefactors.c: In function ‘main’:
primefactors.c:8:5: warning: implicit declaration of function ‘largestprime’ [-Wimplicit-function-declaration]
primefactors.c: At top level:
primefactors.c:12:6: error: conflicting types for ‘largestprime’
primefactors.c:8:20: note: previous implicit declaration of ‘largestprime’ was here
code:
#include <stdio.h>
#include <math.h>
long largetsprime(long);
int main()
{
printf("%d\n", largestprime(600851475143));
return 0;
}
long largestprime(long num)
{
int highest;
int mid = sqrt(num);
for (int i = 2; i < mid; i++) {
if (mid % i == 0) {
if (i % 1 == 0 && i % i == 0)
highest = i;
}
}
return highest;
}
Point-1
You have misspelled largest in function name
long largetsprime(long)
^
s is wrong here
In declaration It should be
long largestprime(long)
^ before t
Point-2
You are using sqrt() library function from math.h, you should compile your program with -lm as:
gcc -Wall -std=c99 -pedantic primefactors.c -lm
Point-3
You are returning int whereas return type of your function is long.
Point-4
One more mistake suggestion in call of printf() you forgot adding suffix for long int.
largestprime(600851475143)
should be:
largestprime(600851475143L)
// ^ added suffix L for long
If you are not aware of suffix L then read: What does the “L” mean at the end of an integer literal?
Thanks to #Eric Postpischil:
point-5: printf() in main() function is printing long type integer whereas you have used %d format specifier to print it:
printf("%d\n", largestprime(600851475143));
^
|
returns long
use %ld instead.
point-6:
if-condition in largest prime function i % 1 == 0 and i % i == 0 are each always true (except the latter is undefined if i is zero) because i % 1 = 0 (every number is divisible by 1).
Typo. The declaration says largeTSprime. Change it to the correct largestprime and it will work.
ProTip #1: use camelCapsOnWordBoundaries or under_scores for readability.
ProTip #2: it's almost never a compiler bug.
You have a typo in the prototype. It should be largestprime instead of largetsprime.
You have a typo in the prototype:
largetsprime != largestprime
Say you have a C code like this:
#include <stdio.h>
int main(){
printf("Hello, world!\n");
printf("%d\n", f());
}
int f(){
}
It compiles fine with gcc, and the output (on my system) is:
Hello, world!
14
But.. but.. how is that possible? I thought that C won't let you compile something like that because f() doesn't have a return statement returning an integer. Why is that allowed? Is it a C feature or compiler omission, and where did 14 come from?
The return value in this case, depending on the exact platform, will likely be whatever random value happened to be left in the return register (e.g. EAX on x86) at the assembly level. Not explicitly returning a value is allowed, but gives an undefined value.
In this case, the 14 is the return value from printf.
compile with -Wall to enable more sanity checking in the compiler.
gcc -Wall /tmp/a.c
/tmp/a.c: In function ‘main’:
/tmp/a.c:5: warning: implicit declaration of function ‘f’
/tmp/a.c:6: warning: control reaches end of non-void function
/tmp/a.c: In function ‘f’:
/tmp/a.c:10: warning: control reaches end of non-void function
Note how it flags up the missing return statements - as "control reaches end of non-void function"?
Always compile using -Wall or similar - you will save yourself heartache later on.
I can recall several occasions when this exact issue has caused hours or days of debugging to fix - it sorta works until it doesn't one day.
14 is exactly the return value of the first printf, also. Probably, the compiler optimized out the call to f() and then we're left with 14 on EAX.
Try compiling your code with different optimization levels (-O0, -O1, -O2, -O3, -Os) and see if the output changes.
18 is the return value of the first print statement. (the number of characters printed)
This value is stored in stack memory.
In second printf function the stack value is 'returned' by function f. But f just left the value that printf created in that slot on the stack.
for example, in this code:
#include <stdio.h>
int main(){
printf("Hello, world!1234\n");
printf("%d\n", f());
}
int f(){
}
Which compiles fine with gcc, and the output (on my system) is:
Hello, world!
18
for details of printf() returning value mail me.
You probably have the warning level of your compiler set very low. So it is allowed, even if the result is undefined.
The default return value from a
function is int. In other words,
unless explicitly specified the
default return value by compiler would
be integer value from function.
So, the ommiting of return statement is allowed, but undefined value will be returned, if you try to use it.
I compile with -Werror=return-type to prevent this. GCC will give an error if you don't return from a function (unless it's void return).