I ran into a problem I am not really understand developing a software on an ESP8266 using the ESP8266 core for Arduino. Basically my program crashes if I pass a String created at calling a function. I have a function with a String as parameter:
void SimpleFunc(String str)
{
...
}
I tried out two ways of calling this function with a very long String. First way is to create a new String variable and pass it:
String veryLongString = "veeeerryyyy loooong ........."; //Much longer in reality!!!
SimpleFunc(veryLongString);
Second way is to pass the String directly:
SimpleFunc("veeeerryyyy loooong .........");
Running the second sketch results in a crash. Here is a part of the stack:
umm_assimilate_up at ...\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1163
String::~String() at ...\esp8266\2.3.0\cores\esp8266/WString.cpp line 720
_umm_free at ...\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1287
free at ...\esp8266\2.3.0\cores\esp8266\umm_malloc/umm_malloc.c line 1733
String::~String() at ...\esp8266\2.3.0\cores\esp8266/WString.cpp line 720
Where is the difference in calling the function this ways? Why is the first approach working well and the second not?
If you call a string by reference
void foo(std::string const &str)
No copy of the underlying characters is made. If you call by value
void food(std::string str)
str is copied, which if it is very long is an expensive operation that could run the machine out of memory.
Related
A freind and I are working together on an ESP32 webserver, and we have hit a wall. My buddie has pretty much given up on it, and I am less skilled than him.
We have the following functions, which compile fine, but cause the ESP32 to crash.
After lots of debugging I am pretty certain the crash occurs when trying to return c
void handle_logs_view(String path){
char** list = sdcard_list_files(path);
for (int i=0;list[i]!=NULL;i++){
Serial.println(list[i]);
}
}
char** sdcard_list_files(String path){
Serial.println("Listing files for "+path);
if (path.compareTo("/")){
char* c[]={"dir1","file1.log","file2.log","file3.log","file4.log","file5.log",NULL};
return c;
}
return NULL;
}
#endif
The results from the exception decoder are as follows:
PC: 0x400d1d1b: handle_logs_view(String) at C:\Users\MickW\Downloads\Front_End-11_create_file_explorer\Front_End-11_create_file_explorer\sample_code\WIFITest/WIFITest.ino line 45
EXCVADDR: 0x00000000
Decoding stack results
0x400d1d1b: handle_logs_view(String) at C:\Users\MickW\Downloads\Front_End-11_create_file_explorer\Front_End-11_create_file_explorer\sample_code\WIFITest/WIFITest.ino line 45
0x400d1e7e: std::_Function_handler >::_M_invoke(const std::_Any_data &) at C:\Users\MickW\AppData\Local\Temp\arduino_build_351256\sketch\WebServer.cpp line 32
0x400d68ff: std::function ::operator()() const at c:\users\mickw\appdata\local\arduino15\packages\esp32\tools\xtensa-esp32-elf-gcc\1.22.0-97-gc752ad5-5.2.0\xtensa-esp32-elf\include\c++\5.2.0/functional line 2271
0x400d69a5: FunctionRequestHandler::handle(WebServer&, HTTPMethod, String) at C:\Users\MickW\Documents\Arduino\hardware\espressif\esp32\libraries\WebServer\src\detail/RequestHandlersImpl.h line 45
0x400d6a12: WebServer::_handleRequest() at C:\Users\MickW\Documents\Arduino\hardware\espressif\esp32\libraries\WebServer\src\WebServer.cpp line 633
0x400d6b8d: WebServer::handleClient() at C:\Users\MickW\Documents\Arduino\hardware\espressif\esp32\libraries\WebServer\src\WebServer.cpp line 314
0x400d1d6e: loop() at C:\Users\MickW\Downloads\Front_End-11_create_file_explorer\Front_End-11_create_file_explorer\sample_code\WIFITest/WIFITest.ino line 22
0x400d8ad5: loopTask(void*) at C:\Users\MickW\Documents\Arduino\hardware\espressif\esp32\cores\esp32\main.cpp line 23
0x40089552: vPortTaskWrapper at /Users/ficeto/Desktop/ESP32/ESP32/esp-idf-public/components/freertos/port.c line 143
This is from the espressif website:
LoadProhibited, StoreProhibited
This CPU exception happens when application attempts to read from or write to an invalid memory location. The address which was written/read is found in EXCVADDR register in the register dump. If this address is zero, it usually means that application attempted to dereference a NULL pointer.
I would be greatful for any help or advice
You are returning a pointer to an automatic (i.e. stack-based) variable. That variable goes away when sdcard_list_files returns, leaving the pointer 'dangling`.
One solution is to declare c as static. Another (in C++) would be to return a std::optional <std::array<std::string>, 6>>.
c should be declared static, preferably at the top level of your code. In your example, c is declared on the stack and will disappear when sdcard_list_files() returns. Declaring c with the static attribute will fix that.
I would like to call the following code in C++, which I cannot change:
void getAge(char *name)
{
// do something
}
When I call it with getAge("hello");, it has the following warning:
warning: deprecated conversion from string constant to 'char*'
but there is no warning in C code. What is the difference, and how do I change the call to avoid the warning in C++?
the function […] can not be changed
Then write a wrapper around the function and copy the string – or, if you feel lucky (= you know that the string won’t be modified inside the original function), explicitly cast away const-ness:
void getAge(char const* name) {
the_namespace::getAge(const_cast<char*>(name));
}
If you’re unsure whether the function modifies its parameters, use something like the following – however, if that’s the case then calling the function with a string literal (getAge("hello")) would have been invalid anyway.
void getAge(char const* name) {
std::string buffer(name);
the_namespace::getAge(&buffer[0]);
}
Here we copy the string into a modifiable buffer and pass an address to its first character to the original function.
The safest way is to copy the string, then call the C function:
void getAgeSafe(const char* name)
{
std::vector<char> tmp = name?
std::vector<char>(name, name+1+strlen(name))
:std::vector<char>();
getAge( tmp.data() );
}
and call getAgeSafe from your C++ code.
A less safe way that relies on the C code never modifying the char* name would be to const_cast, again in a "wrapping" function:
void getAgeUnsafe(const char* name)
{
getAge( const_cast<char*>(name) );
}
but this time the name is more scary, as is the operation. If you call getAge with a compile time constant string like "bob", if getAge modifies its input, undefined behavior results (this is true in both C and C++ -- C++ at least warns you about it).
You can try getAge((char*)"hello").
In c++ you can write it like this,
void getAge(string name)
{
// do something
}
and also include the header file #include<string> because you are using string now
I got some problems in creating my program for mobile using QT C++
When i run it i get this: cannot convert 'std::string' to 'std::string*' in initialization
And theres code for that error:
void rozvrh_b17::pars(string par)
{
data = new std::string*(par);
printf(data->data());
}
//data and par are std::string
//without that new std::string*() it does similiar error
And i ask how to convert std::string to std::string* ??
EDIT:
i made this function to transfer data from one form to another and i need to remember that parameter...
I downvoted you because this question shows no research effort.
string * data = ∥
std::string* is a pointer type. You have a std::string, and it's address is the pointer type you want. This is one of the first principles of using pointers.
What do you really try to do?
If you just want to print the string then this should work:
void rozvrh_b17::pars(string par)
{
printf(par.c_str());
}
If you want to create a copy of string on the heap then you need this:
std::string* data = new std::string(par);
but that doesn't make much sense.
You are trying to assign a string from a pointer to string. In this particular case I see several things.
You don't need a copy at all - simply use par right away
You better make par a const ref: void rozvrh_b17::pars(const string& par)
You should use only string whaen calling new otherwise you create a pointer to string. So do: data = new std::string(par);
I have a function:
void function(const string param1, string *p2param, string *retparam)
when this is invoked from main, execution goes till last line of this function and then fails with
Bus Error(coredump)
The function performs some string manipulation using pointer to string and then the final value is passed to *retparam.
The code goes like this aa.c has
string *f1;//global
string f2= "abc";//global
function_2()
{
stringstream aa;
*f1 += aa<<"test";
//similar concatenation
}
function(param1, *p2param, *retparam)
{
/* assign back the values*/
f1 =&f2;
//call to a function from bb.c
// from bb.c function_2() is in called
retparam = f1
}
The only information I could get is:
pstack core
$ pstack core
core 'core' of 4517: aa_test -t 745
ffffffff7c67109c __1cDstdMbasic_string4Ccn0ALchar_traits4Cc__n0AJallocator4Cc___2T5B6M_v_ (ffffffff7fffce98, 1002805fc, 10010cc90, 0, ffffffff7c8c3bd8, ffffffff7fffce98) + 14
0000000100004498 main (10010b000, 100000, ffffffff7fffce98, ffffffff7fffcf00, ffffffff7fffd288, ffffffff7fffd0b8) + 818
0000000100003a7c _start (0, 0, 0, 0, 0, 0) + 17c
What is causing this error?
Thanks for your valueable inputs which finally has sorted out my issue.
The issue was with the typecast of string variable
code snippet
void function(const string param1, string *p2param, string *retparam) {
//function to call from bb.c has prototype
//fun2(const char **str,stubfunc)
const char *l_str = param1.c_str();
fun2((const char **) &l_str,coverage_hook);
}
//this was ealier called as
//fun2((const char**) ¶m1,coverage_hook); hence was causing the core dump
//why?? still dont know :)
If retparam is the address of an actual string when you pass it in, then what you really want to do before returning is
(*retparam) = f2;
Setting the value of retparam itself isn't doing you any good, since it's a local variable in the function, and changing its value won't change anything in the parent. But you can change the memory it points to, which is what happens here.
You are returning a pointer to f2 which is a local variable that gets destroyed when the function returns.
I'm taking a bit of a stab in the dark here, but I think your problem might be that you're defining the function like this:
void function(const string param1, string *p2param, string *retparam)
{
.
.
.
}
but you're forward-declaring it somewhere (in a header file?) like this:
string function(const string param1, string *p2param, string *retparam);
(promising that it will have return-type string rather than void). So when the function returns, the calling code tries to use its return-value, and pandemonium ensues when it turns out there isn't one.
(If that's not the case — and it may well not be — then I think it would help if you posted the full function definition, as well as the code that invokes the function.)
Okay, so I have two classes, call them A and B--in that order in the code. Class B instantiates class A as an array, and class B also has an error message char* variable, which class A must set in the event of an error. I created a third class with a pure virtual function to set the errorMessage variable in B, then made B a child of that third class. Class A creates a pointer to the third class, call it C--when B initializes the array of A objects, it loops through them and invokes a function in A to set A's pointer to C-- it passes "this" to that function, and then A sets the pointer to C to "this," and since C is B's parent, A can set C->errorMessage (I had to do all this because A and B couldn't simultaneously be aware of each other at compile time).
Anyways it works fine, however, and when I pass command line parameters to main(int,char**), it works unless I pass seven, eight, or more than twelve parameters to it... I narrowed it down (through commenting out lines) to the line of code, in A, which sets the pointer to C, to the value passed to it by B. This made no sense to me... I suspected a memory leak or something, but it seems wrong and I have no idea how to fix it... Also I don't get why specifically seven, eight, and more than twelve arguments don't work, 1-6 and 9-12 work fine.
Here is my code (stripped down)--
//class C
class errorContainer{
public:
virtual ~errorContainer(){ }
virtual void reportError(int,char*)=0;
};
//Class A
class switchObject{
void reportError(int,char*);
errorContainer* errorReference;
public:
void bindErrorContainer(errorContainer*);
};
//Class A member function definitions
void switchObject::reportError(int errorCode,char* errorMessage){
errorReference->reportError(errorCode,errorMessage);
}
void switchObject::bindErrorContainer(errorContainer* newReference){
errorReference=newReference; //commenting out this line fixes the problem
}
//Class B
class switchSystem: public errorContainer{
int errorCode;
char* errorMessage;
public:
switchSystem(int); //MUST specify number of switches in this system.
void reportError(int,char*);
int errCode();
char* errMessage();
switchObject* switchList;
};
//Class B member function definitions
switchSystem::switchSystem(int swLimit){
int i;
switchList=new (nothrow) switchObject[swLimit];
for(i=0;i<swLimit;i++){
switchList[i].bindErrorContainer(this);
}
errorCode=0;
errorMessage="No errors.";
}
void switchSystem::reportError(int reportErrorCode,char* reportErrorMessage){
int len=0,i;
errorCode=reportErrorCode;
if(errorMessage){
delete[] errorMessage;
}
while(reportErrorMessage[len]!='\0'){
len++;
}
errorMessage=new char[len];
for(i=0;i<=len;i++){
errorMessage[i]=reportErrorMessage[i];
}
}
int switchSystem::errCode(){
return errorCode;
}
char* switchSystem::errMessage(){
return errorMessage;
}
Anyone know what I've done wrong here?
It's bugging the crap out of me... I can't seem to fix it.
---EDIT---
okay, I have it set up the way I do so that I can use it like this in main()
int main(int argc,char** argv){
switchSystem sw (2)
sw.switchList[0].argumentCount=2;
sw.switchList[1].argumentCount=0;
sw.switchList[0].identifier="a";
sw.switchList[1].identifier="switch";
sw.init(argc,argv);
if(sw.errCode()>0){
cout<< "Error "<< sw.errCode()<< ": "<< sw.errMessage()<< endl;
}
}
this program is supposed to read the command line arguments and handle user defined "switches"--like how most command line programs handle switches, but instead of testing for all of them at the beginning of main I wanted to try to write a class and some functions to do it for me--create a switchSystem object with the number of switches, set their identifiers, whether or not they take arguments, and then pass the command line arguments to "init()" to sort it out. Then test like, if(sw.isSet("switch")){ ... } etc.
It seems scary that you:
Mix dynamic memory with static string constants ("No errors.") in the same pointer.
Use an explicit while-loop to compute the string's length; have you not heard of strlen()?
Use such low-level C-like string processing, for no good reason ... What's wrong with std::string?
Don't properly account for the terminating '\0' in the string, when allocating space for it and copying it. The length is also not stored, leaving the resulting char array rather difficult to interpret.
reportError() should be declared virtual in switchSystem, as it is in errorContainer.
char* should instead be std::string to avoid all of that needless work.
Is there some reason that you can't use an std::vector<switchObject> instead of new[]?
You shouldn't delete[] errorMessage when it points to a static literal string. This leads to undefined behavior. (Translation: Bad Thing(TM).)
Why are you iteratively counting and copying the contents of a char*? This is begging for trouble. You're not doing anything to protect yourself from harm.
Why must switchObject pass a string to switchSystem? Wouldn't it be better to simply return an error code or throw some class derived from std::exception? Or perhaps it should send a string to a global logging facility?
I think perhaps you should rethink your design instead of trying to fix this.
All of the above message contains really good advice, unless this is homework and you can't use STL I'd recommend that you follow them, your problems will be much less.
For example in this snippet
errorMessage=new char[len];
for(i=0;i<=len;i++){
errorMessage[i]=reportErrorMessage[i];
}
You have allocated len bytes, but you are writing to len+1 bytes, you have not allocated memory for the null terminator '\0', overwriting memory lead to nasty bugs that are difficult to trackdown.
I think the memory leak is your minor cocnern in here. You better throw this code and start over again with a new approach. This is a mess.