Variable Argument Lists/sprintf

Status
Niet open voor verdere reacties.

sbeckers

Gebruiker
Lid geworden
15 aug 2007
Berichten
5
Hey,

Ik ben bezig met een console te maken voor een spel, en natuurlijk zal ik printf moeten gebruiken. MAAR printf kan soms problemen geven als in een string het karakter % in voorkomt. En het %-teken is te belangrijk in een boodschap, ik kan het niet zomaar wegfilteren. Voorbeeld: de Quake 3 engine doet dit wel weg, en elke % wordt vervangen door een punt. Je kunt dus geen % in Quake 3 gebruiken in je naam of in een chatbericht. Ik moest dus mijn eigen printf-functie schrijven, en ik heb het ook gedaan.
Het verschilt in printf dat het niet '%' als speciaal karakter gebruikt maar '\1', of 1, wat nooit wordt gebruikt. Het returnt een char *, en kan dus in andere functies worden gebruikt, bv. bla(strf("int: \1i", 5)); als bla een char * als argument heeft.

Code:
char *strf(char *fmt, ...)
{
	va_list ap;
	int i;
	float f;
	bool b;
	char tmp[2000] = {'\0'};
	const char *p=fmt;
	char ret[2000] = {'\0'};
	va_start(ap,fmt);
	while (*p){
		if (*p == '\1'){
			memset(tmp, 0, 2000);
			p++;
			switch(*p)
			{
			case 'i':
				i = va_arg(ap,int);
				sprintf(tmp, "%i", i);
				strncat(ret, tmp, 2000 - strlen(ret));
				break;
			case 'f':
				f = va_arg(ap, float);
				sprintf(tmp, "%f", f);
				strncat(ret, tmp, 2000 - strlen(ret));
				break;
			case 'b':
				b = va_arg(ap, bool);
				sprintf(tmp, "%i", (int)b);
				strncat(ret, tmp, 2000 - strlen(ret));
				break;
			case 's':
				strncpy(tmp, va_arg(ap, char *), 2000);
				strncat(ret, tmp, 2000 - strlen(ret));
				break;
			default:
				strncat(ret, p, 1);
			}
		} else {
			strncat(ret, p, 1);
		}
		p++;
	}
	va_end(ap);
	return ret;
}

Dit werkt perfect met de volgende code:

char *test = strf("Integer: \1i\nBool: \1b\nString: \1s\n", 5, true, "bla");

Maar om de een of andere reden werkt dit niet met floats. (Dus dit:

char *test = strf("Float: \1f\n", 2.1f);

werkt niet) Hoe komt dit?

En nog een vraag: hoe kan men floats naat strings omzetten, zonder sprintf te gebruiken? Ik weet dat voor integers de niet-ANSI itoa bestaat, maar voor floats?
 
Laatst bewerkt:
je zou de float om kunnen zetten naar 2 integers, hier strings van maken en die strings aan elkaar plakken
en probeer sprintf te gebruiken ipv printf in je float functie weet niet of het helpt maar valt iig op
 
Die printf is inderdaad fout, ik had na 'sprintf...' printf ingevoegd om te kijken wat de waarde van die float bleek te zijn en die was altijd -0.000.... Ik had de sprintf per ongeluk verwijderd ipv van printf. Even aangepast. Maar ook met sprintf werkt het niet (zoals het er nu staat).

Over die 2 ints, je bedoeld waarschijnlijk 1 voor en 1 na de komma. Maar wat dan met wetenschappelijke notatie (bv. 5.2e2)? Het moet normaalgezien werken met een float.

En ik heb ook geprobeerd met double: en het werkt ook niet met doubles.
 
Laatst bewerkt:
Als het per se moet zou je de float kunnen wegschrijven in een bestand en dan als string inlezen maar dat lijkt me een overdreven omweg.. Voor de rest weet ik niet hoe het moet zonder printf() enzo..
 
ik bedoel inderdaad 1 voor en 1 na de komma en hoe bedoel je wetenschappelijke notatie? wil je een float veranderen in een string waarin hij wetenschappelijk staat? dat kan natuurlijk alleen is het meer typewerk

WAT heb je geprobeerd met een double?

edit: welke header gebruik je voor die va_list, va_arg enzo?
 
Laatst bewerkt:
aangezien een float een preciezie van 7 cijfers heeft kun je dat niet goed doen

