[MEGOLDVA] snprintf bug vagy feature?

 ( Kambus | 2009. december 11., péntek - 19:14 )

Az alabbi kodot Linuxon futtatva " 24m" kimenetet kapom "Uptime: 3h 24m" helyett.
Mashol (*BSD, OpenSolaris) teljessen jol mukodik, csak Linuxon csinalja ezt.

Nalatok is ilyen es ha igen ennek van valami oka vagy csak siman bug?

#include <stdio.h>
#include <string.h>

#define addtoline(line, t, c) \
    snprintf(line, sizeof(line), "%s %d%c", line, t, c);

int
main()
{
        char line[256];

        strncpy(line, "Uptime:", sizeof(line));
        addtoline(line, 3, 'h');
        addtoline(line, 24, 'm');

        puts(line);

        return 0;
}

PS: archlinux-on glibc 2.11-el probaltam es csak snprintf csinalja ezt, sima sprintf-el jo.

Hozzászólás megjelenítési lehetőségek

A választott hozzászólás megjelenítési mód a „Beállítás” gombbal rögzíthető.

Nem szeretem a #define-okat. Ráadásul itt sok értelme sincs, mivel a típusok adottak. Nem lehetne inkább inline függvény? Lehet, hogy úgy baja sem lesz. Én nem szeretem ezt a pre-processzorra bízni, ugyanis kevésbé szabványos, mint a szabványos nyelvek. :)
--
http://www.naszta.hu

Koszi a tippet!

Itt nem define-al van a baj, ha csak siman egymas utan rakom snprintf-eket akkor is ugyan ezt produkalja.

Utanna neztem inline fv-knek, de ugylatom ez csak "gnuc" only. Mas compilerrel hogy boldogul?

Én Visual Studio-t (is) használok, ott 2005 és 2008 is fordítja.
--
http://www.naszta.hu

A sprintf-et egyébként sem erőltetném, mert majd minden platformon másképp van egy kicsit.

Kb. ezt csinálnám inkább:


#include <cstdlib>
#include <iostream>

int main()
{
std::cout << "Uptime: " << 3 << "h " << 24 << 'm' << std::endl;
}

--
http://www.naszta.hu

Bocsi, azt nemirtam, hogy c-ben van a kod nem c++.
De azert koszi a valszt.

Én meg kevertem a sprintf-et a stricmp-el... :P Bár még mindig úgy rémlik, hogy van vele baj...

/* Define case insensitive string compare for Windows. */
#if defined( _WIN32 ) && !defined(__CYGWIN__)
# if defined(__BORLANDC__)
# define STRCASECMP stricmp
# else
# define STRCASECMP _stricmp
# endif
#else
# define STRCASECMP strcasecmp
#endif

--
http://www.naszta.hu

Ugylatszik akkor ezt a bajt meg is tapasztaltam. :)
A baj az, hogy nemlatok igazi alternativat snprintf kivaltasara ugy altalanossagban.

Elotte amugy igy oldottam meg, csak amikor NetBSD-n atirkaltam a kodot gondoltam elegansabb egyetlen egysoros makroval letudni a dolgot, Linuxon meg neztem, hogy mibaja.

void
add_to_uptime(char *line, char c, int i)
{
        char tmp[8];

        snprintf(tmp, sizeof(tmp), " %d%c", i, c);
        strncat(line, tmp, sizeof(line) - strlen(tmp));
}

Muszáj C-ben maradni?

Ha stdout-ra írsz ki, akkor sima printf nem jó?

Erre emlékeztem: http://www.gnu.org/software/hello/manual/gnulib/snprintf.html
--
http://www.naszta.hu

Igen, ez tulkepp egy weechat plugin resze, tudom sokkal egyszerubb lenne scriptekkel, csak kivancsisagbol meg tanulsagbol irom c-ben.
Kesobb valamikor lehet majd c++-al is foglalkozni fogok, de egyelore maradok c-nel.

char tombbe kell irjak, sprintf-el jolmegy, csak hat az kevesbe biztonsagos, meg kivansi voltam snprintf-el mertnem jo.

