This question already has answers here:
How to read and write a STL C++ string?
(3 answers)
Closed 4 years ago.
string a;
scanf("%s",&a);
gives me an error "unhandled exception :Access violation writing location 0x000EEFFEE" when i run the program and enter the string for expression.
whereas
char a[20];
scanf("%s",a);
works fine.
Is it only because scanf and printf are inherited from C.
What methods can I use to fix the error.
Is it possible at all to make printf scanf work with c++ strings.
The reasons std::string can't be used with printf is that std::string is not a sequence of characters.
The printf function expects a sequence of characters terminated by '\0'.
For all we know the std::string class could be implemented as:
class string
{
unsigned int capacity;
unsigned int size;
char * p_char_sequence;
};
As you can see, the first two items are not characters.
Also, there are not printf format specifiers for std::string.
In summary, there is no guarantee that the first items in memory of a std::string are the characters; it could be a class or structure.
There is a mitigation, the std::string::c_str() method. This method returns a pointer to a C-Style string equivalent of the std::string contents.
Edit 1: scanf
The scanf function is more interesting. The scanf function requires a pointer to an array of characters. There is no guarantee that the first memory address in a std::string is a sequence of characters. See printf explanation above.
Also, there is no guarantee that the pointer returned by std::string::c_str() is valid for receiving characters. It could be a temporary area in memory, used only for output. Thus it can't be used for scanf.
The scanf function will write characters to the array locations until the input is terminated. This means that if you allocate space for 3 characters and the User inputs 20 characters, the scanf function will start overwriting the array and write to whatever follows it. This could trigger a system error (such as writing outside your program's area) or write to hardware addresses. This is why most references say to use fscanf where a limit or capacity can be specified.
You need to use c-strings with scanf not c++ strings. Redeclare expression as char expression[N]; where N is a large enough number of characters to store the entire word. Also you would not need the & infront of it when you pass it to scanf since arrays are converted to pointers automatically.
An even better approach would be to replace scanf with cin >> expression; And replace printf with cout << expression; that is the way to do it in c++.
Related
This question already has answers here:
What is an off-by-one error and how do I fix it?
(6 answers)
Closed 1 year ago.
I came across a scenario where string concatenation is failing in C++. But I don't see a reason for it to fail.
Code sample is as below:
int main()
{
std::string a;
std::string b = "bbbbbbb";
a.resize(10);
for (int i = 0; i <= 5; i++) {
a[i] = 'a';
}
a = a+b;
printf("\n%s\n", a.c_str());
}
It is outputting aaaaaa.
I was expecting it to output aaaaaabbbbb. If I change a.resize(10); to a.resize(5); I am getting the expected output.
Would be helpful if someone could help me in understanding the behaviour?
In addition to the off-by-one error, after concatenation, the contents of a in main are:
aaaaa\0\0\0\0\0bbbbb
So: five 'a' bytes, then five zero bytes, then five 'b' bytes. The string is fifteen bytes long.
printf, like other C functions, doesn't know about this size, and instead takes the length of the string to be until the first zero byte. In your case, that is "aaaaa".
To print the entire string, use something like std::cout. If you're certain you want printf, it is also possible to pass a length to that with the %.*s specifier.
std::string a;
a.resize(10);
gives you a string of size 10 but whose content is undefined.
You set the first 5 character to something specific and append some more characters to the end. But characters 5-10 never get set to something.
In the execution you are seeing, these characters happen to be zero, but printf – as a C style function — considers the appearance of a null character the end of the string. Therefore it stops printing.
This can be marked solved. The problem was the print macro. ESP_LOGx can't put out c++ Strings.
I'm trying to convert an uin8_t array to a string in c++.
The array is defined in a header file like this:
uint8_t mypayload[1112];
Printing the array itself works, so I'm sure it's not empty.
now I'm trying to convert it to a string:
string qrData;
std::string qrData(reinterpret_cast<char const*>(mypayload), sizeof mypayload);
I also tried:
qrData = (char*)mypayload;
printing the string results in 5 random chars.
Does anybody have hint where I made a mistake?
The only correct comment so far is from Some programmer dude. So all credits go to him.
The comment from Ian4264 is flat wrong. Of course you can do a reinterpret_cast.
Please read here about the constructors of a std::string. You are using constructor number 4. The description is:
4) Constructs the string with the first count characters of character string pointed to by s. s can contain null characters. The length of the string is count. The behavior is undefined if [s, s + count) is not a valid range.
So, even if the string contains 0 characters, the C-Style string-"terminator", all bytes of the uint8_t arrays will be copied. And if you print the string, then it will print ALL characters, even the none printable characters after the '\0'.
That maybe your "random" characters. Because the string after your "terminator" does most probably contain uninitialized values.
You should consider to use the constructor number 5
5) Constructs the string with the contents initialized with a copy of the null-terminated character string pointed to by s. The length of the string is determined by the first null character. The behavior is undefined if [s, s + Traits::length(s)) is not a valid range.
And if you need to add bytes, also possible. The std::string can grow dynamically.
BTW: you do define your "std::string qrData" double, which will not compile
Since you know the size of your data in another variable, why are you using sizeof? It will give you the size of the array, not the size of your data.
This should give you the right result, assuming no other errors in your code
std::string qrData(reinterpret_cast<char const*>(mypayload), data->payload_len);
Incidentally in the code you quoted why is qrData declared twice? That seems a bit suspicious.
qrData = (const char*)mypayload;
string is accept only const char*.
String s = String((char *)data, len); //esp32
I am using ESP8266 Wifi chip with the SMING framework which uses C++. I have a tcpServer function which receives data from a TCP port. I would like to convert the incoming char *data into String data type. This is what I did.
bool tcpServerClientReceive(TcpClient& client, char *data, int size)
{
String rx_data;
rx_data = String(data);
Serial.printf("rx_data=%s\r",rx_data);
}
The contents of rx_data is rubbish. What is wrong with the code? How to make rx_data into a proper string?
Why what you are doing is wrong:
A C style string is an array of char where the last element is a 0 Byte. This is how functions now where the string ends. They scan the next character until they find this zero byte. A C++ string is a class which can hold additional data.
For instance to get the length of a string one might choose to store the length of the stirng in a member of the class and update it everytime the string is modified. While this means additional work if the string is modified it makes the call t length trivial and fast, since it simply returns the stored value.
For C Strings on the other hand length has to loop over the array and count the number of characters until it finds the null byte. thus the runime of strlen depends on the lengh of the string.
The solution:
As pointed out above you have to print it correctly, try either:
#include <iostream>
...
std::cout << "rx_data=" << rx_data << std::endl;
or if you insist on printf (why use c++ then?) you can use either string::c_str(), or (since C++11, before the reutrned array might not be null terminated) string::data(): your code would become:
Serial.printf("rx_data=%s\r",rx_data.c_str());
I would suggest you have a look at std::string to get an idea of the details. In fact if you have the time a good book could help explaining a lot of important concepts, including containers, like std::string or std::vector. Don't assume that because you know C you know how to write C++.
This question already has answers here:
How to scan a string using scanf with C++
(2 answers)
Closed 9 years ago.
I am trying to read a string in C++ using scanf. I tried
char cmd[40];
scanf("%s", cmd);
string str(cmd);
But this gets me a string I want to have plus some non-sense characters.
scanf with the %s specifier will store a null terminator after the input string which prevents garbage characters from getting into the string... unless your code has undefined behavior.
Your issue is likely that the input is overflowing the buffer, resulting in undefined behavior; When using the %s specifier without an appropriate buffer size scanf may write outside the bounds of the buffer. At that point you have no guarantees about how the program will behave.
Never use %s with scanf wihtout providing a buffer size:
char cmd[40];
scanf("%39s", cmd);
string str(cmd);
could you say me what is the mistake in my following code?
char* line="";
printf("Write the line.\n");
scanf("%s",line);
printf(line,"\n");
I'm trying to get a line as an input from the console.But everytime while using "scanf" the program crashes. I don't want to use any std, I totally want to avoid using cin or cout. I'm just trying to learn how to tak a full line as an input using scanf().
Thank you.
You need to allocate the space for the input string as sscanf() cannot do that itself:
char line[1024];
printf("Write the line.\n");
scanf("%s",line);
printf(line,"\n");
However this is dangerous as it's possible to overflow the buffer and is therefore a security concern. Use std::string instead:
std::string line;
std::cout << "Write the line." << std::endl;
std::cin >> line;
std::cout << line << std::endl;
or:
std::getline (std::cin, line);
Space not allocated for line You need to do something like
char *line = malloc();
or
Char line[SOME_VALUE];
Currently line is a poor pointer pointing at a string literal. And overwriting a string literal can result in undefined behaviour.
scanf() doesn't match lines.
%s matches a single word.
#include <stdio.h>
int main() {
char word[101];
scanf("%100s", word);
printf("word <%s>\n", word);
return 0;
}
input:
this is a test
output:
word <this>
to match the line use %100[^\n"] which means 100 char's that aren't newline.
#include <stdio.h>
int main() {
char word[101];
scanf("%100[^\n]", word);
printf("word <%s>\n", word);
return 0;
}
You are trying to change a string literal, which in C results in Undefined behavior, and in C++ is trying to write into a const memory.
To overcome it, you might want to allocate a char[] and assign it to line - or if it is C++ - use std::string and avoid a lot of pain.
You should allocate enough memory for line:
char line[100];
for example.
The %s conversion specifier in a scanf call expects its corresponding argument to point to a writable buffer of type char [N] where N is large enough to hold the input.
You've initialized line to point to the string literal "". There are two problems with this. First is that attempting to modify the contents of a string literal results in undefined behavior. The language definition doesn't specify how string literals are stored; it only specifies their lifetime and visibility, and some platforms stick them in a read-only memory segment while others put them in a writable data segment. Therefore, attempting to modify the contents of a string literal on one platform may crash outright due to an access violation, while the same thing on another platform may work fine. The language definition doesn't mandate what should happen when you try to modify a string literal; in fact, it explicitly leaves that behavior undefined, so that the compiler is free to handle the situation any way it wants to. In general, it's best to always assume that string literals are unwritable.
The other problem is that the array containing the string literal is only sized to hold 1 character, the 0 terminator. Remember that C-style strings are stored as simple arrays of char, and arrays don't automatically grow when you add more characters.
You will need to either declared line as an array of char or allocate the memory dynamically:
char line[MAX_INPUT_LEN];
or
char *line = malloc(INITIAL_INPUT_LEN);
The virtue of allocating the memory dynamically is that you can resize the buffer as necessary.
For safety's sake, you should specify the maximum number of characters to read; if your buffer is sized to hold 21 characters, then write your scanf call as
scanf("%20s", line);
If there are more characters in the input stream than what line can hold, scanf will write those extra characters to the memory following line, potentially clobbering something important. Buffer overflows are a common malware exploit and should be avoided.
Also, %s won't get you the full line; it'll read up to the next whitespace character, even with the field width specifier. You'll either need to use a different conversion specifier like %[^\n] or use fgets() instead.
The pointer line which is supposed to point to the start of the character array that will hold the string read is actually pointing to a string literal (empty string) whose contents are not modifiable. This leads to an undefined behaviour manifested as a crash in your case.
To fix this change the definition to:
char line[MAX]; // set suitable value for MAX
and read atmost MAX-1 number of characters into line.
Change:
char* line="";
to
char line[max_length_of_line_you_expect];
scanf is trying to write more characters than the reserved by line. Try reserving more characters than the line you expect, as been pointed out by the answers above.