Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Automatic strings without malloc

452 views
Skip to first unread message

pozz

unread,
Nov 17, 2021, 5:36:53 AM11/17/21
to
Many times I need to construct a string through a call to sprintf and
pass it to an external function.

char s[32];
sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
lcd_write(s);

The size of s is fixed and calculated for the maximum length of
yourname, that is 9.

Later in time, I need to change the message into:

sprintf(s, "Hello %s, today is %d/%d/%d", yourname, day, month, year);

Of course, I need to re-calculate the worst-case length, that should be
now 35. However this is error-prone and I could forget to change the
size of array.

Is there a better way to manage this situation? I can't use malloc()
because I'm on an embedded system where I can't use heap.

I'm thinking to use sprintf(NULL, ...), such as:

const char fmt[] = "Hi %s, today is %d/%d/%d";
size_t n = sprintf(NULL, fmt, yourname, day, month, year);
char s[n + 1];
sprintf(s, fmt, yourname, day, month, year);
lcd_write(s);

pozz

unread,
Nov 17, 2021, 5:38:12 AM11/17/21
to
Il 17/11/2021 11:36, pozz ha scritto:
> Many times I need to construct a string through a call to sprintf and
> pass it to an external function.
>
>   char s[32];
>   sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
>   lcd_write(s);
>
> The size of s is fixed and calculated for the maximum length of
> yourname, that is 9.
>
> Later in time, I need to change the message into:
>
>   sprintf(s, "Hello %s, today is %d/%d/%d", yourname, day, month, year);
>
> Of course, I need to re-calculate the worst-case length, that should be
> now 35. However this is error-prone and I could forget to change the
> size of array.
>
> Is there a better way to manage this situation? I can't use malloc()
> because I'm on an embedded system where I can't use heap.
>
> I'm thinking to use sprintf(NULL, ...), such as:

I'm sorry, I mean:

snprintf(NULL, 0, ...)

Malcolm McLean

unread,
Nov 17, 2021, 6:00:22 AM11/17/21
to
That's a reasonable solution, as long as you know you have enough stack
(and a C99 compiler).
You've got to ask how likely it is that a bug whereby you overrun the buffer
will persist. If a bug will be caught in the first informal test, then it's not
dangerous. If it could slip through until a late stage, it's more worrying.
Since a buffer overrun will corrupt the variable immediately after the
buffer in memory, this might be detected immediately, or it might not be detected
for some time, depending on what that variable is used for. So a dangerous
bug, unless you've got some memory-checking software that can detect
such situations.
That's your best answer, however the software might be very expensive, or
might not be available at all.

David Brown

unread,
Nov 17, 2021, 6:04:47 AM11/17/21
to
That would work (with your follow-up correction). You might want to put
a limit on "n" to avoid accidental stack overflow. This arrangement
does mean that you are doing the printf part twice.

Very often in small systems you know there is a maximum size - if your
screen is 40 characters wide, then 40 characters is enough in your
array. And if you might need up to 40 characters then you might as well
use all of the 40 chars, even though your string might be shorter - you
really don't want to test your code for the name "Al" on date 1/2/2021
and then find you have a stack overflow on 10/10/2021 with the name
"Rhoshandiatellyneshiaunneveshenk". (Apparently that's a real name -
according to google!)


It is also worth noting that only one task should be calling lcd_write
at a time - otherwise your display will be messed up. You can extend
that and say that only one task will be using the format buffer at a
time, and then it can be a statically array that is shared by all client
code.



Thiago Adams

unread,
Nov 17, 2021, 8:11:23 AM11/17/21
to
See : asprintf
http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1248.pdf

This function is not implemented in some systems but it can be
built with other printf functions.

There is also open_memstream that I wish were part of C standard
because It cannot be implemented on windows without access to FILE internals.
https://linux.die.net/man/3/open_memstream

I create my own memory stream to be possible to use on windows and linux.

Bart

unread,
Nov 17, 2021, 8:47:55 AM11/17/21
to
Does lcd_write() remember where it was up to? If so you can break it up:

lcd_write("Hi ");
lcd_write(yourname);
lcd_write(", today is ");

This then only leaves the date, which has a maximum width, eg,
"dd/mm/yyyy". You could also then use a common routine for turning a
date into a string, if it's needed in a few places.

Scott Lurndal

unread,
Nov 17, 2021, 10:34:50 AM11/17/21
to
pozz <pozz...@gmail.com> writes:
>Many times I need to construct a string through a call to sprintf and
>pass it to an external function.
>
> char s[32];
> sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
> lcd_write(s);

One might consider using 'snprintf' instead of 'sprintf'; it is a bit safer.

I generally always reserve enough space for the largest possible
legal string, particularly when the buffer is on the stack and is
less than a page (4KB) in size.

Malcolm McLean

