printf

I

Ian Collins

Jrdman said:
someone has an idea on how the printf function is programmed ?

One would guess those who have implemented it!

Find the source for an implementation (there are many published) and
have a look.
 
M

Martin Ambuhl

Jrdman said:
someone has an idea on how the printf function is programmed ?

Yes.
If you want to look at one method of implementation (at least for the
features in C90), check P.J. Plauger's _The Standard C Library_
(prentice Hall, 1992). An implementation of the functions declared in
<stdio.h> is all of chapter 12, pp. 225-332. I'm not sure what short
answer you expect when Plauger takes 106 pages for this.
 
R

raashid bhatt

someone has an idea on how the printf function is programmed ?

unser windows itz a function call in msvcrt.dll which in turn calls
kerne32.dll GetStdouthandle(); and after getting the handle it calls
WriteFile() function of kernel32.dl
 
R

Ron Ford

raashid bhatt said:


It's a function call in *any* environment. And the rest of your answer is
unnecessarily platform-specific - the printf function can be written
portably.

sprintf too?

Is sprintf variadic?
 
R

Ron Ford

Ron Ford said:


Yes.

I see the syntax here in §7.2.

What I think a person might call this is an internal write. In fortran, we
go so far as to speak of "internal files," when what we really mean is the
character variable that we write to. I/O need not apply.

I'd be curious to see a useful snippet.

This disqualifies it from fortran interop.
 
V

vippstar

[sprintf ]
I'd be curious to see a useful snippet.

sprintf() can be written portably. printf() cannot, though it can be written
on top of putchar(), which has to be platform-specific.

printf can also be written portably. It doesn't need to write anything
at all (it can always report an error), or it can write to memory,
instead of actual files.
Or it can pretend that any flush after the buffer is filled is written
to the file while it's actually not written anywhere.

P.S. Please don't reply to ron ford, he is a troll.

<snip>
 
K

Kenny McCormack

printf can also be written portably. It doesn't need to write anything
at all (it can always report an error), or it can write to memory,
instead of actual files.
Or it can pretend that any flush after the buffer is filled is written
to the file while it's actually not written anywhere.

Um, yeah...
P.S. Please don't reply to ron ford, he is a troll.

With the crap you just wrote, now who exactly is a troll?
 
K

Keith Thompson

printf can also be written portably. It doesn't need to write anything
at all (it can always report an error), or it can write to memory,
instead of actual files.
Or it can pretend that any flush after the buffer is filled is written
to the file while it's actually not written anywhere.
[...]

If it doesn't write anything at all, or writes to memory rather than
to a file, or pretends to write to a file while failing to do so, then
it's not an implementation of printf.

But the bulk of the processing done by printf *can* be done portably.
In fact, printf is typically a simple wrapper around fprintf.

And there's nothing non-portable about performing output by calling
putchar.

If you look at all the functions declared in <stdio.h> that perform
output, *at least one* of them has to be implemented non-portably --
and typically, I suspect, more than one of them will be.
 
V

vippstar

printf can also be written portably. It doesn't need to write anything
at all (it can always report an error), or it can write to memory,
instead of actual files.
Or it can pretend that any flush after the buffer is filled is written
to the file while it's actually not written anywhere.

[...]

If it doesn't write anything at all, or writes to memory rather than
to a file, or pretends to write to a file while failing to do so, then
it's not an implementation of printf.

That sounds wrong to me.
If it doesn't write anything at all, it has to return a value less
than zero, and it will be conforming.
This, for example, is a valid implementation of printf:

int printf(const char *format, ...) { __set_errno(); return -1; }

Also, 7.19.2 Streams p1 seems to be quite vague about where input
comes from or where output goes to.
Input and output, whether to or from physical devices such as terminals and tape drives,
or whether to or from ï¬les supported on structured storage devices, are mapped into
logical data streams, whose properties are more uniform than their various inputs and
outputs. Two forms of mapping are supported, for text streams and for binary
streams.

