views:

452

answers:

7

I'm trying to add two strings with snprintf but apparently i dont know what i'm doing.

Here is the code block:

char * filename = NULL;

(void)snprintf (filename, sizeof(filename), "%s/%s",
     PATH, FILE);

I also tried:

char * filename = NULL;

(void)snprintf (filename, sizeof(PATH)+sizeof(FILE)+1, "%s/%s",
     PATH, FILE);

PATH and FILE are header defined strings. Occassionally, this code works, occassionally it does not. I'm sure it's some kind of memory issue, what have I done wrong?

EDIT: My issue was for some reason thinking that snprintf allocated memory for you. I'm accepting the answer that cleared that up, since it was my real issue, but I've decided to go with the compile time string concatenation since that is a really nice trick.

+4  A: 

You should allocate memory first.

char * filename = NULL;
filename = malloc(sizeof(PATH) + sizeof(FILE) + 1);
snprintf (filename, sizeof(PATH) + sizeof(FILE) + 1, "%s/%s", PATH, FILE);
Sinan Taifour
sizeof on string literals is dangerous since it yields sizeof pointer not the strlen
dfa
Um... it yields the size of the string literal which includes the null terminator. At least in C++ on a Microsoft compiler.
MSN
... Only if it's a constant at compile time. If it's just a `char*`, it will return 4.
greyfade
A: 

Add this code:

if (!(filename = malloc((LEN + 1) * sizeof(char))))
        return 1;

SET LEN to some value >= PATH and FILE length. Don't forget to deallocate when done.

Brian
+5  A: 

If FILE and PATH are defined in the headers as string literals, then you could concatenate at compile time:

#include <stdio.h>
/* elsewhere in your headers */
#define FILE "foo.ext"
#define PATH "/dir/subdir"

/* After including those headers */
#define FULLPATH (PATH "/" FILE)

int main(int argc, char *argv[]) {
  printf("%s\n", FULLPATH);
}

Or just do it directly when declaring a variable and reference it elsewhere in your code:

#include <stdio.h>

#define FILE "foo.ext"
#define PATH "/dir/subdir"

char fullpath[] = PATH "/" FILE;

int main(int argc, char *argv[]) {
  printf("%s occupies %d bytes\n", fullpath, sizeof(fullpath));
}
Andrew Y
+1: why the downvote?
Christoph
I decided to go with this solution, but I appreciate everyone's input on my original issue, I was under the impression that snprintf allocated memory.
Doldrim
@Bob: for that, there's `asprintf()`, which is a GNU? extension, but it's trivial to implement it yourself: `snprintf(NULL,0,...)` will return the length of the resulting string without producing any output, so you know exactly how much bytes to allocate
Christoph
A: 

I would've just added this as a comment to GMan's answer, but until they merged my account I didn't have the rep to do so. (And now GMan's answer has apparently been deleted, so this makes less sense.)

I think GMan meant

unsigned bufferSize = strlen(PATH) + strlen(FILE) + 2; // "/" and null-terminator

to get the lengths of those constants, not the size of the pointers as slipbull points out. (Edited: Or, if sizeof(STRINGLITERAL) gives the size of the data, including the ending null, then GMan's code would still work, for string constants.)

Although, I think if you know the exact lengths of PATH and FILE (as bufferSize) you shouldn't need snprintf(), just sprintf() would be safe. But there's no harm in using snprintf() since you need the bufferSize for the allocation, anyway.

Edited: Or, Andrew Y may have an even better approach (for compile-time string constants), if that can apply for you.

Rob Parker
+2  A: 

You know the length of the string at compile-time, so there's no need for dynamic allocation.

static char filename[sizeof(PATH) + sizeof(FILE)];
snprintf(filename, sizeof(filename), "%s/%s", PATH, FILE);

But as the strings you want to join are most likely given as literals, there's no need for snprintf() at all:

static const char filename[] = PATH "/" FILE;

Also, because there's some confusion about the string's length:

strlen(PATH) + strlen("/") + strlen(FILE) + 1
= (sizeof(PATH)-1) + 1 + (sizeof(FILE)-1) + 1
= sizeof(PATH) + sizeof(FILE)
Christoph
+1 for the 'const'. Though your first snippet has two subtle bugs, which do not matter in this case, but set a questionnable use example if those two lines are decoupled: filename[] is one byte larger than needed and using of sizeof() in snprintf()'s second argument is incorrect and can lead to non-writing of the null terminated character.
Andrew Y
@Andrew: you're wrong: I did the math in the comments to [this answer](http://stackoverflow.com/questions/1155346/bug-trying-to-add-two-strings-with-snprintf/1155385#1155385); also, from the glibc manual entry about `snprintf()`: "The trailing null character is counted towards this limit, so you should allocate at least size characters for the string s."
Christoph
@Christoph: Ah, indeed! To expand on how I thought of those 'bugs': I missed the '/' when thinking about the first 'bug'; and mixed up in the head the 'snprintf' with 'strncpy' for the second one. If these two lines were far enough from each other, that'd be a reasonable oversight, but in this case it's unforgivable glitch on my side :-)
Andrew Y
A: 
dfa
A: 

On some systems, you can use asprintf:

char *filename = NULL;
asprintf(&filename, "%s/%s", PATH, FILE);

As far as I know, asprintf is a Glibc invention, but has proven so useful that several other libcs have also implemented it.

ephemient