unread,
Nov 17, 2021, 10:37:00 AM11/17/21
to
On Wednesday, 17 November 2021 at 15:34:50 UTC, Scott Lurndal wrote:
> pozz <pozz...@gmail.com> writes:
> >Many times I need to construct a string through a call to sprintf and
> >pass it to an external function.
> >
> > char s[32];
> > sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
> > lcd_write(s);
> One might consider using 'snprintf' instead of 'sprintf'; it is a bit safer.
>
It depends whether wrong results are better or worse than no results.

David Brown

unread,
Nov 17, 2021, 10:56:33 AM11/17/21
to
Generally, a truncated string on the output is better than a stack
overflow with your embedded system crashing or going wild. But your
needs may vary.

Manfred

unread,
Nov 17, 2021, 12:19:30 PM11/17/21
to
This sounds like good advice. It makes sense to make use of the
information given by the maximum string length that can actually be emitted.

I'd add the following:
1) use snprintf instead of sprintf; the former is explicitly designed to
handle the buffer size limit.
2) use a better format specifier instead of plain "%s" for the string
argument, e.g. "%2.12s"

Both of the above help preventing buffer overflow, which is something
you need to ensure it never happens.

Scott Lurndal

unread,
Nov 17, 2021, 1:22:09 PM11/17/21
to
Indeed. Plus a good programmer checks the return value from snprintf.
Always.

Keith Thompson

unread,
Nov 17, 2021, 2:14:43 PM11/17/21
to
pozz <pozz...@gmail.com> writes:
> Many times I need to construct a string through a call to sprintf and
> pass it to an external function.
>
> char s[32];
> sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
> lcd_write(s);

I presume that lcd_write() doesn't retain the address passed to it, so the
display won't be messed up when the array object s ceases to exist.

--
Keith Thompson (The_Other_Keith) Keith.S.T...@gmail.com
Working, but not speaking, for Philips
void Void(void) { Void(); } /* The recursive call of the void */

Keith Thompson

unread,
Nov 17, 2021, 2:22:49 PM11/17/21
to
Sure, truncation is probably better than undefined behavior, but
depending on the context it might not be *much* better.

For a small LCD display, truncation is probably the best fallback
(followed by thinking about how to make the message fit). On the other
hand, truncating "rm -rf /home/user/temp_dir" to "rm -rf /home/user" is
likely to be far worse than crashing the program.

There's no general rule other than that you should always *think*
about what will/should happen if there's not enough room in the target
array.

David Brown

unread,
Nov 17, 2021, 2:35:01 PM11/17/21
to
Of course. The OP corrected himself with a follow-up to use snprintf, I
believe.

> 2) use a better format specifier instead of plain "%s" for the string
> argument, e.g. "%2.12s"

That is a possibility, but often %s is fine (with the limit you have
from snprintf).

>
> Both of the above help preventing buffer overflow, which is something
> you need to ensure it never happens.

Indeed.

David Brown

unread,
Nov 17, 2021, 2:37:39 PM11/17/21
to
Really? I never do. But I make sure my buffers are the right size for
the job - or that it doesn't matter if there is truncation (such as for
log outputs). I prefer to be sure that my inputs to the function are
correct, than to call the function and check for problems afterwards.
(Different people can have different requirements here - but that's the
way I do it.)

David Brown

unread,
Nov 17, 2021, 2:39:59 PM11/17/21
to
On 17/11/2021 20:22, Keith Thompson wrote:
> David Brown <david...@hesbynett.no> writes:
>> On 17/11/2021 16:36, Malcolm McLean wrote:
>>> On Wednesday, 17 November 2021 at 15:34:50 UTC, Scott Lurndal wrote:
>>>> pozz <pozz...@gmail.com> writes:
>>>>> Many times I need to construct a string through a call to sprintf and
>>>>> pass it to an external function.
>>>>>
>>>>> char s[32];
>>>>> sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
>>>>> lcd_write(s);
>>>> One might consider using 'snprintf' instead of 'sprintf'; it is a bit safer.
>>>>
>>> It depends whether wrong results are better or worse than no results.
>>
>> Generally, a truncated string on the output is better than a stack
>> overflow with your embedded system crashing or going wild. But your
>> needs may vary.
>
> Sure, truncation is probably better than undefined behavior, but
> depending on the context it might not be *much* better.
>
> For a small LCD display, truncation is probably the best fallback
> (followed by thinking about how to make the message fit). On the other
> hand, truncating "rm -rf /home/user/temp_dir" to "rm -rf /home/user" is
> likely to be far worse than crashing the program.

Yes - "generally" does not mean "always" ! That's a good example of
when truncation could be rather bad.

>
> There's no general rule other than that you should always *think*
> about what will/should happen if there's not enough room in the target
> array.
>

Alternatively, think about how to ensure that there /always/ will be
enough room in the target array. Either way, always /think/.

Scott Lurndal

unread,
Nov 17, 2021, 4:36:09 PM11/17/21
to
I often use snprintf in place of strcat. For that purpose, checking the return
value is required.


