why no overflow warning when converting int to char - c

int i=9999;
char c=i;
gives no overflow warning, While
char c=9999;
gives,
warning C4305 initializing truncation from int to char
why no overflow warning when converting int to char?

You'll get warning C4244 when compiling with /W4 (which you should always do).
warning C4244: 'initializing' : conversion from 'int' to 'char', possible loss of data

Whether any code construct produces a warning is up to the cleverness of the compiler and the choices made by its authors.
char c=9999;
9999 is a constant expression. The compiler can determine, just by analyzing the declaration with no additional context, that it's going to overflow. (Presumably plain char is signed; if it's unsigned, the conversion is well defined -- but a compiler could still choose to warn about it.)
int i=9999;
char c=i;
This has the same semantics, but for a compiler to warn about the initialization of c, it would have to know that i has the value 9999 (or at least a value outside the range of char) when it analyzes that declaration. Suppose you instead wrote:
int i = 9999;
i = 42;
char c = i;
Then clearly no warning would be necessary or appropriate.
As James McNellis's answer indicates, a suffiently clever compiler can warn about either case if it performs additional analysis of what's going to happen during the execution of the program. For some compiler, it helps to enable optimization, since the analysis required to optimize code (without breaking it) can also reveal this kind of potential run-time error.
I'll note that this is an answer to the question you asked: why is there no warning. The answer you accepted is to the implied question: "I want a warning here; how can I enable it?". I'm not complaining, just observing.

Related

Why doesn't my C compiler warn when I assign a string literal to a non-const pointer?

The following code compiles fine with, for example, the default settings in Xcode 11.3.1:
#include <stdio.h>
int main(int argc, const char * argv[]) {
char* thing = "123";
thing[2] = '4';
printf("%s\n", thing);
return 0;
}
However, at runtime the code traps with EXC_BAD_ACCESS on thing[2] = '4'. I assume this is because the memory for the bytes representing "123" is compiled into my program's binary somewhere that on a modern processor/OS gets marked as for code rather than data. (This answer confirms that — not to mention there's a leaq 0x4d(%rip), %rsi ; "123" line in the disassembly, passing the pointer to an address relative to the instruction pointer!)
Is it just a historical artifact that C allows this, from the era of self-modifying code? I notice that I can also assign void* x = main; without any complaint that I'm discarding modifiers.
This answer says:
According to the C99 rationale, there were people in the committee who wanted string literals to be modifiable, so the standard does not explicitly forbid it.
Is there any further discussion I could read on that? More practically, is there a way to tell clang and/or gcc to flag such assignments (even though they are not actually forbidden) with a warning, without compiling as C++?
The answer you have quoted is an opinion without citation, and frankly nonsense. It is about nothing more than not breaking the vast quantity of existing legacy C code that it is desirable to remain compilable in a modern compiler.
However many compilers will issue a warning if you set the necessary warning level or options. In GCC for example:
-Wwrite-strings
When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char* pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.
When compiling C++, warn about the deprecated conversion from string literals to char *. This warning is enabled by default for C++ programs.
CLANG also has -Wwrite-strings, where is a synonym for -Wwriteable-strings
-Wwritable-strings
This diagnostic is enabled by default.
Also controls -Wdeprecated-writable-strings.
Diagnostic text:
warning: ISO C++11 does not allow conversion from string literal to A
The diagnostic text is different for C compilation - I'm just quoting the manual.
In GCC with -Wwrite-strings:
int main()
{
char* x = "hello" ;
return 0;
}
produces:
main.c:3:15: warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
CLANG produces:
source_file.c:3:15: warning: initializing 'char *' with an expression of type 'const char [6]' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
Opposite to C++ In C string literals have types of non-constant character arrays.
However according to the C Standard any attempt to modify a string literal results in undefined behavior.
Historically the C language did not have the qualifier const. The qualifier const at first appeared in C++. So for the backward compatibility string literals in C have types of non-constant character arrays.
You have the -Wwrite-strings:
When compiling C, give string constants the type const char[length] so that copying the address of one into a non-const char * pointer produces a warning. These warnings help you find at compile time code that can try to write into a string constant, but only if you have been very careful about using const in declarations and prototypes. Otherwise, it is just a nuisance. This is why we did not make -Wall request these warnings.
https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc/Warning-Options.html

