views:

121

answers:

3

I have a struct defined in a header as follows:

#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
   short nr;
   short strategy;
   char tx[LC_ERR_LEN];
} LC_ERRMSG;

Which I use in my code as such:

LC_ERRMSG err;
char *szError;
szError = strerror(sStatus);
snprintf(err.tx,LC_ERR_LEN," %s - %s",szFilename,szError);
/* do something with our error string */

That works. If however, I declare LC_ERRMSG err; globally - i.e. outside the function it is used, or even extern LC_ERRMSG err; (which was my original intention, as I would want to be able to read out the error status in a central location), the code segfaults at the snprintf call.

Can you give me any clues why?

ddd tells me that the memory is initialized to either all zeroes when declared globally, or at least initialized and readable when declared extern. The values szFilename, szError and LC_ERR_LEN are all correct and meaningful.

+2  A: 

If you have:

// structs.hpp
#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
    short nr;
    short strategy;
    char tx[LC_ERR_LEN];
} LC_ERRMSG;

and:

// main.cpp
#include "structs.hpp"
LC_ERRMSG err;

int main()
{
    // ...

    char *szError;
    szError = strerror(sStatus);
    snprintf(err.tx, LC_ERR_LEN, "%s - %s", szFilename, szError);
}

then this should work. However, if you switch the second line of main.cpp to:

extern LC_ERRMSG err;

then you need to make sure that the storage for err is compiled into one of your object files. For example, you could compile this source:

// globals.cpp
#include "structs.hpp"

LC_ERRMSG err;

and link the resulting globals.o into main.o.

Either of the approaches should not cause a segmentation fault. If you are getting a segmentation fault, then the problem could be that LC_ERR_LEN has a different value when globals.cpp is being compiled than when main.cpp is being compiled. Or, perhaps szFilename or szError are NULL/bad. The printf family cannot print NULL or bad pointers with format flag %s; the following code causes a segmentation fault:

#include <stdio.h>

int main()
{
    printf("%s\n", NULL);
}

EDIT: I thought of another potential cause of the problem. You could have a symbol clash if you are using a C compiler as err is a symbol that could be in use as the name of several, different global variables in a large project. If you are using a C++ compiler, then the name mangling process should ensure that each err has its own symbol. Just make sure that you are compiling as C++.

Daniel Trebbien
Thanks for your answer and maxwellb's. The worksforme maxwellb posted is pretty much what I had in my code originally, before I tried to narrow down the problem by declaring "err" elsewhere. I am compiling as c++ with g++ too. No luck so far.What stumps me is that I have no idea how err.tx could be corrupted, even if there were some other code using it in the meantime. It is not being dynamically allocated, so that I could have free'd it accidentally.
relet
Using your debugger, would you verify that `szFilename` and `szError` are non-`NULL` at the `snprintf` line and that `szFilename` is properly null-terminated? Also, what is the address `err.tx`?
Daniel Trebbien
+2  A: 

+1 for Daniel's Answer. Here's a Works-for-me. Works for you? Upvote Daniel's answer.


// structs.hpp
#define LC_ERR_LEN 300
typedef struct dLC_ERRMSG {
    short nr;
    short strategy;
    char tx[LC_ERR_LEN];
} LC_ERRMSG;

// error.cpp
#include "structs.hpp"

LC_ERRMSG err;

// main.cpp
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "structs.hpp"
extern LC_ERRMSG err;

int main()
{
    // ...
    char *szFilename = "EXAMPLE.LOG";
    int sStatus = 0;
    char *szError;
    szError = strerror(sStatus);
    snprintf(err.tx, LC_ERR_LEN, "%s - %s", szFilename, szError);

    printf( "err.tx: %s", err.tx );
}

// Output:
err.tx: EXAMPLE.LOG - No error
maxwellb
+1  A: 

Your linker can simply throw away the symbols, which it believes are not used (the GNU linker does so). In this case you can explicitly link the object file with that symbol.

With C++ you can not control the order of initialization of global objects defined in other compilation units without any additional efforts (see http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.12).

Use the "construct on first use" idiom, which simply means to wrap your static object inside a function.

Sergey
Thanks a lot! That solved it.
relet