char buffer[1024];
char *bp = buffer;
size_t remaining = sizeof(buffer);
int diag;

diag = snprintf(bp, remaining, "%s", string_to_append_to_buffer);
if (diag != -1 && diag < remaining) {
bp += diag, remaining -= diag;
} else {
/* Handle overflow/error as necessary */
}

....

The only problem is that the return value for snprintf is 'int', while
the buffer size is size_t; which means on systems with a 64-bit size_t,
the return value isn't large enough to express the correct return value
when the size of the result value exceeds 4GB. Not generally a problem in
actual code, but something to be aware of.

Keith Thompson

unread,
Nov 17, 2021, 6:18:51 PM11/17/21
to
David Brown <david...@hesbynett.no> writes:
> On 17/11/2021 20:22, Keith Thompson wrote:
>> David Brown <david...@hesbynett.no> writes:
>>> On 17/11/2021 16:36, Malcolm McLean wrote:
>>>> On Wednesday, 17 November 2021 at 15:34:50 UTC, Scott Lurndal wrote:
>>>>> pozz <pozz...@gmail.com> writes:
>>>>>> Many times I need to construct a string through a call to sprintf and
>>>>>> pass it to an external function.
>>>>>>
>>>>>> char s[32];
>>>>>> sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
>>>>>> lcd_write(s);
>>>>> One might consider using 'snprintf' instead of 'sprintf'; it is a bit safer.
>>>>>
>>>> It depends whether wrong results are better or worse than no results.
>>>
>>> Generally, a truncated string on the output is better than a stack
>>> overflow with your embedded system crashing or going wild. But your
>>> needs may vary.
>>
>> Sure, truncation is probably better than undefined behavior, but
>> depending on the context it might not be *much* better.
>>
>> For a small LCD display, truncation is probably the best fallback
>> (followed by thinking about how to make the message fit). On the other
>> hand, truncating "rm -rf /home/user/temp_dir" to "rm -rf /home/user" is
>> likely to be far worse than crashing the program.
>
> Yes - "generally" does not mean "always" ! That's a good example of
> when truncation could be rather bad.

Depending on the context, "generally" can mean either "usually" or
"always". I've found that people generally interpret it in the most
inconvenient way possible.

>> There's no general rule other than that you should always *think*
>> about what will/should happen if there's not enough room in the target
>> array.
>
> Alternatively, think about how to ensure that there /always/ will be
> enough room in the target array. Either way, always /think/.

That's not always possible, especially if the "array" is a display
device. But yes, "always think" is a good idea. I think.

William Ahern

unread,
Nov 18, 2021, 12:00:18 AM11/18/21
to
That is generally the best approach, but doesn't work well with snprintf (or
stringification of data types generally), especially in light of the express
concern about robustness to drive-by edits of the format string.

The way to avoid string buffer errors is to avoid strings and stick to
concrete data types or to process data in a rigorously streaming fashion.
That's true of C and other languages--strings aren't data structures,
they're the antithesis of a data structure. But one nonetheless often finds
themselves dealing with strings either on input or output, unfortunately,
and facing the cruel economic calculus of worse is better. Which I suppose
is why every language spends so much effort chasing a better string type
despite the very phrase, string type, being a contradiction in terms.

pozz

unread,
Nov 18, 2021, 4:08:06 AM11/18/21
to
I know, this is a drawback.


> Very often in small systems you know there is a maximum size - if your
> screen is 40 characters wide, then 40 characters is enough in your
> array. And if you might need up to 40 characters then you might as well
> use all of the 40 chars, even though your string might be shorter - you
> really don't want to test your code for the name "Al" on date 1/2/2021
> and then find you have a stack overflow on 10/10/2021 with the name
> "Rhoshandiatellyneshiaunneveshenk". (Apparently that's a real name -
> according to google!)

The example was general, because this is a problem I often have: compose
a string now to pass to a function.

For LCD example, if you use a variable-width font, it's difficult to
calc the maximum chars. It depends how many 'i's are in the string.
You could consider the worst case of a string composed by all 'i's...

pozz

unread,
Nov 18, 2021, 4:08:15 AM11/18/21
to
Il 17/11/2021 20:14, Keith Thompson ha scritto:
> pozz <pozz...@gmail.com> writes:
>> Many times I need to construct a string through a call to sprintf and
>> pass it to an external function.
>>
>> char s[32];
>> sprintf(s, "Hi %s, today is %d/%d/%d", yourname, day, month, year);
>> lcd_write(s);
>
> I presume that lcd_write() doesn't retain the address passed to it, so the
> display won't be messed up when the array object s ceases to exist.

Yes of course

David Brown

unread,
Nov 18, 2021, 4:50:36 AM11/18/21
to
It works perfectly well in the type of programming I do. Different
kinds of work have different requirements. In small-systems embedded
programming (which is what I usually work with, and also what the OP is
doing), you know what you are passing to your printf type functions.
You know what the output is connected to (a screen, a UART, a log in
flash, etc.).

