I use a 16x2 character LCD to display some text. What I want is first line is fixed and second line is scrolling.
I wrote a program which works fine but the problem is after some time Arduino does not respond. I suspect there might be a bug or memory leak in the code.
The relevant code is like this.
void scrollTextFromRight(int line, char text[])
{
const char space[16] = " ";
char screen[16];
char * longText;
longText = malloc(sizeof(char) * (sizeof(text) + 17));
memset(longText, '\0', sizeof(char) * (sizeof(text) + 17));
memset(screen, '\0', sizeof(screen));
for (int i = 0; i < 16; ++i)
{
longText[i] = space[i];
}
for (int j = 0; j < sizeof(text) + 17; ++j)
{
longText[16+j] = text[j];
}
for (int i = 0; i < sizeof(text) + 17; ++i)
{
lcd.setCursor(0, line);
strncpy(screen, longText + i, 17 );
lcd.print(screen);
delay(350);
}
}
I call this function from main program like this:
scrollTextFromRight(1, "Scrolling text");
Update 1 :
After reading the comments and answers I freed allocated memory space with free function. I uploaded the new code and testing whether it works as expected.
I added this part after the third for loop.
free longText;
Update 2 :
After reading the comments I decided to use Arduino's String class. The code became like this:
void scrollTextFromRight(int line, String text)
{
const String space = " ";
const String longText = space + text + ' ';
int displaySize = 16;
for (int i = 0; i <= longText.length(); ++i)
{
lcd.setCursor(0, line);
String display = longText.substring(i, i + displaySize);
lcd.print(display);
delay(350);
}
}
When you declare the argument as char text[], the compiler translates it as char* text. That is, it's a pointer.
And getting the size of a pointer (e.g. sizeof(text)) gives you the size of the pointer and not whatever it points to. If it's a null-terminated byte string, then use strlen to get the length (but note that the null-terminator not counted).
Or even better, stop using C strings and functions, because Arduino is actually programmed in C++ and have its own standard String class that should be used for all strings.
Also note that
const char space[16] = " ";
creates an array of 16 elements, and set all those elements to the space character ' '. But it's not a null-terminated string, because the terminator doesn't fit in the array.
You also know about the memset function, but seems to have forgotten the memcpy function when you copy from your arrays.
Instead of the explicit loop copying from space, you could simply do
memcpy(longText, space, sizeof space); // Using sizeof since space is not null-terminated
Lastly, be careful with the strncpy function, it might not null-terminate the destination string.
Related
I am learning c++, I write some code to convert a string to uppercase and display it. I assign a string str with "asdf" and then create a char array pointer and allocate a length same as that of the string.
But after I assign indices of char array with uppercase chars when I try to display char array there are many junk characters appended to the end. Why does this happen as I have only allocated the char array with a size = "length of string" then how does char array have junk chars at the end even after the actual allocated size.
string str{ "asdf" };
char* str_c = new char[str.length()];
for (int i = 0; i < str.length(); i++) {
str_c[i] = toupper(str[i]);
}
cout << str_c; // displays ASDF²▌r┐│w²A∙
Your char array needs to be one character longer than the length of the string, for the null terminator
string str{ "asdf" };
char* str_c = new char[str.length() + 1];
for (int i = 0; i < str.length(); i++) {
str_c[i] = toupper(str[i]);
}
str_c[str.length()] = '\0';
cout << str_c; // displays ASDF
In C-style strings (char*) the length of the string is not stored in memory. Thus, it must use some other way to determine where the string ends. The way it does that is by assuming that the string is all the bytes until the byte that is equal to zero (so-called null-terminator).
Without explicitly allocating an extra byte for the null terminator, and setting it, your string continues with the garbage that happens to be after the bytes you have allocated until it encounters (accidentally) the nearest 0.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Sizeof an array in the C programming language?
I've been fiddling with C to become better acquainted with it and think I may have stumbled upon a initialization/pointer issue that I'm unsure of how to resolve. The below program is an implementation of ROT13, so it takes an input string, and shifts each letter by 13, resulting in the cipher text. The output of my program displays the correct shift, but it won't work for more than 4 characters, making me wonder if sizeof is being used incorrectly. Any other suggestions are appreciated, I'm sure I've messed a few things up at this point.
#include <stdio.h>
#include <string.h>
void encrypt(char *);
int main(void){
char input[] = "fascs";
encrypt(input);
return 0;
}
void encrypt(char *input){
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
printf("Input: %s \n", input);
int inputCount = sizeof(input);
printf("Characters in Input: %i \n\n", inputCount);
//holds encrypted text
char encryptedOutput[inputCount];
//Initialize counters
int i, j = 0;
// loop through alphabet array, if input=current letter, shift 13 mod(26),
// push result to output array, encryptedOutput
for(i = 0; i < inputCount; i++){
for(j = 0; j < 26; j++){
if(input[i] == alphabet[j]){
encryptedOutput[i] = alphabet[(j + 13) % 26];
}
}
}
//Nul Termination for printing purposes
encryptedOutput[i] = '\0';
printf("Rot 13: %s \n\n", encryptedOutput);
}
sizeof() in encrypt will not behave as you want it to. Inside encrypt, the sizeof(char *) is 4(on a 32bit machine) or 8(on a 64 bit machine), which you can see is the size of a pointer.
To get the sizeof(input) you must change sizeof to strlen. Hence solution = strlen(input)
Why this happens?? when you pass an array into a function, that array is internally represented as a pointer. At the called-function's end input is just a pointer, which gives either 4 or 8 bytesize depending upon your machine.
To get the sizeof of input, just use a macro like this:
#define SIZEOF(x) (sizeof(x)/sizeof(x[0]))
and use this in the function that defines x. In your program, x is input in main()
sizeof returns the size of the type of its argument. It cannot determine how many characters are in a pointer to a character array.
You should consider using the strlen function if you know that your string is null-terminated.
input has type char* (read as "pointer to char"). sizeof(input) gives you the size of the pointer. You probably want to use strlen to find the length of the string, or pass the length in to the function as an additional argument.
This line causes your problem.
int inputCount = sizeof(input);
sizeof only determines the size of the variable in this case char *. And every pointer has the size of 4 bytes on a 32 bit system.
You can't determine the size of an array during runtime. You could either
* pass the size of the input as an parameter
* because in your case it is a string, use the strlen in string.h to get the length of the string if the string is terminated by \0.
But in both cases you can't simply allocate the output buffer using
char output[variable_containing_size];
You would need to use malloc() to dynamically allocate memory during runtime or even easier pass the output parameter as parameter to your function.
#include <stdio.h>
#include <string.h>
#define BUFFER_LENGTH 80
void encrypt(const char * input, char *output);
int main(void){
char input[BUFFER_LENGTH] = "fascs";
char output[BUFFER_LENGTH] = {0}; // initialize every field with \0
encrypt(input, output);
return 0;
}
void encrypt(const char *input, char *output){
char alphabet[] = "abcdefghijklmnopqrstuvwxyz";
printf("Input: %s \n", input);
int inputCount = strlen(input);
printf("Characters in Input: %i \n\n", inputCount);
//Initialize counters
int i, j = 0;
// loop through alphabet array, if input=current letter, shift 13 mod(26),
// push result to output array, output
for(i = 0; i < inputCount; i++){
for(j = 0; j < 26; j++){
if(input[i] == alphabet[j]){
output[i] = alphabet[(j + 13) % 26];
}
}
}
//Nul Termination for printing purposes
output[i] = '\0';
printf("Rot 13: %s \n\n", output);
}
But in this case the encrypt() function does no size checks at all, and if you're not careful this could easily lead to buffer overflows.
Basically, here, I'm trying to reverse an array, and convert the reversed int array into a string (I'm trying to write the equivalent of Java's BigInteger class in C++ - basically turning the input into big endian ordering, breaking down the operations, reversing the result back to little endian format, and returning the string).
And as you can see below, it outputs some strange characters (I think it's an out of range reference?) - but I'm not entirely sure what caused this output?
I would really appreciate if you could take a look at it:
Sample input
int a[] = {1, 2, 3};
int rA[3];
reverseIntArray(a, rA, 3);
string aString = intArrayToString(a, 3);
cout << aString << endl;
Console output
123\216\377
As you can see - it calculates the answer correctly, with the exception of the \277_\377.
I'll post the rest of the relevant functions:
reverseIntArray
void reverseIntArray(int array[], int reversedArray[], int arrayLength) {
for (int i = 0; i < arrayLength; i++) {
reversedArray[i] = array[arrayLength - 1 - i];
}
}
intArrayToString
string intArrayToString(int digits[], int length) {
// convert int array to char array
char digitsChar[length];
for (int i = 0; i < length; i++) {
digitsChar[i] = '0' + digits[i];
}
// convert char array to string
string intString(digitsChar);
return intString;
}
I'm quite sure this is a subtle issue to do with pointers, but I'm still relatively new to C++ (migrating from Java) and I've stared at this for hours but haven't come up with any ideas.
The std::string constructor you are using is assuming that the string you pass is properly terminated, which it isn't and that leads to undefined behavior as the std::string constructor goes beyond the end of the digitsChar array.
Three possible solutions:
Make room for another character in the digitsChar array and terminate it:
char digitsChar[size + 1];
for (...) { ... }
digitsChar[3] = '\0';
string intString(digitsChar);
Use another constructor where you pass the length of the character array:
string intString(digitsChar, length);
Append the characters directly to the string:
string intString;
for (int i = 0; i < length; i++) {
intString += '0' + digits[i];
}
There are of course other solutions as well, like for example using std::ostringstream.
I need reverse my char string only with pointers. How can I do this? My code:
// this cannot be modified !!!
char s[10] = "abcde";
char *pS;
// my code
pS = new char;
int count = 5;
for (int i = 0; i < 10; i++)
{
if (s[i] != '\0') // not null
{
pS[count - 1] = s[i];
count--;
}
}
cout << "Reversed = " << pS;
Sometimes if works fine, I see only 5 chars, they are reversed. But sometimes I see some extra chars (looks like temp symbols). Where I miss something? Thank you!
your char array "s" contains 10 chars, but you only initialize the first 6 chars of that array with "abcde" and the \0 terminator.
When you loop over the complete array, you access not initialized chars.
I also see, that you try to write to memory, which you didn't allocate.
You only allocate memory for 1 char for you "pS" pointer, but you try to access it's memory like it is an array of chars in your for-loop.
Instead of using hardcoded:
int count = 5;
you also could use the string function strlen() to determine the length of the c-string.
Edited (untested code):
char s[10] = "abcde";
char pS[10];
for (int i = 0; i < strlen(s); i++)
{
if (s[i] == '\0') // not null
{
// stop loop, as soon as you reach the end of the original string
break;
}
pS[strlen(s) - 1 - i];
}
// now add the termination char \0 to your pS array
pS[strlen(s)] = '\0';
cout << "Reversed = " << pS;
Just giving you the hint how to reverse the string using pointers:
Take two pointers front and rear where front is pointing to first char of string and rear is pointing to last char of string.
Check if front is less than rear
If yes, swap the value of first and last character. If no , just print the string.
Increment front pointer and decrement rear pointer
Repeat from step 2.
After reading another book I fully understand pointers and how to correctly allocate memory. Here is my final code which correctly reverse array of char string (I don't need universal code, just working example + without std methods for reversing):
// not edited part - based on exercise (I mean I cannot change pS to char[5] etc.
char s[10] = "abcde";
char *pS;
pS = new char[strlen(s) + 1]; // allocate correct memory size based on string size
cout << "Size is " << sizeof(pS) << endl; // just for testing
int count = strlen(s); // for iteration
pS[count] = '\0'; // last symbol must be '\o' (thanks to Mr.Yellow)
for (int i = 0; i < 10; i++) // 10 because array of char still has 10 elements
{
if (s[i] != '\0') // looks like "not garbage memory"
{
count--;
pS[count] = s[i]; // set correct value
}
}
cout << "Reversed = " << pS << endl;
Thank you to all who helps me!
I want to create jagged character two dimensional array in c++.
int arrsize[3] = {10, 5, 2};
char** record;
record = (char**)malloc(3);
cout << endl << sizeof(record) << endl;
for (int i = 0; i < 3; i++)
{
record[i] = (char *)malloc(arrsize[i] * sizeof(char *));
cout << endl << sizeof(record[i]) << endl;
}
I want to set record[0] for name (should have 10 letter), record[1] for marks (should have 5 digit mark )and record[3] for Id (should have 2 digit number). How can i implement this? I directly write the record array to the binary file. I don't want to use struct and class.
in C++ it would like this:
std::vector<std::string> record;
Why would you not use a struct when it is the sensible solution to your problem?
struct record {
char name[10];
char mark[5];
char id[2];
};
Then writing to a binary file becomes trivial:
record r = get_a_record();
write( fd, &r, sizeof r );
Notes:
You might want to allocate a bit of extra space for NUL terminators, but this depends on the format that you want to use in the file.
If you are writing to a binary file, why do you want to write mark and id as strings? Why not store an int (4 bytes, greater range of values) and a unsigned char (1 byte)
If you insist on not using a user defined type (really, you should), then you can just create a single block of memory and use pointer arithmetic, but beware that the binary generated by the compiler will be the same, the only difference is that your code will be less maintainable:
char record[ 10+5+2 ];
// copy name to record
// copy mark to record+10
// copy id to record+15
write( fd, record, sizeof record);
Actually the right pattern “to malloc” is:
T * p = (T *) malloc(count * sizeof(T));
where T could be any type, including char *. So the right code for allocating memory in this case is like that:
int arrsize[3] = { 10, 5, 2 };
char** record;
record = (char**) malloc(3 * sizeof(char *));
cout << sizeof(record) << endl;
for (int i = 0; i < 3; ++i) {
record[i] = (char *) malloc(arrsize[i] * sizeof(char));
}
I deleted cout'ing sizeof(record[i]) because it will always yield size of (one) pointer to char (4 on my laptop). sizeof is something that plays in compiling time and has no idea how much memory pointed by record[i] (which is really a pointer - char * type) was allocated in the execution time.
malloc(3) allocates 3 bytes. Your jagged array would be an array containing pointers to character arrays. Each pointer usually takes 4 bytes (on a 32-bit machine), but more correctly sizeof(char*), so you should allocate using malloc(3 * sizeof(char*) ).
And then record[i] = (char*)malloc((arrsize[i]+1) * sizeof(char)), because a string is a char* and a character is a char, and because each C-style string is conventionally terminated with a '\0' character to indicate its length. You could do without it, but it would be harder to use for instance:
strcpy(record[0], name);
sprintf(record[1], "%0.2f", mark);
sprintf(record[2], "%d", id);
to fill in your record, because sprintf puts in a \0 at the end. I assumed mark was a floating-point number and id was an integer.
As regards writing all this to a file, if the file is binary why put everything in as strings in the first place?
Assuming you do, you could use something like:
ofstream f("myfile",ios_base::out|ios_base::binary);
for (int i=0; i<3; i++)
f.write(record[i], arrsize[i]);
f.close();
That being said, I second Anders' idea. If you use STL vectors and strings, you won't have to deal with ugly memory allocations, and your code will probably look cleaner as well.