8 Replies - 16626 Views - Last Post: 14 August 2012 - 08:19 AM

#1 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 614
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

DIY sprintf

Post icon  Posted 01 August 2012 - 02:20 PM

Try to implement your own bare bones version of sprintf in C.

Limitations
  • No changing the mysprintf function's signature.
  • No calling of functions which you didn't write yourself (e.g. strlen).
  • No using macros which you didn't write yourself (e.g. va_start).
  • Your compiler should be outputting 32-bit code.
  • mysprintf must null terminate the string it returns through the "out" argument.


The embedded format tag should be the same as the regular sprintf function, that is, the '%' character. To see the (tasteless) result of the simple test, you only need to implement the '%c' format tag which should insert the relevant character argument in to the "out" string (in the same was sprintf does). If you want a further challenge, implement the '%x' (hexadecimal) and '%d' (decimal) format tags, then uncomment the call to "ExtraTest" in "main" to test your implementation. '%x' should be able to output any unsigned integer from 0x0-0xffffffff. '%d' should be able to output any two's compliment signed integer from -2147483648 to 2147483647.

Use the following code as a template and implement the mysprintf function.
#include <stdio.h>

void __cdecl mysprintf(char *out, const char *fmt, ...) {
	/* 
	your implementation here
	*/
}

void BasicTest() {
	char str[256];
	mysprintf(str, "'%cn%cy a %cri%cn%c%cis %cl%co%ced to%cto%cch %cy %cri%ca%ces.'\n- C%c%c cl%cs%c",
		79, 108, 70, 101, 100, 32, 97, 108, 119, 
		32, 117, 109, 112, 118, 116, 43, 43, 97, 115);
	printf("Simple test\n%s\n", str);
}

void ExtraTest() {
	char str[256];
	mysprintf(str, "0x%x, 0x%x, 0x%x, 0x%x, 0x%x", 3405691582, 3735928559, 0xaabbccdd, 0x0, -1);
	printf("\nExtra test\n%s\n", str);
	mysprintf(str, "%d, %d, %d, %d, %d", -10, 117283, 0x1f, 0, -999999);
	printf("%s\n", str);
}

int main() {
	BasicTest();
	// ExtraTest();
	return 0;
}



Please note that Linux users who use GCC should change the function signature to the following.

void __attribute__((__cdecl__)) mysprintf(char *out, const char *fmt, ...)


Or alternatively just leave out the calling convention, since __cdecl is default.

Some hints for anyone who wants them.
  • The __cdecl calling convention passes arguments on the stack from right to left. Each argument is expanded to 32-bits in 32-bit compilers and 64-bits in 64-bit compilers. By retrieving the address of a known argument, you can easily calculate the location of the rest of the arguments.
  • To negate a two's complement number, negate all the bits and add 1.
  • To convert a binary number to an arbitrary base string of digits, keep dividing the value by the base (until the value is less than 1) and take the remainder (before each division) as a digit. Bare in mind that this will produce a reverse set of digits.


Finally, a correctly implemented "mysprintf" with both "SimpleTest" and "ExtraTest" uncommented should output the following.

Spoiler


edit:
Almost forgot. Please put your solution in spoiler tags for the benefit of others. Thanks.

Is This A Good Question/Topic? 1
  • +

Replies To: DIY sprintf

#2 sepp2k  Icon User is online

  • D.I.C Lover
  • member icon

Reputation: 2089
  • View blog
  • Posts: 3,179
  • Joined: 21-June 11

Re: DIY sprintf

Posted 02 August 2012 - 03:03 AM

I think you should mention somewhere early in the post (possibly the title), that this challenge is Windows specific. So non-Windows people don't have to read the whole post before noticing that this isn't for them.
Was This Post Helpful? 1
  • +
  • -

#3 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 614
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Re: DIY sprintf

Posted 02 August 2012 - 05:42 AM

View Postsepp2k, on 02 August 2012 - 11:03 AM, said:

