best practice on precedence of variable declaration and error handling in C
is there an advantage in one of the following two approaches over the other?
here it is first tested, whether fopen
succeeds at all and then all the variable declarations take place, to ensure they are not carried out, since they mustn't have had to
void func(void) {
FILE *fd;
if ((fd = fopen("blafoo", "+r")) == NULL ) {
fprintf(stderr, "fopen() failed\n");
exit(EXIT_开发者_如何转开发FAILURE);
}
int a, b, c;
float d, e, f;
/* variable declarations */
/* remaining code */
}
this is just the opposite. all variable declarations take place, even if fopen
fails
void func(void) {
FILE *fd;
int a, b, c;
float d, e, f;
/* variable declarations */
if ((fd = fopen("blafoo", "+r")) == NULL ) {
fprintf(stderr, "fopen() failed\n");
exit(EXIT_FAILURE);
}
/* remaining code */
}
does the second approach produce any additional cost, when fopen
fails?
would love to hear your thoughts!
No, it doesn't have any cost. Both examples most likely compile to the same resulting binary. Since variable declaration without assignment doesn't actually do anything in C, it doesn't generate any code. The space available for those variables will be simply skipped over by the stack pointer when needed.
When fopen
fails, the application exits, so nobody really cares if the variables were initialized. There is no cost, because the application has already terminated and the statically allocated memory already freed. As mentioned, the memory isn't even allocated, but it would have if you had set all of them to a default value, like 0. Even if you change this behavior, there is no additional cost.
Also, the second code is probably preferable because it is C89 compliant.
Stack variable allocation for all variables in the function in the same scope likely happens at the top of your function in both cases, so either method is fine.
The variable declarations in your example would not cost extra with most compilers. The only time they might is if they had initialization values which required any non-trivial code to produce.
For example:
void func(const char * filename) {
FILE *fd;
int a, b, c;
size_t z = strlen(filename);
float d, e, f;
/* variable declarations */
if ((fd = fopen(filename, "+r")) == NULL ) {
fprintf(stderr, "fopen() failed\n");
exit(EXIT_FAILURE);
}
/* remaining code */
/* Some code that uses z */
}
In this example strlen
may be called before the attempt to open the file but it's value never used. Placing the call to strlen
after the fopen
could result in better code. This isn't really the best example though, because compilers often know that several functions like strlen
are purely functional (no side effects and only use their arguments to produce results) and could move the call to strlen
below the fopen
itself, but you should get the idea.
It shouldn't make any difference, but you can always compare the assembly output of the two versions to make sure!
Space on the stack is always prepared on function entry, probably in order to get a nice function mapping variable names to base pointer + offset for the compiler/compiler writer. As for initialization, it shouldn't make any difference either. The content is undefined until something is written into the variables in both versions.
In C++ however, you might get into some problems when jumping past initialization (which you aren't doing in the code in your example), but C++ is much more complicated to reason about.
精彩评论