views:

113

answers:

6

What is the format string modifier for char-as-number?

I want to read in a number never exceeding 255 (actually much less) into an unsigned char type variable using sscanf.

Using the typical

 char source[] = "x32";
 char separator;
 unsigned char dest;
 int len;

 len = sscanf(source,"%c%d",&separator,&dest);
 // validate and proceed...

I'm getting the expected warning: argument 4 of sscanf is type char*, int* expected.

As I understand the specs, there is no modifier for char (like %sd for short, or %lld for 64-bit long)

  • is it dangerous? (will overflow just overflow (roll-over) the variable or will it write outside the allocated space?)
  • is there a prettier way to achieve that than allocating a temporary int variable?
  • ...or would you suggest an entirely different approach altogether?
+1  A: 

You can use %hhd under glibc's scanf(), MSVC does not appear to support integer storage into a char directly (see MSDN scanf Width Specification for more information on the supported conversions)

Hasturkun
Depending of the size of an int (not necessarily 4 bytes), %hdd might not be 1 byte.
Dpp
Since I have 100% control over compiler environment and hardware (closed-source firmware for an embedded product), this seems like the perfect solution.
SF.
A: 

I want to read in a number never exceeding 255 (actually much less) into an unsigned char type variable using sscanf.

In most contexts you save little to nothing by using char for an integer.

It generally depends on architecture and compiler, but most modern CPUs are not very well at handling data types which are of different in size from register. (Notable exception is the 32-bit int on 64-bit archs.)

Adding here penalties for non-CPU-word-aligned memory access (do not ask me why CPUs do that) use of char should be limited to the cases when one really needs a char or memory consumption is a concern.

Dummy00001
embedded project, a processor with both 32-bit and 8-bit instruction set (ARM), size IS a concern.
SF.
A: 

It is not possible to do.

sscanf will never write single byte when reading an integer.

If you pass a pointer to a single allocated byte as a pointer to int, you will run out of bounds. This may be OKay due to default alignment, but you should not rely on that.

Create a temporary. This way you will also be able to run-time check it.

Pavel Radzivilovsky
A: 

Probably the easiest way would be to load the number simply into a temporary integer and store it only if its in the required boundaries. (and you would probably need something like unsigned char result = tempInt & 0xFF; )

PeterK
A cast into an (unsigned char) is safer since a char can have 7 bits on some architectures. (the define CHAR_BIT (limits.h) usually gives this number)
Dpp
A: 

It is dangerous to use that. Since there an implicit cast from a unsigned char* to an int*, if the number is more than 0xFF it is going to use bytes (max 3) next to the variable in the stack and corrupt their values.

The issue with %hhd is that depending of the size of an int (not necessarily 4 bytes), it might not be 1 byte.

It does not seem sscanf support storage of numbers into a char, I suggest you use an int instead. Although if you want to have the char roll-over, you can just cast the int into a char afterward, like:

int dest;
int len;

len = sscanf(source,"%c%d",&separator,&dest);
dest = (unsigned char)dest;
Dpp
You are incorrect, the `hh` modifier in `%hhd` indicates that next pointer will be a pointer to `signed char` or `unsigned char`.
Hasturkun
A: 

It's dangerous. Scanf will write integer sized value over the character-sized variable. In your example (most probably) nothing will happen unless you reorganize your stack variables (scanf will partially overwrite len when trying to write integer sized "dest", but then it returns correct "len" and overwrites it with "right" value).

Instead, do the "correct thing" rather than relying on compiler mood:

 char source[] = "x32";
 char separator;
 unsigned char dest;
 int temp;
 int len;

 len = sscanf(source,"%c%d",&separator,&temp);
 // validate and proceed...

 if (temp>=YOUR_MIN_VALUE && temp<=YOUR_MAX_VALUE) {
   dest = (unsigned char)temp;
 } else {
   // validation failed
 }
MaR
It is actually the other way around, it will overwrite 'char separator' and a part of the 'char source[]'. The Latter should be better defined 'const char source[]' by the way.
Dpp
@Dpp: yep, you are right (keep forgeting how stack works :o) The other suggestion is indeed also right but should be aimed at OP (I didn't want to change more than necessary).
MaR