I think you should mention somewhere early in the post (possibly the title), that this challenge is Windows specific. So non-Windows people don't have to read the whole post before noticing that this isn't for them.

I didn't intend it to be Windows specific, but I see that the syntax of __cdecl is. I modified my post. Thanks for pointing that out.

This post has been edited by Aphex19: 02 August 2012 - 06:02 AM

Was This Post Helpful? 2
  • +
  • -

#4 simeesta  Icon User is offline

  • Deadly Ninja


Reputation: 218
  • View blog
  • Posts: 591
  • Joined: 04-August 09

Re: DIY sprintf

Posted 02 August 2012 - 06:32 PM

Here'e my version. It took look me longer than it should have though. Lots of fun!
Spoiler

Was This Post Helpful? 1
  • +
  • -

#5 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 614
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Re: DIY sprintf

Posted 03 August 2012 - 06:22 AM

Nice solution simeesta. Can't fault it.

Might as well post mine too.

Spoiler

This post has been edited by Aphex19: 03 August 2012 - 09:19 AM

Was This Post Helpful? 0
  • +
  • -

#6 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1138
  • View blog
  • Posts: 7,105
  • Joined: 07-September 06

Re: DIY sprintf

Posted 11 August 2012 - 11:08 AM

Well here is mine. it only took a few hours, but that was still probably too long.

It is in C++ since I couldn't get gcc to compile it (plus I like C++ better :)). I have more case statements than are actually used for the challenge, but when reading up on printf formatting I found them and added them in.

Spoiler

Was This Post Helpful? 1
  • +
  • -

#7 Aphex19  Icon User is offline

  • Born again Pastafarian.
  • member icon

Reputation: 614
  • View blog
  • Posts: 1,873
  • Joined: 02-August 09

Re: DIY sprintf

Posted 14 August 2012 - 05:54 AM

Looks great BetaWar. I really like your m_i2a function, very versatile. I think that function's a good candidate for a snippet. By the way, GCC compiles your solution fine for me (on Windows). Also, I apologise for the delay in replying, I've been stuck in somewhat of a programming loop these last couple of days.

This post has been edited by Aphex19: 14 August 2012 - 05:58 AM

Was This Post Helpful? 0
  • +
  • -

#8 BetaWar  Icon User is offline

  • #include "soul.h"
  • member icon

Reputation: 1138
  • View blog
  • Posts: 7,105
  • Joined: 07-September 06

Re: DIY sprintf

Posted 14 August 2012 - 07:55 AM

Hm, interesting. My gcc kept telling me that it didn't know how to deal with the ... in the function so I finally gave up and moved over to g++ which worked fine. I was compiling it on Arch. The one thing that I haven't checked yet with the m_i2a is whether it actually works with max int or min int when outputting in base 10 (the only base that is currently allowed to print out negative numbers).

I also believe that I can make it more efficient by cutting out one of the for loops completely and moving around some variables (though it will likely make it look uglier). I'll mess around with it when I have a chance and potentially post something as a snippet once I am happy with it :)
Was This Post Helpful? 0
  • +
  • -

#9 jimblumberg  Icon User is offline

  • member icon


Reputation: 3991
  • View blog
  • Posts: 12,315
  • Joined: 25-December 09

Re: DIY sprintf

Posted 14 August 2012 - 08:19 AM

BetaWar The only problem I had compiling your code with C using gcc on Linux is the default argument.
int m_i2a(char* out, unsigned int i, unsigned int base = 10);

C doesn't allow default arguments. Also this __attribute__((__cdec1)), is not required in a Linux program and it looks incorrect, you seem to have a 1 (one) instead of an l (ell) in your cdecl statement.

Edit: I also had to #include <stdbool.h> in order to use the bool return type.

Jim

This post has been edited by jimblumberg: 14 August 2012 - 08:25 AM

Was This Post Helpful? 0
  • +
  • -

Page 1 of 1