scanf, sscanf, ... strange behaviour with non static locals

Go To Last Post
20 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

Has anyone noticed strange behaviour of the scanf family with non static local variables?

#include 

int main(void)
{
	static unsigned char localstring[] = "P2.3.4Hallo";

	unsigned char command;				// Domonet received command
	unsigned int mask;					// Domonet received bitmask
	unsigned int ev;					// Domonet received event
	unsigned int add;					// Domonet received address to send
	unsigned char str[9];				// Domonet received command to send

	sscanf(localstring, "%c%u.%u.%u%s", &command, &mask, &ev, &add, str);
	
	for (;;)
	{
	}
}

Does the trick while

#include 

int main(void)
{
	unsigned char localstring[] = "P2.3.4Hallo";

	unsigned char command;				// Domonet received command
	unsigned int mask;					// Domonet received bitmask
	unsigned int ev;					// Domonet received event
	unsigned int add;					// Domonet received address to send
	unsigned char str[9];				// Domonet received command to send

	sscanf(localstring, "%c%u.%u.%u%s", &command, &mask, &ev, &add, str);
	
	for (;;)
	{
	}
}

Doesn't. I've witnessed the same behaviour with scanf and non static destination variables, Is it my poor knowledge of the C programming language or is there a bug somewhere?

Jörg,

Do you know if the allignment error in printf is taken care off, i submitted the bug report a few months ago... The day after it was removed from the bug list

Koen.

Assumptio mater errorum est

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Can you elaborate what doesn't work for you? I copy&pasted your code,
and ran it through simulavr/GDB. Everything looks normal to me:

...
Breakpoint 1, main () at foo.c:13
13         sscanf(localstring, "%c%u.%u.%u%s", &command, &mask, &ev, &add, str);
(gdb) info loc
localstring = "P2.3.4Hallo"
command = 0 '\0'
mask = 0
ev = 0
add = 0
str = "\0\0\0\0\0\0\0\0"
(gdb) n
15         for (;;)
(gdb) info loc
localstring = "P2.3.4Hallo"
command = 80 'P'
mask = 2
ev = 3
add = 4
str = "Hallo\0\0\0"
(gdb) q

(I don't have a real device handy right now.)

Regarding the old bug, well, if it has been closed, it supposedly has
been fixed. Just browse the closed bug reports, and you are supposed
to see the comments.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The data isn't correctly returned if the localstring isn't declared as static...
declared with the static qualifier the output is as you ran to your simulator. Without the static qualifier it returns...

command = 0
mask = 0
ev = 0
add = 4
str = "Hallo\0"

(Simulated in AVRStudio 4.08 SP1, but noticed it in silicon to.)

wich is obviously wrong... I've noticed the same with;

scanf("%lX %u", &lval, &uval);

if lval isn't type qualified as static the returning value for lval is incorrect

Assumptio mater errorum est

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Nope sorry, I've been using your example without `static' in my test.

Sorry, I still cannot reproduce that anyhow.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Are you perhaps compiling with the min scanf lib? I've noticed the problem with the float lib?

The second problem is compiled with the min scanf lib.

Assumptio mater errorum est

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I've used the standard version (everything but floating point
support), but I've also verified against the floating-point one,
similar behaviour.

Well, a few things have been fixed in avr-libc's scanf()
implementation, my guess would that your problem has somehow been
fixed that way, too.

If you can wait a few days, they are eager to ship a new version of
WinAVR anyway...

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks Jörg,

Also for clarifying the printf lib problem, i read your comment on my bug report at savanah... I think I should first take a look at TFM with every new release of WinAVR :roll:
Comming from the AVRFreaks distribution i was under the impression that there where only two flavours min and flt. I'll start removing all reference to the min printf lib in all projects that need padding and allignment.
Nevertheless, i spoke about this problem with you about two years ago... Seems that i'm not the only one with my head in the clouds.

Anyway, thanks for all your effort.

Assumptio mater errorum est

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yeah, I've noticed that, too. Sorry for being ambiguous. I'd gladly
accept a better wording for that part of the docs.

Btw., I hoped Mfile would finally help on that issue, too. ;-) (It
clearly has 3 different settings for each option.)

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Ive got question regarding scanf

Thats when i want to read 2 strings seperated by spaces ' ' , and want to read the 2 strings like this:

void function1(void)
{
 scanf(string1, "%s");
}


void function2(void);


int main(void)
{
  :code:

 function1();
 function2();

 :code:
}

The fist string is read propperly but the 2nd string is disregared when I type this in a terminal window:
"ABC DEF"[CR]

string1 = ABC
string2 waits fir user input!

Other C compilers use DEF for the 2nd string automaticly I think.
Im using this scanf programming style for commands such as
CD [path]
OPENFILE [filename]
etc

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

That is indeed strange, that on does work for me have you tried declaring the string static?
Maybe we just have to wait on the new release since the devteam has worked on many issues in the scanf lib.

Koen.

Assumptio mater errorum est

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I think the problem depends on how the input buffering works. scanf()
consumes a single char at a time from the input stream, and tries a
conversion. If the char cannot be converted, it is pushed back onto
the stream (by means of unputc(), which will make it be held in a
special area inside the FILE* structure). So if no more formats are
available, it will simply cease reading input data. If you've been
able to enter a full line before anyway, somehow you application must
have buffered it somewhere. Of course, the next call to scanf should
then start reading the old input buffer at the same spot where the
previous scanf stopped.

Actually, I always found it more practical to read full lines into a
local buffer, and then sscanf it.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Yes you describe how SCANF should work, but on the 4 april WINAVR version it doesn't somehow... :D

Ill play with SSCANF tomorrow!

Thanks for the input

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Uhm it doesnt matter that I declare different character arrays inside each function for scanf does it?