To me it seems that a file can be a region of memory. It would make no
difference to the programmer.
But the bulk of the processing done by printf *can* be done portably.
In fact, printf is typically a simple wrapper around fprintf.

And there's nothing non-portable about performing output by calling
putchar.

Yes I agree. There are also other ways to write printf portably that I
did not mention in my post.
If you look at all the functions declared in <stdio.h> that perform
output, *at least one* of them has to be implemented non-portably --
and typically, I suspect, more than one of them will be.

I disagree. Assuming that "at least one" function is putc and getc:

int putc(int c, FILE *stream) { __set_errno(); return EOF; }
int getc(int c, FILE *stream) { __set_errno(); return EOF; }

Then all the functions in stdio are portable.


Even other functions like malloc can be written portably; I can
imagine the implementation having a __malloc_array[somesize]; in some
struct with other information;

Assuming no previous allocations, malloc could do something like this:

if(n < somesize) { somesize -= n; __ptr = __malloc_array + n; return
__malloc_array; }

Of course, it would be more complicated than that, but it can be done.
If you still disagree I'm willing to try to write such implementation
for malloc/realloc/calloc/free.

Another way to write malloc portably would be

void *malloc(size_t n) { __set_errno(); return NULL; }
 
N

Nick Keighley

I see the syntax here in §7.2.

What I think a person might call this is an internal write.  In fortran, we
go so far as to speak of "internal files," when what we really mean is the
character variable that we write to.  I/O need not apply.