Strict aliasing and flexible array member

I thought I knew C pretty well, but I'm confused by the following code:
typedef struct {
int type;
} cmd_t;
typedef struct {
int size;
char data[];
} pkt_t;
int func(pkt_t *b)
{
int *typep;
char *ptr;
/* #1: Generates warning */
typep = &((cmd_t*)(&(b->data[0])))->type;
/* #2: Doesn't generate warning */
ptr = &b->data[0];
typep = &((cmd_t*)ptr)->type;
return *typep;
}
When I compile with GCC, I get the "dereferencing type-punned pointer will break strict-aliasing rules" warning.
Why am I getting this warning at all? I'm dereferencing at char array. Casting a char * to anything is legal. Is this one of those cases where an array is not exactly the same as a pointer?
Why aren't both assignments generating the warning? The 2nd assignment is the equivalent of the first, isn't it?
When strict aliasing is turned on, the compiler is allowed to assume that two pointers of different type (char* vs cmt_t* in this instance) will not point to the same memory location. This allows for a greater range of optimizations which you would otherwise not want to be applied if they do indeed point to the same memory location. Various examples/horror-stories can be found in this question.
This is why, under strict-aliasing, you have to be careful how you do type punning. I believe that the standard doesn't allow for any type-puning what-so-ever (don't quote me on that) but most compilers have exemption for unions (my google-fu is failing in turning up the relevant manual pages):
union float_to_int {
double d;
uint64_t i;
};
union float_to_int ftoi;
ftoi.d = 1.0;
... = ftoi.i;
Unfortunately, this doesn't quite work for your situation as you would have to memcpy the content of the array into the union which is less then ideal. A simpler approach would be to simply to turn off strict-aliasing via the -fno-strict-aliasing switch. This will ensure that your code is correct and it's unlikely to have a significant performance impact (do measure if performance matters).
As for why the warning doesn't show up when the line is broken up, I don't know. Chances are that the modifications to the source code manages to confuse the compiler's static analysis pass enough that it doesn't see the type-punning. Note that the static analysis pass responsible for detecting type-punning is unrelated and doesn't talk to the various optimization passes that assume strict-aliasing. You can think of any static analysis done by compilers (unless otherwise specified) as a best-effort type of thing. In other words, the absence of warning doesn't mean that there are no errors which means that simply breaking up the line doesn't magically make your type-punning safe.

The following assignment only causes a warning. What does this say about how C treats type equivalence?

The following assignment only causes a warning. What does this say about how C treats type equivalence?
int *iptr;
float *fptr;
float fval;
fval = 0.0;
fptr = &fval;
iptr = fptr;
In particular, I'm referring to the assignment in the last line of the above snippet of code; namely,
iptr = fptr;
It says more about the behavior of the compiler you're using than about C, though the compiler's behavior is (unfortunately IMHO) permitted by the C standard.
The types int* and float* are distinct, and the language defines no implicit conversion between them.
Attempting to assign one to the other without a cast (i.e., an explicit type conversion) is a constraint violation, which means that a compiler is required to issue a diagnostic message.
The C standard does not require such a diagnostic to be fatal. By issuing a warning, your compiler has done its job as far as the C standard is concerned. It could, with equal validity, have rejected your program (and in my opinion that would have been more user-friendly). But either behavior is conforming.
You can probably invoke your compiler in a way to make it treat this as a fatal error. For example, if you're using gcc the -pedantic-errors option (along with an option to specify which version of the C standard to use) should do the trick.
If you really want to perform that assignment (with all the risks that entails), you can use a cast:
iptr = (int*)fptr;
In addition to (probably) turning off the compiler warning, a pointer cast should act as a reminder that you're doing something tricky, and it may blow up in your face if you don'w know exactly what you're doing.

