If you assume only US-ASCII, your solution isn't half bad. I would not use array notation for strings; it is correct C, but not idiomatic for C-style string processing. Also, since you modify the string in place, why do you bother returning the pointer? The caller by definition has it already. The only reason to return the pointer is to users can call chains of functions, but I personally don't like that style, since you are relying on a side-effect of the function, and there is no clear distinction whether the string is an input / output / I-O argument or return value. You also don't need extra parentheses around the expressions in the if test; anyone who programs in C needs to know simple operator precedence well enough to read that statement.
I also removed your extra variable, which is not needed: You can modify the pointer that is passed in; the caller's copy will not be modified. Once that's done, I think that a for loop is clearer than a while loop, but that's a question of taste.
Another deep philosophical question is: Should you modify the string in place? I can understand why one would want to do that in C, because there copying strings involves memory management, and a huge amount of work for tracking allocating and freeing (which is usually handled wrong, which is why amateur-written C code tends to leak memory like the Titanic). But in general, it is cleaner if a functio is a "pure" function, which gets constant arguments in, and return a new value. More about that below.
C++:
void touppercase(char *instr ) {
for(; *instr!='\0'; instr++)
{
assert(*instr <= '~'); // Twiddle is the last ASCII character before internationalization kicks in
if (*instr<='a' && *instr=<='z')
*instr += 'a'-'A';
}
}
The moment you get internationalization or Unicode into the game, this becomes exponentially difficult. I'm not even sure whether you can define what "capitalize" means in a locale-independent fashion; it is possible that the definition of what the uppercase character is might depend on language and country. Furthermore, there exist lowercase characters for which there is no uppercase equivalent. Until 6 weeks ago, the german "sharp ess" a.k.a. "es-zet" was one such example: it only existed in lowercase form, and when capitalizing a word, it was replaced by a double 's": "Straße" -> "STRASSE" (that word means "street"). See how the string length changes? Starting in 2018, the german organization that standardizes orthography defined a new character which is an uppercase es-zet, but I doubt that many unicode implementations are ready for it. And I don't know whether the standards bodies for Austria, Switzerland, and other german-speaking places have followed that example yet. There are other examples of characters that behave strangely when capitalizing. But what this leads to: In Unicode, the length of a string may change when it is capitalized, so you are forced to allocate a new string and copy it (see above).
I think the correct answer to the question is: Anyone who tries to implement this today is crazy and stupid; one should instead find a good string library with internationalization, and call the correct function. Now, if you give this answer in a homework problem or during a job interview, and get in big trouble, please don't blame me.