Compiling the following snippet using C++11(demo here):
#include <stdint.h>
int main() {
const uint8_t foo[] = {
'\xf2'
};
}
Will trigger a warning(at least on GCC 4.7), indicating that there's a narrowing conversion when converting '\xf2' to uint8_t.
Why is this? sizeof(char) is always 1, which should be the same as sizeof(uint8_t), shouldn't it?
Note that when using other char literals such as '\x02', there's no warning.
Although char doesn't necessarily have to be 8-bits long, that's not the problem here. You are converting from signed char to unsigned (uint8_t), that's the reason for the error.
This:
const int8_t foo[] = {
'\xf2'
};
will compile fine.
Looking at my system, the constant \xf2 is overflowing, since it's out of range of a signed char. It's being represented as -14 which is then implicitly converted to an unsigned int, giving a value of 4294967282. That's then narrowed to a char, producing this warning:
warning: narrowing conversion of ‘'\37777777762'’
Using (unsigned char)'\xf2' removes the warning.
Related
There are some cases where a byte array is implemented in a library using a char type, which is a signed type for many compilers.
Is there a simple, readable and correct way to initialize a signed char with a hex value which is greater than 127 and not bigger than 255?
Currently I end up with the following, and I keep thinking that there must be something simpler:
const unsigned char ff_unsigned = 0xff;
const char ff_signed = static_cast<const char>(ff_unsigned);
I want a solution with no warnings, even when using higher compiler warning levels than the default.
The following solution e.g. creates C4310: cast truncates constant value with MSVC 2013:
const char ff_signed = char(0xff);
Yes there is. Use single quotation characters with \x as the prefix. That denotes a hexadecimal literal char type.
For example: '\xff'.
But note that char can be signed or unsigned and up to and including C++11 it can even be a 1's complement signed type.
const char ff_unsigned = '\xff';
0xff is an int and '\xff' is a char.`You can use
const char ff_signed = (char)0xff;
or
const char ff_signed = '\xff';
I would like to store an unsigned char into a char by means of a shift. As the two data types have the same length (1 byte on my machine), I would have expected the following code to work:
#include <iostream>
#include <cstring>
#include <cstdio>
using namespace std;
int main () {
printf ("%d\n", sizeof(char));
printf ("%d\n", sizeof(unsigned char));
unsigned char test = 49;
char testchar = (char) (test - 127);
printf ("%x\n", testchar);
return 0;
}
but it doesn't. In particular, I got the following output:
1
1
ffffffb2
that suggests that the char has been casted to int. Does anybody has an explanation and, hopefully, a solution?
%x is a specifier for a 4-byte int. To print one byte char use %hhx.
printf typecasts its arguments according to the format specifiers passed to it.That is why testchar was type promoted to int.
printf is a variable argument function, and as such it's arguments are subject to default promotion rules. For this case, your char is promoted to an int, and in that process is sign extended.
A 2's complement int of 4 bytes with the binary pattern 0xffffffb2 is -78. Print it as a char with the %hhx specifier.
See also Which integral promotions do take place when printing a char?
%x is only for printing unsigned int, however you supply a char.
Using %x with a negative value of char causes undefined behaviour.
Aside: The C Standard specification of printf is not particularly clear; some feel that passing anything except exactly an unsigned int causes undefined behaviour. Others (including myself) feel that it's OK to pass arguments that are not specifically unsigned int, but after the default argument promotions, have type int with a non-negative value. The standard does guarantee that non-negative ints have the same representation as the unsigned int with the same value.
Some of the other answers suggest %hhx, but that is not any better than %x. The standard (on a sensible interpretation) specifies that %hhx only be used with an unsigned char argument, and %hhd only be used with a signed char argument. There is actually no modifier for plain char.
Either way you look at it, nowhere can printf be used to convert negative values to positive representations in a well-defined manner. You must convert the argument yourself and then use a matching format specifier. In this case:
printf ("%hhx\n", (unsigned char)testchar);
would be one option. IMO %x could be used here, but as mentioned above, some disagree.
NB. The wrong format specifier is used in printf ("%d\n", sizeof(char)); and the line following that. The specifier for size_t is %zu. So you could either use %zu, or cast the argument to int, or even better:
printf("1\n");
What happens is !!!!
1) unsigned char test = 49; // hex value 31 gets assigned
2) char testchar = (char) (test - 127); // 49-127 = -78 ie; 0xb2 (as unsigned),converting it to signed char results F padding before b2 to indicate it as negative
3) printf ("%x\n", testchar); //Since %x is a specifier for a 4-byte int (as #Don't You Worry Child said) ffffffb2, 4 byte output is obtained
So try as per #Don't You Worry Child said
I would have expected the following code to work:
It won't.
Ignoring the issues other people have pointed out with how you're printing the character, there is no guarantee in the standard that your code will work. Why?
Because char does not have to be signed. Whether char is signed or unsigned is implementation-dependent. Some implementations make char signed, others make it unsigned.
As such, there's no guarantee that (char) (test - 127) will produce a value that can be represented by char.
C++(14) does allow lossless conversion between unsigned char and char. The stadnard says (3.9.1/1):
For each value i of type unsigned char in the range 0 to 255 inclusive, there exists a value j of type char such that the result of an integral conversion (4.7) from i to char is j, and the result of an integral conversion from j to unsigned char is i.
error: overflow in implicit constant conversion [-Werror=overflow]
#include<stdio.h>
int main()
{
char ch=200;
printf("\n%d",ch);
return 0;
}
I am running this code on http://ideone.com/YNkKT6#view_edit_box and getting the implicit conversion error.
What modification do I need and what is the reason?
n3376 3.9.1/1
Plain char, signed char, and unsigned char are three distinct types. A char, a signed char, and an
unsigned char occupy the same amount of storage and have the same alignment requirements (3.11); that is,
they have the same object representation.
What is char is implementation-defined, so, you need unsigned char here, that handles values (0-255).
looks like your char is signed char, which accept value from -128 to 127. and 200 is too large for it and will overflow to be a negative number.
to fix it, change char to int or unsigned char
int main()
{
unsigned char ch=200;
printf("\n%d",ch);
return 0;
}
The -Wconversion GCC parameter produces the warning from the title when compiling this program:
#include <iostream>
#include <array>
#include <string>
int main ()
{
std::string test = "1";
std::array<unsigned char, 1> byteArray;
byteArray[0] = byteArray[0] | test[0];
return 0;
}
Here is how I compile it: g++- -Wall -Wextra -Wconversion -pedantic -std=c++0x test.cpp and I'm using GCC 4.5.
Am I doing something illegal here? Can it cause problems in certain scenarios? Why would the | produce an int?
Am I doing something illegal here?
You're converting from a signed type to an unsigned type. If the signed value were negative, then the unsigned result would be an implmentation-defined non-negative value (and therefore not the same as the initial value).
Can it cause problems in certain scenarios?
Only if the value might be negative. That might be the case on somewhat exotic architectures where sizeof (char) == sizeof (int), or if your code were doing something more complicated than combining two values with |.
Why would the | produce an int?
Because all integer values are promoted before being used in arithmetic operations. If their type is smaller than int, then they are promoted to int. (There's somewhat more to promotion than that, but that's the rule that's relevant to this question).
yes, a string is made up of char which is signed, you have an array of unsigned char.
as for as | producing an int, it's called integer promotion. basically the compiler makes them both ints, does a |, then makes them char's again.
It runs into a problem though. By the C/C++ standards, integer promotion happens if the type promoted to can hold all values of the type promoted from. so it promotes the unsigned char to an unsigned int and the signed char to a signed int. Promotion of a signed value sign-extends it. So say you have -1 or 0xFF or 11111111. That is extended to 10000000000000000000000001111111 signed int ((int)-1). Obviously that will have a different result than |'ing with 11111111 ((char)-1) would intuitively expect.
See here for more info: here it explains a sort of 'workaround'
Result of unsigned char | char is int per integer conversion rules. When you assign int back to unsigned char, assignment can truncate its int value - compiler does not know what how big the value you have in this int.
To silence the compiler:
byteArray[0] = static_cast<unsigned char>(byteArray[0] | test[0]);
When I try to compile the following C++ program using the Visual Studio 2010 C++ compiler (X86) with warning level /W4 enabled, I get a signed/unsigned mismatch warning at the marked line.
#include <cstdio>
#include <cstdint>
#include <cstddef>
int main(int argc, char **argv)
{
size_t idx = 42;
uint8_t bytesCount = 20;
// warning C4389: '==' : signed/unsigned mismatch
if (bytesCount + 1 == idx)
{
printf("Hello World\n");
}
// no warning
if (bytesCount == idx)
{
printf("Hello World\n");
}
}
This confuses me, since I'm only using unsigned types. Since the comparison
bytesCount == idx
causes no such warning, it probably has to do with some strange implicit conversation that happens here.
Thus: what is the reason why I get this warning and by what rules does this conversation happen (if this is the reason)?
1 is a signed literal. Try bytesCount + 1U.
The compiler is probably creating a temporary value of the signed type due to the addition of signed and unsigned values ( bytesCount + 1 )
1 is an int. The type of an integral arithmetic expression depends on the types involved. In this case, you have an unsigned type and a signed type where the unsigned type is smaller than the signed type. This falls under the C++ standard on expressions (section 5.10 [expr]):
Otherwise, if the type of the operand with signed integer type can
represent all of the values of the type of the operand with unsigned
integer type, the operand with unsigned integer type shall be
converted to the type of the operand with signed integer type.
I.e., the type of the expression bytesCount + 1 is int which is signed by default.
Since 1 is of type int the expression bytesCount + 1 is int (signed).
In fact when a type smaller than int is used in a mathematical expression it is promoted to int, so even + bytesCount and bytesCount + bytesCount are considered int and not uint8_t (while bytesCount + 1U is an unsigned int since that is larger than int).
The following program outputs true three times.
#include <iostream>
int main()
{
unsigned short s = 1;
std::cout << (&typeid( s + 1U ) == &typeid(1U)) << std::endl;
std::cout << (&typeid( + s ) == &typeid(1)) << std::endl;
std::cout << (&typeid( s + s ) == &typeid(1)) << std::endl;
}
The other answers already tell you that bytesCount + 1 is interpreted as signed int. However, I'd like to add that in bytesCount == idx, bytesCount is also interpreted as signed int. Conceptually, it is first converted to signed int, and it is only converted to unsigned int after that. Your compiler does not warn about this, because it has enough information to know that there is not really problem. The conversion to signed int cannot possibly make bytesCount negative. Comparing bytesCount + 1 is equally valid, equally safe, but just that slight bit more complex to make the compiler no longer recognise it as safe.