I followed this tutorial (http://codebase.eu/tutorial/linux-socket-programming-c/) and made a server. The thing is that when the server receives a string from the client, I don't know how to compare it. For example, the following doesn't work:
bytes_received = recv(new_sd, incomming_data_buffer, 1000, 0);
if(bytes_received == 0)
cout << "host shut down." << endl;
if(bytes_received == -1)
cout << "receive error!" << endl;
incomming_data_buffer[bytes_received] = '\0';
cout << "Received data: " << incomming_data_buffer << endl;
//The comparison in the if below doesn't work. The if isn't entered
//if the client sent "Hi", which should work
if(incomming_data_buffer == "Hi\n")
{
cout << "It said Hi!" << endl;
}
You are attempting to compare a character pointer with a string literal (which will resolve to a character pointer), so yeah, the code you have definitely won't work (nor should it). Since your in C++, I would suggest this:
if(std::string(incomming_data_buffer) == "Hi\n")
cout<<"It said Hi!"<<endl;
Now, you need to include string for this work, but I assume you are already doing this, especially if you are comparing strings using this method other places in your code.
Just an explanation of what's going on here, since you appear to be relatively new to C++. In C, string literals are stored as const char*, and mutable strings are simply character arrays. If you've ever programmed C, you might remember that (char* == char*) doesn't actually compare strings, you would need the strcmp() function for that.
C++, however, introduces the std::string type, which can be directly compared using the '==' operator (and concatenated using the '+' operator). But, C code still runs in C++, so char* arrays are not necessarily promoted to std::string unless they are being operated on by a std::string operator (and even then, if I recall, they aren't so much promoted as the operator allows string/char* comparisons), so (std::string == char*) will perform the expected comparison operation. When we do std::string(char*), we call the std::string constructor, which returns a string (in this case, a temporary one) that is compared with your string literal.
Note that I am assuming incomming_data_buffer is of type char*, you are using it like it is, although I can't see the actual declaration.
Related
I have the following code:
char szBuf[256] = "";
std::string szFmt = "You have recieved %s item."
string szName = "Fork";
snprintf(szBuf, sizeof(szBuf), szFmt.c_str(), szName);
I'm trying to combine szFmt with szBuf while combining szFmt with szName according to.However, when I execute this code in win10, I getting such an weird output:
You've received the LÃý item.
And when I try to execute the code in OSX El Capitan, I'm getting the following error
cannot pass object of non-trivial type 'string' throgh variadic function;
call will about at runtime
So what is the problem, and how can I solve this?
Note: I checked this question, but in the answer, they are passing directly "Fork", which also works in me; however it doesn't work when I pass it as szName.
Better solutions were mentioned in the comments (ostringstream) but for further educational value, I'll address the immediate problem here.
Varargs (the mechanism through which the printf family of functions can accept a variable number of arguments) are not as strictly type checked as the rest. In this case, snprintf is expecting a char* to the string, but you're passing szName which is a string object. So you need to call .c_str() on that as well:
snprintf(szBuf, sizeof(szBuf), szFmt.c_str(), szName.c_str());
Using std::ostringstream will simplify things, especially since it's more type-safe than the printf functions inherited from C, but also since it can handle all standard types and with proper overloading of the output operator << you can also use it very easily for custom classes.
The important thing to remember with std::ostringstream is that it is an ordinary output stream, just like std::cout, and if you can use std::cout then you can also use std::ostringstream (or any other standard output stream).
Now for how to use it:
std::ostringstream ostr; // Define the stream object
ostr << "You have recieved " << szName << " item.";
And that's about it.
To access the string you use the str function:
std::cout << "The output is " << ostr.str() << '\n';
And if you want to copy it into a char buffer for some reason:
// Use `strncpy` to not overflow the destination buffer
std::strncpy(szBuf, ostr.str().c_str(), sizeof szBuf - 1);
// Remember that `strncpy` might not terminate the destination, so do it explicitly
szBuf[sizeof szBuf - 1] = '\0';
I wrote a program to swap two strings using pointers.
char *names[]={"Sachin","Kapil","Ajay","Sunil","Anil"};
cout<<"String II is ";cout<<names[1];
cout<<"\nString IV is ";cout<<names[3];
char *t;
t=names[1];
names[1]=names[3];
names[3]=t;
cout<<"\nString II is ";cout<<names[1];
cout<<"\nString IV is ";cout<<names[3];
In the first line I used an array of char pointers to store the address of the 1st characters of the strings.
When I cout names[1] and names[3]:
The entire string was printed,but shouldn't the address of the character pointer alone be printed?
Also,how is the program printing the entire string instead of printing the first character alone?
After I compiled I got a warning from line 1 saying:
warning: deprecated conversion from string constant to 'char*' [-Wwrite-strings]|
The program runs as intended,but I would love to know how it is working.I came across an implementation where they used cout.write(names[i],len) to print the entire string(which makes sense),but to my surprise the above method works too (even without any sort of increase in the pointer value to print the next character).
Any tips and suggestions on the issues listed above would be appreciated.Thank you!
First up - why you don't just get the address output.
When you 'insert' any char* into a stream such as std::cout<<s; where s is of type char* the insertion logic assumes s points to the first character of a 'C' String. A 'C' String is a series of characters ending in a nul ('\0' character).
So the insertion logic outputs that series of characters.
You'll get a different result if you use std::cout<<static_cast<void*>(c); because the insertion of a void* pointer is treated differently.
The << operator on streams is overloaded to do different things depending on what you're inserting.
Now why you get that warning. You're uncovering some of the unhappy legacy of C++ particularly baggage inherited from C.
In the bad old days of early C without the const modifier there was no way of indicating that string literals (e.g. "Sachin") couldn't be modified at run-time and on different platforms doing so had different effects ranging from harmlessy amending the data, throwing an access exception to modifying all literal references to that string value in the code (there may be several).
So when C++ came along and introduced const (soon back adopted by C) it was quite rightly seen fit to make the type of string literals in the code const char[]. However some compilers (when you've got the wrong settings!) still allow you to assign literals to char[] (or char*) for backward compatibility.
Unless you're maintaining ancient code and the senior programmer has instructed you accordingly never leave that option enabled.
The stream library recognises the char* type and treats it as a null terminated c-style string which is simply an array of chars terminated by null.
To get it to print addresses, type cast the string to void*.
i.e.
cout << "String II is "; cout << (void *)names[1];
Will now output:
String II is 0x400a0b
As to why the first character is not printed only, it's because the stream library treats it as null terminated C string. But if you really only wanted the first character printed you could just as easily done this:
cout << "String II is "; cout << *names[1];
or:
cout << "String II is "; cout << names[1][0];
Manipulating pointers like you are is OK, but manipulation what they contain is not when declared like you have them. So you should really declared them const. This also removes the compilation warnings.
So the code would become.
const char *names[]={"Sachin","Kapil","Ajay","Sunil","Anil"};
cout << "\nString II is "; cout << (void*)names[1];
cout << "\nString IV is "; cout << (void*)names[3];
const char *t;
t = names[1];
names[1] = names[3];
names[3] = t;
cout << "\nString II is "; cout <<(void*)names[1];
cout << "\nString IV is "; cout <<(void*)names[3];
Okay so basically i have a struct like this
struct person{
const char* name;
const char* about_me;
const char* mom_name;
const char* age;
};
And then in order to make my code versatile i have
struct Person PersonAsArray[MAX_ARRAY - 1];
And then i have a file that reads in a bunch of stuff and eventually i parse it. but when i parse it i get a std::string so i gotta convert it to a const char* so heres some more of my code:
getline(file, line);
//break the line up into 2 parts (because in the file its "name=John")
//these two parts are called id and value
if(id == "name"){
const char* CCvalue = value.c_str();
cout << CCvalue << endl; // its fine here
PersonAsArray[i].name = CCvalue; //i is incremented each time i need a new struct
}
if(id == "age"){
PersonAsArray[i].age = atoi(value.c_str());
}
//and some more of this stuff... eventually i have
cout << PersonAsArray[0].name << endl;
cout << PersonAsArray[0].about_me << endl;
cout << PersonAsArray[0].mom_name << endl;
cout << PersonAsArray[0].age << endl;
but when i finally cout everything, i end up with something that looks like this. I'm just a little curious on whats going on and why its giving me symbols? and its not always the same symbols. Sometimes i get the smiley face, sometimes i dont even get the whole row of rectangles. I have no idea what im doing and its probably some major flaw in my coding. But this also happens when i do something like this
string hi = "hello"
for(i = 0; hi[i] != '\0'; i++){
char x = hi[i];
string done = "";
if(x == 'h') done += "abc";
if(x == 'e') done += "zxc";
if(x == 'l') done += "aer";
if(x == 'o') done += "hjg";
cout << done;
}
I think i remember getting these flower like shapes and i think i even saw chinese characters but again they were not consistent even if i didnt change anything in the program, if i ran it several times, i would see several different combination of symbols and sometimes no symbols would appear.
You did not read the documentation!
The value returned by std::string::c_str() does not live forever.
The pointer obtained from c_str() may be invalidated by:
Passing a non-const reference to the string to any standard library function, or
Calling non-const member functions on the string, excluding operator[], at(), front(), back(), begin(), rbegin(), end() and rend().
The destructor is one such "non-const member function".
Once the pointer is invalidated, you cannot use it. When you try, you either get the data stored at some arbitrary place in memory (your computer's futile attempts to make sense of that data, as if it were text, are resulting in the flowers and Chinese characters you describe) or other unpredictable, bizarre symptoms.
Unfortunately you did not present a complete, minimal testcase so we have no idea how value really fits into your code, but it's clear that it does not survive intact between your "its fine here" and your problematic code.
Don't store the result of std::string::c_str() long-term. There's no need to, and it's rarely useful to.
tl;dr Make person store std::strings, not dangling pointers.
The problem is that you have something like
{
std::string value;
// fill value
PersonsAsArray[i].name = value.c_str();
}
Now, value is a local variable which gets destroyed upon exiting the scope in which it is declared. You store the pointer to its internal data to a .name but you are not copying it so after destruction it points to garbage.
You should have a std::string name field instead that const char*, that will handle copying and retaining the content by itself and its copy assignment operator or allocate memory for the const char* manually, for example through strdup.
Why does the C++ standard allow the following?
#include <iostream>
#include <string>
int main()
{
std::string s(10, '\0'); // s.length() now is 10
std::cout << "string is " << s << ", length is " << s.length() << std::endl;
s.append(5, '\0'); // s.length() now is 15
std::cout << "string is " << s << ", length is " << s.length() << std::endl;
// the same with += char and push_back
// but:
s += "hello"; // s.length() returns 20 string is "hello"
std::cout << "string is " << s << ", length is " << s.length() << std::endl;
return 0;
}
Why does it add 0 and count it?
It looks like broken integrity of string, doesn't it? But I checked standard and it is correct behavior.
Why does standard allows following?
Because the people designing C++ strings decided that such things should be allowed. I'm not sure if anyone that was part of the team that designed C++ strings are on SO... But since you yourself say that the standard allows it, that's the way it is, and I doubt it's about to change.
It's sometimes quite practical to have a string that can contain "anything". I can think of a few instances when I've had to work around the fact that C style strings can't contain zero-bytes. Along with the fact that long C style strings take a long time to find the length of, the main benefit of C++ strings is that they are not restricted to "what you can put in them" - that's a good thing in my book.
Not sure what is problem here.
Adding '\0' in the middle of the std::string changes nothing - null character is treated like any other. The only thing that can change is if you use .c_str() with function that accepts null-terminated strings. But then it's not problem of .c_str(), only with the function that treats '\0' specially.
If you want to know how many characters has this string as if treated like null-terminated string, use
size_t len = strlen(s.c_str());
Note that it's O(n) operation, because that's how strlen works.
If you ask why += operator doesn't add the implicit null character of string literal "hello" to the string, I say the reverse (adding it) is unclear and definitely not what you want 99% of the time. On the other hand, if you want to add '\0' to your string, just append it like a buffer:
char buffer[] = "Hello";
s.append(buffer, sizeof(buffer));
or (even better) drop the char arrays and null-terminated strings altogether and use C++-style replacements like std::string as NTS-replacement, std::vector<char> as contiguous buffer, std::vector as dynamic array with pointers replacement, and std::array (C++11) as standard C array replacement.
Also, (as mentioned by #AdamRosenfield in comments), your string after adding "hello" does have in fact 20 characters, it's probably only that your terminal doesn't print nulls.
NUL char '\0' is the ending character for c style string, not std::strings. However, it supports this character to get values from a const char pointer so that it can find the end of a c-style string. Otherwise, it is treated just like other characters
std::string is more of a container for characters than anything else and \0 is a character. As a real world example, take a look at the CreateProcess function in Windows. The lpEnvironment parameter takes a null-terminated block of null-terminated strings (i.e. A=1\0B=2\0C=3\0\0). If you're building a block it's convenient to use an std::string.
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Why does std::cout output disappear completely after NULL is sent to it
It seems if you try:
std::cout << NULL << endl;
std::cout << "hell" << endl;
it print out nothing and C++ IO stops working for all subsequent outputs.
but it works fine in C stdio:
printf("%s\n", NULL);
printf("%s\n", "hell");
(null)
hell
Is there any good reason why C++ IO can't do the same thing?
(edited in response to comments)
alright, to make it clear, NULL does have a type, say const char*
const char* getxxx(); // may return NULL,
cout << getxxx(); // won't work if NULL returned
Huh? I see no reason why cout should fail simply because you executed
std::cout << 0 << std::endl;
It should output 0\n. And it does. End of story.
(In case you're confused, please know that in C++, #define NULL (0).)
In case you wrote:
T* p = 0;
std::cout << p << std::endl;
then it will display the address 0, (generally in hexadecimal and padded to the pointer size, since this is the preferred way of looking at pointers).
(This is btw the behavior you would get using the C definition of NULL, which is #define NULL ((void*)0).)
Only if you write
char* p = 0;
std::cout << p << std::endl;
are you in trouble. Now you're calling
template<class traits>
basic_ostream<char,traits>& operator<<(basic_ostream<char,traits>& out, const char* s);
for which the Standard (section 27.7.3.6.4) says:
Requires: s shall not be a null pointer.
When you do pass a null pointer, the rule 17.6.4.9 applies, which states that:
Each of the following applies to all arguments to functions defined in the C++ standard library, unless explicitly stated otherwise.
* If an argument to a function has an invalid value (such as a value outside the domain of the function or a pointer invalid for its intended use), the behavior is undefined.
So you're in the land of "undefined behavior". There's no guarantee that failbit gets set and the program continues.
Please note that printf behavior didn't actually depend on the type of NULL. It's the format string "%s" that caused treatment as a string (pointer to NUL-terminated character sequence).
printf("%s", str) is not required to handle NULL strings, so by passing NULL you are asking for trouble.
The semantically equivalent statement with IOStreams is:
std::cout << static_cast<char const*>(NULL);
And this is not required to handle NULL string either.