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.
Related
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.
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 have declared and initialized a string in char* like below:
int length;
cout<<"Enter string length: ";
cin>>length;
char* str = new char[length];
cout<<"Enter your string here:";
for(int i = 0;i < length;i++)
cin>>str[i];
cout<<str<<endl;
Compile using DevC++, it give me what I type there, but in Visual C++ it print out string with some random character at the end.For example; I type "hello" Visual C++ give me:"hello ^^&*(Y&".Can someone explain to me why does this happen ?
You're not null terminating the string. You need to assign a char array of length + 1 to allow room for the null character at the end of the string.
You're missing a null-terminator at the end of the string. I'm guessing non-VC++ compiler handles it somehow, but in VC++ you have to allocate length + 1 characters and then set last one to '\0'. Example:
char* str = new char[length + 1];
cout << "Enter your string here:";
for (int i = 0; i < length; i++)
cin >> str[i];
str[length] = '\0';
You're never terminating your string. Put str[length-1] = '\0';.
Or, better, you should allocate new char[length+1];.
IMHO...
Change this
for(int i = 0;i < length;i++)
cin>>str[i];
to
cin>>str;
No for loop required. You can read a string as a whole instead of char by char.
Alternately, if you want to read it char by char, you need a \0 to mark the end of the string because cout assumes that the string is terminated with a \0.
So essentially, you need
for(int i = 0;i < (length - 1);i++)
cin>>str[i];
str[i] = '\0'.
You read length -1 chars and the terminate it with \0.
Is this expression correct?
{
char a;
char *temp;
for(int j = 0; j < len; j++)
{
strcpy(&temp[j], (char*)a);
}
}
in this code a gets updated externally by the user input/key stroke. I want to copy all incoming/updated a to the temp as an entire string.
Since 'a' in your example is not null-terminated and you want to assign a single character in string buffer, you can do:
int const buffer_size = 5;
char a = 'c';
char *temp = new char[buffer_size]; // Remember to allocate your string buffer
temp[index] = a;
// .....
delete[] temp; // free buffer.
index is an int which you can use to keep track of next position in buffer.
No for a few reasons:
temp isn't initialised, it's pointing to some random location and doesn't have any memory allocated for it, so you're just going to be writing over a random part of memory (and/or crash).
a is a single char and you're treating it's value as a string (char*) using strcpy (I'm assuming you meant (char*)&a which is still wrong).
strcpy continues copying chars from the source (a) to the destination until it hits a '\0' in the source...which could be anywhere since a is not a NUL terminated string but a char.
If you want to write/append a single char to a string/buffer you just do buffer[position] = character, where buffer is a char[] or a char* pointing to a block of allocated memory, position is the position in the buffer you want to stick the char, and character is obviously that char.
Anyway, I've got no idea what you're trying to do or the logic behind why you're trying to do whatever it is you're trying to do like this.
EDIT: And also you have this tagged as C++, why aren't you using std::string and std::cin if you are in fact using C++?
Your code is wrong in so many ways:
You cannot cast a char to a char*.
You have to do something like this: (char *)&a; and since a is a char, you don't need to cast it at all.
You don't need to strcpy it. You can just do something like:
temp[j] = a;
char *temp has not associated memory assigned to it. So you need to do something like this: char *temp = malloc(sizeof(char) * len);
Complete code here:
{
char a = myFunToGetInput();
char *temp = malloc(sizeof(char) * len));
for(int j = 0; j < len; j++) {
temp[j] = a;
}
}
Or if you have used memset before:
{
char a = myFunToGetInput();
char *temp = malloc(sizeof(char) * len));
memset(temp, (unsigned char)a, len);
}
For some weird reason, it keeps on creating uninitilized values when I pass in the length as 12, it creates an array of about 16 and stores the rest with crap that I don't want. Anyone know why this isn't working? It's for an assignment that's due tomorrow and this is my last problem... Any help would be appreciated thanks.
char * convertToUppercase (char* toUpSize, int length) {
std::cout << "ToUpsize: " << toUpSize << "\nLength: " << length << "\n";
char * upsized = new char[length];
for (int i = 0; toUpSize[i]; i++) {
upsized[i] = toupper(toUpSize[i]);
}
return upsized;
}
I think you either write i< length in the for loop, instead of toUpSize[i] as:
for (int i = 0; i < length; i++) {
upsized[i] = toupper(toUpSize[i]);
}
Or pass toUpSize as null-terminated string if you want to write toUpSize[i] in the for loop condition. If you do so, then you've to put \0 at the end of upsized after you exit from the loop, at index i for which toUpSize[i] is \0. And to accomplish this, you've te move the definition of i outside the for loop, so that you can use it after you exit from the loop.
Null-terminated string is what which has \0 character at the end of the string.
char x[] = {'N', 'a', 'w', 'a', 'z' };
char y[] = {'N', 'a', 'w', 'a', 'z', '\0' };
Here, x is not a null-terminated string, but y is a null-teminated string.
If the strings are defined as:
char z[] = "Nawaz";
const char *s = "Nawaz";
Here z and s are null-terminated string, because both of them are created out of "Nawaz" which is a null-terminated string. Note that sizeof("Nawaz") would return 6, not 5, precisely because there is an \0 at the end of the string.
You need to null-terminate the returned array if you want to print it like a string. Make sure that it ends with a null-terminator. Depending on how you calculate the length argument you may need to add extra space for it to the array. You may also want to make sure that the array that you pass in is null-terminated.
You need to add the termination char:
char * convertToUppercase (char* toUpSize, int length) {
std::cout << "ToUpsize: " << toUpSize << "\nLength: " << length << "\n";
char * upsized = new char[length];
int i;
for (i = 0; toUpSize[i]; i++) { // stops when you get toUpSize[i]==0
upsized[i] = toupper(toUpSize[i]);
}
upsized[i] = '\0'; //add termination
return upsized;
}
Your code assumes length to be the length of the allocated array, not the length of the string. strlen(toUpSize) counts the chars that are not '\0' from position 0 in toUpSize.
E.g.: strlen("abc\0def") -> 3
sizeof("abc\0def") -> 8!
Why are you even bothering with char pointers? This is C++, not C.
#include <string>
#include <algorithm>
#include <cstring>
#include <iostream>
std::string to_upper_case(std::string str)
{
std::transform(str.begin(), str.end(), str.begin(), toupper);
return str;
}
int main()
{
std::cout << to_upper_case("hello world\n");
}
If you decide to stick to the C solution, reserve one more char for the NUL terminator and put it there:
char * upsized = new char[length + 1]; // note the +1
upsized[length] = 0;