3

Have a look at the following code :

Case 1 :

char a = 'x' ^ 'y';

It is working fine. But when I use variable instead of constants as here :

Case 2:

char x = 'x';
char y = 'y';
char a = x ^ y; // Error!

In java language : case 1 is working and value of a comes out to be 1 but case 2 is not working.

In C and C++ : both cases are working, and the value of a comes out to be 1

In C# : both the cases are not working.

In javascript : both cases are working, which is not a HLL, and value of a comes out to be 0.


I understand that java is converting variables to integer to do binary operation, but why it is working in case 1 and not in case 2, Why same is not working in C# And why the values are different in the case of javascript.

Updated When I made the variables final than it is working in java, but still not in C#

final char x = 'x';
final char y = 'y';
char a = x ^ y;

But still I cannot understand why constants are working but when using variable they are not. And why same is not happening with other high level programming language.
I think it is a basic operation and should be working in all programming languages with same behaviour.

Note To test all above cases in javascript I am replacing 'char' with 'var' in all cases and they are working.

afzalex
  • 8,598
  • 2
  • 34
  • 61
  • 2
    What's the exact error? – Drew McGowen Aug 08 '14 at 14:43
  • 4
    No repro for C or C++: http://coliru.stacked-crooked.com/a/6d6e17d1d399877c Or do you mean at file/namespace scope? In that case, error for C only: http://coliru.stacked-crooked.com/a/4dadf0da0374e92e – Deduplicator Aug 08 '14 at 14:50
  • 2
    Neat twist for Java: if `x` and `y` are both `final`, it compiles! – yshavit Aug 08 '14 at 14:50
  • yeah, good question. I never managed to find out why... – nafas Aug 08 '14 at 14:52
  • @DrewMcGowen In eclipse using java 1.7, the error is "Type mismatch: cannot convert from int to char". – azurefrog Aug 08 '14 at 14:53
  • 2
    both cases work in c and c++ – mch Aug 08 '14 at 14:54
  • @azurefrog Which makes sense. Both operators are promoted to integral types (char promotes to int), and then the bitwise is performed. The weirdness is that it compiles when it's a constant. – yshavit Aug 08 '14 at 14:54
  • @yshavit I agree completely about the weirdness. I can only guess that since with constants/final variables, since the value is known at compile time, the compiler is doing some sort of optimization which bypasses the type promotion error. – azurefrog Aug 08 '14 at 14:56
  • I'm not sure which is more vexing: (i) the answer, or (ii) the 3 downvotes. – Bathsheba Aug 08 '14 at 14:57
  • 2
    @Bathsheba: The downvotes are probably because this question is **much too broad**: Different explanations for the ***four different languages***. Still, close-voting would be better. – Deduplicator Aug 08 '14 at 14:58
  • Case 1 works in c# if you say `var a = 'x' ^ 'y';` as there is implicit conversion from `char` to `int` but not `int` to `char` – DavidG Aug 08 '14 at 14:59
  • 2
    @Bathsheba The question is also quite badly formulated. Here is some code. It works. Here's some other code. It doesn't work. But the first one doesn't work in two other languages. Oh and I think it won't work in another one. And JS is not HLL. Meh, I give up. – juanchopanza Aug 08 '14 at 15:01
  • I think it is a basic operation and should be working in all high level languages. And there could be some explanation by which I can understand that why it is working in 1 language and not in other. For this I cannot use only `java` or only `C` tag, that's why I was needed to add all these languages. @Bathsheba – afzalex Aug 08 '14 at 15:04
  • Related question: [Why can I pass 1 as a short, but not the int variable i?](http://stackoverflow.com/questions/11432508/why-can-i-pass-1-as-a-short-but-not-the-int-variable-i) – CodesInChaos Aug 08 '14 at 15:05
  • @azurefrog I found that language (about the bypassing the error) in the JLS and put it in an answer, so I deleted my comment. – yshavit Aug 08 '14 at 15:09
  • Okay @Bathsheba, I have edited the code again so it could be easy for other to get it. – afzalex Aug 08 '14 at 15:47
  • We still need the clarification at which scope they are declared for C and C++. For C and file scope, the answer is simple: You cannot use non-constant expressions in an initializer with linkage. (And the operands of `^` are promoted to `int`, the result is `int`, undefined behaviour if the result traps (unlikely), `char` is of implementation-defined signedness, if `char` is signed, the result of the implicit conversion of the initialization is implementation-defined, too, if the value isn't representable (also unlikely).) – mafso Aug 08 '14 at 15:56
  • All code is inside the local method, in all languages. C, C++, C#, Java and javascript @mafso – afzalex Aug 08 '14 at 16:00
  • How can code be inside a local variable? Sorry, if this sounds picky, I really don't understand what you mean. And inside a block, this is valid C (despite some implementation-defined aspects). – mafso Aug 08 '14 at 16:01
  • "When I made the variables final" -- `final` is not a valid C keyword. – Jongware Aug 08 '14 at 17:48

3 Answers3

5

Answering for Java only.

The expression 'x' ^ 'y' is a constant expression; x ^ y is not, unless both variables are declared final. Furthermore, the result is an int; ^ is an integral bitwise operator, meaning that both operands have to be promoted to an integral type before being evaluated. char promotes to int.

So you have this int expression, and you try to narrow it to a char. In the general case, this could lead to a loss of precision (ints are 4 bytes, chars are 2), so the compiler doesn't let you do that without you explicitly stating it's what you want to do (via a cast to char). However, you can implicitly narrow constant expressions, if their value would fit into the new type. From JLS 5.2:

  • A narrowing primitive conversion may be used if the type of the variable is byte, short, or char, and the value of the constant expression is representable in the type of the variable.

(Emphasis added)

Intuitively, this makes total sense: the error is there to tell you that you may lose precision, and so it wants you to confirm that you know that; in a sense, it's a loud warning. But if the compiler can know absolutely that this won't happen, as it can for a constant expression, then it makes things a bit easier for you and "hides" that warning.

yshavit
  • 42,327
  • 7
  • 87
  • 124
1

I will consider C languages that is C, C++ and C#.

In C# there is no implicit conversion from an integral type to type char. According to section "11.1.5 Integral types" of the ECMA 334 Standard "C# Specification"

• There are no implicit conversions from other types to the char type. In particular, even though the sbyte, byte, and ushort types have ranges of values that are fully representable using the char type, implicit conversions from sbyte, byte, or ushort to char do not exist.

So you need explicitly to cast the result of the operator to type char. For example

using System;

namespace ExclusiveOr
{
    class Program
    {
        static void Main(string[] args)
        {
            char a = ( char )('x' ^ 'y' );
            char c = 'x', d = 'y';
            char b = ( char )( c ^ d );

            Console.WriteLine("a = {0}, b = {1}", (int)a, (int)b);
        }
    }
}

The output is

a = 1, b = 1

According to the C Standard (section 6.7.9 Initialization)

4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals

So for example this code will be compiled

#include <stdio.h>

char a = 'x' ^ 'y';

int main(void) 
{
    printf( "a = %d\n", a );

    return 0;
}

The output is

a = 1

However this code will not be compiled

#include <stdio.h>

char c = 'x';
char d = 'y';

char b = c ^ d;

int main(void) 
{
    printf( "b = %d\n", b );

    return 0;
}

will not be compiled. The GCC compiler will issue error

prog.c:8:1: error: initializer element is not constant char b = c ^ d;

However if you will make varaible b local then the code will be compiled successfully

#include <stdio.h>

char c = 'x';
char d = 'y';


int main(void) 
{
    char b = c ^ d;
    printf( "b = %d\n", b );

    return 0;
}

The output is

b = 1

In C++ there is no such a restriction for objects with the static storage duration so all examples of programs similar to C programs showed above will be compiled.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
0

Under normal circumstances, the operands of the following binary operators are "widened" before the operation is performed.

(From the JLS 5.6.1)

  • The multiplicative operators *, /, and % (§15.17)

  • The addition and subtraction operators for numeric types + and - (§15.18.2)

  • The numerical comparison operators <, <=, >, and >= (§15.20.1)

  • The numerical equality operators == and != (§15.21.1)

  • The integer bitwise operators &, ^, and | (§15.22.1)

  • In certain cases, the conditional operator ? : (§15.25)

In the case of char operands, widening will convert the operands to int.

For the arithmetic and bitwise operations, the type of the operation's result is the same as the "wider" of the two operands.

When x and y have type char, the expression x ^ y gives you an int value. That can't be assigned back to a char without a type cast, and hence you get a compilation error.


For the case where you are using char literals, the same widening process happens. But there is a "special exception" in the language which allows the value of a constant expression to be implicitly narrowed, provided that the constant expression's value will fit into the type. In this case, 'x' ^ 'y' will "fit" into a char, so the assignment is allowed.

The JLS reference for this exception for constant expressions is JLS 5.2.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216