But it can be an entirely different matter in other kinds of programming
where you might have the format string coming from an external file of
translations made by a third party, or the endless variety of
complicating factors that can occur on big systems. Scott could be
right that a good programmer always checks the return value of snprintf
when doing the kind of coding he does - but it is not right for the kind
of coding /I/ do.

> The way to avoid string buffer errors is to avoid strings and stick to
> concrete data types or to process data in a rigorously streaming fashion.

The way to avoid string buffer errors is the same as you avoid any other
errors - good development practices. That runs the whole gamut from
high level concerns to low-level details. It includes making sure you
have clear specifications for the code you are writing, making sure the
programmer is appropriately qualified, having code review practices,
testing regimes, automatic checking tools, making sure the data coming
into your code is appropriate, making sure you correctly handle all
cases (including worst cases and pathological cases), and so on. It's
just like any other coding error.

Malcolm McLean

unread,
Nov 18, 2021, 6:57:04 AM11/18/21
to
I tend to use C++ for strings because C++ strings are assignable, destructible,
returnable, and C++ manages the buffer. C isn't a good language for doing
string processing in (though it is a good language for implementing string
processing algorithms themselves in).
>
> > The way to avoid string buffer errors is to avoid strings and stick to
> > concrete data types or to process data in a rigorously streaming fashion.
> The way to avoid string buffer errors is the same as you avoid any other
> errors - good development practices. That runs the whole gamut from
> high level concerns to low-level details. It includes making sure you
> have clear specifications for the code you are writing, making sure the
> programmer is appropriately qualified, having code review practices,
> testing regimes, automatic checking tools, making sure the data coming
> into your code is appropriate, making sure you correctly handle all
> cases (including worst cases and pathological cases), and so on. It's
> just like any other coding error.
>
This all costs money, it demands management skills and resources
the company may not have, and it can lead to programmers feeling
over-managed.
A very important factor is the testability of the code, and the consequences
of an error. You can over-engineer processes as well as actual code.

Thiago Adams

unread,
Nov 18, 2021, 8:54:28 AM11/18/21
to
I moved from C++ to C and I don't miss std::string. Quite the opposite, I don't feel
comfortable using std::string/std::wstring anymore even in C++.

One problem of std::string tries to do two different jobs at the same time
using the same object. One job is "string builder" and a the other "string holder"

Using std::string as string holder is a waste of memory. For instance:
std::map<std::string, something>
Map doesn't want build string, it just need to hold a string.

Another bad thing about std::string is to have to use .c_str() to make
it compatible with C strings. Clearly std::string is not a built-in
language string. "literals" are not std::string.

void F1(std::string s);
void F2(const std::string& s);
F("this is very bad because heap is used");
F("this is also very bad heap is used");


I have being using std::wstring for a long time. When moving to C
everything in my code is char* and it means UTF8. Life is so much
easier with much less conversions. It is amazing how few function we
need to work with UTF8. The same existing function can be used.
For instance, strcat is fine, strlen (if you need byte len), strdup etc.. everything just works.
u8"utf encoded" also completes the task.

What is missing. Because I create windows programs I miss the open_memstream.
This is for the job of "build strings" and to use the same FILE* functions.

So in C:
const char * or char * to represent and hold strings.
snprintf and open_memstream to build (and format) strings.

Dangerous parts...

strncpy because may not add NULL. ( I have heart of strlcpy )

replacement .
object->text = strdup("new text"); //this may cause a leak

especially for this replacement problem I think C++ has an advantage.

Something I use this that free the previous string.
replace_string(&object->text, strdup("new text"));

Philipp Klaus Krause

unread,
Nov 18, 2021, 9:29:52 AM11/18/21
to
Am 17.11.21 um 14:11 schrieb Thiago Adams:
>
> See : asprintf
> http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1248.pdf

asprintf allocates on the heap "as if by malloc", so if he can't use
malloc, he can't use asprintf.

David Brown

unread,
Nov 18, 2021, 10:05:24 AM11/18/21
to
On 18/11/2021 12:56, Malcolm McLean wrote:
> On Thursday, 18 November 2021 at 09:50:36 UTC, David Brown wrote:
>> On 18/11/2021 05:46, William Ahern wrote:

> I tend to use C++ for strings because C++ strings are assignable, destructible,
> returnable, and C++ manages the buffer. C isn't a good language for doing
> string processing in (though it is a good language for implementing string
> processing algorithms themselves in).

None of that is the slightest help to the OP. C++ classes that manage
dynamic memory are easier to get right than manual memory management in
C - but there are still useless if you want (with good reason) to avoid
any use of heap memory.