actual shell code:

char rootshell(void)
{
/*static*/ unsigned char	szCommand[21];		//Koen Kempeneers idea to make it static. havent tried YET.
	do
	{
	// Add the commands to the shell list.
	NanoShell_Init();
	NanoAddCommand("cls", &cls);
	NanoAddCommand("cd", &shell_cd);
	NanoAddCommand("dir", &shell_dir);
	NanoAddCommand("memdump", &shell_memdump);
		printf_P(PSTR("$:"));

		scanf_P(PSTR("%20s"), &szCommand);
		
		if (NanoShellXQT(szCommand))
		{
			printf_P(PSTR("Unknown Command\r\n"));
		}
	} while (strcmp(szCommand,"exit"));
	NanoShell_DeInit();
	return(0);
}

function example:

void shell_cd(void)														//Change Directory
{
	unsigned char szPath[30];


	//pPtr = (unsigned char*) s_Ext_Buffer;
	//readstring(chString);

	//lcd_printf("\nSearching: %s\n", sPtr);
	printf_P(PSTR("path: "));
	scanf_P(PSTR("%s"), &szPath);
	switch(fat_cd(szPath))
	{
		case 1:		printf_P(PSTR("\r\n%s/"), szPath); break;				//succes display "szPath/"
		case 2:		printf_P(PSTR("Error: path is file!\n")); break;
		default :	printf_P(PSTR("Error: No such path.\n")); break;
	}
}

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I cannot imagine how else it would do. Just look into the source
code, please (the actual implementation is in the file
libc/stdio/vfscanf.c). Whenever it requires a character for a
conversion, it calls getc().

What kind of buffering are you using?

I've just tried it with a simple line-buffered line-editing input
routine I once wrote for testing, and it works for me the way you
intended. (I'm going to publish that little test program some day,
but it's not yet ready.)

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

:oops:

Im not buffering anything regarding the UART/RS232.... That must be the problem right?

I have a plain RX routine that checks if theres data or not,, if not it just returns nothing.

So make a buffered UART routine to solve my problems with scanf?

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

> Im not buffering anything regarding the UART/RS232.... That must be
> the problem right?

Most likely, yes. As I wrote, scanf() simply calls getc() as it needs
data to proceed. It then just stops reading, and returns, once the
format instructions have been worked down completely.

If you still send data to the UART, you'll for sure get data overruns
(DOR bit is set). My sample implementation (soon to be published :)
takes both, FE (framing error - e. g. sending a ) and DOR as an
indication for EOF/error.

> So make a buffered UART routine to solve my problems with scanf?

I think so. For any practical application, better make it
interrupt-driven, too.

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

the speed of which rs232 is send to the device isn't faster then my hand typing.. thats troughly maximum of 22 bytes/sec at tops :P

The avr must be getting really bored when processing 22 bytes/sec @ 16MHz

8)

Writing interrupt driven loop-buffered uart thingy now,, or ill rob someones else,, , there's no point in re-inventing the wheel.

MY MICROCONTROLLER CAN BEAT THE HELL OUT OF YOUR MICROCONTROLLER /ATMEL

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Hi,

I have also noticed that sscanf appears to operate strangely when used with non-static variables.

Environment is WinAVR 20040720 with GCC 3.4.1 & avr-libc 1.0.4 on a ATmega128

I have something like

static void SetConfigItem(const char * configString)
{
static uint8_t configItem;

sscanf("99", "%u",&configItem);

... etc

If configItem is declared static as show above, then it is correcly assigned the value of 99. However, if the variable is not static, my program usually crashes.

I have tried the min,std & float forms of the scanf. The latter two exhibit this behaviour, the min form appears to assign an incorrect value to configItem.

If I make configItem var global, it also seems to work ok, but I don't understand why it needs to be global or static.
I am indeed testing the value assigned to configItem in the scope of the function.

Any ideas would be appreciated - thanks.

Steve Pattinson
Netway Communications Pty Ltd

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

(Better use new threads for unrelated items.)

That's because you probably don't know what you're doing. You request
a 16-bit wide unsigned conversion, but only supply storage for 8 bits
(uint8_t). Obviously, another 8 bits of possibly important to you
data might be overwritten that way.

While C99 offers the `hh' modifier for that purpose, the current stdio
implementation only implements the C89 (ISO-C 1990) feature set, so
you stand no chance to convert directly into an 8-bit variable, sorry.
While it would certainly be possible to extend the functionality to
cover all the interesting C99 items (as we don't implement wchar_t's
in the library it would e. g. make no sense to implement conversions
for it in stdio), I somewhat hesitate to do this in order to not bloat
the already huge stdio code even more. What do people think? (It
would probably be easy enough to add those modifiers for intmax_t,
ptrdiff_t, and size_t without much bloat, as they are merely aliases
for already existing conversions. Anything else, in particular `ll'
and `hh' modifiers would cause much bloat though.)

Jörg Wunsch

Please don't send me PMs, use email if you want to approach me personally.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

dl8dtl wrote:

While it would certainly be possible to extend the functionality to
cover all the interesting C99 items (as we don't implement wchar_t's
in the library it would e. g. make no sense to implement conversions
for it in stdio), I somewhat hesitate to do this in order to not bloat
the already huge stdio code even more. What do people think? (It
would probably be easy enough to add those modifiers for intmax_t,
ptrdiff_t, and size_t without much bloat, as they are merely aliases
for already existing conversions. Anything else, in particular `ll'
and `hh' modifiers would cause much bloat though.)

I would say to use your best judgment. In general, I think that it would be good to support C99 items, where it is applicable to the AVR, and where the costs do not outweigh the benefits. When the item in question will bloat the code, then perhaps it would be good to see if it is worth putting it in the large version of the library.