I'm trying to use MurmurHash (returning 64 bit hashes on a 64bit comoputer) and have sent it the simple 3 letter string 'yes' as follows
char* charptr = "yes";
cout << MurmurHash64A(charptr, 3, 10);
(where 3 is the length and 10 is the seed)
This gives a 64bit hashed response as expected, I can set up more pointers to C strings holding yes and they all return the same hashed value.
But if I try to sending it a C++ string:
string mystring = "yes";
string* strptr = &mystring;
cout << MurmurHash64A(strptr, 3, 10);
...I get a different result to the C string method, what's more if I set up several of these strings in the same way, they all give different results.
This suggests to me that strings are maybe not stored in contiguous memory locations, some Googling backed this up.
So I then tried to set up a vector in dynamic memory as this was the only way I could think of to force contigous memory.
Just like the C++ string method it returned a different result from the C string method and when I set up several they all return a different result from each other. I set them up like follows:
char yes[3] = {'y', 'e', 's'};
vector<char> *charvec = new vector<char>;
void* myvecptr3 = &charvec;
charvec->reserve(3);
charvec->push_back(yes[0]);
charvec->push_back(yes[1]);
charvec->push_back(yes[2]);
As I understand it my char vector will start at the address the vector is given and fill consecutive bytes with my three characters in the same way as a C string.
I am confused why I'm getting different results, any help appreciated?
Thanks
C
&mystring points at the string object. You want to use mystring.c_str() to get a pointer to a raw character array.
For the vector, you want &(*charvec)[0]. But you probably don't want to use new; you could just do vector<char> charvec; void *myvecptr3 = &charvec[0];.
The reason is that std::string itself stores a pointer to the char array. Try
string mystring = "yes";
cout << MurmurHash64A(mystring.c_str(), 3, 10);
And you would not need to work with char vector indeed.
Related
I am doing this IoT based project on displaying data to connected display( I've used the MAX7219 module, in this case) with the help of nodeMCU. The idea here is that the string which is stored in my firebase database is to be display on the led display.
I've had no trouble in getting the value from the database to my nodeMCU but there is this little problem with converting that string to char array since the code i am using( Max72xx_Message_serial, which was available as an example with the max72xx library) has used char array but i can only fetch the stored data in string format. I've modified that code so as to connect with firebase but the main issue is to convert the string fetched from the database to char array.
I tried toCharArray() but it still shows conversion error.
void readfromfirebase(void)
{
static uint8_t putIndex = 0;
int n=1;
while (Firebase.available())
{
newMessage[putIndex] = (char)Firebase.getString("Submit Message"); // this line produces the error
if ((newMessage[putIndex] == '\n') || (putIndex >= BUF_SIZE-3)) // end of message character or full buffer
{
// put in a message separator and end the string
newMessage[putIndex++] = ' ';
newMessage[putIndex] = '\0';
// restart the index for next filling spree and flag we have a message waiting
putIndex = 0;
newMessageAvailable = true;
}
else if (newMessage[putIndex] != '\r')
// Just save the next char in next location
{putIndex++;}
n++;
}
}
I think you are confusing the types
getString returns a String object wich can be converted to a char[] using the methods of the String class.
I assume your newMessage is of type char[] or char*.
Then I would advise you to go for the String.c_str() method, because it returns a C style null-terminated string, meaning a char*.
See https://www.arduino.cc/reference/en/language/variables/data-types/string/functions/c_str/ for reference.
It also sets the last character of the string to 0. So methods like strlen, strcmp etc will work.
! be carefull not to modify the array returned by c_str(), if you want to modify it you chould copy the char[] or use string.toCharArray(buf, len).
Your Code might then look like the following.
String msg = Firebase.getString("Submit Message");
newMessage = msg.c_str();
// rest of your code
If newMessage is a buffer storing multiple messages, meaning char* newMessage[3].
String msg = Firebase.getString("Submit Message");
newMessage[putIndex] = msg.c_str();
// rest of your code
Be careful, because you are storing multiple characters in an array, so use strcmp to compare these arrays!
If you are new to C I would recommend reading.
https://www.cprogramming.com/tutorial/c/lesson9.html
https://www.arduino.cc/reference/en/language/variables/data-types/stringobject/ (as pointed out by #gre_gor)
Currently I'm writing a rather extensive homework assignment that - among other things - reads a file, builds a binary search tree and outputs it.
Somewhere inside all that I've written a recursive method to output the values of the binary search tree in order.
void output(node* n)
{
if(n->leftChild != NULL)
output(n->leftChild);
cout << n->keyAndValue << " || ";
outputString += n->keyAndValue << '|';
if(n->rightChild != NULL)
output(n->rightChild);
}
No problem with that, but you'll notice the line outputString += n->keyAndValue << '|';, because I also want to have all the values inside a char array (I am not allowed to use strings or other more current features of C++) that I can use later on in a different method (e.g. Main method).
The Char-Array is declared as follows:
char *outputString;
This being just one of the ways I've tried. I also tried using the const keyword and just regularly building an array char outputString[]. With the version I've shown you I encounter an error when - later on in the program in a different method - calling the following code:
cout << outputString;
I get the following error:
Unhandled exception at 0x008c2c2a in BST.exe: 0xC00000005: Access Violation reading location 0x5000000000.
Any clue as to how I'd be able to build a dynamic char array, assign values to it numerous times using += and outputting it without triggering an access violation? I am sorry for asking a rather basic question but I am entirely new to C++.
Thanks and Regards,
Dennis
I'm guessing that since you can't use std::string, you also can't use new[].
You can concatenate strings with a function like this:
char *concat(const char *s1, const char *s2)
{
size_t len = strlen(s1) + strlen(s2);
char *result = (char*)malloc(len+1);
strcpy(result, s1);
strcat(result, s2);
return result;
}
This can be done more efficiently, but that probably doesn't matter for homework. And you need to check for errors, etc. etc.
You also need to decide who is going to call free on s1 and s2.
For what it is worth, the efficient version looks like this:
char *concat(const char *s1, const char *s2)
{
size_t len1 = strlen(s1);
size_t len2 = strlen(s2);
char *result = (char*)malloc(len1+len2+1);
memcpy(result, s1, len1);
memcpy(result+len1, s2, len2);
result[len1+len2] = '\0';
return result;
}
It's more efficient because it only walks the input strings once.
+= on pointers does pointer arithmetic, not string concatenation. Eventually you get way beyond your array that outputString was pointing to, and trying to print it leads to a segfault.
Since you can't use std::string, you need to use strcat along with new[] and delete[] and make sure you allocated your original array with new[].
I'm reading a multi-dimensional char array from a file
char pszBillToAddress[3][31];
Each row of this array holds a line of an address, and ultimately I need to separate all of the components into separate strings for Address, City, State, and Zip, but for now getting each row into its own CString is my goal. What would be a good way to go about doing this? Use a for loop to append all the characters in a row to a CString?
for (int i = 0; i < 3; ++i)
{
strAddress[i] = pszBillToAddress[i];
}
Assuming that those are truly zero terminated strings. If there's any possibility that they will be filled to the end of the array with characters and the null terminator is missing, you'll need a different approach.
The way this is setup, I am assuming each column is a different line of the address with a c-string of 31 characters max?
In any case, pszBillToAddress[0] (same for [1] and [2]) are already c-strings. If you want them in a single c-string, you could do a few things. Perhaps the easiest is to use a string char x[93]; and use strncat() but this is a "C" way of doing things.
I mean something like this:
char pszBillToAddress[3][31];
char x[93];
*x = '\0'; /* Empty string */
/* Retrieve data here somehow */
strncat(x, pszBillToAddress[0], 31);
strncat(x, pszBillToAddress[1], 31);
strncat(x, pszBillToAddress[2], 31);
Say I have a vector of null terminates strings some of which may be null pointers. I don't know even if this is legal. It is a learning exercise. Example code
std::vector<char*> c_strings1;
char* p1 = "Stack Over Flow";
c_strings1.push_back(p1);
p1 = NULL; // I am puzzled you can do this and what exactly is stored at this memory location
c_strings1.push_back(p1);
p1 = "Answer";
c_strings1.push_back(p1);
for(std::vector<char*>::size_type i = 0; i < c_strings1.size(); ++i)
{
if( c_strings1[i] != 0 )
{
cout << c_strings1[i] << endl;
}
}
Note that the size of vector is 3 even though I have a NULL at location c_strings1[1]
Question. How can you re-write this code using std::vector<char>
What exactly is stored in the vector when you push a null value?
EDIT
The first part of my question has been thoroughly answered but not the second. Not to my statisfaction at least. I do want to see usage of vector<char>; not some nested variant or std::vector<std::string> Those are familiar. So here is what I tried ( hint: it does not work)
std::vector<char> c_strings2;
string s = "Stack Over Flow";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
// char* p = NULL;
s = ""; // this is not really NULL, But would want a NULL here
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
s = "Answer";
c_strings2.insert(c_strings2.end(), s.begin(), s.end() );
const char *cs = &c_strings2[0];
while (cs <= &c_strings2[2])
{
std::cout << cs << "\n";
cs += std::strlen(cs) + 1;
}
You don't have a vector of strings -- you have a vector of pointer-to-char. NULL is a perfectly valid pointer-to-char which happens to not point to anything, so it is stored in the vector.
Note that the pointers you are actually storing are pointers to char literals. The strings are not copied.
It doesn't make a lot of sense to mix the C++ style vector with the C-style char pointers. Its not illegal to do so, but mixing paradigms like this often results in confused & busted code.
Instead of using a vector<char*> or a vector<char>, why not use a vector<string> ?
EDIT
Based on your edit, it seems like what your'e trying to do is flatten several strings in to a single vector<char>, with a NULL-terminator between each of the flattened strings.
Here's a simple way to accomplish this:
#include <algorithm>
#include <vector>
#include <string>
#include <iterator>
using namespace std;
int main()
{
// create a vector of strings...
typedef vector<string> Strings;
Strings c_strings;
c_strings.push_back("Stack Over Flow");
c_strings.push_back("");
c_strings.push_back("Answer");
/* Flatten the strings in to a vector of char, with
a NULL terminator between each string
So the vector will end up looking like this:
S t a c k _ O v e r _ F l o w \0 \0 A n s w e r \0
***********************************************************/
vector<char> chars;
for( Strings::const_iterator s = c_strings.begin(); s != c_strings.end(); ++s )
{
// append this string to the vector<char>
copy( s->begin(), s->end(), back_inserter(chars) );
// append a null-terminator
chars.push_back('\0');
}
}
So,
char *p1 = "Stack Over Flow";
char *p2 = NULL;
char *p3 = "Answer";
If you notice, the type of all three of those is exactly the same. They are all char *. Because of this, we would expect them all to have the same size in memory as well.
You may think that it doesn't make sense for them to have the same size in memory, because p3 is shorter than p1. What actually happens, is that the compiler, at compile-time, will find all of the strings in the program. In this case, it would find "Stack Over Flow" and "Answer". It will throw those to some constant place in memory, that it knows about. Then, when you attempt to say that p3 = "Answer", the compiler actually transforms that to something like p3 = 0x123456A0.
Therefore, with either version of the push_back call, you are only pushing into the vector a pointer, not the actual string itself.
The vector itself, doesn't know, or care that a NULL char * is an empty string. So in it's counting, it sees that you have pushed three pointers into it, so it reports a size of 3.
I have a funny feeling that what you would really want is to have the vector contain something like "Stack Over Flow Answer" (possibly without space before "Answer").
In this case, you can use a std::vector<char>, you just have to push the whole arrays, not just pointers to them.
This cannot be accomplished with push_back, however vector have an insert method that accept ranges.
/// Maintain the invariant that the vector shall be null terminated
/// p shall be either null or point to a null terminated string
void push_back(std::vector<char>& v, char const* p) {
if (p) {
v.insert(v.end(), p, p + strlen(p));
}
v.push_back('\0');
} // push_back
int main() {
std::vector<char> v;
push_back(v, "Stack Over Flow");
push_back(v, 0);
push_back(v, "Answer");
for (size_t i = 0, max = v.size(); i < max; i += strlen(&v[i]) + 1) {
std::cout << &v[i] << "\n";
}
}
This uses a single contiguous buffer to store multiple null-terminated strings. Passing a null string to push_back results in an empty string being displayed.
What exactly is stored in the vector when you push a null value?
A NULL. You're storing pointers, and NULL is a possible value for a pointer. Why is this unexpected in any way?
Also, use std::string as the value type (i.e. std::vector<std::string>), char* shouldn't be used unless it's needed for C interop. To replicate your code using std::vector<char>, you'd need std::vector<std::vector<char>>.
You have to be careful when storing pointers in STL containers - copying the containers results in shallow copy and things like that.
With regard to your specific question, the vector will store a pointer of type char* regardless of whether or not that pointer points to something. It's entirely possible you would want to store a null-pointer of type char* within that vector for some reason - for example, what if you decide to delete that character string at a later point from the vector? Vectors only support amortized constant time for push_back and pop_back, so there's a good chance if you were deleting a string inside that vector (but not at the end) that you would prefer to just set it null quickly and save some time.
Moving on - I would suggest making a std::vector > if you want a dynamic array of strings which looks like what you're going for.
A std::vector as you mentioned would be useless compared to your original code because your original code stores a dynamic array of strings and a std::vector would only hold one dynamically changable string (as a string is an array of characters essentially).
NULL is just 0. A pointer with value 0 has a meaning. But a char with value 0 has a different meaning. It is used as a delimiter to show the end of a string. Therefore, if you use std::vector<char> and push_back 0, the vector will contain a character with value 0. vector<char> is a vector of characters, while std::vector<char*> is a vector of C-style strings -- very different things.
Update. As the OP wants, I am giving an idea of how to store (in a vector) null terminated strings some of which are nulls.
Option 1: Suppose we have vector<char> c_strings;. Then, we define a function to store a string pi. A lot of complexity is introduced since we need to distinguish between an empty string and a null char*. We select a delimiting character that does not occur in our usage. Suppose this is the '~' character.
char delimiter = '~';
// push each character in pi into c_strings
void push_into_vec(vector<char>& c_strings, char* pi) {
if(pi != 0) {
for(char* p=pi; *p!='\0'; p++)
c_strings.push_back(*p);
// also add a NUL character to denote end-of-string
c_strings.push_back('\0');
}
c_strings.push_back(deimiter);
// Note that a NULL pointer would be stored as a single '~' character
// while an empty string would be stored as '\0~'.
}
// now a method to retrieve each of the stored strings.
vector<char*> get_stored_strings(const vector<char>& c_strings) {
vector<char*> r;
char* end = &c_strings[0] + c_strings.size();
char* current = 0;
bool nullstring = true;
for(char* c = current = &c_strings[0]; c != end+1; c++) {
if(*c == '\0') {
int size = c - current - 1;
char* nc = new char[size+1];
strncpy(nc, current, size);
r.push_back(nc);
nullstring = false;
}
if(*c == delimiter) {
if(nullstring) r.push_back(0);
nullstring = true; // reset nullstring for the next string
current = c+1; // set the next string
}
}
return r;
}
You still need to call delete[] on the memory allocated by new[] above. All this complexity is taken care of by using the string class. I very rarely use char* in C++.
Option 2: You could use vector<boost::optional<char> > . Then the '~' can be replaced by an empty boost::optional, but other other parts are the same as option 1. But the memory usage in this case would be higher.
How can I access each member in a std::string variable? For example, if I have
string buff;
suppose buff conatains "10 20 A" as ASCII content. How could I then access 10, 20, and A separately?
Here is an answer for you on SO:
How do I tokenize a string in C++?
There are many ways to skin that cat...
You can access the strings by index. i.e duff[0], duff[1] and duff[2].
I just tried. This works.
string helloWorld[2] = {"HELLO", "WORLD"};
char c = helloWorld[0][0];
cout << c;
It outputs "H"
Well I see you have tagged both C and C++.
If you are using C, strings are an array of characters. You can access each character like you would a normal array:
char a = duff[0];
char b = duff[1];
char c = duff[2];
If you are using C++ and using a character array, see above. If you are using a std::string (this is why C and C++ should be tagged separately), there are many ways you can access each character in the string:
// std::string::iterator if you want the string to be modifiable
for (std::string::const_iterator i = duff.begin(); i != duff.end(); ++i)
{
}
or:
char c = duff.at(i); // where i is the index; the same as duff[i]
and probably more.