We often hear forms of the following question:
Here’s an answer, courtesy of Tucker Taft:
I certainly agree that ANSI C and C++ are statically typed languages, but I would debate the “strength” of their typing. Essentially any support for implicit conversion (implicit “casting,” “promotion”, “usual” arithmetic conversions, etc.) “weakens” a type system (but also makes it “friendlier” in some ways).
C allows implicit conversion between all integer types and all enumeration types. C++ at least cuts off implicit conversion to enumeration types, but retains implicit conversion among all integer (and floating-point) types. Also, in both C and C++, typedefs for pointer/array types are essentially “macros”; all pointer types with the same target type are implicitly interconvertible.
Finally C++ allows the user to define a number of their own implicit conversion operators, which basically allows the user to “weaken” the type system as they see fit.
Of course, all of this implicit conversion serves a purpose, but it does tend to move C/C++ toward the “weaker” end of the weak vs. strong typing spectrum.
Note that the “strong” distinctions between integer types helps dramatically in catching (at compile-time) array indexing errors in Ada programs, by making sure that if you have an array indexed by a count of apples, you don’t index into it with a count of oranges (without an *explicit* conversion). The advantages of “strongly” distinguishing enumeration types is even more obvious (and the designers of C++ recognized this).
The strong distinctions between access types (pointer types) in Ada also has advantages, allowing access types to be represented as offsets within their storage pool rather than as addresses, and giving more high-level control over storage management.
Strong typing can be carried too far, and some amount of implicit conversion is essential to make OOP palatable. But note that in Ada 95, even with OOP, we don’t allow implicit conversions that truncate the extension part of a record (this is a relatively common mistake in C++ when passing parameters by value). Instead, in Ada 95, the language distinguishes between a specific type T and the class-wide type T’Class, and allows implicit conversions to T’Class from T or any of its derivatives, but not to the specific type T. Conversions to the class-wide type never implicitly truncate the extension part. Conversions to a specific type can truncate, and hence must be explicit.
Note also that in Ada there are three distinct kinds of conversions, implicit ones, explicit ones, and unchecked ones. Only the unchecked ones are potentially unsafe. The explicit ones are safe, with either compile-time or run-time checks to ensure that. In C there are only implicit and explicit/unchecked conversions. C++ has recently added a checked, explicit “dynamic” cast, but still it will be common to use “normal” explicit casts for both checked and unchecked conversions, thereby making it more difficult to identify places where the type system might be compromised.
Hence, the bottom line is that the type checking is (objectively) “stronger” in Ada than C/C++, though that doesn’t necessarily mean “better” — whether one is “better” for a particular style of programming than the other is a “religious” issue IMHO. I know my religion currently favors the stronger checking of Ada in most cases [except perhaps for multiply/divide, where I personally believe the checking should either be weaker, or directly support the concept of "units"/"dimensions"].
Some older and more wide-ranging material on comparing Ada and C/C++ can be found on the AdaIC archives at http://archive.adaic.com/intro/c.html, including experience reports.