I've seen it so often now if as a whole the community is anti string to the extent it annoys members here...
Why not remove it (as in disable it as an option or something along the lines, default off you have to explicitly enable it somehow) ? obviously nobody likes it
instead replace it with a few handy char routines instead?
Why not remove it
Mostly because the problems are not all with the String class. Its design is fine. It is the implementation that leaves a bit to be desired. The real problem, though, is that the free() function has a fundamental flaw that needs to be addressed.
Once that is, the String class will be marginal. It will be fine for small strings - a dozen characters read from the serial port, for instance.
instead replace it with a few handy char routines instead?
Like strcat(), strcpy(), strtok(), strcmp(), you mean? These already exist, and are well documented, and can be used instead, today. No change needed.
You can get away with using the String Class with some care taken.. usually.
Problem is that many people don't know there's a reason to take care until it bites them, and then they post about a problem with crashes. You don't see many posts from people who managed to stay within limits, like the 2K RAM limit.
We will see a lot of this forever. People who learn C++ on PC's generally don't learn C strings and have no clue what goes on in the magic box let alone why a smaller box should be different.
I've seen it so often now if as a whole the community is anti string
No, not anti-string at all.
Very, very anti-String.
Problem is that many people don't know there's a reason to take care until it bites them, and then they post about a problem with crashes. You don't see many posts from people who managed to stay within limits, like the 2K RAM limit.
This is what I have found. I've been trying to 'teach myself' how to code, I have no background in Electronics or coding. I see something like a string that seems to make sense if used in the way I want to use it. Two weeks later... no, start again. It breaks and I had no way of knowing why, or what to use instead.
The big caveat with C strings is making sure you don't write past the end of the array. You -must- code for that yourself either explicitly or implicitly (like never writing any string too long, not good for general use but okay for special cases).
Set yourself up with some bookmarks, it's good to have references.
These are the library modules used in the AVR C++ Arduino uses:
http://www.nongnu.org/avr-libc/user-manual/modules.html
This is the C string library page:
http://www.nongnu.org/avr-libc/user-manual/group__avr__string.html
You #include <string.h> to use those functions. All the names are shorthand, you get to know them.
Some easy basics you can do most simple things with:
strlen() is string length
strcpy() is string copy, it puts the terminating zero at the end of the copy. It is string = string.
strstr() is string string, it searches for a substring within a string
strcmp() string compare tells you if one string is >, ==, or less than another, good for sorting
strcat() string concatenation, adds one string to the end of another
You will some with an n in the middle. The n tells you that character count is used.
strncpy() is strcpy for up to n characters and does NOT put a zero at the end.
strncpy is perfect for writing over part of a string with another, in BASIC it is mid$()
Not simple but very useful is strtok(), string token, that you can use to parse strings with.
Also don't forget the mem (memory) functions, the 2 most basic:
memset(), to set some number of bytes equal to a given value
memmove(), copies bytes and **is safe to use when the destination overlaps the source**
There's more of all of them. And if you don't see what you want then remember that C strings are just 1 dimension byte arrays you can easily process in loops without needing any library whatsoever. Those functions are only for convenience once you understand how C strings work.
One function C strings don't have that C++ String Class does is a function to tell you where the data actually is. That's because with C strings you don't one, ^^ YOU tell the function where the data is and where it goes ^^ and IT doesn't go anywhere else, unlike mind-of-their-own String objects.
Hope this helps with your jitters. The territory is really quite simple and rock solid stable.
Me, I love strings. Use them all the time and never have a problem with them. I hate chars.
Why not remove it (as in disable it as an option or something along the lines, default off you have to explicitly enable it somehow) ?
Instead of removing strings, they just have to make about a 1-line change to the library code in free() that has a bug in it. Then some, at least, of the problems will go away.
You potentially still have problems with memory fragmentation, but as some posters have observed, these do not always bite you.
If stability is important then I don't think it is a good idea to use dynamic memory allocation on systems without a robust memory management system, unless you understand the patterns of allocation and deallocation that will occur. I doubt that it would ever be practical to implement a generally robust memory management system within the constraints of an Arduino.
At the same time, dynamic helper classes such as String are IMO going to be particularly useful to novices because they take away the pain of dealing with buffers and points and so on. So the people most likely to use this are also the ones least likely to understand when and how to use dynamic allocation safely.
Given that one of the main goals of Arduino seems to be to make development accessible to novices, this strikes me as a step in the wrong direction.
Problem is that many people don't know there's a reason to take care until it bites them, and then they post about a problem with crashes. You don't see many posts from people who managed to stay within limits, like the 2K RAM limit.
This is what I have found. I've been trying to 'teach myself' how to code, I have no background in Electronics or coding. I see something like a string that seems to make sense if used in the way I want to use it. Two weeks later... no, start again. It breaks and I had no way of knowing why, or what to use instead.
The problem with the String class on the Arduino is that it assumes sophisticated memory management (like a reference-counting or other garbage-collector). You don't have that on a 2K microcontroller, so eventually intermediate results (usually of string-concatenation) bung up memory and the processor crashes. Compilers cannot analyse every possible execution of your code so they cannot determine every case of where a String object becomes unreferenced and thus can be freed.
Using char * rather than String forces the programmer to handle string lifecycles and memory allocation, and the programmer usually knows how the program is mean to run and when a char array can be reused.
Compare with the String class in the Java language - this does have a garbage-collector to recover dead objects so you can have nice intuitive String operations and memory runs out only if you genuinely are hanging on to too much stuff - you never have to call free() and it all just works. But that runs on systems with MB of RAM and ROM...
What Nick said, a small fix to free() would do wonders for use of the String class.
Just plain 'strings' aren't a problem at all. It's the Strings that trip so many people up.
Using char * rather than String forces the programmer to handle string lifecycles and memory allocation, and the programmer usually knows how the program is mean to run and when a char array can be reused.
It forces an MCU programmer to know about the hardware which is blasphemy to purist comp-sci dweebs. How many programmers does it take to change a light bulb? Can't be done, it's a hardware problem!
I'm going to agree with GoForSmoke here. Tests in other threads have shown that, with free() fixed, code that previously crashed regularly worked indefinitely.
Whilst is it possible to fragment and run out of memory with only 2 Kb to spare, code that allocates (and then frees) the same sizes strings all the time will not suffer from it.
In particular, if you avoid doing string concatentation which is probably the worst offender in causing fragmentation. (That is, building up a string by adding one character to it all the time). In any case, this sort of string-building is necessarily slow.
The main problem with Strings is, with a limited RAM machine the potential to have a problem because you didn't properly account for how (worst case) memory might be used, and how you will deal with the condition that results. On small, simple programs you won't see much problem, but as the programs grow the need for total memory management BY THE PROGRAMMER becomes more and more the major requirement. One call to a function that uses Strings without having memory properly cleaned up and things crash. Is that the fault of Strings, or bad programming practice by a programmer that didn't keep track of his free memory.
It is interesting to note that in the early days all programmers had memory usage tables and when they wrote a program they documented exactly how memory would be used because it was tight. Once again we are using a system with tight memory constraints and we expect the software to do the programmers job. If you want unlimited memory, don't use an arduino.
Oh - it wasn't all that many years ago that there existed machine with 4Kx8bit memory, for program AND data and we made it work... 16K x 8 bit cost $80.00 US.
Oh - it wasn't all that many years ago that there existed machine with 4Kx8bit memory, for program AND data and we made it work... 16K x 8 bit cost $80.00 US.
And before that in the minicomputer era a 16K x 16 bit core memory board would set you back some thousands of dollars.
Lefty
For example
void loop()
{
char c[10];
int n;
}
//what happens exactly here
with each iteration of the loop does a local variable get created (c/n) and recreated over and over again?... at the time i did not know so I simply made a global var c/n (but whatever i called them in my project)
so i knew i was never going to fill the memory full of undestroyed pointers and unused memory allocations (just to be on the safe side)
so with each loop would all the local variables be destoyed along with any memory allocation?
In your example the stack is incremented* by 12 (10 plus 2 for the int) to make room for those variables. They are thus uninitialized because they have whatever was on the stack. When loop exits that stack space is reclaimed.
If you make them global the stack doesn't get altered but you have 12 bytes less available for the stack because they have to go somewhere.
The stack and heap share the same piece of memory (RAM) starting at wherever your global variables end. The heap grows upwards and the stack grows downwards from the top of memory. If they happen to collide: trouble!
* decremented really, because the stack grows downwards.
I don't know if there's any time savings but a global gets allocated once while or making non-static locals or passing a load of parameters to a function happens over and over.
True, a stack-based variable will take a couple of machine cycles to reserve the extra stack. If you only ever need the one copy, a global (or static) variable would suit your needs.
Ok, guys. For your reference, let me clear up how memory works ;) (for those who know already, this is for those who don't.)
There are basically two types of "dynamic" memory: the "stack", and the "heap".
The stack can be thought of like a pile of coins. Coins are put on to the pile in order, and then removed afterwards in reverse order.
Any "local" variables, along with any variables passed to functions, are placed on the stack, used within the function they are defined in, and then removed from the stack again.
The stack starts at the top of the memory space, and grows downwards.
Then there is the "heap". This is more like a mound of coins. Coins can be put into the mound wherever they will fit. If there is no room inside the mound for them, then they are dropped on the top of the mound. Coins can be pulled out from anywhere in the mound. The same goes for variables. These are all "dynamically allocated" variables. Anything that uses "malloc()" or "free()", and any classes or functions which use these functions within themselves.
The heap is located at the bottom of the memory area.
The two main issues with the heap are memory leaks and memory fragmentation.
Memory leaks occur when some function places coins into the mound, and then forgets that it put them there, so they never get taken out again. Variables created in the heap must be removed after use, or the heap will just grow and grow and grow.
Memory fragmentation occurs when small variables are removed from the heap to be replaced by larger variables. The space left by the small variable is too small to accept the larger variable, so the larger variable is instead placed on the top of the heap, causing the heap to grow. This most often occurs when working with strings and you want to add something to the end of a string (concatenate) or join two strings together.
As the heap gets more and more fragmented and grows bigger and bigger over time, and as more and more local variables get pushed into the stack, there is a big risk that the heap and the stack will both grow so big that they meet in the middle. When this happens you have problems. Big problems. This is when crashes occur.
You can get "memory de-fragmentation" programs for the PC which effectively re-arrange the heap to remove the holes in it. This makes programs run faster, as they can allocate bigger chunks of memory, and helps to reduce the overall size of the heap. Quite useful. There is nothing (as far as I have seen) like it for micro-controllers - mainly due to the lack of space in the first place ;)
So, the issue with the String class isn't with the String class itself - it's a very useful class (in the right situations). The issue is with dynamic allocated memory causing fragmentation in the heap, which overflows and crashes into the stack.
So yes, a fix to "free()" will improve things, but on a small micro-controller with very limited RAM space dynamic memory allocation in itself is something to be shunned whenever possible.
So, the issue with the String class isn't with the String class itself - it's a very useful class (in the right situations). The issue is with dynamic allocated memory causing fragmentation in the heap, which overflows and crashes into the stack.
Part of the problem IS with the String class itself. When you want to append one character to an existing String, the length of the new String is the length of the old String plus the length of the String to append. That amount of space is allocated, and the old String is copied there, then the String to be appended is tacked on, then the old String's space is freed.
Append another character, and the whole process is repeated, because the String has no room to grow.
On other platforms, extra space is allocated, so that there is some room to grow. Perhaps 10 extra bytes, so you can add 10 characters, before a malloc/copy/free operation is required again.
So, the issue with the String class isn't with the String class itself - it's a very useful class (in the right situations). The issue is with dynamic allocated memory causing fragmentation in the heap, which overflows and crashes into the stack.
Part of the problem IS with the String class itself. When you want to append one character to an existing String, the length of the new String is the length of the old String plus the length of the String to append. That amount of space is allocated, and the old String is copied there, then the String to be appended is tacked on, then the old String's space is freed.
Append another character, and the whole process is repeated, because the String has no room to grow.
On other platforms, extra space is allocated, so that there is some room to grow. Perhaps 10 extra bytes, so you can add 10 characters, before a malloc/copy/free operation is required again.
Well, yes the String class could be written better, but even written better it's still not a good class in the context of the Arduino - even with extra space it is still using dynamic allocation, and thus is bad.
PaulS is quite right, and I think the String library initially allowed for that. However with only 2 Kb of RAM to play with they decided to eliminate (possibly) 9 extra bytes which may or may not be used. This gives an advantage in some situations and a disadvantage in others.
http://en.wikipedia.org/wiki/There_ain%27t_no_such_thing_as_a_free_lunch
with each iteration of the loop does a local variable get created (c/n) and recreated over and over again?
Yes. Automatic local variables are allocated on the stack and thrown away when the stack is unwound at the end of the call. In general, the more tightly you restrict the scope of the data the less chance there is for dependencies and unintended interactions between different parts of your code. This is why use of global data is best avoided where possible, and use of local data and arguments is generally preferred.
I think the String library initially allowed for that.
Paul Stoffgren's implementation did. The Arduino one removed that.
The option to allocate data in other-than-one-character larger increments needs to be part of the String class.
Typically, the user of the class has an idea of how much data is going to be stored in the String instance. Setting the minimum increment to some small value for small Strings and a larger value for larger Strings (say 5 and 15) would drastically reduce the amount of malloc/copy/free operations performed, which would have a big impact on the fragmentation issue. The default minimum increment should, of course, be 1.
with each iteration of the loop does a local variable get created (c/n) and recreated over and over again?
Yes. Automatic local variables are allocated on the stack and thrown away when the stack is unwound at the end of the call. In general, the more tightly you restrict the scope of the data the less chance there is for dependencies and unintended interactions between different parts of your code. This is why use of global data is best avoided where possible, and use of local data and arguments is generally preferred.
With 2k RAM I prefer globals and keeping control of my code. A few locals don't hurt but whole buffers in short often-called functions, it's good to be aware of because some day you might be writing code that needs to be fast rather than 'do it this way every time'.
awareness != mandatory practice
I've likened using C++ Strings on Arduino because C++ Strings are okay on a PC to putting a bathtub on a bicycle just because your house has one. I still feel the same. There's a lot of coding practices I've happily done on PC's that I wouldn't think of doing on Arduino.
with each iteration of the loop does a local variable get created (c/n) and recreated over and over again?
Yes. Automatic local variables are allocated on the stack and thrown away when the stack is unwound at the end of the call. In general, the more tightly you restrict the scope of the data the less chance there is for dependencies and unintended interactions between different parts of your code. This is why use of global data is best avoided where possible, and use of local data and arguments is generally preferred.
With 2k RAM I prefer globals and keeping control of my code. A few locals don't hurt but whole buffers in short often-called functions, it's good to be aware of because some day you might be writing code that needs to be fast rather than 'do it this way every time'.
awareness != mandatory practice
I've likened using C++ Strings on Arduino because C++ Strings are okay on a PC to putting a bathtub on a bicycle just because your house has one. I still feel the same. There's a lot of coding practices I've happily done on PC's that I wouldn't think of doing on Arduino.
I too like the use of global variables in my arduino projects. I like that I can define them all at the top of the sketch with comments on what they represent. I kind of limit my use of local variables to things like the for statements and such. I know this is against the concept of good variable scoping practice that C/C++ encourages programmers to use and does make sense for larger projects where teams of programmers are working on the same application program and have to not step on each others coding functions, but with the limited SRAM space and me being the only one writing the code, global variables work better for me and I haven't heard of a good reason I should avoid them in my Arduino sketches. Sometime I just have to be an outlaw in heart I guess. ;)
Lefty
Instead of removing strings, they just have to make about a 1-line change to the library code in free() that has a bug in it. Then some, at least, of the problems will go away.
So what is this one line change? Can it be posted so I can fix my library? I'd just rather fix things instead of hanger flying on the subject. Might eliminate some peoples constant string bashing and often mis diagnosis of problems people are having.
The problem is that the fix is not to source that is built during normal sketch compilation, but to the libraries provided pre-compiled in Arduino.
Have a look at this bug http://code.google.com/p/arduino/issues/detail?id=857
and this thread: http://arduino.cc/forum/index.php/topic,95914.30.html
Personally, I am not a fan of String, even if some of the problems people run into are due to this bug. Anything that smells of dynamic allocation has no place, in my opinion, on such a memory-constrained platform. The problem is that statically allocated and strings and all the string.h stuff is complicated to a beginner. The String *seems* easy, so you can get something going faster than if you have to grok pointers and in-memory layout of strings and crap just to print hello world. But I think folks do not appreciate the limits of what you can do with String -- and with lots of other things like arrays and C strings too -- with so little memory, and wind up being forced to learn the details anyhow.
Part of the problem IS with the String class itself. When you want to append one character to an existing String, the length of the new String is the length of the old String plus the length of the String to append. That amount of space is allocated, and the old String is copied there, then the String to be appended is tacked on, then the old String's space is freed.
No this is not true. String uses realloc() which, if you'd care to read the implementation in realloc.c (http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/libc/stdlib/realloc.c?revision=2127&root=avr-libc&view=markup) will always attempt an in-situ block-expansion. For most cases this will result in no pointer change at all and a simple adjustment of the allocated length - usually out into free space in most use-cases on a small system like this. A block-copy is only required if the original allocation either came from the free list and that space is now exhausted or there is a new block allocated in front of the one that needs to be expanded.
Memory fragmentation occurs when small variables are removed from the heap to be replaced by larger variables. The space left by the small variable is too small to accept the larger variable, so the larger variable is instead placed on the top of the heap, causing the heap to grow. This most often occurs when working with strings and you want to add something to the end of a string (concatenate) or join two strings together.
to be shunned whenever possible.
It's actually quite hard to cause significant memory fragmentation to occur in the avr-libc implementation of the heap. free() (http://svn.savannah.nongnu.org/viewvc/trunk/avr-libc/libc/stdlib/malloc.c?root=avr-libc&view=markup) will always coalesce blocks above and below the one it's being asked to release to create one larger block in the free list so even a poor String concat implementation that used malloc()+free() instead of realloc() would generally only end up with one block in the free list due to every second free() being coalesced with the previous one and then being big enough to satisfy the next malloc().
If you understand it, use it. If you don't, don't.
For most cases this will result in no pointer change at all and a simple adjustment of the allocated length - usually out into free space in most use-cases on a small system like this.
Assuming the String is at the top of the heap in 'most cases'. Otherwise not.
Andy Brown, I think you will find that Nick knows more about the code than you credit. And he isn't given to make unfounded claims.
So is there anything in the external programming that can be done to free the no longer used allocated memory space?
So is there anything in the external programming that can be done to free the no longer used allocated memory space?
Perhaps enable the WDT, let if time out, and have the sketch start all over? :D
Perhaps enable the WDT, let if time out, and have the sketch start all over?
Close to what my magic 8-Ball suggested. :)
Andy Brown, I think you will find that Nick knows more about the code than you credit. And he isn't given to make unfounded claims.
I don't have any argument with Andy. I think he has the fixed malloc/free library on his site anyway.
There is a bug in 'free' in the current Arduino library. I think we are all agreed on that. What Andy says about free spaces being combined by the library would almost certainly be correct. But the bug in question is something to do with the first (or last?) allocated block not being freed correctly, or something like that.
I have heard people say that, with the fixed free() installed, they can use String objects at some length with no problems, which supports what Andy says about the library combining unused blocks.
So is there anything in the external programming that can be done to free the no longer used allocated memory space?
In one of the threads about this problem I suggested incorporating the corrected free function into existing code, and having a define along the lines of:
#define free myfree
in one of the header files that gets pulled into all libraries (including the String library). For the sake of a few dozen extra instructions you then have a workaround until the real library is fixed.
But the bug in question is something to do with the first (or last?) allocated block not being freed correctly, or something like that.
Then is there any way in the code to keep that particular block filled with nothing?
I took what Andy wrote to be saying there is no bug/problem. My error.
global variables work better for me and I haven't heard of a good reason I should avoid them in my Arduino sketches.
Lefty
I can share at least one reason why you should reconsider globals. Here is a little sketch showing how a global can cause a very bad day.
This calculates the square of 2.
//A global
int i = 666;
template <typename T> struct Base{
Base( int i_NewVal ) : i( i_NewVal ) {return;}
int i;
};
template <typename T> struct Squared : public Base<T>{
Squared( int i_NewVal ) : Base<T>( i_NewVal * i_NewVal ) {return;}
T Result() { return i; }
};
void setup()
{
Squared< long > s_Square( 2 );
Serial.begin( 9600 );
Serial.print( "2 squared = " );
Serial.print( s_Square.Result() );
return;
}
void loop(){return;}
int i = 666;
I haven't even tried to run your sketch, but straight away, I sense trouble. ;)
@pyro
You sketch is not about globals, rather it warns of the perils one faces by using poorly named variables :)
The name of the variable could be something intuitive, I used 'i' for this example.
It does not affect the example. The sketch below works fine.
//A global
int i = 666;
void setup()
{
int i = 2 * 2;
Serial.begin( 9600 );
Serial.print( "2 squared = " );
Serial.print( i );
return;
}
void loop(){return;}
In my previous example,
The global is a non-dependant name, it is satisfying a rule where non-dependant names in dependant types take the last definition seen. The compiler doesn't consider the base class during name lookup, even though its 'i' is the closer scope. This is quite different to the snippet above where the closer scope is setup::i which is also used.
So rather than receiving an error about 'i' being undefined in the class ( 'this->' is required to make the name dependant ), the previous definition of 'i' is silently used.
As a minimum, place the global in a namespace so it has to be explicitly used.
As a minimum, place the global in a namespace so it has to be explicitly used.
Very interesting posts. I would add as a minimum don't go against common practices by naming a global with a name usually "reserved" for narrow-scoped counters and array indexes. IOW, don't produce code that's confusing to _read_ in the first place. :)
You sketch is not about globals, rather it warns of the perils one faces by using poorly named variables
I see it more as a problem with having local and global variables with the same name, and making assumptions about which will be used when.
Having local and global variables of the same name is rarely a good idea.
I going to agree with PaulS here. In your (rather obscure) example pYro_65 you set up a case where the compiler chose a global instead of a class variable by using the same name for both. To fix the sketch BTW you need to change the derived class to:
template <typename T> struct Squared :
public Base<T>{
Squared( int i_NewVal ) :
Base<T>( i_NewVal * i_NewVal ) {
return;
}
T Result() {
return Base<T>::i;
}
};
Notice the qualification of "i" there. But you could say the same thing about virtually any variable. If you had a global i_NewVal and then misspelt i_NewVal in the constructor you would have a similar issue.
A fairly simple fix in fact is to have a naming convention for class variables, eg.
int m_myVariable; // m_ = member; or:
int myVariable_; // using trailing underscore on class variables
Yes I prefer namespaces or trailing '_G'
The example is a good point I think as many are avoiding 'this->' to keep their code 'pretty', whereas blatant use will save you without even knowing it was a problem. i_NewVal is fine as its callee scope is non-dependant. Further templated functions would require further qualification.
The important part of my post was showing how the global was the sole cause of problems, the code otherwise would not have compiled.
So rather than receiving an error about 'i' being undefined in the class ( 'this->' is required to make the name dependant ), the previous definition of 'i' is silently used.
Interesting and thank for clarifying this.
One question tho
When allocation static memory does that automatically become a global variable ?
Kim
I'm not sure I understand your last question... Are you asking if qualifying a variable as "static" makes it global ?
Scope and lifetime are two related but distinct properties of a variable.
Interesting and thank for clarifying this.
One question tho
When allocation static memory does that automatically become a global variable ?
Kim
No. A global (i.e., defined in the "global" scope) variable and static variable are different.
A global can be accessed anywhere. A static can only be accessed in the function it is defined in. A static variable retains its value across subsequent calls to the function.
The memory for a static is, as the name suggests, static - it is always at the same location, so theoretically it can be accessed globally through the address:
int *superGlobal;
void myFunc()
{
static int localStatic = 4;
superGlobal = &localStatic; // Assign address of localStatic to superGlobal pointer
localStatic++;
}
void myOtherFunc()
{
int myLocal;
myFunc();
myLocal = *superGlobal; // "myLocal" now contains whatever localStatic contains.
}
Not something that is done often - it's more common to either return the value from the function, or return a pointer to the static variable. Such functions, though, are not re-entrant, and are frowned on in today's multithreaded environments. Perfectly fine on the Arduino though :)
:smiley-eek-blue:
I hope you are aware that the code above is fine as an "extreme" didactic example, but it's definitely a coding horror(1). And the reason is, it's the perfect recipe for spaghetti code ;)
(1) As in "Code Complete 2"; see Jeff Atwood's blog icon for an effective visual representation of the concept :)
Re-entrant code can build up the stack really fast if you're not very, very careful. Remember, UNO has only 2k of RAM for stack and heap space.
Thanks
Coming from Assembly and a bit of basic, I'm trying to get my head around the GNU C compiler and the way it does things.
Been reading the GNU documentation, but sometimes it is "clear as mud" :-)
K
Re-entrant code can build up the stack really fast if you're not very, very careful.
You're thinking of
recursive code?
Right. Functions that call themselves or call others that call them, etc.
It's been a while since I wrote that kind of stuff.
Right. Functions that call themselves or call others that call them, etc.
It's been a while since I wrote that kind of stuff.
Re-entrant is entirely different.
Re-entrant code can be safely called by two different threads at the same time without trashing any global / static variables. They often involve passing of pointers to work areas to the function, so each thread has its own bit of memory.
Something else I wouldn't try with a 328P chip.
Long live the state machine!
Something else I wouldn't try with a 328P chip.
Long live the state machine!
Hell yeah :)
I had fun a bit back trying to move a few non-re-entrant library routines (from the printf family) into kernel space on RetroBSD so as to reduce user-process size (kind of a pre-cursor to shared libraries) a bit back... Not nice trying to convert them to re-entrant ;)
The String class is kind of like training wheels on a bicycle. Babies need them in order to learn how to ride (or code), but as soon as you want to do anything remotely useful you have to take them off and throw them away.
So many of the "Why don't they fix this bug?" complaints could easily be rewritten as:
"Wah! I want to be able to do BMX tricks but I'm scared to let go of my training wheels."
The String class is kind of like training wheels on a bicycle. Babies need them in order to learn how to ride (or code), but as soon as you want to do anything remotely useful you have to take them off and throw them away.
So many of the "Why don't they fix this bug?" complaints could easily be rewritten as:
"Wah! I want to be able to do BMX tricks but I'm scared to let go of my training wheels."
Wow, another String class whiner! :smiley-roll: Sad that the String class whiners almost never correct/adjust the usually simple they constantly whine about. Sad
Re-entrant would also be where an ISR calls a function.
Say you are in the middle of a Serial.print. A timer interrupt fires. Inside the timer you do a Serial.print (a bad idea, I know). Then the Serial.print function is "re entered". And say inside Serial.print it keeps a variable (eg. for working out how to convert a float). That variable is now changed by the re-entry. So the original one (the one that got interrupted) is now corrupted.
This is another reason why you should keep interrupt service routines short. And why those people that try to work around Serial.print "not working" inside an ISR by adding a line to turn interrupts on, are really shooting themselves in the foot.
Regarding the String class, there is actually a method 'reserve()' that was added with 1.0.
Strange enough totally undocumented on http://arduino.cc/en/Reference/StringObject, but available: http://code.google.com/p/arduino/issues/detail?id=449
I assume with this many dynamic mem allocation probs should be solved if one stays within the reserved memory. And we still can use the richer api. Tested it and it solved my mem leak problem immediately.
Is it love of C++ String, dislike of C string or just not used to C string that drives people to accept the behind your back actions and overhead RAM bytes of C++ String?
Look at all String does to "make it easier" to manipulate text in a number-like manner. Ooooh! Power windows! Hey, turn the car on just so I can open the window! No, that's not inefficient, that's con-veeen-yence!
Is it love of C++ String, dislike of C string or just not used to C string that drives people to accept the behind your back actions and overhead RAM bytes of C++ String?
Look at all String does to "make it easier" to manipulate text in a number-like manner. Ooooh! Power windows! Hey, turn the car on just so I can open the window! No, that's not inefficient, that's con-veeen-yence!
How is your world of horses, buggies, and telegrams/bag phones? C strings are difficult for non programmers like me and appear to somewhat primitive operations compared to the ease of Strings. The "old timers" just don't get it. ;)
When you're on a horse, you don't expect a steering wheel. An MCU is not a PC.
As far as not knowing C strings, once you understand what a char array with NULL terminator the rest with few exceptions is very straightforward. Yes there is some memorizing of shortened words but strcat() means string concatenation isn't hieroglyphs. Strtok() takes a bit of study but after writing your own array parsing it's no surprise at all. In fact I'd go so far as to say that writing your own version of any of those commands and then refining/rewriting to compact code will teach not only C string but better array handling and coding in general.
The old timers get that an MCU is not a PC. Many of us have written lots of code on both. We know to avoid the traps that the inexperienced walk right into and would rather save others from wasting their time.
I find C++ Strings annoying and unreliable on any platform. In Java I use strings but in C++ I use C-style char arrays.
How is your world of horses, buggies, and telegrams/bag phones?
I saw a feature on TV recently about small-scale logging, using draught horses to extract the timber.
The horses could go where the tractors could not, and their impact on the environment much less, and they could work woodlands too small for ordinary commercial operations.
A reasonable analogy, I think.
The right tool/technique for the right problem.
I'm fairly new to Arduino's, and C coding for that matter as well but have done PHP / MySQL / ASP / HTML coding before.
Can someone please explain to me, what to use if we shouldn't use strings? I see, in many forum posts, where people have problems they're simply being told "don't use strings", and more often than not no sample of an alternative piece of code is given for their problem so the OP in said forum thread doesn't learn anything. And, consequently other newbies don't learn anything useful either and still use strings, since most of the tutorials and forum sample code has them.
C++ Strings are objects that require memory allocation routines (malloc, free) to be called when they are used. For a long time, there was a bug in the implementation of free that the arduino uses that meant that you could run out of memory, so Strings were a dangerous proposition.
More recent versions of the IDE do not have this defect, but manipulating Strings still requires that you free and allocate memory. Given that arduinos have so little RAM, there is still the possibility that you will run out of space or fragment your free memory in such a way that there isn't a big enough contiguous piece to satisfy a request. At this point, your sketch will likely fail in unexpected ways, which will be hard to debug and when you ask for help on the forums you will be told "Don't use Strings".
There's nothing inherently wrong with C++ Strings if you have the memory for them and suitable error handling for dealing with low memory conditions, but for arduino, where you don't have these things, they are not sensible, particularly if your sketch is expected to run in perpetuity. As an alternative, arrays of char let you see exactly what memory you are using without any 'sleight of hand' stuff happening behind the scenes.
I see, in many forum posts, where people have problems they're simply being told "don't use strings" ...
There is a terminology problem here. The posts usually say don't use "String" (which is a specific class) not "don't use strings".
Depending on your requirements you can use an array of characters using the normal C functions like strcpy and strcat. A lot of the Arduinos only have 2048 bytes of RAM, so you don't have a lot of memory for arrays, dynamically allocated strings, or anything else.
At least with manually allocated arrays you know how much memory they are taking. And for constants, you can use strings directly from program memory, eg.
Serial.println (F ("hi there"));
... where people have problems ...
Some problems are caused by the String class, when it had bugs. Some are caused even afterwards because of memory fragmentation. Some are unrelated. However when people post a lengthy sketch, which uses String extensively (as opposed to "strings") one of the first suggestions is to rework without using the String class.
... and more often than not no sample of an alternative piece of code is given for their problem ...
I have examples below which show receiving serial data. They stores strings into static buffers but do not use "String".
http://www.gammon.com.au/forum/?id=11425
Can someone please explain to me, what to use if we shouldn't use strings? I see, in many forum posts, where people have problems they're simply being told "don't use strings", and more often than not no sample of an alternative piece of code is given for their problem so the OP in said forum thread doesn't learn anything. And, consequently other newbies don't learn anything useful either and still use strings, since most of the tutorials and forum sample code has them.
Don't expect any of the "string" advocates to fix your code using the "string" functions, most likely due to the fact that the real code issue rarely ever has to do with using Strings. If persons can't wrap their head around the real issue, or are just stumped, they can always say "praise Jesus, drink more milk, and don't use Stings". Maybe the cure..., maybe not.
Don't expect any of the "string" advocates to fix your code using the "string" functions
Give a man a fish...
Don't expect any of the "string" advocates to fix your code using the "string" functions
... because there have been dozens, probably hundreds, of examples posted showing how to do this yourself and it is not hard.
If persons can't wrap their head around the real issue, or are just stumped, they can always say "praise Jesus, drink more milk, and don't use Stings". Maybe the cure..., maybe not.
I can wrap my head around issues, thanks. When I see something obviously wrong, I mention it. If that doesn't fix it, we can move onto subtle things.
Example: If your care won't start, and it's out of petrol, that's
probably the problem. Putting petrol in doesn't guarantee it will start, maybe the spark plugs are bad, but let's fix the simple stuff first.
I see, in many forum posts, where people have problems they're simply being told "don't use strings" ...
My bad..... I didn't see the Capital S in "Strings", and misread it as "strings"
328P has 2k for heap and stack. That's not much room to have storage elements that copy themselves just to add 1 character, not to mention what that could do in time-critical apps.
I see the String Object school play down awareness of both hardware and what the software is doing. They learn to use/reply on wasteful black boxes and don't bother with simpler more direct code. That's okay when you're not pushing much more than halfway to limits. But step much beyond and then your ability to implement solutions begins with learning what you avoided for however long you did including different approaches to solutions that go with all that.
It's easier to match the tools (commands and techniques) to the environment. AVR's tend to be very small to small. What is trivial on a PC is not always going to fit well on an AVR. A bathtub that goes well in a house will be a poor fit on a bicycle regardless of the fact that yes you can fit a bathtub on a bicycle. Oh well let's be reasonable and just fit on a sink instead shall we?
Don't expect any of the "string" advocates to fix your code using the "string" functions, most likely due to the fact that the real code issue rarely ever has to do with using Strings. If persons can't wrap their head around the real issue, or are just stumped, they can always say "praise Jesus, drink more milk, and don't use Stings". Maybe the cure..., maybe not.
Better read this then:
http://forum.arduino.cc/index.php?topic=172156.msg1284047#msg1284047
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently.
Another happy customer, once he removed "String".
Don't expect any of the "string" advocates to fix your code using the "string" functions, most likely due to the fact that the real code issue rarely ever has to do with using Strings. If persons can't wrap their head around the real issue, or are just stumped, they can always say "praise Jesus, drink more milk, and don't use Stings". Maybe the cure..., maybe not.
Better read this then:
http://forum.arduino.cc/index.php?topic=172156.msg1284047#msg1284047
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently.
Another happy customer, once he removed "String".
What was really said :
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently. The flicker that remained ended up a simple fix. I shortened the length of the wires connecting the buffers as much as physically possible (approximately an inch). I am still unsure why it only was a problem with the Uno and not the Duemilanove. Regardless, it works perfectly on both now. I am attaching a photo of the 1st panel running on the Raspberry Pi with an Uno (Left) and a Duemilanove (Right). All addresses work perfectly and no flicker.
What was really said :
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently. The flicker that remained ended up a simple fix. I shortened the length of the wires connecting the buffers as much as physically possible (approximately an inch). I am still unsure why it only was a problem with the Uno and not the Duemilanove. Regardless, it works perfectly on both now. I am attaching a photo of the 1st panel running on the Raspberry Pi with an Uno (Left) and a Duemilanove (Right). All addresses work perfectly and no flicker.
The inconsistent behaviour was resolved by removing the use of the problematic String class, as Nick Gammon indicated by his quote. The fact that there was also a hardware issue causing flickering is hardly relevant to this thread.
What was really said :
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently. The flicker that remained ended up a simple fix. I shortened the length of the wires connecting the buffers as much as physically possible (approximately an inch). I am still unsure why it only was a problem with the Uno and not the Duemilanove. Regardless, it works perfectly on both now. I am attaching a photo of the 1st panel running on the Raspberry Pi with an Uno (Left) and a Duemilanove (Right). All addresses work perfectly and no flicker.
I'm not satisfied with clues but that comes from years of debugging. So I went to that thread and ASKED the poster what that meant. Apparently Strings are Arduino code where char arrays are C++ to the beginner.
There's also the "reworking" part that you might have 'got' if you had looked at the original String-based code and the posts saying to ditch Strings, etc, that should have told you that the post you quoted does NOT actually support your view.
What was really said :
So reworking the arduino code to utilize C++ chars rather than strings seems to have solved the problem with it acting inconsistently. The flicker that remained ended up a simple fix. I shortened the length of the wires connecting the buffers as much as physically possible (approximately an inch). I am still unsure why it only was a problem with the Uno and not the Duemilanove. Regardless, it works perfectly on both now. I am attaching a photo of the 1st panel running on the Raspberry Pi with an Uno (Left) and a Duemilanove (Right). All addresses work perfectly and no flicker.
The inconsistent behaviour was resolved by removing the use of the problematic String class, as Nick Gammon indicated by his quote. The fact that there was also a hardware issue causing flickering is hardly relevant to this thread.
Perhaps you missed the "I am still unsure why it only was a problem with the " part above. Obviously the OP had two different hardware setups (Uno and Duemilanove) on which he was originally getting different results. He made both code and hardware changes on the uno setup and got his desired results. Probably different microcontrollers on the two boards with different memory capacities requiring "string" usage on the one with less memory or less efficient memory handling.
If you're getting memory corruption then even trivial changes could potentially affect the symptoms - simply choosing a different board type could be enough, if it affects the resulting executable image.
In the posted PDF of the TLC5490 driver there are several errors that could well be a part of the issue. The power supply is a 7805 with minimal I/O bypassing, there is no current limiter/CC supply for the LED's and the comment about lead length's is really snarky... rather than indicate a fix it indicates bypassing problems and/or a lack of terminations (Pull-ups/downs) on the control lines. While Strings are certainly responsible for many of the issues, that the completed design works at all is to me a wonder.. I had to make those mistakes myself many years ago and learn from them.
Doc