[comp.lang.c] printf in dump routine

nor1675@dsacg2.UUCP (Michael Figg) (03/01/89)

-----------------------------------------------------------------

Following is the relevent piece of code from a dump routine I have been 
trying to write on an Amiga. I'm trying to pass a pointer to a variable
that I want to dump in hex and the number of bytes to print:
  
    .
    .
    long  test_long;
    test_long = 1234;
    print_data((char *)&test_long,sizeof(test_long));
    .  
    .
    void print_data(pnt,num)
    char *pnt;
    int num;
    {
      int i;
      for(i = 0; i < num; i++)
      {
	 printf("%02x",*(pnt + i));
      }
    }
    .
    What I would hope to get from this is '000004d2' but actually got 
    '000004ffffffd2'. This was done with Lattice v5.0.1. I also tried 
    similar code at work on a PC with MS v5.1. There I got 'ffd2040000',
    which except for the leading 'ff' looks like it was printing the bytes
    backwards. Any clues as to why I'm getting this output and how to do it
    right?  Thanks
						->Mike<-



-- 
"Hot Damn! Groat Cakes Again                   Michael Figg
Heavy on the thirty weight!"                   DLA Systems Automation Center
                                               Columbus, Oh.
                                               (614)-238-9036

byrum@cs.odu.edu (Terry Franklin Byrum) (03/01/89)

Try this instead...

void print_data(pnt,num)
unsigned char *pnt;
int num;
{
  int i;
  for(i = num - 1; i >= 0; i--)
     printf("%02x ",*(pnt + i));
}

> ... There I got 'ffd2040000', which except for the leading 'ff' looks 
> like it was printing the bytes backwards. Any clues ...

Have you ever heard of low-high (Little Endian) architecture.  Most PC
type computers store values least significant byte first.  If you want
a number written (normally) you must scan backwards.

Also, on most PC machines char is signed by default.  Due to sign extension,
if the byte value is greater than 7F Hex, unwanted leading FFs will appear
on promotion to int.  The solution is to specify unsigned char* instead of
char*, either as the function's formal parameter or as a cast within the
printf argument.

		... Frank

Frank Byrum, BROOKS Financial Systems, Suffolk VA, (804)539-7202  1st Law of SE
INET:byrum@cs.odu.edu UUCP:..!sun!xanth!{byrum,brooks!frank}  Semper Sic Factum
-- 

	Inventions have long since reached their limit --
	and I see no hope for further developments.
		- Julius Frontinus, world famous engineer, Rome, 10 AD

                                |  Terry Franklin (Frank) Byrum
                                |  BROOKS Financial Systems, Inc.
   ___                          |  INET:  byrum@cs.odu.edu
  /__  _.  __. _ _  /_          |  UUCP:  ...!sun!xanth!brooks!byrum 
 /   _/ \_(_/|_|\|_/ \          |  DISCLAIMER: No one listens anyway! 

piet@ruuinf (Piet van Oostrum) (03/02/89)

In article <652@dsacg2.UUCP>, nor1675@dsacg2 (Michael Figg) writes:
 `    .
 `    long  test_long;
 `    test_long = 1234;
 `    print_data((char *)&test_long,sizeof(test_long));
 `    .
 `    void print_data(pnt,num)
 `    char *pnt;
 `    int num;
 `    {
 `      int i;
 `      for(i = 0; i < num; i++)
 `      {
 `	 printf("%02x",*(pnt + i));
 `      }
 `    }

		 Any clues as to why I'm getting this output and how to do it
 `    right?  Thanks

The problem is that this code is highly unportable. So what you get depends
very much on the machine/compiler you have. There are two problems:

1. The ENDIAN-NESS of the machine. This means whether the bytes in a long
   are addressed from the most significant byte or from the least
   significant byte. What you are doing is taking a long and getting bytes
   out of it. On a BIG-ENDIAN machine (such as the 68000) the first byte
   you get is the upper (most significant) one, on a SMALL-ENDIAN (like the
   8086) it is the lower byte. This explains the difference between the
   Amiga and MS-DOS.

2. The char type that your compiler has. This can be signed or unsigned
   (old compilers usually have only signed. In your case both compilers
   apparently have signed chars, which means that a char that has its
   8th bit set is passed to printf as a negative integer, which cuases
   printf to print extra ff's (how many depends on the size of int, which
   -- not surprisingly -- is 4 bytes on the Amiga and 2 bytes on MS-DOS).
   This problem is easily solved by

	 printf("%02x",*(pnt + i) & 0xff);
or
	 printf("%02x",pnt[i] & 0xff);
-- 
Piet van Oostrum, Dept of Computer Science, University of Utrecht
Padualaan 14, P.O. Box 80.089, 3508 TB Utrecht, The Netherlands
Telephone: +31-30-531806. piet@cs.ruu.nl (mcvax!hp4nl!ruuinf!piet)

edwin@hcr.UUCP (Edwin Hoogerbeets) (03/04/89)

In article <652@dsacg2.UUCP> nor1675@dsacg2.UUCP writes:
>I'm trying to pass a pointer to a variable
>that I want to dump in hex and the number of bytes to print:
>  
>    long  test_long;
>    test_long = 1234;
>    print_data((char *)&test_long,sizeof(test_long));
>    void print_data(pnt,num)
>    char *pnt;
>    int num;
>    {
>      int i;
>      for(i = 0; i < num; i++)
>      {
>        printf("%02x",*(pnt + i));
>      }
>    }

Your problem is that *(pnt + i) has type char. Thus, a char is pushed
onto the stack and printf is called, right? Well, not really. First,
the char is promoted to a int, which under Lattice is 32 bits. But
wait! A char is a signed quantity, so the promotion dutifully sign
extends the char. Any character >= 128 has the high bit set, and this
bit gets copied during sign extension. You got "000004ffffffd2" because
the "d2" has the high bit set (and extended in the call to printf)
while the other bytes don't have the high bit set.

Your solution is simple:

Change your argument declaration to

unsigned char *pnt;

or better yet, 

register unsigned char *pnt;

How's that for a declaration!

Happy hacking,

Edwin
HCR co-op subterfuge team