>>
>>> The way to avoid string buffer errors is to avoid strings and stick to
>>> concrete data types or to process data in a rigorously streaming fashion.
>> The way to avoid string buffer errors is the same as you avoid any other
>> errors - good development practices. That runs the whole gamut from
>> high level concerns to low-level details. It includes making sure you
>> have clear specifications for the code you are writing, making sure the
>> programmer is appropriately qualified, having code review practices,
>> testing regimes, automatic checking tools, making sure the data coming
>> into your code is appropriate, making sure you correctly handle all
>> cases (including worst cases and pathological cases), and so on. It's
>> just like any other coding error.
>>
> This all costs money, it demands management skills and resources
> the company may not have, and it can lead to programmers feeling
> over-managed.

You pick details that match the needs of the project and the
capabilities of the company. A small company might not have a large
enough programming team to make code reviews possible. The quality
control you need for a simple desktop program is going to be different
from what you need for controlling a car engine. And so on.

> A very important factor is the testability of the code, and the consequences
> of an error. You can over-engineer processes as well as actual code.
>

Of course. Lots of bugs have occurred by people putting in "just in
case" test code that is untestable in the lab, but fails at some point
after deployment.

Ben Bacarisse

unread,
Nov 18, 2021, 11:38:46 AM11/18/21
to
Thiago Adams <thiago...@gmail.com> writes:

> One problem of std::string tries to do two different jobs at the same
> time using the same object. One job is "string builder" and a the
> other "string holder"
>
> Using std::string as string holder is a waste of memory. For instance:
> std::map<std::string, something>
> Map doesn't want build string, it just need to hold a string.

Why would you write that if you wanted just to reference the strings?
You'd use std::map<std::string &, something>, or similar. Your
builder/holder distinction is unclear to me.

--
Ben.

Jeremy Brubaker

unread,
Nov 18, 2021, 12:47:11 PM11/18/21
to
On 2021-11-17, Keith Thompson <Keith.S.T...@gmail.com> wrote:
>
> Depending on the context, "generally" can mean either "usually" or
> "always". I've found that people generally interpret it in the most
> inconvenient way possible.

So, do you mean that people *usually* interpret it in the most
inconvenient way possible or that people *always* interpret it in the
most inconvenient way possible?

I find that generally people can be very imprecise with their language.

--
Jeremy Brubaker

Thiago Adams

unread,
Nov 18, 2021, 1:52:54 PM11/18/21
to
We can create a map<const char*, something> or create a small
string class RAII for it. But this is a workaround and represents
exceptions of usage and more rules. (when use/not to use std::string)

If you try std::map<std::string &, something> then a lot of operations will
become different.

e.g

std::map<std::string&, X> map;
map["a"] = X(1);

will give you 10 lines of error.

If you have a map of references the string must live in some place.
They must live more than the map. I never tried to do this, but now
just to see what happens it is hard to make it work.

https://godbolt.org/z/r95M1Tn6W


>Your builder/holder distinction is unclear to me.
If std::string wants to build the string (manage the size and allocation/deallocation)
concatenate string using += + etc.. more data is necessary. For instance, the capacity of
the buffer is also included inside the string object.

On the other hand, if you have a string just to hold a string (like char*) this is enough
for a key in a map and we will never need concatenated etc this object after its creation.
So it is waste of memory. Even size is not necessary in this case.






Keith Thompson

unread,
Nov 18, 2021, 2:22:09 PM11/18/21
to
Jeremy Brubaker <jbru...@orionarts.invalid> writes:
> On 2021-11-17, Keith Thompson <Keith.S.T...@gmail.com> wrote:
>>
>> Depending on the context, "generally" can mean either "usually" or
>> "always". I've found that people generally interpret it in the most
>> inconvenient way possible.
>
> So, do you mean that people *usually* interpret it in the most
> inconvenient way possible or that people *always* interpret it in the
> most inconvenient way possible?

Yes.

> I find that generally people can be very imprecise with their language.

Yes.

Keith Thompson

unread,
Nov 18, 2021, 2:28:00 PM11/18/21
to
Thiago Adams <thiago...@gmail.com> writes:
> On Thursday, November 18, 2021 at 1:38:46 PM UTC-3, Ben Bacarisse wrote:
>> Thiago Adams <thiago...@gmail.com> writes:
>>
>> > One problem of std::string tries to do two different jobs at the same
>> > time using the same object. One job is "string builder" and a the
>> > other "string holder"
>> >
>> > Using std::string as string holder is a waste of memory. For instance:
>> > std::map<std::string, something>
>> > Map doesn't want build string, it just need to hold a string.
>> Why would you write that if you wanted just to reference the strings?
>> You'd use std::map<std::string &, something>, or similar. Your
>> builder/holder distinction is unclear to me.
>
> We can create a map<const char*, something> or create a small
> string class RAII for it. But this is a workaround and represents
> exceptions of usage and more rules. (when use/not to use std::string)

Two const char* values can be unequal but refer to the same string value.

[...]