how to deal with array subscript warning in C when using isxdigit()? [duplicate]

This question already has answers here:
array subscript has type 'char'
(4 answers)
Closed 9 years ago.
I have the following code in C:
char input[127] = "hello world";
isxdigit(input[0]);
but I got the following warning:
warning: array subscript has type 'char'
What is the reason and how to fix it?
The reason is that in your C implementation, isxdigit() is implemented as a macro using an array lookup.
As long as your input string contains only characters <=127 you can fix it either with a cast:
isxdigit ((int)input[0]);
or, if you think casts are ugly, with the aid of a benign expression, such as adding 0:
isxdigit (0 + input[0]); /* Avoid cast by using an int-typed expression. */
isxdigit (+input[0]); /* Same thing using unary plus instead of addition. */
Since the C Standard requires that isxdigit also be a function, you can alternatively call the function instead of the macro:
(isxdigit)(input[0]);
or even
#undef isxdigit
isxdigit(input[0]);
This should produce no warning, since input[0] is promoted to int (assuming a prior #include <ctype.h>.)
As cmaster rightfully points out, these solutions break down once your char is signed and contains negative values. In that case you must first cast to unsigned char (and then to int if you want).
The reason is that isxdigit has prototype int isxdigit(int). So to remove this warning you should explicitly cast argument to int
isxdigit((int)input[0]);
The point is, that the standard does not define whether or not the type char is signed, leaving this decision to the implementation. As such (int)(char)128 may either evaluate to 128 or -128. The latter is obviously bad for array indices, as your compiler noticed (smart guy, that compiler).
So the correct fix is to cast to unsigned char, not to int.
isxdigit((unsigned char)input[0])
If you go with the other, wrong answers, you may end up crashing on utf8 strings! (utf8 uses codes greater than 127 for multibyte characters.)
Edit: Of course, Pascal Cuoq is right in saying, that such an implementation would not be allowed to implement isxdigit as a table lookup macro, however, what I said applies to any occurence of this compiler warning, including any lookup table you implement on your own. It is very unfortunate that the signedness of char is not defined, and one should better know about this issue and know to avoid it correctly.

strange unsigned char casting

What is the purpose / advantage / difference of using
/* C89 compliant way to cast 'char' to 'unsigned char'. */
static inline unsigned char
to_uchar (char ch)
{
return ch;
}
versus a standard cast ?
Edit :
Found in a base64 code in gnulib
Maybe the programmer who wrote the function doesn't like the cast syntax ...
foo(to_uchar(ch)); /* function call */
foo((unsigned char)ch); /* cast */
But I'd let the compiler worry about it anyway :)
void foo(unsigned char);
char s[] = "bar";
foo(s[2]); /* compiler implicitly casts the `s[2]` char to unsigned char */
Purpose
To:
Cast from char to unsigned char
Do more with the cast than from a conventional cast
Open your mind to the possibilities of customized casting between other types and select from the advantages below for those also
Advantage
One could:
Break on these kinds of casts when debugging
Track and quantify the use of casts through profiling tools
Add limits checking code (pretty grim for char conversions but potentially very useful for larger/smaller type casts)
Have delusions of grandeur
There is a single point of casting, allowing you to carefully analyze and modify what code is generated
You could select from a range of casting techniques based on the environment (for example in C++ you could use numeric_limits<>)
The cast is explicit and will never generate warnings (or at least you can force choke them in one place)
Difference
Slower with poor compilers or good compilers with the necessary optimization flags turned off
The language doesn't force consistency, you might not notice you've forgotten to use the cast in places
Kind of strange, and Java-esque, one should probably accept and study C's weak typing and deal with it case by case rather than trying to conjure special functions to cope

Resources