0

I have to copy uint32_t number into the middle of the char[] buffer. The situation is like this:

char buf[100];    
uint8_t pos = 52; // position in buffer to which I want to copy my uint32_t number
uint32_t seconds = 23456; // the actual number

I tried to use memcpy like this:

memcpy(&buf[position], &seconds, sizeof(seconds));

But in buffer I'm getting some strange characters, not the number i want

I also tried using byte-shifting

int shiftby = 32;
for (int i = 0; i < 8; i++)
{
  buf[position++] = (seconds >> (shiftby -= 4)) & 0xF;
}

Is there any other option how to solve this problem?

yeuop
  • 159
  • 1
  • 9
  • 1
    What is your expected output? Your `memcpy` code looks fine. – Daniel Kleinstein Sep 04 '21 at 07:46
  • Your loop is on the right track but has some issues - for instance, `buf[position]` is a *byte* (8 bits) and you are writing *nibbles* (4 bits). – Daniel Kleinstein Sep 04 '21 at 07:47
  • Please provide a [complete minimal reproducible example](https://stackoverflow.com/help/minimal-reproducible-example) including the test code that is used to see "some strange characters" as well as the exact actual result vs expected result. – kaylum Sep 04 '21 at 07:48
  • 3
    Putting a number into a `char` array won't put the digits of the number as characters. If you want the formatted number you need to use `sprintf()`. – Barmar Sep 04 '21 at 07:49
  • Your example code suggests that the `uint32_t` is actually a _container_ for either an 8 digit BCD representation or that you are trying to present the value in hexadecimal. If that is the case you need to be clear, otherwise most will assume that you want a decimal presentation, and in that case the number of digits output will be variable and as many as 10, so it begs questions of field-width, padding, left/right justification etc. In short your requirement is as yet ill-defined. Some examples of expected output strings would help clarify perhaps. – Clifford Sep 04 '21 at 11:41

2 Answers2

2

What you're doing in your memcpy code is to put the value 23456 in buff, starting at byte 52 (so bytes 52-55, since the size of seconds is 4 bytes). What you want to do (if I understand you correctly) is to put the string "23456" in buff, starting at byte 52. In this second case, each character takes one byte, and each byte would hold the ASCII value of its character.

Probably the best way to do that is to use snprintf:

int snprintf(char *buffer, size_t n, const char *format-string,
             argument-list);

In your example: snprintf(&buff[position], 5, "%d", seconds) Note that the n arguments holds the number of digits, rather than the size of the variable. As I said - you take one byte per digit/character.

Obviously you should calculate the number of digits in seconds rather than hard-code it if it can change, and you should also check the return value of snprintf to see if the operation was performed successfully

Asaf Bialystok
  • 126
  • 1
  • 10
  • Thanks for solution! However this code is for embedded systems (STM32) , will it be ok with the performance? I heard that sprintf and similar functions can radically decrease it – yeuop Sep 04 '21 at 09:03
  • @yeuop Didn't notice the embedded tag... Embedded isn't really my strong suit. I'm not sure what would be the performance cost of using snprintf, but if it is high, It's possible that there are "light weight" implementations for snprintf and the like that are more suitable for embedded envs, or otherwise you would have to look for an alternative. Hopefully someone with more knowledge on the matter can comment and shed some light on this. Other option is that you do it "manually", i.e take each digit, figure out its ASCII value and put it in the buffer, but I think there are better options. – Asaf Bialystok Sep 04 '21 at 09:14
  • @yeuop ; Indeed `snprintf() `et al` may be prohibitive in terms of footprint, if you are not already using formatted output elsewhere, adding it just for this will add a significant amount of code for very little benefit. If you are already using formatted output (`printf()`, sprintf()` etc.) using it for this is likely to have little additional overhead as they all share a great deal of code. I'd post an answer, but currently your requirements are unclear as noted in my comment to your question. – Clifford Sep 04 '21 at 11:44
  • We are using `snprintf()` in an ARM Cortex-M4 without any problems. But this chip has 2mb flash. I could not find which lib implementation we are including but it must be a reduced one. Looking at the build results I see the most dominat functions: `_svfprintf_r` at 3,91KB and `_vfiprintf_r` at 2.17KB. – Bart Sep 06 '21 at 07:31
  • @yeuop Just use the version I posted to your previous question here https://stackoverflow.com/questions/68964088/ignoring-to-show-a-number-using-sprintf. If you want decimal numbers instead of hex, just tweak the code to add `'0'` to each raw binary number. – Lundin Sep 06 '21 at 08:19
  • @Bart It is very hard to justify blowing up 4kb flash just to use a sloppy, dangerous function from stdio.h. If you need advanced formatting of float numbers then _maybe_ it is justified, otherwise not. – Lundin Sep 06 '21 at 08:20
  • @Bart Every seasoned embedded programmer already has a lib of well-tried, production quality that can be used as drop in for the standard bloat lib. Binary to decimal/hex string conversions are trivial beginner-level stuff. Decoding float numbers manually is a little bit more intricate but still manageable. A hand-made replacement for printf bloat is typically about ~10x times faster and ~500x more memory efficient on the average embedded system. – Lundin Sep 06 '21 at 08:32
0

It is unclear how you are intending to represent this uint32_t, but your code fragment suggest that you are expecting hexadecimal (or perhaps BCD). In that case:

for( int shiftby = 28; shiftby >= 0 ; shiftby -= 4 )
{
    char hexdigit = (seconds >> shiftby) & 0xF ;
    buf[position++] = hexdigit < 10 ? hexdigit + '0' : hexdigit + 'A' - 10 ;
}

Note that the only real difference between this and your code is the conversion to hex-digit characters by adding conditionally either '0' or 'A' - 10. The use of shiftby as the loop control variable is just a simplification or your algorithm.

The issue with your code is that it inserted integer values 0 to 15 into buf and the characters associated with these values are all ASCII control characters, nominally non-printing. How or whether they render as a glyph on any particular display depends on what you are using to present them. In Windows console for example, printing characters 0 to 15 results in the following:

00 = <no glyph>
01 = '☺'
02 = '☻'
03 = '♥'
04 = '♦'
05 = '♣'
06 = '♠'
07 = <bell> (emits a sound, no glyph)
08 = <backspace>
09 = <tab>
10 = <linefeed>
11 = '♂'
12 = '♀'
13 = <carriage return>
14 = '♫'
15 = '☼'

The change above transforms the values 0 to 15 to ASCII '0'-'9' or 'A'-'F'.

If a hexadecimal presentation is not what you were intending then you need to clarify the question.

Note that if the encoding is BCD (Binary Coded Decimal) where each decimal digit is coded into a 4 bit nibble, then the conversion can be simplified because the range of values is reduced to 0 to 9:

char bcddigit = (seconds >> shiftby) & 0xF ;
buf[position++] = bcddigit + '0' ;

but the hex conversion will work for BCD also.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • The look-up table version should be branch free and overall faster: https://stackoverflow.com/a/68965236/584518. At the expense of 17 bytes .rodata flash. – Lundin Sep 06 '21 at 08:22
  • @Lundin True. Also a lookup table makes no assumption about the ordering of the character set - although that is a rather academic point. The code would be simpler, recovering some it the space used by the lookup table. In the unlikely event this is BCD, I'd go with the arithmetic solution. – Clifford Sep 06 '21 at 22:21