Both approaches are used in the industry. In your example, people could make an assumption about the maximum size for the resulting filename and use a local array this way:
for (int i = 0; i <= 10; i++) {
char filename[1024];
snprintf(filename, sizeof filename, "%s%d", fn. i);
FILE *f = fopen(filename, "w");
if (f != NULL) {
// read f, do something useful ...
fclose(f);
} else {
// report the error?
}
}
Note that the truncation can be detected with if (snprintf(filename, sizeof filename, "%s%d", fn. i) >= (int)sizeof filename)
.
If no assumption should be made about the filename length or if the filename composition method is more complicated, returning an allocated string may be a more appropriate option, but testing for memory allocation errors should also be done:
for (int i = 0; i <= 10; i++) {
char *filename = concat(fn, i);
if (filename == NULL) {
/* handle the error */
...
// break / continue / return -1 / exit(1) ...
}
FILE *f = fopen(filename, "w");
if (f == NULL) {
/* report this error, using `filename` for an informative message */
} else {
// read f, do something useful...
// keep `filename` available for other reporting
fclose(f);
}
free(filename);
}
If you are not ready to perform all this bookkeeping, you should probably use a different language with more elaborate object life cycle management or with a garbage collector.
Finally, using C99 compound literals, you could define concat
to fit your simplified use case:
char *concat(char *dest, const char *s, int b) {
sprintf(dest, "%s%d", s, b);
return dest;
}
#define CONCAT(a, b) concat((char[strlen(a) + 24]){""}, a, b)
CONCAT
defines an unnamed local variable length char
array of the appropriate size and constructs the concatenation of string a
and int b
into it. I changed the case to uppercase to underscore the fact that a
is evaluated twice in the expansion, and thus should not be an expression that involve side-effects.
You could use this macro as expected in your second code fragment:
FILE *f;
char *tmp;
for (i = 0; i <= 10; i++) {
f = fopen(CONCAT(fn, i), "w");
// read f, do something useful ...
fclose(f);
}
I probably would not recommend this type of usage, but this is only my opinion.