This is an entirely valid, but useless statement
"AB";
It's a string literal: a series of char
with an implicit NUL-terminator (subtly different from NULL
); three bytes. Because it doesn't "do anything" a compiler might ignore it completely, but it could generate those bytes in the program code: 0x41
, 0x42
, 0x00
. To actually start doing something with it, you can assign it to a variable
auto x = "AB";
What's the type of this variable? With no runtime type information, you could force the compiler to tell you with a deliberate error
auto x = "AB";
float f = x;
That error message is
cannot convert 'const char*' to 'float'
And what is that exactly? The cdecl
utility can help
$ cdecl
Type `help' or `?' for help
cdecl> explain const char *x
declare x as pointer to const char
Note the difference with
cdecl> declare x as const pointer to char
char * const x
So what is "const char
"? Remove the invalid statement. Create a second pointer with the same string literal, and a third pointer to a different string.
auto x = "AB";
auto y = "AB";
auto z = "JK";
Serial.println(reinterpret_cast<uintptr_t>(x), HEX);
Serial.println(reinterpret_cast<uintptr_t>(y), HEX);
Serial.println(reinterpret_cast<uintptr_t>(z), HEX);
Printing the numeric value of the pointers, the first two are the same. (And the third is three bytes away.) The compiler can see two literals result in the same string, and generates just one copy. Each of the characters in the string is a const char
that cannot be changed. This allows sharing; otherwise changing a character would affect all the uses of that string. If you try
y[0] = 'T';
the error is "assignment of read-only location '* y'
". You see "const char *
" a lot in function declarations. It's a promise, enforced by the compiler, that the function will not change any of the characters through that pointer. Compare that to a writable string buffer: "char *
". You can pass either to the const
one, because it's not going to change anything anyway. But you can only pass a writable buffer if the function does not declare const
, because the function is allowed to modify it. The strcat
function you're using exemplifies this
char* strcat( char* dest, const char* src );
The src
being added is not modified, but the dest
is. Using "a bunch of characters with a zero at the end", and str
functions like that is how string operations are done "the old way". It gets less efficient with longer strings because you sometimes have to scan the whole string looking for the NUL-terminator. And if it's not where it's supposed to be for some reason, bad things happen. Or if the buffers you create are not big enough, bad things happen.
To address such issues, C++ has its own std::string
; but the Standard Library is not available on all Arduino platforms, so they created their own String
class with a Java[Script]-like API. That has its own gotchas, especially on more constrained platforms.
In your usage
if any of the KeyText
is more than a single character plus NUL, then strcat
will overwrite past the end of CodeNo
. You might be tempted to use strncat
, but that's actually the wrong variant: you might end up losing the NUL in the buffer. strlcat
does not have that problem.
But even better: a string literal is an easier- to-type array of single char
const char KeyText[] = "123456789*0#";
You don't have to put the '\0'
so that strcat
starts in the right place; you don't even use it. Just overwrite that last character directly. The NUL at the end of the original string of spaces (at index [4]
) is never touched
CodeNo[3] = KeyText[y*3+x];