As @Secure notes, C's printf
function is inspired by BCPL's writef
function. And if you look at the wikipedia page for BCPL, it has an example that shows that BCPL writef
also used %
to introduce a format specifier.
So we can infer that C used %
either because BCPL did, or for the same reasons that BCPL did. My gut feeling is that it was simply that %
is one of the least commonly used ASCII characters ... or so the authors thought. It is also likely that they didn't spend a lot of time in weighing the various alternatives. At the time, both BCPL and C were obscure languages, and the authors most likely had more important things to deal with.
However, there is a minor spanner in the works. While C was inspired by BCPL, it is not entirely clear whether the C borrowed BCPL I/O libraries or the other way around. I dimly recall that BCPL's I/O libraries went through a process of evolution about the time that the infix byte indexing operator was added to the language. (Actually, I think I know who would know about that.)
Let's start at the beginning: mixed C and C++ code is fairly common. So you're in a big club to start with. We have huge C codebases in the wild. But for obvious reasons many programmers refuse to write at least new stuff in C, having access to C++ in the same compiler, new modules start to be written that way -- at first just leaving the existing parts alone.
Then eventually some existing files get recompiled as C++, and some bridges can be deleted... But it may take really long time.
You are ahead somewhat, your full system is now C++, just most of it is written "C-style". And you see mix of styles a problem, what you should not: C++ is a multi-paradigm language supporting many styles, and allow them to co-exist for good. Actually that is the main strength, that you are not forced to a single style. One that would be suboptimal here and there, with some luck not everywhere.
Re-working the codebase is a good idea, IF it is broken. Or if it is in the way of development. But if it works (in the original sense of word), please follow the most basic engineering principle: if it ain't broke, don't fix it. Leave the cold parts alone, put your effort where it counts. On the parts that are bad, dangerous -- or in new features, and just refactor parts to make them a bed.
If you seek general things to address, here's what worth evicting from a C codebase:
- all the str* functions and char[] -- replace them with a string class
- if you use sprintf, create a version that returns a string with the result, or puts it in the string, and replace usage. (If you never bothered with streams do yourself a favor and just skip them, unless you like them; gcc provides perfect type safety out of the box for checking formats, just add the proper attribute.
- most malloc and free -- NOT to with new and delete, but vector, list, map and other collectons.
- the rest of memory management (after the previous two points it must be pretty rare, cover with smart pointers or implement your special collections
- replace all other resource usage (FILE*, mutex, lock, etc) to use RAII wrappers or classes
When you're done with that you approach the point where the codebase can be reasonably exception-safe, so you can drop return-code football using exceptions and rare try/catch in high-level functions only.
Beyond that just write new code in some healthy C++, and if some classes are born that are good replacement in existing code, pick them up.
I didn't mentions syntax-related stuff, obviously use refs instead of pointers in all new code, but replacing old C parts just for that change is no good value. Casts you must address, eliminate all you can, and use C++ variants in wrapper functions for the remainder. And very importantly, add const wherever applicable. These interleave with the earlier bullets. And consolidate your macros, and replace what you can make into enum, inline function or template.
I suggest reading Sutter/Alexandrescu's C++ Coding Standards if not yet done and follow them closely.
Best Answer
In some cases a program can be run in different ways and exhibit different behavior on how it is called. If you call
vim
asvi
, it runs in a compatibility mode. Sometimes it is to try to maintain one version of several related programs - for examplemailq
andnewaliases
on many unix systems are a link tosendmail
so that these programs stay in sync)Java programs are typically invoked as:
The first version is where you have a Manifest file that indicates the main class, the second version runs the main method in the class
Foo
found in the class path.The information presented for Java is either a path to the jar or the name of the class being invoked.
The location of the jar isn't important enough to be something to code from (and was actually not part of the original spec). A Jar can be named anything really, and often includes version numbers. Whats more, there's no guarantee that the class was even stored in a .jar (it could have been extracted).
Invoking a Java application with
-jar
has only one way to enter it - the class defined in the Manifest. There's no renaming that can be done.The other option, of invoking it with the class name points directly to the execution unit. Furthemore, it can't be named multiply - you can't have
Bar.class
be the code forclass Foo
it just doesn't work that way.This should show that there's really no point to passing the information of
argv[0]
in the C sense to a Java application - its either going to bejava
, meaningless and arbitrary, or the name of the class that is being invoked (that you are already executing code out of (you could do something likegetClass().getEnclosingClass().getName()
if you were desperate...)).There is a point here, you can define multiple Main methods in classes in a .jar or on the class path. And you could have them behave differently just as if there was a series of if statements based on what
argv[0]
was.I have in the past had code akin to
java -cp Foo.jar com.me.foo.Test
which invoked theTest
class's Main method rather than the one defined in the one defined in the Manifest.