F() macro garbled c_string

There are plenty to read flash memory

See avr-libc: <avr/pgmspace.h>: Program Space Utilities


#define pgm_read_byte_near(address_short) __LPM((uint16_t)(address_short))
#define pgm_read_word_near(address_short) __LPM_word((uint16_t)(address_short))
#define pgm_read_dword_near(address_short) __LPM_dword((uint16_t)(address_short))
#define pgm_read_float_near(address_short) __LPM_float((uint16_t)(address_short))
#define pgm_read_ptr_near(address_short) (void*)__LPM_word((uint16_t)(address_short))
#define pgm_read_byte_far(address_long) __ELPM((uint32_t)(address_long))
#define pgm_read_word_far(address_long) __ELPM_word((uint32_t)(address_long))
#define pgm_read_dword_far(address_long) __ELPM_dword((uint32_t)(address_long))
#define pgm_read_float_far(address_long) __ELPM_float((uint32_t)(address_long))
#define pgm_read_ptr_far(address_long) (void*)__ELPM_word((uint32_t)(address_long))
#define pgm_read_byte(address_short) pgm_read_byte_near(address_short)
#define pgm_read_word(address_short) pgm_read_word_near(address_short)
#define pgm_read_dword(address_short) pgm_read_dword_near(address_short)
#define pgm_read_float(address_short) pgm_read_float_near(address_short)
#define pgm_read_ptr(address_short) pgm_read_ptr_near(address_short)
#define pgm_get_far_address(var)

And functions


const void * memchr_P (const void *, int __val, size_t __len)

int memcmp_P (const void *, const void *, size_t) ATTR_PURE

void * memccpy_P (void *, const void *, int __val, size_t)

void * memcpy_P (void *, const void *, size_t)

void * memmem_P (const void *, size_t, const void *, size_t) ATTR_PURE

const void * memrchr_P (const void *, int __val, size_t __len)

char * strcat_P (char *, const char *)

const char * strchr_P (const char *, int __val)

const char * strchrnul_P (const char *, int __val)

int strcmp_P (const char *, const char *) ATTR_PURE

char * strcpy_P (char *, const char *)

int strcasecmp_P (const char *, const char *) ATTR_PURE

char * strcasestr_P (const char *, const char *) ATTR_PURE

size_t strcspn_P (const char *__s, const char *__reject) ATTR_PURE

size_t strlcat_P (char *, const char *, size_t)

size_t strlcpy_P (char *, const char *, size_t)

size_t strnlen_P (const char *, size_t)

int strncmp_P (const char *, const char *, size_t) ATTR_PURE

int strncasecmp_P (const char *, const char *, size_t) ATTR_PURE

char * strncat_P (char *, const char *, size_t)

char * strncpy_P (char *, const char *, size_t)

char * strpbrk_P (const char *__s, const char *__accept) ATTR_PURE

const char * strrchr_P (const char *, int __val)

char * strsep_P (char **__sp, const char *__delim)

size_t strspn_P (const char *__s, const char *__accept) ATTR_PURE

char * strstr_P (const char *, const char *) ATTR_PURE

char * strtok_P (char *__s, const char *__delim)

char * strtok_rP (char *__s, const char *__delim, char **__last)

size_t strlen_PF (uint_farptr_t src)

size_t strnlen_PF (uint_farptr_t src, size_t len)

void * memcpy_PF (void *dest, uint_farptr_t src, size_t len)

char * strcpy_PF (char *dest, uint_farptr_t src)

char * strncpy_PF (char *dest, uint_farptr_t src, size_t len)

char * strcat_PF (char *dest, uint_farptr_t src)

size_t strlcat_PF (char *dst, uint_farptr_t src, size_t siz)

char * strncat_PF (char *dest, uint_farptr_t src, size_t len)

int strcmp_PF (const char *s1, uint_farptr_t s2) ATTR_PURE

int strncmp_PF (const char *s1, uint_farptr_t s2, size_t n) ATTR_PURE

int strcasecmp_PF (const char *s1, uint_farptr_t s2) ATTR_PURE

int strncasecmp_PF (const char *s1, uint_farptr_t s2, size_t n) ATTR_PURE

char * strstr_PF (const char *s1, uint_farptr_t s2)

size_t strlcpy_PF (char *dst, uint_farptr_t src, size_t siz)

int memcmp_PF (const void *, uint_farptr_t, size_t) ATTR_PURE

static size_t strlen_P (const char *s)

I’m not aware of any parallel interface that would send multiple bytes in one go, underneath the surface it’s usually serialized (bits, one after the other) or at best for some lcd the bytes are parralelized (4 or 8 data bits)

So if you don’t do it yourself, that’s what happens anyway below the surface.


So, would something like the following be appropriate?

const char* CF(const __FlashStringHelper * line){
  size_t len = strlen_PF(line)
  char* output = new char[len + 1];
  strncpy_PF(output, line, len);
  return output;

CF(F("Hello World"));

Honestly unsure the exact difference between the _P and _PF functions.

No, that code leaks memory. You shouldn't use raw owning pointers, and definitely not return them.

You could just use the String class:

String CF(const __FlashStringHelper *line) {
  return line; // Not a very useful function ...

Still, I don't see a reason why you would do this unnecessary copy into RAM. Just load the string character-by-character and print it to whatever output you want. Making a copy is a waste of time and memory.

A 16-bit pointer can only address 64KiB of memory, some AVR MCUs have more than that, e.g. an ATmega2560 has 128KiB of Flash. To address these extra bytes of flash you need different access functions and larger pointers. You shouldn't be using the _PF functions here.

Because I'm not just printing out, I'm storing strings to eventually print out in a menu setup. I also realize that I will need to call `delete[ ]' on c-strings I get out of this function, unless there was another memory leak I am missing?

When I used String before, that was when I was running out of memory. I'm trying to switch to c-string to have more control over the memory and overall use less. Part of that is getting these strings from Flash to try to save more space

you could use a global temporary cString buffer (and the n or l versions of cString concatenation) but I still think you should just display as you go rather than building a string in memory and then asking to print it - which will go through every byte again...

The string is already stored in Flash, no need to store it in RAM as well. Just store a pointer to the Flash string in your menu, with a flag to indicate whether it's in Flash or RAM.

This is a very bad idea, it is a memory leak waiting to happen. The C++ Core Guidelines are very firm on this one: I.11: Never transfer ownership by a raw pointer (T*) or reference (T&)
Use a container like String or a smart pointer.

There is no significant difference in the memory usage of manually allocating it using new[] and letting the String allocate it. String has disadvantages over character arrays with statically known sizes, but String is definitely preferable over raw calls to new[] because String cannot leak memory (it can fragment memory if used incorrectly).

But then why are you wasting space by copying the Flash strings back to RAM? If your goal is to save RAM, leave them in Flash and only get them out character-by-character when you actually need them.