Declaring an array of negative length - c

What happens in C when you create an array of negative length?
For instance:
int n = -35;
int testArray[n];
for(int i = 0; i < 10; i++)
testArray[i]=i+1;
This code will compile (and brings up no warnings with -Wall enabled), and it seems you can assign to testArray[0] without issue. Assigning past that gives either a segfault or illegal instruction error, and reading anything from the array says "Abort trap" (I'm not familiar with that one). I realize this is somewhat academic, and would (hopefully) never come up in real life, but is there any particular way that the C standard says to treat such arrays, or is does it vary from compiler to compiler?

It's undefined behaviour, because it breaks a "shall" constraint:
C99 §6.7.5.2:
If the size is an expression that is
not an integer constant expression...
...each time it is evaluated it shall
have a value greater than zero.

Undefined behavior, I believe, though don't quote me on that.
This gives the error error: size of array 'testArray' is negative in gcc:
int testArray[-35];
though, as you've seen:
int n = -35;
int testArray[n];
does not give an error even with both -Wall and -W.
However, if you use -pedantic flag, gcc will warn that ISO C90 forbids variable length array.

Visual studio erro message for compilation, you can use -1 to say an empty array. It expects int and you are passing int, so no compiler error.

Related

Why does GCC not detect overflow on variable initialization?

Why is it compiled without errors? What am I doing wrong?
#include <stdio.h>
int main (){
int n1 = 90, n2 = 93, n3 = 95;
int i = 2147483647;
int ii = 2147483646;
int iii = 2147483650;
char c1[50] = {'\0'};
char c2[50] = {'\0'};
char c3[50] = {'\0'};
n1 = sprintf(c1, "%d", i+i);
n2 = sprintf(c2, "%d", ii);
n3 = sprintf(c3, "%d", iii);
printf("n1 = %d, n2 = %d, n3 = %d\n i = |%s| \n ii = |%s|\niii = |%s|\n", n1, n2, n3, c1, c2, c3);
return 0;
}
gcc filename -Wall -Wextra -Werror
I guess %d can't be more than int, but it's compiled and as a result:
n1 = 2, n2 = 10, n3 = 11
i = |-2|
ii = |2147483646|
iii = |-2147483646|
I was expecting a GCC error.
There is an error in your initialization to iii, where the constant provided does not fit in int.
GCC will diagnose this issue if you enable -pedantic. From the documentation:
-Wpedantic
-pedantic
Issue all the warnings demanded by strict ISO C and ISO C++; reject all programs that use forbidden extensions, and some other programs that do not follow ISO C and ISO C++. For ISO C, follows the version of the ISO C standard specified by any -std option used.
When doing so, I get the error:
.code.tio.c: In function ‘main’:
.code.tio.c:7:15: error: overflow in conversion from ‘long int’ to ‘int’ changes value from ‘2147483650’ to ‘-2147483646’ [-Werror=overflow]
int iii = 2147483650;
^~~~~~~~~~
cc1: all warnings being treated as errors
Try it online!
Other problems
Arithmetic leading to signed integer overflow
The arithmetic operation i+1 triggers signed integer overflow, which has undefined behavior, so the compiler is free to do whatever it wants with that code.
Note that both operands to the + operator have type int, so if any result is generated, it would be an int. However, since signed integer overflow is undefined, no result may be generated (e.g., the program could just halt), or a random result may be generated, or some other overflow behavior may occur that matches your observation.
In the general case, there isn't any way for the compiler to know if any particular operation will actually cause overflow. In this case, static code analysis may have revealed it. I believe GCC does perform some rudimentary static code analysis, but it is not required to identify every instance of undefined behavior.
Using sprintf instead of snprintf
While it is safe to use sprintf in your particular context, it is generally preferable to use snprintf to guard against buffer overflow exploits. snprintf simply needs an extra parameter to indicate the size of the buffer, and it will NUL terminate the string for you.
I can really see after jxh's excellent answer somebody would still be saying "but whyyy?"
Here's why:
typedef int HANDLE;
#define HKEY_LOCAL_MACHINE ((HANDLE)0x80000001)
No, HANDLE isn't int anymore, but it was in 1994. Everybody and their brother depended on signed overflow just working at compile time. If you changed it you broke your platform headers. That didn't happen until the big 64 bit port.
The ancient compilers simply didn't check for constant out of range. They just parsed the constant with something analogous to strtol; the overflow was really a runtime overflow in the compiler itself; without code written to detect it it simply didn't exist.
The static analysis didn't see 0x80000001; it saw -bignum. This used to bite people when cross compiling to different bitnesses; sometimes compile time constants were just wrong. One by one all this stuff got cleaned up, but there were too many places that depended on no warning on overflow (because the last thing you want is warnings in the platform headers), so it was left as is.

How to filter the expression akin to `int c = 1/0*0` in C language?

OS: Ubuntu 18.04
GCC: 7.5.0
I'm writing an expression generator to test my simple debugger, and want to filter the expressions with division by zero behavior. However i encounter a troubling problem.
As for the definite division by zero behavior akin to int c = 1/0, it will raise a signal so i can handle these cases by signal(). Nevertheless, in the case akin to int c = 1/0*0, cis equal 0 , and the program will never trap into the signal handler.
The test code as below.
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
void ss(int sig){
printf("division by zero.\n");
exit(0);
}
int main(){
int c;
signal(SIGFPE, ss);
c = (1u/0u)*0u;
printf("%d\n", c);
}
Here is the result.
gcc -o divide_0 divide_0.c
divide_0.c: In function ‘main’:
divide_0.c:15:11: warning: division by zero [-Wdiv-by-zero]
c = (1u/0u)*0u;
^
0
How can i capture the warning in this case?
Forcing the compiler to execute a division by zero is actually hard:
First, as you have noted, the compiler may evaluate 1/0 at compile-time. To prevent it from knowing the divisor is zero, we can use a volatile object, as in volatile int zero = 0; c = 1/zero;. The volatile qualifier tells the compiler the object may be changed by means unknown to it, so it cannot assume it is zero and must get the value when the expression is being evaluated.
Multiplying by zero in (1u/0u)*0u is counterproductive, even after we change the divisor to zero. The compiler can reason that any defined result of (1u/zero)*0u is zero, and therefore it is allowed to use zero as the union of the defined results (zero) and the undefined results (whatever the compiler likes), so it can replace (1u/zero)*0u with zero, 0. That is, it must still evaluate zero because it is volatile, but then it is free to just produce zero as the result without doing the division.
The compiler does not have to use a division instruction to evaluate a division operator. I tested with Apple Clang 11, and it was evaluating 1u/zero with instructions equivalent to zero == 1 ? 1 : 0;. In other words, it just did a compare and a set, not a division, presumably because compare and set is faster. When I changed this to 13u/zero, then the compiler used a divide instruction. I expect your best bet for getting a divide instruction would be to use volatile int unknown = 0; unknown = unknown/unknown;. Even then, a compiler would be allowed by the C standard to perform the division using any instructions it wanted, not a divide. But I presume compilers will generally generate a divide instruction in this case.
Then this code will execute a division at run-time. Whether that causes a signal and what happens with that signal depends on your computing platform.
Division by zero is not guaranteed to generate a signal.
Whenever your expression evaluator performs a division, it needs to check if the divisor is 0, and if so perform an appropriate action.
I assume that the compiler somehow just removes the division. It is allowed to do that. So instead try this code:
int main(int argc, char **argv){
int c;
int d = argc - 1;
signal(SIGFPE, ss);
c = 1u/d;
printf("%d\n", c);
}
Call it without arguments. The trick here is that the compiler cannot know how many arguments you will give the program, so it cannot optimize it away. Well, not as easily anyway.
The compiler itself complains about this since it can tell that your code is faulty and unsafe. However, you can try to use -Wno-div-by-zero argument for gcc at your own risk.

GCC __builtin_constant_p always returning 0

I have the following code:
const int a = 10;
int b = __builtin_constant_p(a);
printf("%d\n", b);
output is 0.
I read the man, the value of 0 doesn't mean that a is not a compile time constant, just that gcc can't prove that it is. Anyway I can get this output to be 1?
A const-qualified variable is not a constant expression in C, but GCC does not document __builtin_constant_p as determining if the argument is a constant expression anyway. Rather, it's documented to "determine if a value is known to be constant at compile time and hence that GCC can perform constant-folding on expressions involving that value". So it should be usable for what you want.
The problem is almost certainly just that you compiled with -O0 (no optimization, the default), in which case no constant-folding can take place because you have it turned off. Turn on optimization (at least -O1, but normally you want -O2 or -Os) and it should do what you want.
A variable is never a constant (unless constant folding is used, but you have to have optimization enabled for that), even if it is const-qualified.
__builtin_constant_p will return true for a constant only. For example:
int b = __builtin_constant_p(10);
printf("%d\n", b);
will print 1.
Note that your code will print 1 also if you compile with optimization enabled (-O at minimum, but any other legal -O flag will work except for -O0).

C: gcc implicitly converts signed char to unsigned char and vice versa?

I'm trying to learn C at got stuck with datatype-sizes at the moment.
Have a look at this code snippet:
#include <stdio.h>
#include <limits.h>
int main() {
char a = 255;
char b = -128;
a = -128;
b = 255;
printf("size: %lu\n", sizeof(char));
printf("min: %d\n", CHAR_MIN);
printf("max: %d\n", CHAR_MAX);
}
The printf-output is:
size: 1
min: -128
max: 127
How is that possible? The size of char is 1 Byte and the default char seems to be signed (-128...127). So how can I assign a value > 127 without getting an overflow warning (which I get when I try to assign -128 or 256)? Is gcc automatically converting to unsigned char? And then, when I assign a negative value, does it convert back? Why does it do so? I mean, all this implicitness wouldn't make it easier to understand.
EDIT:
Okay, it's not converting anything:
char a = 255;
char b = 128;
printf("%d\n", a); /* -1 */
printf("%d\n", b); /* -128 */
So it starts counting from the bottom up. But why doesn't the compiler give me a warning? And why does it so, when I try to assign 256?
See 6.3.1.3/3 in the C99 Standard
... the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised.
So, if you don't get a signal (if your program doesn't stop) read the documentation for your compiler to understand what it does.
gcc documents the behaviour ( in http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation ) as
The result of, or the signal raised by, converting an integer to a signed integer type when the value cannot be represented in an object of that type (C90 6.2.1.2, C99 6.3.1.3).
For conversion to a type of width N, the value is reduced modulo 2^N to be within range of the type; no signal is raised.
how can I assign a value > 127
The result of converting an out-of-range integer value to a signed integer type is either an implementation-defined result or an implementation-defined signal (6.3.1.3/3). So your code is legal C, it just doesn't have the same behavior on all implementations.
without getting an overflow warning
It's entirely up to GCC to decide whether to warn or not about valid code. I'm not quite sure what its rules are, but I get a warning for initializing a signed char with 256, but not with 255. I guess that's because a warning for code like char a = 0xFF would normally not be wanted by the programmer, even when char is signed. There is a portability issue, in that the same code on another compiler might raise a signal or result in the value 0 or 23.
-pedantic enables a warning for this (thanks, pmg), which makes sense since -pedantic is intended to help write portable code. Or arguably doesn't make sense, since as R.. points out it's beyond the scope of merely putting the compiler into standard-conformance mode. However, the man page for gcc says that -pedantic enables diagnostics required by the standard. This one isn't, but the man page also says:
Some users try to use -pedantic to check programs for strict ISO C
conformance. They soon find that it does not do quite what they want:
it finds some non-ISO practices, but not all---only those for which
ISO C requires a diagnostic, and some others for which diagnostics
have been added.
This leaves me wondering what a "non-ISO practice" is, and suspecting that char a = 255 is one of the ones for which a diagnostic has been specifically added. Certainly "non-ISO" means more than just things for which the standard demands a diagnostic, but gcc obviously is not going so far as to diagnose all non-strictly-conforming code of this kind.
I also get a warning for initializing an int with ((long long)UINT_MAX) + 1, but not with UINT_MAX. Looks as if by default gcc consistently gives you the first power of 2 for free, but after that it thinks you've made a mistake.
Use -Wconversion to get a warning about all of those initializations, including char a = 255. Beware that will give you a boatload of other warnings that you may or may not want.
all this implicitness wouldn't make it easier to understand
You'll have to take that up with Dennis Ritchie. C is weakly-typed as far as arithmetic types are concerned. They all implicitly convert to each other, with various levels of bad behavior when the value is out of range depending on the types involved. Again, -Wconversion warns about the dangerous ones.
There are other design decisions in C that mean the weakness is quite important to avoid unwieldy code. For example, the fact that arithmetic is always done in at least an int means that char a = 1, b = 2; a = a + b involves an implicit conversion from int to char when the result of the addition is assigned to a. If you use -Wconversion, or if C didn't have the implicit conversion at all, you'd have to write a = (char)(a+b), which wouldn't be too popular. For that matter, char a = 1 and even char a = 'a' are both implicit conversions from int to char, since C has no literals of type char. So if it wasn't for all those implicit conversions either various other parts of the language would have to be different, or else you'd have to absolutely litter your code with casts. Some programmers want strong typing, which is fair enough, but you don't get it in C.
Simple solution :
see signed char can have value from -128 to 127 okey
so now when you are assigning 129 to any char value it will take
127(this is valid) + 2(this additional) = -127
(give char a=129 & print it value comes -127)
look char register can have value like..
...126,127,-128,-127,-126...-1,0,1,2....
which ever you will assign final value will come by this calculation ...!!