feature. Ha jol tudom a C az ?osszes? FIXME masolos fuggvenyrol azt mondja hogy ha a forras osszeer a cellal, a mukodes meghatarozatlan.
==
`Have some wine,' the March Hare said in an encouraging tone.
Alice looked all round the table, but there was nothing on it but tea.

En is erre gondoltam elosszor, de mint mondtam sprintf megeszi siman, illetve mas rendszerek alatt jo snprinfel is.

ez boven belefer a meghatarozatlanba
==
`Have some wine,' the March Hare said in an encouraging tone.
Alice looked all round the table, but there was nothing on it but tea.

Ott a pont. :)

-- beneztem valamit --

a memmove kivételécel.

> Sol omnibus lucet.

A problema az, hogy a C99 szabvanynak mar nem felel meg ez a fajta programozasi stilus ;) Az eredmeny definialatlan, amennyiben a cel es forras atlapolodik. Ha mar mindenkepp makrot akarsz, akkor ird at mondjuk igy, bar nekem igy sem tetszik lol

#define addtoline(line, t, c) \
    snprintf(line + strlen(line), sizeof(line) - strlen(line), " %d%c", t, c);

Huu koszi szepen!
Nem ragaszkodok mindenaron makrokhoz, csak szepiteni akartam kicsit a kodomon, de igy mar valoban kevesbe szep.

Az viszont erdekelne, hogy Te hogyan oldanad ezt meg szernted is elegansan?

Függvénnyel:

inline void akarmi( char * s, int i, char c )
{
...
}

--
http://www.naszta.hu

Megegyszer koszonom mindenkinek a segitseget.
Azt hiszem igoor megoldasat fogom hasznalni.

Mint mondtuk, nem javasoljuk a makrók használatát! Ne szokj hozzá! Néhony ok:

- makrón nem fut a debuger,
- nincsenek névterek, keveredhetsz másik könyvtárral a nevekben,
- ennek folyománya: nem várt hatások,
- nincs tipus ellenőrzés, általános függvényekre ott a template,
- ha konstanst adsz meg vele: nem változó nevét látod, hanem csak egy számot, degug-kor,
- nem is hatékonyabb, mert inline függvényekkel elérhető az, hogy a függvényhívásnak se legyen költsége.
--
http://www.naszta.hu

Koszi a tippeket, majd igyekszem betartani. :)
inline fvkkel az a bajom, hogy pl OpenSolaris-on default cc-vel nemfordul le, igy nemtunik tul kompatibilisnek.

> inline fvkkel az a bajom, hogy pl OpenSolaris-on default cc-vel nemfordul le, igy nemtunik tul kompatibilisnek.

#if nemtommi
# define INLINE inline
#else
# define INLINE
#endif

INLINE func(args)

vagy valami ilyesmi.

Ez esetben tényleg makrózni kell, sajnos. Azért én nyomkodnám egy kicsit a torkukat valami dev fórumon, hogy ha már nem támogatják (miért is nem?), legalább a pre-processzoruk helyettesítse be a nagy semmit az inline helyére. :)
--
http://www.naszta.hu

Ha tenyleg ezt kell megoldani akkor miert nem egyetlen snprintf-et hasznalsz ? Gyorsabb is fenti kodnal, egyszerubb is, es a valasztott javitastol is joval gyorsabb.
(Ha utanna megy streambe, akkor nem is stringbe kene nyomni hanem kozvetlenul ki)


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Direkt egyszerusitettem le a problemat snprintf-re koncentralva. Egyetlen snprintf azert nem jo, mert nemcsak percek es orak, hanem napok es hetek is vannak ami persze attol fugg mennyi ideig megy a gep. Ezt vegig if-ezni szerintem megintcsak csunya lenne.

Itt a teljes kod: sysinfo.c

Szivesen fogadok egyeb kritikakat, csak kerlek vegyetek figyelembe, hogy meg kezdo vagyok es a plugin is fejlesztes/teszteles alatt all. :)