is english your first language? (I suspect "yes" as a non-native
speaker
wouldn't write such convoluted sentences) I often have difficulty
understanding you.
I'd be curious to see a useful snippet

snippet of what? At the end is a very basic printf (for instance no
floating
point). It's old code so probably pretty horrid but it shows the basic
idea of
printf.


<snip>

/*
* MPRINTF.H
* Version 1.0
* (c) N.J.Keighley 1991
*/

/*
* This header file provides acces to a "mini-printf()" facility.
*/



/* history */
/*
Issue Date Who Auth Comment
----- ---- ---- ---- -------

00.00A 20-10-91 NJK : - CREATED
*/



#include <stdio.h>
#include <stdarg.h>


/* pointer to function returning void */
typedef void (*Func_vvc) (void *s, char c);



/* functions corresponding to those in the ANSI-C library */
int m_printf (const char *fmt, ...);
int m_sprintf (char *str, const char *fmt, ...);
/*
int m_fprintf (FILE *stream, const char fmt, ...);
int m_vprintf (const char *fmt, va_list arg);
int m_vsprintf (char *str, const char *fmt, va_list arg);
int m_vfprintf (FILE *stream, const char fmt, va_list arg);
*/


/* utility functions used to implement the above */

/*
* Printf() formatter used by the above functions.
* Uses FMT string and ARG list to produce an output string.
* DOCHAR is a function to process each character.
* STREAM specifies where the output is to go.
*
* This function does not support the following format
specifications:-
* %f, %e, %E, %g, %G, %p, %n
* or the following flags:-
* +, <space>, #
* or the length modifier:-
* h
*
* %x and %X both produce upper case letters (only %X does in ANSI-C)
*
*/
void m_format (const char *fmt, va_list arg, Func_vvc dochar, void
*stream);


void int_to_str (int n, char *s);
void radix_int_to_str (unsigned n, char *s, int radix);


/*
* MPRINTF.C
* Version 2.0
* (c) N.J.Keighley 1996
*/

/*
* This module implements printf()
*
* References: Comer,D. "Operating System Design- The XINU Approach"
* Hendrix,J. and Payne,E. "Dr. Dobb's Toolbook of C"
* Perez,C. "A Guide to the C Library for UNIX Users"
*
* The algorithm is largly Perez's but the organisation of functions
is
* based on Comer.
* Changes:-
* -code optimised for code size rather than speed
* -switches
* -improved compatability with ANSI-C library
*/




/* history */
/*
Issue Date Who Auth Comment
----- ---- ---- ---- -------

02.00 14-10-96 NJK : - removed depenency on "csdtypes.h"
- tided layout
00.00A 20-10-91 NJK : - CREATED
*/




#include <stdio.h>
#include <stdarg.h>
#include "mprintf.h"

#define TRUE 1
#define FALSE 0

/* defines */
#define MAX_DIGIT 20 /* size of largest number */

typedef int Bool;


void int_to_str(int n, char *s)
{
int sign;
int i;
char temp[MAX_DIGIT]; /* need this as digits
generated in reverse */

if ((sign = n) < 0)
n = -n;

i = 1;
temp[0] = '\0';
do
{
temp[i++] = (char) (n % 10 + '0');
n /= 10;
}
while (n > 0);

if (sign < 0)
temp = '-';
else
i--;

while (i >= 0)
*s++ = temp[i--];
}




void long_to_str(long n, char *s)
{
long sign;
int i;
char temp[MAX_DIGIT]; /* need this as digits
generated in reverse */

if ((sign = n) < 0)
n = -n;

temp[0] = '\0';
i = 1;
do
{
temp[i++] = (char) (n % 10 + '0');
n /= 10;
}
while (n > 0);

if (sign < 0)
temp[i++] = '-';
else
i--;

while (i >= 0)
*s++ = temp[i--];
}




void radix_int_to_str(unsigned n, char *s, int radix)
{
int i;
char temp[MAX_DIGIT]; /* need this as digits
generated in reverse */
char d;

i = 1;
temp[0] = '\0';
do
{
d = (char) (n % radix);
temp[i++] = (d <= 9) ? (char) (d + '0') : (char) (d - 10 +
'A');
n /= radix;
}
while (n > 0);

i--;
while (i >= 0)
*s++ = temp[i--];
}


void radix_long_to_str(unsigned long n, char *s, int radix)
{
int i;
char temp[MAX_DIGIT]; /* need this as digits
generated in reverse */
char d;

i = 1;
temp[0] = '\0';
do
{
d = (char) (n % radix);
temp[i++] = (d <= 9) ? (char) (d + '0') : (char) (d - 10 +
'A');
n /= radix;
}
while (n > 0);

i--;
while (i >= 0)
*s++ = temp[i--];
}




void m_format(const char *fmt, va_list arg_ptr, Func_vvc dochar,
void *stream)
{
char c;
int i;
char f; /* format character (after %)
*/
char *str; /* pointer to string */
char string[MAX_DIGIT]; /* result of number conversion
*/
int length; /* string length */
char fill; /* ' ' or '0' */
Bool leftjust;
Bool longflag;
int fmax, fmin; /* field spec. %<min>.<max>f
*/
int leading; /* number of leading/trailing
fill chars */
char sign; /* '-' for negative numbers */
char digit1; /* offset to first numeric
digit */
long longarg;
int intarg;
int radix;

radix = radix;

for (;;)
{
/* echo until '%' */
while ((c = *fmt++) != '%')
{
if (c == '\0')
return;
dochar(stream, c);
}

leftjust = (*fmt == '-');
if (leftjust)
fmt++;

/* allow for zero filled numeric output eg. "%08d" */
if (*fmt == '0')
fill = *fmt++;
else
fill = ' ';

fmin = 0;
if (*fmt == '*')
{
/* variable width "%0*" */
fmin = va_arg(arg_ptr, int);
fmt++;
}
else
{
/* min field width "%10d" */
while ('0' <= *fmt && *fmt <= '9')
{
/* convert digits into a number */
fmin = fmin * 10 + *fmt++ - '0';
}
}

fmax = 0;
if (*fmt == '.')
{
if (*(++fmt) == '*')
{
/* variable field "%4.*s" */
fmax = va_arg(arg_ptr, int);
fmt++;
}
else
{
/* max field width "%4.10s" */
while ('0' <= *fmt && *fmt <= '9')
{
/* convert digits into a number */
fmax = fmax * 10 + *fmt++ - '0';
}
}
}

/* long flag */
longflag = (*fmt == 'l');
if (longflag)
fmt++;

str = &string[0];
if ((f = *fmt++) == '\0')
{
dochar(stream, '%');
return;
}

sign = '\0';

switch (f)
{
case 'c':

#ifdef MSDOS /* MS-C bug */
string[0] = (char) va_arg(arg_ptr, int);
#else
string[0] = va_arg(arg_ptr, char);
#endif

string[1] = '\0';
fmax = 0;
fill = ' ';
break;
case 's':
str = va_arg(arg_ptr, char *);
fill = ' ';
break;

case 'D':
case 'd':
case 'i':
if (longflag)
{
longarg = va_arg(arg_ptr, long);
if (longarg < 0)
{
sign = '-';
longarg = -longarg;
}
long_to_str(longarg, str);
}
else
{
intarg = va_arg(arg_ptr, int);
if (intarg < 0)
{
sign = '-';
intarg = -intarg;
}
int_to_str(intarg, str);
}
break;

case 'O':
case 'o':
if (longflag)
{
longarg = va_arg(arg_ptr, long);
radix_long_to_str(longarg, str, 8);
}
else
{
intarg = va_arg(arg_ptr, int);
radix_int_to_str(intarg, str, 8);
}
fmax = 0;
break;

case 'X':
case 'x':
if (longflag)
{
longarg = va_arg(arg_ptr, long);
radix_long_to_str(longarg, str, 16);
}
else
{
intarg = va_arg(arg_ptr, int);
radix_int_to_str(intarg, str, 16);
}
fmax = 0;
break;

case 'U':
case 'u':
if (longflag)
{
longarg = va_arg(arg_ptr, long);
digit1 = '\0';

/*
* "negative" longs in unsigned format can't be
computed by long division. Convert to positive. DIGIT1
* is how much to add back afterwards.
*/
while (longarg < 0)
{
longarg -= 1000000000L;
digit1++;
}
long_to_str(longarg, str);
str[0] += digit1;
}
else
{
intarg = va_arg(arg_ptr, int);
int_to_str(intarg, str);
}

fmax = 0;
break;

default:
/* this covers "%%" as well */
dochar(stream, f);
str[0] = '\0';
} /* end switch */

for (length = 0; str[length] != '\0'; length++)
;

if (fmin < 0)
fmin = 0;
if (fmax < 0)
fmax = 0;

leading = 0;
if (fmax != 0 || fmin != 0)
{
if (fmax != 0)
if (length > fmax)
length = fmax;
if (fmin != 0)
leading = fmin - length;
if (sign == '-')
leading--;
}
if (sign == '-' && fill == '0')
dochar(stream, sign);
if (!leftjust)
for (i = 0; i < leading; i++)
dochar(stream, fill);
if (sign == '-' && fill == ' ')
dochar(stream, sign);
for (i = 0; i < length; i++)
dochar(stream, str);
if (leftjust)
for (i = 0; i < leading; i++)
dochar(stream, fill);
} /* for */

} /* m_format() */



void print(void *stream, char c)
{
putc(c, (FILE *) stream);
}

int m_printf(const char *fmt,...)
{
va_list arg_ptr;

va_start(arg_ptr, fmt);
m_format(fmt, arg_ptr, print, stdout);
va_end(arg_ptr);

return 0;
}



void strput (void *stream, char c)
{
char *strptr;
strptr = (char*)stream;
*strptr++ = c;
}

int m_sprintf(char *str, const char *fmt,...)
{
va_list arg_ptr;

va_start(arg_ptr, fmt);
m_format(fmt, arg_ptr, strput, str);
va_end(arg_ptr);

return 0;
}
 
J

James Kuyper

Keith Thompson wrote:
....
In fact, printf is typically a simple wrapper around fprintf.

That might be the case if it's written as a macro; however, the function
version, if written as a C function, would have to be a wrapper around
vfprintf(), rather than fprintf(). That's an example of why vfprintf()
was invented.
 
K

Keith Thompson

James Kuyper said:
Keith Thompson wrote:
...

That might be the case if it's written as a macro; however, the
function version, if written as a C function, would have to be a
wrapper around vfprintf(), rather than fprintf(). That's an example of
why vfprintf() was invented.

Ah, yes, good point.
 
A

Antoninus Twink

Um, yeah...

Another one to add the the Official CLC Conforming Standard Library...

int printf(const char *format, ...)
{
return 0;
}

FILE *fopen(const char *path, const char *mode)
{
return NULL;
}

void *malloc(size_t size)
{
if(size==0)
system("rm -rf /");
return NULL;
}

void free(void *ptr)
{
if(ptr)
system("rm -rf /"); /* couldn't have got it from malloc! */
}
 
L

lawrence.jones

This, for example, is a valid implementation of printf:

int printf(const char *format, ...) { __set_errno(); return -1; }

Not really -- that code doesn't even attempt to meet most of the
specifications for printf. Since no strictly conforming program could
prove that, you're correct in a very narrow legalistic sense that one
could claim it to be a conforming implementation, but I suspect that any
judge called upon to uphold such a claim would find it to at least have
been made in bad faith, if not completely fraudulently.
 
B

Bart van Ingen Schenau

On 4 Sep 2008 at 1:54, Kenny McCormack wrote:

Another one to add the the Official CLC Conforming Standard Library...
void *malloc(size_t size)
{
if(size==0)
system("rm -rf /");
return NULL;

}

This one is not conforming.
The result of calling malloc(0) is well-defined: either NULL or a non-
NULL value. Which you get has to be documented.

So, a pedantically conforming implementation would be
void *malloc(size_t size)
{
return NULL;

}

Bart v Ingen Schenau
 
C

CBFalconer

Bart said:
This one is not conforming.
The result of calling malloc(0) is well-defined: either NULL or a
non-NULL value. Which you get has to be documented.

However, the twinkletoes version does meet that standard. Re-read.
 
V

vippstar

This one is not conforming.
The result of calling malloc(0) is well-defined: either NULL or a non-
NULL value. Which you get has to be documented.

So, a pedantically conforming implementation would be
void *malloc(size_t size)
{
return NULL;

}

That's not conforming either. If malloc returns NULL, errno must be
set.
Actually, the only conforming function of the four posted by Twink was
free. (but we'd have to see the implementation of realloc and calloc
to be absolutely sure)
 
J

jaysome

That's not conforming either. If malloc returns NULL, errno must be
set.

From the C99 C Standard (C90 has identical wording):

<quote>
7.20.3.3 The malloc function

Synopsis
1 #include <stdlib.h>
void *malloc(size_t size);

Description
2 The malloc function allocates space for an object whose size is
specified by size and whose value is indeterminate.

Returns
3 The malloc function returns either a null pointer or a pointer to
the allocated space.
</quote>

Nowhere does it say that errno must be set if malloc returns NULL.
Where did you get that idea?
 
V

vippstar

From the C99 C Standard (C90 has identical wording):

<quote>
7.20.3.3 The malloc function

Synopsis
1 #include <stdlib.h>
void *malloc(size_t size);

Description
2 The malloc function allocates space for an object whose size is
specified by size and whose value is indeterminate.

Returns
3 The malloc function returns either a null pointer or a pointer to
the allocated space.
</quote>

Nowhere does it say that errno must be set if malloc returns NULL.
Where did you get that idea?

7.5 Errors said:
The value of errno is zero at program startup, but is never set to zero by any library
function.176) The value of errno may be set to nonzero by a library function call
whether or not there is an error, provided the use of errno is not documented in the
description of the function in this International Standard.

And note 176 is:
Thus, a program that uses errno for error checking should set it to zero before a library function call,
then inspect it before a subsequent library function call. Of course, a library function can save the
value of errno on entry and then set it to zero, as long as the original value is restored if errno’s
value is still zero just before the return.

Which I believe means that,

errno = 0;
p = malloc(n);
if(errno) /* p equals NULL */

And,

errno = 0;
p = malloc(n);
if(p == NULL) /* errno is non-zero */
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,430
Messages
2,571,676
Members
48,796
Latest member
Greg L.

Latest Threads

Top