ik gebruik altijd doubles omdat die een grotere preciezie hebben maar floats kun je even makkelijk omzetten naar strings met dit stukje code wat ik geschreven heb:

Code:
#include <iostream>
#include <iomanip>

using namespace std;

int main(int argc, char *argv[])
    {
    cout << setprecision(8);
    float myf = 10.112347;
    char myf_s[20];
    char myf_s2[20];

    int postcomma = static_cast<int>(myf/1*1);
    int pastcomma = static_cast<int>((myf-static_cast<int>(myf/1000000*1000000))*1000000);
    cout << myf << endl << postcomma << endl << pastcomma << "\n\n\n\n";

    itoa(postcomma, myf_s, 10);
    itoa(pastcomma, myf_s2, 10);
    cout << myf_s << endl << myf_s2;
    cin.get();
    }

uitvoer:
Code:
10.112347
10
112346



10
112346
de bovenste zijn getallen de onderste charlists

houd er rekening mee dat strings wat anders is dan een character list maar aangezien jij het over itoa had zul je dit wel bedoelen, het omzetten zou toch geen probleem mogen zijn
 
Maar wat ik bedoel als je 12.0001 die 0001 omzet in een integer maakt hij er dan geen 1 van? Dan krijg je in je string 12.1..
 
houd er rekening mee dat strings wat anders is dan een character list

Ik gebruik nooit de C++ string class. Als ik het over strings heb, heb ik het dus over character arrays. Ik gebruik eigenlijk alleen C voor dit project.

Ik heb de juiste headers gebruikt. Voor dat stukje code heb ik string.h, stdio.h, stdlib.h en stdarg.h.

Als ik doubles gebruik geeft die altijd 0 terug, zonder dat het programma crasht. code:
Code:
int i;
float f;
+double d;
bool b;
char tmp[2000] = {'\0'};

...

 strncat(ret, tmp, 2000 - strlen(ret));
break;
+case 'd':
+ d = va_arg(ap, double);
+ sprintf(tmp, "%d",d );
+ strncat(ret, tmp, 2000 - strlen(ret));
+break;
case 'b':
 b = va_arg(ap, bool);

En Murdocki, inderdaad: werkt die code ook met bv. 2.01? Wat geeft die dan? "2.1" of 2.01"?

edit: ik heb het stukje code geprobeerd met 10.01 en die 1000000 vervangen door 100 en de output na de komma is dus 1, dus samengeplakt is dat 10.1. Dus dat stukje code werkt niet altijd.
 
Laatst bewerkt:
er bestaan in C atoi en atof (ascii to float) functies. Die zijn echter windowsspecifiek denk ik. Ik weet ook niet meer in welke header ze zitten (google is je vriend).

Persoonlijk gebruik ik voor conversies deze 'universele' manier met stringstreams (echter wel een onderdeel van C++)
Code:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;

template <class doelType, class inputType>

doelType convert(const inputType & t)
{
    stringstream s;   //een stringstream aanmaken voor zowel lezen als schrijven
    s << t;           //de input in de stringstream plaatsen
    doelType resultaat;  //een variable van het juiste output-type aanmaken
    s >> resultaat;      //de opgeslagen input, naar de output converteren
    s.str("");  //de streaminhoud wissen
    return resultaat;
}

int main()
{
    string s = "18.67";
    double d = convert<double>(s); //geeft d de waarde 18.67
    d -= 2;     //je kan nu gewoon rekenen met d
    cout << "d = " << d << endl
         << "Geef een integer in aub: ";
    cin >> s;    //in de string inlezen
    if (cin.peek()){  //inputbuffer leegmaken
        string rest;
        getline(cin, rest);
    }
    int i = convert<int>(s);
    i -= 2;
    cout << "i = " << i << endl;
    cin.get();
    return 0;
}

--Johan
 
Johan, ik heb je code geprobeerd met dit:

strncat(ret, convert<string>(f).c_str(), 2000 - strlen(ret));

ipv

sprintf(tmp, "%f", f);
strncat(ret, tmp, 2000 - strlen(ret));

Maar het probleem blijft hetzelfde. Met andere woorden, het ligt niet aan sprintf, maar aan de variable argument list.

edit: ik heb het zowal met GCC als met MS Visual C++ gecompileerd, en beide geven geen foutmeldingen maar het programma crasht toch.
 
Laatst bewerkt:
Status
Niet open voor verdere reacties.

Nieuwste berichten

Terug
Bovenaan Onderaan