Problems with transforming a c++ string to a const char * - c++

I am trying to make a pointer to a constant character array from a c++ string.
In the last four lines, I am adding the three strings to one string. This should be used to create a pointer to a constant array. This pointer should then be returned to be used in another function. When I am debugging step by step, the "cout" at the end of the function shows the correct behaviour. When I am looking at the returned value in the main function, it points to garbage data. What am I doing wrong while returning the pointer?
const char *checkMultiID(void){
string startID = "USB0::0x2A8D::0x0101::";
string usbID = "MY54500604";
string endID = "::0::INSTR";
char answerID;
int correctFunctionInput = 0;
cout << "ID = " << usbID << "? [Y/N]" << endl;
scanf("%c", &answerID);
while(correctFunctionInput == 0){
if ((answerID == 'Y') || (answerID == 'N')){
correctFunctionInput = 1;
}
else{
cout << "Incorrect Input. Please repeat." << endl;
scanf("%c", &answerID);
}
}
if (answerID == 'N'){
cout << "Please Type in the ID like MY..." << endl;
getline (cin, usbID);
}
string fullID = startID + usbID + endID;
const char *idChar = &fullID[0];
cout << idChar << endl;
return idChar;
}

You are returning a pointer to data which is handled by a container, the c++ string, which is going out of scope, therefore is being deconstructed at the end of the function. What you want to do to get the exact behavior you describe is use a heap allocation, like so:
char* result = new char[fullID.length()+1];
std::copy(string.c_str(),string.c_str()+fullID.length()+1,result);
What you should to is return the c++ string directly, because I guarantee you that you will forget to deallocate this string eventually.
const string checkMultiID(){
return fullID;
}

You can add static keyword to fullID like :
static string fullID = startID + usbID + endID;
Better option would be to just return string.
Edit:
1201programalarm is right.
To avoid this you can do :
static string fullID;
fullID = startID + usbID + endID;
In this case after second call of checkMultiID() value from the first call will be deleted.
If you change function return type to std::string then you can just return fullID and in another function call c_str() method of returned string.
const char * result = checkMultiID().c_str();
This will solve your problem and it's the simplest solution.

Related

why passing string to a function which accepts LPSTR does noy work?

Following code gives empty string and length = 0, but while debugging I can see the childDisplayName has correct name.
CHAR fileSystemName[MAX_PATH + 1] = { 0 };
DWORD serialNumber = 0; DWORD maxComponentLen = 0;
string childDisplayName = "";
DWORD fileSystemFlags = 0;
if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
(LPSTR)&childDisplayName, MAX_PATH+1,
&serialNumber, &maxComponentLen,
&fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
{
cout << childDisplayName << "length: "<<childDisplayName.length()<<endl;
}
following code works fine. I am not getting why LPSTR works when I pass char array and does not work when I pass a string.
CHAR fileSystemName[MAX_PATH + 1] = { 0 };
DWORD serialNumber = 0; DWORD maxComponentLen = 0;
CHAR childDisplayName[MAX_PATH + 1] = { 0 };
DWORD fileSystemFlags = 0;
if (GetVolumeInformationA("C:\\", // L"\\MyServer\MyShare\"
childDisplayName, MAX_PATH+1,
&serialNumber, &maxComponentLen,
&fileSystemFlags, fileSystemName, sizeof(fileSystemName)) == true)
{
cout << childDisplayName << "length: "<<strlen(childDisplayName)<<endl;
}
string childDisplayName = ""; creates an empty empty string (zero size and unspecified capacity). Using that as a data-buffer to write into is not likely to go well.
You can do this: string childDisplayName(MAX_PATH + 1, ' '); to create a string with the proper space allocated.
Secondly, as #churill wrote, the address of a string is not the address of the characters in it. Instead use childDisplayName.data() to get a char* to the internal storage of the string that you can write in - but make sure not to write outside the range [data(); data() + size()).
EDIT: A bit on how std::string and .data() works.
I made a small example program:
#include<iostream>
#include<string>
void print(const std::string& s)
{
std::cout << "String size: " << s.size() << '\n';
std::cout << "String contents: >>" << s << "<<\n";
std::cout << "String as c-string: >>" << s.c_str() << "<<\n";
std::cout << '\n';
}
int main() {
std::string bla = "";
auto bladata = bla.data();
for (int i = 0;i < 5;++i) {
bladata[i] = '!';
}
print(bla);
std::string bla2(10, '\0');
auto bla2data = bla2.data();
for (int i = 0;i < 5;++i) {
bla2data[i] = '!';
}
print(bla2);
}
When run this outputs:
String size: 0
String contents: >><<
String as c-string: >>!!!!!╠╠╠╠╠╠╠╠╠╠╠<<
String size: 10
String contents: >>!!!!! <<
String as c-string: >>!!!!!<<
What is going on here? First thing to notice is that an empty std::string is created with zero size and unspecified capacity - looking in my debugger, I know that on my system that unspecified capacity is 15, so as long as I don't go beyond that nothing should crash. But this is obviously not something you should do in real code (writing here is strictly undefined behavior).
This means that the bla string is size 0, and contains a 15 character char buffer, where I set the first 5 characters to '!'. So when I try to print its size() or print it as a std::string it is identical to any regular empty string. However, if I use .c_str() to print the internal buffer directly, then it prints as any old char* and just prints whatever is in memory until it encounters a null-character.
On the other hand, bla2 is initialized to contain 10 null-characters. That means that its size is 10, and its capacity is at least 10 (in my case it happens to also be 15). This means that after the loop it still reports as size 10, regardless of how many '!'s I put into the buffer, and when I print it as a std::string it prints all the 10 characters it contains; both the 5 '!'s and the 5 '\0's. However, when I print it as a char* it prints the 5 '!'s and then stop as soon as it encounters a null-character.
In first code snippet, I cannot get the correct name of childDisplayName.
Although it seems to have several characters, but an exception is triggered when I want to output it.
So you passed a wrong address to the GetVolumeInformationA.Because the address of a string is not the address of the characters
You can test like the following code:
string s("test");
CHAR cs[] = "test";
cout << (void*)&s << endl;
cout << (void*)&s[0] << endl;
cout << (void*)&cs << endl;
cout << (void*)&cs[0] << endl;
Output:
As #churill says, std::string only manages a dynamic array of characters.So you can not pass the address of string to the function.
And according to MSDN:
If you modify the contents of the string returned by the const overload of data, the behavior is undefined. You also get undefined behavior if the terminal null character is changed to any other value. The returned pointer may be invalidated if a non-const reference to the string is passed to a standard library function. It can also be invalidated by a call to a non-const member function.
So even if you really succeed in passing childDisplayName.data() into the function. This is also an undefined behavior.I recommend that you pass in the parameter types required by the function correctly, instead of trying to pass other undefined or untyped behaviors, which will cause you a lot of confusion.