How can I fix warnings like: "comparison between signed and unsigned"?

I have been advised to use the following options with GCC, as it helps to avoid a lot of common errors. It turns on a bunch of warnings and -Werror turns them into errors.
gcc -pedantic -W -Wall -Wextra -Wshadow -Wstrict-overflow=5 -Wwrite-strings -std=c99 -Werror
Given the following test code:
#include <stdio.h>
int main(void)
{
int arr[8]={0,10,20,30,40,50,60,70};
int x;
printf("sizeof(arr): %d\n", sizeof(arr));
printf("sizeof(int): %d\n", sizeof(int));
for(x = 0; x < sizeof(arr)/sizeof(int); x++)
{
printf("%d\n",arr[x]);
}
return 0;
}
I get this:
test.c:11: error: comparison between signed and unsigned
I know that one way I can fix this is turning off the warnings, but they haven't made me use these settings to turn them off in the end.
Another way is to cast the stuff, but I have been told that casting is deprecated.
Also, I could make x into an unsigned int:
unsigned x;
But it doesn't solve the general problem when I have to compare signed values with unsigned values using these compiler options. Is there an cleaner way instead of casting?
Replace
int x;
/* ... */
for(x=0;x<sizeof(arr) / sizeof(int);x++)
by
for(size_t x=0;x<sizeof(arr) / sizeof(int);x++)
But it doesn't solve the general problem when I have to compare signed values with unsigned values using these compiler options. Is there an cleaner way insted of casting?
In such cases, try to figure out if the signed number can ever have a value which will cause overflow. If not, you can ignore the warning. Otherwise a cast to the unsigned type (if this is the same size or bigger than the signed component) is good enough.
This really depends on the data type. It is possible to avoid this by implicitly casting the values to a type which contains a superset of all the values available in the signed and unsigned type. For instance you could use a 64 bit signed value to compare an unsigned 32 bit and a signed 32 bit value.
However this is a corner case and you need to do operations like verify the size of your types. Your best solution is to use the same type for both operands.
If you must cast, do consider that you could be causing an overflow and consider if that is important to your application or not.
The crux of the matter is that comparing signed and unsigned values admits some weird cases. Consider, for instance, what happens in the unsigned array length is larger than the maximum that can be represented by a signed int. The signed counter overflow (remaining "less than" the array size), and you start addressing memory you didn't mean to...
The compiler generates a warning to make sure that you're thinking about them. Using -Werror promotes that warning to an error and stops the compilation.
Either be rigorous about choosing the signedeness of your types, or cast the trouble away when you're sure it doesn't apply, or get rid of -Werror and make it a policy to address all warnings with a fix or an explanation...
One workaround would be to selectively disable that one warning in this special case.
GCC has pragma diagnostic ignored "-Wsomething"
// Disable a warning for a block of code:
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wmissing-prototypes"
// ... Some code where the specified warning should be suppressed ...
#pragma GCC diagnostic pop
Recent versions of GCC (I am not actually sure since when, but 4.8.x should support it) show the corresponding -Wsomething option. This is important since most warning options are not set explicitly but en bloc with options like -Wall.
An error message would look like this:
readers.c: In function ‘buffered_fullread’:
readers.c:864:11: error: comparison between signed and unsigned integer expressions [-Werror=sign-compare]
if(got < sizeof(readbuf)) /* That naturally catches got == 0, too. */
The [-Werror=sign-compare] part tells You You can use "Wsign-compare" for "Wsomething" to suppress the warning.
And of course, You should only do that where it is appropriate (it does not quite help readability), e.g. when exactly the behaviour that the compiler warns about is wanted (or, if You may not introduce bigger changes in the code base).
test.c:11: error: comparison between signed and unsigned
You could declare x as an unsigned int, since size_t is unsigned
EDIT:
If you don't want to cast, and don't want to declare it as unsigned, i don't think there's much to do.
Maybe bitwise operations are a way of solving it, removing the sign bit. I have to say, IMO its very questionable though.
We suppress this warning in our Visual Studio compiles, since it happens a lot and almost never means anything significant. Of course, not all coding standards allow that.
You can make types agree (declaring variables to be size_t or unsigned int instead of int, for example), or you can cast, or you can change your compilation line. That's about it.
Regardless of the casting deprecation dilemma, I'd still suggest separating out the logic from the for loop.
int range = (int)(sizeof(arr) / sizeof(int));
int x;
for(x = 0; x < range; x++)
{
printf("%d\n", arr[x]);
}
Although this uses casting which you said was deprecated, it clears up the location where the casting is taking place. In general, I advise against cramming a lot of logic into your for loop declarations. Especially in your case where you're using size_t division, which (because it is integer division) could have the possibility of truncating the answer. The for loop declaration should be clean, and should generate no errors. Your casting is occuring in a different location which means if you want to change the way you are creating the range, you don't have to muck about making the for declaration even longer.

Resources