> On the other hand, if you have a string just to hold a string (like
> char*) this is enough for a key in a map and we will never need
> concatenated etc this object after its creation. So it is waste of
> memory. Even size is not necessary in this case.

A char* value doesn't *hold* a string, it *refers to* a string. And a C
string, unlike a C++ std::string, cannot contain null characters (which
may well be perfectly ok for most applications).

Siri Cruise

unread,
Nov 18, 2021, 3:25:39 PM11/18/21
to
In article <sn2lvp$sl7$1...@dont-email.me>,
pozz <pozz...@gmail.com> wrote:

> Many times I need to construct a string through a call to sprintf and
> pass it to an external function.

If it's too crippled to have malloc, you can do your own. Many
systems have blank common or bss after the end of all memory
initialised with code and data. You might even have the extern
address 'edata' for the bss start and 'end' for bss end. If so
you can use that as a heap:
void *heap = edata, *heapend = end;
If all else fails, use the load map to determine the maximum N
char heap[N]; char *heapend = heap+N;
with cc -DN=biggestthatloads ...
Once you got your heap area, you can do something cheap like
void *marker = 0;
void *mark (size_t n) {
void *address = marker ? marker : heap;
if (address+n>=heapend) return 0;
marker = address+n; return address;
}
void release (void *address) {
marker = address;
}
....
char *s = mark(strlen(yourname)
+strlen("Hi %s, today is %d/%d/%d"_
+4+2+2+1;
sprintf(s, "Hi %s, today is %d/%d/%d",
yourname, day, month, year);
lcd_write(s);
/*string no longer needed*/release(s);

--
:-<> Siri Seal of Disavowal #000-001. Disavowed. Denied. Deleted. @
'I desire mercy, not sacrifice.' /|\
Discordia: not just a religion but also a parody. This post / \
I am an Andrea Doria sockpuppet. insults Islam. Mohammed

Ben Bacarisse

unread,
Nov 18, 2021, 3:41:54 PM11/18/21
to
Thiago Adams <thiago...@gmail.com> writes:

> On Thursday, November 18, 2021 at 1:38:46 PM UTC-3, Ben Bacarisse wrote:
>> Thiago Adams <thiago...@gmail.com> writes:
>>
>> > One problem of std::string tries to do two different jobs at the same
>> > time using the same object. One job is "string builder" and a the
>> > other "string holder"
>> >
>> > Using std::string as string holder is a waste of memory. For instance:
>> > std::map<std::string, something>
>> > Map doesn't want build string, it just need to hold a string.
>> Why would you write that if you wanted just to reference the strings?
>> You'd use std::map<std::string &, something>, or similar. Your
>> builder/holder distinction is unclear to me.
>
> We can create a map<const char*, something> or create a small
> string class RAII for it. But this is a workaround and represents
> exceptions of usage and more rules. (when use/not to use std::string)

That's not my view. It makes what you mean explicit. You either copy
to string into the map or you reference it. Whatever it you do in C
will be (logically) one or the other, but it won't be explicit -- at
least not in the type itself.

> If you try std::map<std::string &, something> then a lot of operations will
> become different.
>
> e.g
>
> std::map<std::string&, X> map;
> map["a"] = X(1);
>
> will give you 10 lines of error.

Yes, sorry. I meant to suggest a pointer. You can't use (raw)
references in maps.

--
Ben.

David Brown

unread,
Nov 18, 2021, 5:19:12 PM11/18/21
to
On 18/11/2021 21:25, Siri Cruise wrote:
> In article <sn2lvp$sl7$1...@dont-email.me>,
> pozz <pozz...@gmail.com> wrote:
>
>> Many times I need to construct a string through a call to sprintf and
>> pass it to an external function.
>
> If it's too crippled to have malloc, you can do your own. Many
> systems have blank common or bss after the end of all memory
> initialised with code and data. You might even have the extern
> address 'edata' for the bss start and 'end' for bss end. If so
> you can use that as a heap:

It is not a matter of a system not having "malloc" and a heap - it is a
matter of deciding not to use it. In small systems with limited ram and
no paging mechanisms, it is quite realistic to either run out of heap
space or - worse, since it is harder to find by testing - for heap
fragmentation to mean that memory allocations may fail as there is not
enough consecutive free space. And in embedded systems, failure to get
the memory you need often means failure of the system - the device does
not do its job.

Thus a 0 return from malloc can be as bad as a string buffer overflow or
any other error - the system is broken, the car crashes, the microwave
burns your food, the washing machine eats your socks. And while you can
avoid the string buffer overflow by making sure your buffer is large
enough and your string is limited in length, making sure your heap can
/never/ get fragmented is typically either extremely difficult, or
impossible. The solution is to avoid dynamic memory in any situation
where failure to get the memory is not tolerable.

Siri Cruise

unread,
Nov 18, 2021, 8:22:36 PM11/18/21
to
In article <sn6jgl$val$1...@dont-email.me>,
David Brown <david...@hesbynett.no> wrote:

> It is not a matter of a system not having "malloc" and a heap - it is a
> matter of deciding not to use it. In small systems with limited ram and

So you cut off your hand and now you want to be told how to
fashion a hook. Got it. Good luck.

Malcolm McLean

unread,
Nov 19, 2021, 4:46:55 AM11/19/21
to
On Friday, 19 November 2021 at 01:22:36 UTC, Siri Cruise wrote:
> In article <sn6jgl$val$1...@dont-email.me>,
> David Brown <david...@hesbynett.no> wrote:
>
> > It is not a matter of a system not having "malloc" and a heap - it is a
> > matter of deciding not to use it. In small systems with limited ram and
> So you cut off your hand and now you want to be told how to
> fashion a hook. Got it. Good luck.
>
David Brown probably didn't take the decision himself not to use malloc.
Dynamic memory is often banned, by over-all coding requirements that
are decided upon by senior mangement or customers.

The question then is what to do when you have a string whose length isn't
known at compile time.

Thiago Adams

unread,
Nov 19, 2021, 8:38:20 AM11/19/21
to
And there is a warning when we compare the result of snprintf (int)
against sizeof that returns size_t.

I put cast and this is very annoying.

char buffer[10];
if (snprintf(buffer, sizeof(buffer), "%s", psz) >= (int)sizeof(buffer)) {
//error
}

Actually sizeof returning size_t requires more casts in other places
of my code.


Malcolm McLean

unread,
Nov 19, 2021, 10:13:24 AM11/19/21
to
size_t is a nuisance. The justification for it is that sizes of things in memory
may exceed the range of an int. But it turns into a bulldozer. Every count,
every index variable, as well as sizes of things in bytes, become touched by
size_t.

David Brown

unread,
Nov 19, 2021, 11:27:00 AM11/19/21
to
On 19/11/2021 02:22, Siri Cruise wrote:
> In article <sn6jgl$val$1...@dont-email.me>,
> David Brown <david...@hesbynett.no> wrote:
>
>> It is not a matter of a system not having "malloc" and a heap - it is a
>> matter of deciding not to use it. In small systems with limited ram and
>
> So you cut off your hand and now you want to be told how to
> fashion a hook. Got it. Good luck.
>

Eh, no. You haven't got it at all. Small systems embedded programming
is a different world from PC programming - the requirements are
different, the hardware is different, the rules are different. We don't
write code with error handlers that log problems and send debug data
back to the developers - we write code that may have to run for /years/
without a glitch.

(I'm not suggesting embedded programmers don't make mistakes - merely
that it is common to impose restrictions and coding standards to reduce
the risk of errors or their consequences.)

David Brown

unread,
Nov 19, 2021, 11:29:38 AM11/19/21
to
On 19/11/2021 10:46, Malcolm McLean wrote:
> On Friday, 19 November 2021 at 01:22:36 UTC, Siri Cruise wrote:
>> In article <sn6jgl$val$1...@dont-email.me>,
>> David Brown <david...@hesbynett.no> wrote:
>>
>>> It is not a matter of a system not having "malloc" and a heap - it is a
>>> matter of deciding not to use it. In small systems with limited ram and
>> So you cut off your hand and now you want to be told how to
>> fashion a hook. Got it. Good luck.
>>
> David Brown probably didn't take the decision himself not to use malloc.

Note that I am not the OP here - I merely work with a similar kind of
programming as the OP.

> Dynamic memory is often banned, by over-all coding requirements that
> are decided upon by senior mangement or customers.
>
> The question then is what to do when you have a string whose length isn't
> known at compile time.
>

The answer for small-systems embedded programming is to change the
question - make sure you /do/ know the length of the string. Or rather,
know the maximum length you will have to handle, make sure you /can/
handle that length, and make sure that any external input is limited to
that length.

Siri Cruise

unread,
Nov 19, 2021, 12:01:22 PM11/19/21
to
In article <sn8j86$ejj$1...@dont-email.me>,
David Brown <david...@hesbynett.no> wrote:

> >> It is not a matter of a system not having "malloc" and a heap - it is a
> >> matter of deciding not to use it. In small systems with limited ram and
> >
> > So you cut off your hand and now you want to be told how to
> > fashion a hook. Got it. Good luck.
> >
>
> Eh, no. You haven't got it at all. Small systems embedded programming

Yes, I do. If you want to allocate variable sized data, then you
have to allocate variable size data. Blank comman was set up to
do that on machines for more primitive than anything you got.
Blank common got whatever was left in memory after all the code,
initialised data, and overlay buffers were loaded.

The alternative to things like blank common is constant bound
arrays which are expected to be larger than anything that will be
used. That way instead of one large chunk of memory that can be
reused, you scatter the unused memory throughout the data where
it cannot be reused.

David Brown

unread,
Nov 19, 2021, 12:25:50 PM11/19/21
to
On 19/11/2021 18:00, Siri Cruise wrote:
> In article <sn8j86$ejj$1...@dont-email.me>,
> David Brown <david...@hesbynett.no> wrote:
>
>>>> It is not a matter of a system not having "malloc" and a heap - it is a
>>>> matter of deciding not to use it. In small systems with limited ram and
>>>
>>> So you cut off your hand and now you want to be told how to
>>> fashion a hook. Got it. Good luck.
>>>
>>
>> Eh, no. You haven't got it at all. Small systems embedded programming
>
> Yes, I do.

No, you don't get it. This has nothing to do with what /can/ be done
regarding memory - it has to do with what /should/ be done, or not done,
in order to be entirely sure that the program will work and continue to
work, without risk of failures. It doesn't matter that a modern
microcontroller can have more memory than early PC's, never mind earlier
computers. We are not talking about restrictions due to limited hardware.

(Oh, and I can almost guarantee that I have worked with more limited
hardware than you. The smallest device I used had no ram at all -
merely the processor registers and IIRC 32 bytes of eeprom, along with
1K flash for code memory. But again, we are not talking about
limitations due to small memory sizes.)

Guillaume

unread,
Nov 19, 2021, 1:38:16 PM11/19/21
to
Le 19/11/2021 à 18:00, Siri Cruise a écrit :
> The alternative to things like blank common is constant bound
> arrays which are expected to be larger than anything that will be
> used. That way instead of one large chunk of memory that can be
> reused, you scatter the unused memory throughout the data where
> it cannot be reused.

Memory management is a whole topic in itself and would require hundreds
of pages to be covered decently.

But just to say quickly - small constant reallocations can be much worse
in terms of memory use than a fixe-size allocation used frequently - in
particular in terms of memory fragmentation and execution time.

It's all a very complex, and context-dependent matter.

Chris M. Thomasson

unread,
Nov 19, 2021, 3:35:11 PM11/19/21
to
True. Btw, have you read this:

https://www.stroustrup.com/JSF-AV-rules.pdf

;^o

Malcolm McLean

unread,
Nov 19, 2021, 4:44:13 PM11/19/21
to
Had a quick look. It's a mixture of useful rules and bans on ridiculous practices which
no-one sane would use (e.g. comments embedded in #include file paths).
It would be nice to get the meaningful gist in shorter form.

Chris M. Thomasson

unread,
Nov 19, 2021, 5:58:36 PM11/19/21
to
On 11/19/2021 9:25 AM, David Brown wrote:
> On 19/11/2021 18:00, Siri Cruise wrote:
>> In article <sn8j86$ejj$1...@dont-email.me>,
>> David Brown <david...@hesbynett.no> wrote:
>>
>>>>> It is not a matter of a system not having "malloc" and a heap - it is a
>>>>> matter of deciding not to use it. In small systems with limited ram and
>>>>
>>>> So you cut off your hand and now you want to be told how to
>>>> fashion a hook. Got it. Good luck.
>>>>
>>>
>>> Eh, no. You haven't got it at all. Small systems embedded programming
>>
>> Yes, I do.
>
> No, you don't get it. This has nothing to do with what /can/ be done
> regarding memory - it has to do with what /should/ be done, or not done,
> in order to be entirely sure that the program will work and continue to
> work, without risk of failures. It doesn't matter that a modern
> microcontroller can have more memory than early PC's, never mind earlier
> computers. We are not talking about restrictions due to limited hardware.
>
> (Oh, and I can almost guarantee that I have worked with more limited
> hardware than you. The smallest device I used had no ram at all -
> merely the processor registers and IIRC 32 bytes of eeprom, along with
> 1K flash for code memory. But again, we are not talking about
> limitations due to small memory sizes.)

Damn. I remember working with an old version of Quadros a long time ago.
No malloc allowed.

[...]

David Brown

unread,
Nov 20, 2021, 7:30:55 AM11/20/21
to
These kinds of rule sets are often a mixture. They typically include a
lot of rules that are banning things that are undefined behaviour in C
(or in this case, C++) and shouldn't be used anyway, as well as things
that are so silly that few programmers would have imagined making such
mistakes. And they also often include things that are downright
unhelpful and contrary to writing good, safe code - perhaps in the name
of portability or because some people have poor quality tools. But they
also have lots of good stuff too.

In many ways, such coding standards are important simply because they
are a standard and enforce a consistency, even if the rules are not all
good.

luser droog

unread,
Nov 20, 2021, 11:49:52 PM11/20/21
to
I agree with this. I've had an uneasy relationship to these strictly specified
rules. Even in situations (ok, 1 situation) where I was in charge and got to
write up the coding standard. There's an inherent risk of fossilization or
"false crystallization" (to borrow from Ouspensky) in making these things.
And yet where would we be without them?? Wandering among foreigners,
all speaking jibberjabber in the ruins of Babel? An army of chained
monkeys playing with typewriters? <additional hyperbole>
0 new messages