Passing values ​to an array within a loop

I'm trying to make a program in C++ in which the number of mathematical signs are counted. I am using isdigit to figure this out, but when I pass the value of my string, it gives me a warning.
This is the code that I have. The line digito[i] = entrada[i] is where I think the problem lies, but I do not understand why.
cout << "Input the operation" << endl;
cin >> input;
string digit[] = { "" };
string sign[]={""};
int cn = 0, cs = 0;
for (int i = 0; i < input.size(); i++) {
if (isdigit(input[i])) {
cout << "There is a digit in position " << i << endl;
cn += 1;
digit[i] = input[i];
}
else {
cout << "There is a sign in position " << i << endl;
// sign[i] = input[i];
cs += 1;
sign[i] = input[i];
}
}
It takes me to this code as the problem:
static _CONSTEXPR17 void assign(char& _Left, const char& _Right) noexcept
{ // assign an element
_Left = _Right;
}
Those two strings are problematic. You've unnecessarily declared them as arrays with one element each, and initialized each string to empty.
string digito[] = { "" };
string signo[]={""};
Yet afterwards, you're indexing them with non-zero indices:
digito[i] = entrada[i];
This line is problematic because of two reasons; going beyond the array bounds, and incompatible types.
digito[i] is the type of std::string (because digito is std::string[]), while entrada[i] is char (assuming entrada is std::string). std::string has an overload of its operator= that allows assigning to a single character, but that's not what you want here, I assume.
As for the second problem, std::string requires you to enlarge it before you random-access it at a given index. The best way to do this in this case would be during construction, dropping the erroneous array use:
std::cin >> entrada;
std::string digito(entrada.size(), ' ');
std::string signo(entrada.size(), ' ');
That being said, I'm not sure if this code does what you want it to. Given an input string of:
2+2/3
You'll get two such strings:
digito = "2 2 3"
signo = " + / "
If your actual goal was to tokenize the input (divide into numbers and operators), a much better way would be to use either two std::vector<char>s, or, even better:
using Number = int;
enum class Operator { Plus, Minus, Div, Mul };
using Token = std::variant<Number, Operator>
using Sequence = std::vector<Token>;
A consistent, strongly-typed data model will make it much easier to write correct code that produces it and operates on the results afterwards.

address of first string element yields unexpected result

