Regarding scope, the parameters for a Function or Sub only have scope within the Function or Sub that they are declared in. The same thing holds true for variables declared with the keyword Dim (or ReDim for arrays). For undeclared variables, the scope is the next highest scope if a matching identifier exists (module level private variables, then global variables). If no identifier with a greater scope exists, it is implicitly declared as a Variant in local scope. So, in this line...
Sub convert(speedmph, speedkph, speedfts)
...the parameters speedmph, speedkph, and speedfts are all local to Sub convert (and are implicitly Variant because there aren't As statements), and in this line...
Dim speedmph As Double, speedkph As Double, speedfts As Double
...the parameters speedmph, speedkph, and speedfts are all local to Sub problem1().
That out of the way, let's address why your MsgBox shows anything at all. The default method of passing parameters is ByRef, so if you were to actually specify all of the implicit parts of your Sub declaration, it would look like this:
Public Sub convert(ByRef speedmph As Variant, ByRef speedkph As Variant, _ ByRef speedfts As Variant)
When you call it like this...
Call convert(speedmph, speedkph, speedfts)
...you pass it references to the local variables speedmph, speedkph, speedfts you declared in Sub problem1(). The Sub convert assigns values to those references, but each procedure only holds it's own copy of the reference in local scope. The identifiers are completely irrelevant at this point. In fact, this works exactly the same as your sample code (only the identifiers have been changed):
Sub problem1() Dim speedmph As Double, speedkph As Double, speedfts As Double speedmph = Val(InputBox("Enter a speed (mph)")) Call convert(speedmph, speedkph, speedfts) MsgBox "The speed is " & speedkph & " km/hr and " & speedfts & "ft/s." End Sub Sub convert(foo, bar, baz) bar = foo * 1.609 baz = foo * 1.467 End Sub
Now to answer your specific question, the reason you can't use a "placeholder" value of 0 is that 0 isn't a variable - it's a literal. When it's passed to Sub convert, the only place the reference exists is as a variable on the call stack - Sub problem1() doesn't hold a reference to it, so there's no way for Sub problem1() to tell what Sub convert does with it. The stack variable is destroyed when Sub convert returns and the call stack unwinds. Basically, passing a literal to a ByRef parameter is functionally equivalent to passing it ByVal.
string.Format("{0}{1}", foo, bar)in .NET?Call convert(speedmph, speedkph, speedfts)I useCall convert(speedmph, 0, speedfts).0is a number, not a variable. If you need to pass the value back by reference,speedkphis the "placeholder".