Reputation: 890
It has been pointed out in quite a few places that to concatenate multiple strings it is more easy and also elegant to use snprintf instead of running multiple runs of strcat. But I've been getting incorrect results because of that. In this little snippet below which is part of program to convert 12hr to 24hr time, I first separate the hr,min and sec part and after converting the time, re-join the strings again.
here's the snippet
/*
char *timestr = strcat(hr, min);
timestr = strcat(timestr, sec);
timestr = strcat(timestr, AMPM);
*/
///*
char *timestr = (char *)malloc(20);
snprintf(timestr, sizeof(timestr), "%s:%s:%s", hr,min,sec);
//*/
the first version works fine but the bottom one doesn't. Here's the expected output which is what I see with strcat
(gdb) p timestr
$1 = 0x7fffffffe8cd "190545PM"
this is what I see with snprintf
(gdb) p timestr
$1 = 0x801007040 "19:05:4"
I am unable to fathom what is going wrong. Isn't printing to the stream supposed to take care of the trailing NULL character?
Upvotes: 0
Views: 1082
Reputation: 2788
You may use another sibling function, asprintf(...)
, to allocate a proper piece of memory automatically:
char* timestr;
asprintf(×tr, "%s:%s:%s", hr,min,sec);
The original solution doesn't work because sizeof(char*)
== 8 (bytes) rather than 20, it's the pointer size itself and it doesn't depend on contents pointed by that or this pointer.
Upvotes: 0
Reputation: 30926
In case of snprintf
if the second argument is n
then manual says
If the resulting string would be longer than n-1 characters, the remaining characters are discarded and not stored, but counted for the value returned by the function.
So what is the case here for n
? sizeof(temp)
is sizeof(char*)
which is not the same as 20 bytes you allocated. You can't apply sizeof
to a pointer to get the memory size it points to. (On a typical system sizeof(char*)
varies).
Solution:
snprintf(timestr, 20, "%s:%s:%s", hr,min,sec);
or if you keep it in some variable (which is recommended).
const int size = 20;
int ret = snprintf(timestr, size, "%s:%s:%s", hr,min,sec);
if( ret < 0){
fprintf(stderr,"Error in snprintf operation\n");
exit(1);
}
if( ret >= size){
fprintf(stderr,"Error: more characters than expected\n");
exit(1);
}
Check the return value of snprintf
to understand whether same error occured in that case negative number is returned.
Upvotes: 2
Reputation: 13580
sizeof(timestr)
doesn't return you the number of bytes that you've allocated,
it returns you the number of bytes that a pointer to char
needs to be stored
in memory. The correct call would be:
char *timestr = malloc(20);
snprintf(timestr, 20, "%s:%s:%s", hr,min,sec);
Note that snprintf
will try to write at nost 20 characters (including the
0-terminating byte). If the resulting length is not large enough, timestr
will
be cropped. So it's better to get the number of bytes that you would need by
passing NULL
and size 0 to snprintf
. In this case it will return how many
character would be required. So
int len = snprintf(NULL, 0, "%s:%s:%s", hr, min, sec);
if(len < 0)
{
fprintf(stderr, "could not execute snprintf\n");
return; // or whatever, do not continue
}
char *timestr = malloc(len + 1);
if(timestr == NULL)
{
// error handling
return; // or whatever, do not continue
}
sprintf(timestr, "%s:%S:%s", hr, min, sec);
would give you the exact amount of bytes for the string.
Upvotes: 1