Mint ahogy lentebb is irtam strlen/strncat hasznalta kerulendo dolog CPU hasznalat miatt, ha lehetosegunk van az alap string hosszat megjegyezni, es ugy boviteni.
Az ilyen kiirjuk vagy nem irjuk ki dolog kellemetlen lehet a hasznalonak, mi van, ha valaki mondjuk partnerei uptime-jaibol statistikat akar gyartani, nem lesz konnyu dologa ertelmezni.
Nem biztos, hogy jo otlet stringeket kozvetlenul gyartania az info gyujto fuggvenyekben. Egyreszt lefoglalasz eleg "sok" memoriat neki, masreszt nem kulonul el a string formazastol (ami kesobb lehet akar parameter is). Lehet nem volna hulyeseg masutt stringge konvertalni.
Nem tudom menyire draga a weechat_printf, weechat_command hivasa, de ha nem dragak akkor lehet erdemesebb sok kissebb stringet beljuk tolni, mint egy nagyot neked osszerakni.

Komoly kritkikam nincs a fentieket is ignoralhatod. Csak ilyen aprobb szemelyes izles dolgok.
Szerintem jo.


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Koszi az eszreveteleket.
Meggondolom strncat es tarsai levaltasat, az ilyen komolyabb dolgokba mint statisztika nem gondoltam meg bele, erdekes otlet.

weechat_printf meg annyira nem lenne problema, de weechat_command nem csak teljesitmenyben hanem "kepernyo helyben" is tobbe kerul. Gondolj bele, hogy egyszerre tobben elkezdenek epenist lengetni, hogy kinek van jobb gepe/leetebb oprendszere vagy akarmi.. :) hamar szetfloodolnak a csatit. Szerintem, ha eltud ferni egy sorban akkor igy sokkal diszkretebb, mint tobbre szetszedni.

Megmondom oszinten, en ezt (eroforraspazarlo modon, de) ugy inteznem el, hogy az addtoline egy fuggveny lenne, amin belul egy tmp valtozoba snprintf-elnek, majd (opcionalisan reallokalva az atadott line-t, bar ha belefer, akkor nem szukseges, de ekkor kell meretellenorzes is) strncat-tal hozzafuznem a line vegere az elejet. A tmp valtozonak teljesen jo az alloca is, itt egeszen biztosan el fog ferni a stacken az a par karakter.

Arra figyelni kell, hogy a fuggveny argumentuma immar akkor nem char* hanem char** es a stringet &line formaban kell atadni. Valamint a sor nem lehet const (de ez eddig sem volt lehetseges).

Ja, es sizeof helyett strlen.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

A megoldasod kevesbe lesz erroforras (CPU) pazarlo, ha nem strncat -ot hasznalsz, hanem megjegyzed string tenyleges hosszat ill. lefoglalt helyet, es memcpy-vel teszed a vegere...
Mondanam a tovabbi/mas tipekket , de a vegen lehet C++ Std::String -hez jutnank C-ben:)


Amit nem lehet megirni assemblyben, azt nem lehet megirni.

Igazad van, am en - bunos modon - sosem vonzodtam kulonoskeppen a mem* nevu fuggvenyekhez. Hacsak lehet, sztringkezelesre a str* fuggvenycsaladot hasznalom.
--

()=() Ki oda vagyik,
('Y') hol szall a galamb
C . C elszalasztja a
()_() kincset itt alant.

http://www.kernel.org/doc/man-pages/online/pages/man3/sprintf.3.html

Idézet:
Some programs imprudently rely on code such as the following

sprintf(buf, "%s some further text", buf);

to append text to buf. However, the standards explicitly note that the results are undefined if source and destination buffers overlap when calling sprintf(), snprintf(), vsprintf(), and vsnprintf(). Depending on the version of gcc(1) used, and the compiler options employed, calls such as the above will not produce the expected results.

Ehh, hogy ezt hogy nem vettem eszre. :(

> Ehh, hogy ezt hogy nem vettem eszre. :(

Ha nem akartad volna kétszer elvégeztetni ugyanazt a munkát, akkor nem lett volna semmi probléma. :-)