Sunday, June 06, 2010

Complaint Against Pass-by-Reference in C++

Since the day I learned about pass-by-reference in C++, I have had this little peeve about it. The following page describes the advantages of passing by reference: http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=166
So here is my plug for pass-by-address. Using * to pass the address of a variable makes it clear that, after the called function returns, its value could be different. However, if passed by reference, just looking at the function call does not make it obvious that the value could be changed. One has to look at the signature of the called function to know that. For example, the following code uses pass by value:

void increment (int n)
{
n++;
}

void myFunc ()
{
int k = 10;
increment (k);
std::cout << k << std::endl; // displays 10
}

If we want increment() to actually change the value of its argument in the calling function, then we can use pass-by-address to pass the address of the int variable. This is the only option available in C.

void increment (int* n)
{
(*n)++;
}

void myFunc ()
{
int k = 10;
increment (&k);
std::cout << k << std::endl; // displays 11
}

Looking at the call to increment() in myFunc(), I can say that the value of k that is printed might not be 10. However, consider the pass-by-reference option added in C++:

void increment (int& n)
{
n++;
}

void myFunc ()
{
int k = 10;
increment (k);
std::cout << k << std::endl; // displays 11
}

This is the same as the pass-by-value code (which prints 10) but with just one '&' character added to the signature of the increment() function. The myFunc() function is unchanged. Reading just the code for myFunc(), I have no clue that k is 11 by the time it is printed. To figure out how this happened, I would have to look up the increment() function signature, which may be in one of the many header files for a library that the application may be using. I think that the syntax could be improved by requiring some character in the increment() call that makes it explicitly clear to the programmer that a reference to the variable is being passed and not a copy. For example, it could be:

increment (@k);

Do you find the C++ pass-by-reference syntax irksome, too? Let me know in the comments section.

Sunday, May 02, 2010

Are Java references "pointers"?

The difference between Java references and C++ pointers is a much-discussed topic on the Internet. Java purists mostly shun the word "pointer" as a historical abomination that made code insecure. They insist on never calling a Java reference a pointer. However, I found it interesting that Java has a NullPointerException class - not a NullReferenceException.

A few days ago, I happened to come across an article titled "Java is Pass-by-Value, Dammit!" that gave me a part of the answer. In it, Scott Stanchfield tries to clear the confusion about Java's parameter passing - pass-by-reference or pass-by-value - and in doing so, shows that Java references act more like C/C++ pointers than C++ references. The article also mentions NullPointerException in passing and provides a quote from the Java Language Specification (JLS) , 3rd Edition as part of the legacy of pointers in Java. The JLS states (at http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.3.1 - emphasis not mine):
"The reference values (often just references) are pointers to these objects, and a special null reference, which refers to no object."

However, Java does not allow pointer arithmetic as C++ does. Probably to emphasize this and to distance Java from C++, Sun later tried to create a distinction between Java references and pointers. To be fair to Sun, there is a subtle difference between Java references and C++ pointers but it is in the way they are implemented. This is explained in the Best Rated Answer on the page http://www.geekinterview.com/question_details/32288. However, the behavior of both is the same, as far as programming with them is concerned (except that many memory-related problems are avoided by eliminating pointer arithmetic).

Saturday, December 19, 2009

Why double-to-float Conversion Always Needs an Explicit Cast

A few days ago, I finally found the answer to the question that I posed in my previous post, thanks to Steve Luke's reply at JavaRanch. I created a post in the Beginning Java forum there, which led to a refresher for me on the subject of floating-point types and their ranges. An int literal may be implicitly narrowed on assignment to a byte variable if the literal value is within the range of a byte (The range of a floating-point type (double or float) is a little more complex. Apart from the possibility of overflow or underflow to positive or negative infinity, there is also the possibility of underflow to zero. This could occur if the absolute value of a number is too small for the type. So, while a double may be within the minimum and maximum float values, it may be too close to zero to fit into a float. Here is an illustration of underflow to zero from The Java Language Specification (Third Edition) (sec. 5.1.3, pg. 84). The output of:

System.out.println("(float)1e-50==" + (float)1e-50);

is:

(float)1e-50==0.0

So, the compiler refuses to implicitly convert any double value to a float. I guess that is simpler for the lazy compiler than to calculate whether the literal double value can be accurately represented as a float. Reading Section 4.2.3 Floating-Point Types, Formats, and Values of the Java Language Specification, I can see why this might not be a trivial deduction.

Thursday, September 17, 2009

Primitive Assignments in Java

While studying to upgrade my Sun Certified Java Programmer status from version 1.4 to version 6, I came across what appears to be an anomaly in the Java compiler. In Java, a literal integer (like 8) is always implicitly an int, i.e. a 32-bit value. A byte is only 8 bits. However, the following narrowing assignment is legal. No explicit cast is required.

     byte b = 30;

The compiler automatically narrows the literal 30 to a byte. It can do this because 30 is a compile-time constant that is within the range of a byte. Now, recall that a floating point literal (like 43.4) is implicitly a double, i.e. a 64-bit value; but a float is only 32 bits. Consider the following code:

     float f = 43.4;

You might think that this is legal because 43.4 is a compile-time constant that is well within the range of a float. If the compiler obliges us by implicitly casting an int constant to a byte, why shouldn't it do the same for double to float assignments? However, the compiler does no such thing. The above code does not compile. The literal value has to be either explicitly cast to a float, or have an f (or F) appended to it to make it a float literal. Thus, the following assignments are all legal and compile successfully:

     float f = (float) 43.4;
     float f1 = 43.4f;
     float f2 = 43.4F;

Why is the compiler so finicky about floating points? I have not been able to think of an answer. Can you?