(Note: I'm playing devil's advocate here to some extent. My view is that safety is important, but lack of provable safety is not some terrible Demogorgon that we should hide in fear from. I think a lot of the concern over safety is valid, but in some contexts it's just overhyped.)
Which is one reason why I don't buy the "memory safety" argument as a very strong one for adopting Rust. There are other much better reasons to do so for a certain class of programming, in my opinion.
[0] https://en.wikipedia.org/wiki/Buffer_overflow#Choice_of_prog...
I'm not sure how default access in C or C++ isn't explicitly avoiding checks. By definition "a[b]" is an unchecked dereference. It doesn't get more explicit than "by definition." Of course if by "explicit" you mean "syntax exists that demarcates unchecked access" then C and C++ will never satisfy. I'd argue that's a contrived and artificially narrow use of "explicit" meant, er, explicitly to exclude C and C++ from being acceptable by definition and therefore not terribly fair.
I understand that this is... obscure for some reason and I'm not saying it never happens, but let's be realistic....
Thus if you have a to_upper(char*) function, you don't know what it takes or does without looking it up. Does it uppercase a single character or a whole string? How do you even tell what you were passed without potentially reading past the end of a buffer?
If I happen to have a pointer-to-char and pass it to a to_upper function that operates on strings, it will just write on invalid memory, because C can't distinguish between the two.
So don't do that.
extern int toupper(int);
(via #include <ctypes.h>) that will upper case a single character. If, on the other hand, I saw: extern char *to_upper(const char *);
I would expect that to_upper() returns a new string (freeable via a call to free()) that is the upper case version of the given string.> If I happen to have a pointer-to-char and pass it to a to_upper function that operates on strings, it will just write on invalid memory, because C can't distinguish between the two.
Um ... how do you "happen" to have a pointer-to-char? And unknowingly call to_upper()? I'm lost as to how this can happen ...
int frobnicate(char*)
and you make that kind of assumption, then your code may or may not work, depending on what the function does internally. You simply do not know whether I am operating on null-terminated char sequences or a single char.>Um ... how do you "happen" to have a pointer-to-char?
char* text = "some text";
char* c = text[2]
There you go.>And unknowingly call to_upper()?
Who said anything about unknowingly calling a function? It's "toupper", not "string_to_upper" or "char_to_upper". The function signature simply doesn't tell you what the function requires of its input.
PS: char* is also a pointer-to-byte in C.
a.c:2: warning: initialization makes pointer from integer without a cast
a.c:2: error: initializer element is not constant
What you really want is: char * text = "some text";
char * c = &text[2];
which still doesn't prove your point because c is still pointing to a NUL-terminated string.If fronnicate() really takes a single character, I might ask why the function requires a pointer to char for a single character instead of:
int frobnicate(char);
but if you are going to really argue that point, so be it. Discard the fact that in idiomatic C, a char * is generally considered a NUL-terminated string (and here I'm talking ANSI C and not pre-ANSI C where char * was used where void * is used today).You are also shifting the argument, because in your original comment I replied to, the function you gave was to_upper(). toupper() is an existing C function.
P.S. char * is a pointer-to-character, not a "pointer-to-byte", pedantically speaking. All the C standard says is that a 'char' is, at minimum, 8 bits in size. It can be larger. Yes, there are current systems that this is true.
>which still doesn't prove your point because c is still pointing to a NUL-terminated string.
No, it's pointing at a char that happens to be part of a nul-terminated string. The semantic intent of that distinction is entirely lost because C fails to make a distinction. I could easily overwrite that nul, and it would no longer be the case. Then it's suddenly an array of chars, and everything pointing at it is now a new type of thing.
char* s = (char*) rand();
This also will point at a 'nul terminated string' with very high probability. Doesn't mean it is safe to call string functions on it...
>I might ask why the function requires a pointer to char for a single character instead of int frobnicate(char)
You could say the same about any pointer argument. Obviously pointers are useful for a reason. If frobnicate returned a char, I would just end up dereferencing a pointer to stick it back in the string it came from. Whether that is frobnicate's job or it's caller's job is a matter of API design, and should not be determined by C, especially when it makes no preference for any other kind of pointer.
>You are also shifting the argument, because in your original comment I replied to, the function you gave was to_upper
My arbitrary example function name doesn't matter one iota. Get over it, and stop being needlessly dense.