The goal is to write a function that outputs the address of the first element of a string that is equal to a character, however I am confused by the result.
When evaluating the function with a string that contains said character in the first place, I get a difference of 2 bytes between the address of the beginning of the string and the address of the first element. E.g. 0x7ffe559716d0 and 0x7ffe559716e0.
Shouldn't the address be the same?
#include <iostream>
#include <string>
using namespace std;
const char* first_char(const char* str, const char ch)
{
for (int i = 0; str[i] != 0; ++i)
{
if (str[i] == ch)
return (i+str);
}
return 0;
}
int main() {
string str1 = "jasdfgjhk";
const char ch1 = 'j';
cout << &str1 << endl;
//should be the same address as above?
cout << (void*)first_char(&str1[0], ch1) << endl;
return 0;
}
Change this:
cout << &str1 << endl;
to this:
cout << (void*)str1.data() << endl;
and you will get the same address as the one returned by your function.
The reason is that std::string is not just an array of characters, it's a class, who has a data member that is an array and stores the characters of the string.
By using data(), you are getting that array. When you print its address it gives you the actual address of the array, and of the class as before
Please note that
&str
is the starting address of 'string' object. The string object is not the string itself. It contains a (hidden) dynamic pointer that points to the string itself. So by the above you get "something" like a pointer to a pointer to the string.
But with:
&str1[0]
you really get a pointer to the first character in the string.

char array/string is empty when sent to function

I have a function that at the moment doesn't do anything because the char array I send as parameter has no value. I can write out the data before the function and get the expected output but if I write out in the function nothing come out even if It's the first thing I do.
I first write out the string and then convert it to a char array. I have also tried with just a normal string as parameter with the same result.
std::cout << block;
block = this->removeNullCharacters(block.c_str());
Output of the first cout is : "0/"
The output of the function below is nothing. Nothing at all is shown up in the console.
std::string FileSystem::removeNullCharacters(const char * input){
std::string out = "";
for(int i = 0; i < 512; ++i){
std::cout << i << ": " << input[i];
/*if(input[i] == '\0'){
return out;
}
else{
out += input[i];
}*/
}
return out;
}
Not clear at all what you try to achieve or, in other words, you question is not complete.
input is a pointer to char and points to the first element of an array of chars of size 512.
out is an empty string and returned at the end of the function.
block is schizophren, a pointer to char and a std::string at the same time i guess. You need to fix this one really.
Try this. I think you want to pass a pointer to a sequence of chars to the function and concatenate each char to the string until the end of the sequence (which is terminated by \0). Forget about the size completely and simply use the null termination as a loop condition.
std::string FileSystem::removeNullCharacters(const char * input){
std::string out = "";
for(int i = 0; input[i] != '\0'; ++i){
std::cout << i << ": " << input[i];
out += input[i];
}
return out;
}
But you gotta make sure you pass in an array of chars or a valid pointer to char (which is the beginning of a sequence of chars and the last element is \0).

How to return a string from a function?

I made this little program just to get better understanding of dealing with strings.But i stuck in a small problem. Here is the code.
#include<iostream>
#include<string>
using namespace std;
string& add( string&x ){
string t; // <= Is this the problem???Declaring local string variable
cout <<"Size of String :" <<x.size() << endl;
for(int i=0; i<x.size();i++){
int n = x[i] - '0';
t[i] = n + 2 + '0';
}
for(int i=0;i<x.size();i++)
cout <<"t["<<i<<"]="<<t[i]<<endl; //This line is showing output as I wanted
cout <<"\nt = " << t << endl; // <=why the output of this line is blank?
cout <<"size of t="<<t.size() << endl; // <=and why the size of string t is zero?
return t;
}
int main(){
string a;
cin >> a ;
string b = add(a);
cout << "b =" << b << endl;
system("pause");
return 0;
}
I/p :123
o/p:
size of String :3
t[0]=3 t[1]=4 t[2]=5
t=
size of t = 0
b =
I am having problem with referencing the variable, passing the string as a reference and returning the string..
can anybody help me ??
Yes, it is a problem. You end up with a dangling reference. At the exit from the function, the local string t is destroyed, and the returned reference end up referring anything that happens to be at the memory location where t was. Using it later will cause undefined behaviour.
Just return the string by value
string add( /* const */ string&x ) // should use `const` probably if you don't modify x
the compiler is smart enough to avoid un-necessary copies (see copy elision).
PS: You should use += operator to append a char to a string, that is, replace t[i] = n + 2 + '0'; by t[i] += n + 2 + '0';. std::string is a class and the [] operator is used to read/write from an INITIALIZED string (you cannot append by incrementing the counter past the end of the string, and you initial string has length 0). Use its overloaded operator += to append.
I believe using useful functions like itoa and atoi is the best way to convert between integers and strings and its so easier too.
#include<stdio.h>
#include<iostream>
#include<string>
using namespace std;
string add( char * x ){
int n = atoi(x) + 2;
char m[10];
itoa(n, m, 10);
return m;
}
int main(){
char a[10];
cin >> a ;
string b = add(a);
cout << "b =" << b << endl;
system("pause");
return 0;
}
After its string t; declaration, t is the empty string. So you are not allowed to assign values to t[0],t[1] etc -- they don't exist. (Technically, t[0] exists as the null terminator of t.cstr(), but let's not go there.)
After your illegal assignments to t[i], the length is still zero. You were lucky not to generate an access violation !