toma@tekgvs.LABS.TEK.COM (Tom Almy) (08/03/90)
I submitted this to the msdos.programmer group last week, but after looking at the C++ group (to which I don't normally subscribe) I decided to post here as well. I have found several bugs in the Turbo C++ tutorial that make me believe this was really a rush job (or they don't know how to write C++ programs.) But there are several performance issues of (Turbo) C++ that should be mentioned. It has already been pointed out that Turbo C++ is slower compiling a C program that Turbo C V2.0. But the problem gets worse with C++ programs (the larger header files are mostly to blame). Executables are bigger as well. For instance, compare hello.c with hello.cpp: C C++ Compile time: 2 sec 3.8 sec Executable size: 6074 17852 (The c program does printf("Hello world"), while the c++ program does cout << "Hello world". The calc program example from the Stroustrup book (with addition of trig functions) compiles to a 35440 byte file, while the same program rewritten in JPI Modula-2 (just to test it out) is 11510. (The source file in C++ is 60% the size, though!). ============================ But to the meat of the bugs. I've always been concerned about dynamic storage management problems in C++, which the destructor functions are suppossed to circumvent. I have felt the entire scheme was too problematic, and I still think so because the Borland tutorial writer can't get it correct! In program listdemo.cpp, the quantity of free memory is reported before and after running the bulk of the program (which demonstrates dynamic lists and constructor/descructor operation). But the numbers aren't the same! The problem is in the constructor function for Lists, which creates the list head. A list object consists of just one item, a pointer to a node. But the definition of the constructor is: List::List () { Node *N; N = new Node; N->Item = NULL; N->Next = NULL; Nodes = NULL; // sets node pointer to "empty" // because nothing in list yet } Note that the constructor generates a Node, which it initializes and then ignores. Since N is a Node* rather than a Node, the Node doesn't get deleted. But the whole thing is stupid. This function works correctly and doesn't leave the garbage around: List::List () { Nodes = NULL; // sets node pointer to "empty" // because nothing in list yet } and it is in better agreement with the comment! ======================= The second problem, actually a pair of problems, occurs in xstring.cpp. The String class has two elements, a pointer to char, char_ptr, and an integer, length. A descructor is defined to delete the character string (which is fine). The program overloads the + operator to do a string concatenation in the line: CString = AString + BString; The example in the book (which is corrected on the disk) modifies This to hold the concatenated string. Of course this doesn't work because This is AString, and thus AString gets modified as a side effect. But the example does have an unfortunate side effect, it wastes the creation of a String. The function: String String::operator+ (String& Arg) { String Temp( length + Arg.length ); strcpy(Temp.char_ptr, char_ptr); strcat(Temp.char_ptr, Arg.char_ptr); return Temp; } Since the scope of Temp is the function, the return Temp command uses the constructor to build a copy of Temp (which gets returned) and then the String destructor is called to delete Temp. I guess this is correct operation, but it surely is inefficient, and I can't figure out a way around it. The biggest problems is the lack of a String class overload of the = operator. The standard C++ (and C) assignment operation occurs, copying the temporary into CString, element by element. Unfortunately this wipes out the string that was already assigned to AString, and it can never be reclaimed. The fix is to write: String &String::operator= (String& Arg) { delete char_ptr; length = Arg.length; char_ptr = new char [length + 1]; strcpy(char_ptr,Arg.char_ptr); return *this; } perhaps there is a cleaner way of doing this. Tom Almy toma@tekgvs.labs.tek.com Standard Disclaimers Apply
steve@taumet.com (Stephen Clamage) (08/05/90)
toma@tekgvs.LABS.TEK.COM (Tom Almy) writes: |It has already been pointed out that Turbo C++ is slower compiling a C program |that Turbo C V2.0. But the problem gets worse with C++ programs (the larger |header files are mostly to blame). Executables are bigger as well. For |instance, compare hello.c with hello.cpp: | C C++ |Compile time: 2 sec 3.8 sec |Executable size: 6074 17852 |(The c program does printf("Hello world"), while the c++ program does | cout << "Hello world". You have to be very careful in such comparisons. Any C++ program using streams includes the entire stream i/o package, which is very large. It has much more functionality than does printf(), so it is bigger. You could also compare a C program which used write(1, "Hello, world", 12); in place of printf() and get a smaller program -- because write has much less functionality than printf(). Besides, you can use printf() instead of streams in C++ if you wish. |The calc program example from the Stroustrup book (with addition of trig |functions) compiles to a 35440 byte file, while the same program rewritten |in JPI Modula-2 (just to test it out) is 11510. (The source file in C++ is |60% the size, though!). There can be no validity to a claim that two programs written in two different programming languages are the "same". It is a well-known theorem in the theory of computation that one cannot *in general* determine whether two programs have the same behavior. Apart from that, you are looking at a) Different functionality of C++ stream i/o vs Modula-2 i/o. b) Differences in implementation of floating-point operations. (Are you linking in comparable FP libraries? Does one of the programs include both 80x87 and software FP?) c) Differences in quality of code generation. IMHO, comparing executable file size for toy programs is an entirely irrelevant exercise. You are really comparing runtime library size, which completely swamps anything in the toy program. In the above case, we can see that *without floating-point*, the basic Turbo C++ runtime library for any program using stream i/o is 17K, assuming debugging was in fact disabled. If you really just want to say hello, use write() instead of using stream i/o. If you are writing a clone of 1-2-3 or WordPerfect, the 40% reduction in source file size seems far more relevant than a few percent difference in executable file size. -- Steve Clamage, TauMetric Corp, steve@taumet.com
diamond@tkou02.enet.dec.com (diamond@tkovoa) (08/06/90)
In article <380@taumet.com> steve@taumet.com (Stephen Clamage) writes: >There can be no validity to a claim that two programs written in two >different programming languages are the "same". It is a well-known >theorem in the theory of computation that one cannot *in general* >determine whether two programs have the same behavior. False and true, respectively. No machine (or no program, if you prefer) can correctly check all possible pairs of programs. Every machine will fail (loop forever) for at least some cases. However, some machines can check SOME programs. Some humans can too. I am sure that you can verify, and you can write a program to verify, that the following two programs (executed by processors conforming to their respective standards; and using the ASCII standard character set or, if you prefer, similar ISO version) are the same: int main(int argc, char **argv) { PROGRAM junk(OUTPUT); printf("Hello, world.\n"); BEGIN return 0; WRITELN('Hello, world.'); } END. (In fact I thought of giving an example with null programs, but didn't because it might mislead as to the amount of generality that this case has.) -- Norman Diamond, Nihon DEC diamond@tkou02.enet.dec.com This is me speaking. If you want to hear the company speak, you need DECtalk.
steve@taumet.com (Stephen Clamage) (08/06/90)
diamond@tkou02.enet.dec.com (diamond@tkovoa) writes: |In article <380@taumet.com> steve@taumet.com (Stephen Clamage) writes: |>There can be no validity to a claim that two programs written in two |>different programming languages are the "same"... |However, some machines can check SOME programs... |I am sure that you can verify, and you can write a program to verify, |that the following two programs (executed by processors conforming to |their respective standards; and using the ASCII standard character set |or, if you prefer, similar ISO version) are the same: |int main(int argc, char **argv) { PROGRAM junk(OUTPUT); | printf("Hello, world.\n"); BEGIN | return 0; WRITELN('Hello, world.'); |} END. The C program returns the zero status to whatever program invoked it. There is no such guarantee for the Pascal program. So these trivial toy programs might NOT be the "same" in their external characteristics, let alone what goes on internally! The orginal posting talked about a calculator program in C and in Modula. I believe that Modula follows Pascal-like rules about arithmetic exceptions, whereas there is no guarantee that any C system (ANSI included) will complain about, say, overflow or divide by zero. As to the null program, there might STILL be external differences due to what each language specifies about what must happen in its environment at startup and shutdown. -- Steve Clamage, TauMetric Corp, steve@taumet.com
sakkinen@tukki.jyu.fi (Markku Sakkinen) (08/07/90)
Note2: Third trial to post! Note1: This is the second posting of an article that apparently disappeared while the news software was out of order here for three days. Sorry if somebody receives it twice. In article <380@taumet.com> steve@taumet.com (Stephen Clamage) writes: >toma@tekgvs.LABS.TEK.COM (Tom Almy) writes: > >|It has already been pointed out that Turbo C++ is slower compiling a C program >|that Turbo C V2.0. But the problem gets worse with C++ programs [...] >| ... >You have to be very careful in such comparisons. Any C++ program >using streams includes the entire stream i/o package, which is very >large. It has much more functionality than does printf(), so it is >bigger. [...] > ... Right! Often in the newsgroups people claim that Ada code is much larger and slower and C code, when they get in the Ada programmes a lot of run-time support unheard of in the C world, e.g. array bounds checking. >|The calc program example from the Stroustrup book (with addition of trig >|functions) compiles to a 35440 byte file, while the same program rewritten >|in JPI Modula-2 (just to test it out) is 11510. (The source file in C++ is >|60% the size, though!). > >There can be no validity to a claim that two programs written in two >different programming languages are the "same". It is a well-known >theorem in the theory of computation that one cannot *in general* >determine whether two programs have the same behavior. This is a dangerous fallacy. Although the *general* problem is undecidable, it is in many particular cases possible to prove that two programmes are (or are not) equivalent. Of course, the problem is much more difficult when the programming languages are different; especially if one of the languages has as irregular and fuzzily defined semantics as C and its derivatives. (Without seeing the source code, we cannot guess how much it was "the same program" in the quoted case, of course.) > ... >IMHO, comparing executable file size for toy programs is an entirely >irrelevant exercise. [...] > ... Here I agree wholeheartedly! Markku Sakkinen Department of Computer Science University of Jyvaskyla (a's with umlauts) Seminaarinkatu 15 SF-40100 Jyvaskyla (umlauts again) Finland SAKKINEN@FINJYU.bitnet (alternative network address)