2

I am doing a little experimenting on the source code of Doom. I am not very familiar with C, but I am giving it a go. The code I am trying to modify looks like this:

{"sndserver", (int *) &sndserver_filename, (int) "sndserver"},

And I am trying to do this instead (to remove having the same location hardcoded in multiple places):

{"sndserver", (int *) &sndserver_filename, (int) sndserver_filename},

But it gives me the error in the title. I tried declaring another variable in the same location sndserver_filename originates from that does the conversion there but as soon as I try to use it here I get the same error.

Is there a reason that the string will convert to an int, but the variable won't, and is there a way around it?

Context:

#ifdef SNDSERV
extern char*    sndserver_filename; // value is "./sndserver"
extern int  mb_used;
#endif

Struct declaration and initialization

typedef struct
{
    char*   name;
    int*    location;
    int     defaultvalue;
    int     scantranslate;      // PC scan code hack
    int     untranslated;       // lousy hack
} default_t;

default_t   defaults[] =
{
    {"mouse_sensitivity",&mouseSensitivity, 5},
    {"sfx_volume",&snd_SfxVolume, 8},
    {"music_volume",&snd_MusicVolume, 8},
    {"show_messages",&showMessages, 1},


#ifdef NORMALUNIX
    {"key_right",&key_right, KEY_RIGHTARROW},
    {"key_left",&key_left, KEY_LEFTARROW},
    {"key_up",&key_up, KEY_UPARROW},
    {"key_down",&key_down, KEY_DOWNARROW},
    {"key_strafeleft",&key_strafeleft, ','},
    {"key_straferight",&key_straferight, '.'},

    {"key_fire",&key_fire, KEY_RCTRL},
    {"key_use",&key_use, ' '},
    {"key_strafe",&key_strafe, KEY_RALT},
    {"key_speed",&key_speed, KEY_RSHIFT},

// UNIX hack, to be removed. 
#ifdef SNDSERV
    {"sndserver", (int *) &sndserver_filename, (int) "sndserver"},
    {"mb_used", &mb_used, 2},
#endif

#endif

#ifdef LINUX
    {"mousedev", (int*)&mousedev, (int)"/dev/ttyS0"},
    {"mousetype", (int*)&mousetype, (int)"microsoft"},
#endif

    {"use_mouse",&usemouse, 1},
    {"mouseb_fire",&mousebfire,0},
    {"mouseb_strafe",&mousebstrafe,1},
    {"mouseb_forward",&mousebforward,2},

    {"use_joystick",&usejoystick, 0},
    {"joyb_fire",&joybfire,0},
    {"joyb_strafe",&joybstrafe,1},
    {"joyb_use",&joybuse,3},
    {"joyb_speed",&joybspeed,2},

    {"screenblocks",&screenblocks, 9},
    {"detaillevel",&detailLevel, 0},

    {"snd_channels",&numChannels, 3},



    {"usegamma",&usegamma, 0},

    {"chatmacro0", (int *) &chat_macros[0], (int) HUSTR_CHATMACRO0 },
    {"chatmacro1", (int *) &chat_macros[1], (int) HUSTR_CHATMACRO1 },
    {"chatmacro2", (int *) &chat_macros[2], (int) HUSTR_CHATMACRO2 },
    {"chatmacro3", (int *) &chat_macros[3], (int) HUSTR_CHATMACRO3 },
    {"chatmacro4", (int *) &chat_macros[4], (int) HUSTR_CHATMACRO4 },
    {"chatmacro5", (int *) &chat_macros[5], (int) HUSTR_CHATMACRO5 },
    {"chatmacro6", (int *) &chat_macros[6], (int) HUSTR_CHATMACRO6 },
    {"chatmacro7", (int *) &chat_macros[7], (int) HUSTR_CHATMACRO7 },
    {"chatmacro8", (int *) &chat_macros[8], (int) HUSTR_CHATMACRO8 },
    {"chatmacro9", (int *) &chat_macros[9], (int) HUSTR_CHATMACRO9 }

};
fibpi
  • 23
  • 3
  • you have to keep it constant. But using a macro can work. – Jean-François Fabre Apr 06 '17 at 16:34
  • 1
    Both lines don't look kosher. It might have been a viable way in the 90ies for a specific architecture and a specific compiler, but it is most likely a problem in modern C and modern highly optimising compilers. However, without a [mcve] it is hard to tell what the problems are. – too honest for this site Apr 06 '17 at 16:38
  • I'll add in some further context, but it's a large code fragment, which I know is also frowned upon. – fibpi Apr 06 '17 at 16:42
  • @Jean-FrançoisFabre, I have tried using #define int somevar = (int) sndserver_filename but it leads to the same error. – fibpi Apr 06 '17 at 16:46
  • Please review what a [mcve] means! Concentrate on teh first two properties. – too honest for this site Apr 06 '17 at 16:48
  • Possible duplicate of [C: differences between char pointer and array](http://stackoverflow.com/questions/1335786/c-differences-between-char-pointer-and-array) –  Apr 06 '17 at 19:15

2 Answers2

0

What your compiler said: the initializer element is not a constant expression. An address like &sndserver_filename is a constant expression (the linker fills in the symbol's value), but a pointer variable may hold any address--the actual value may depend on the flow of the program, so it can't be used to initialize anything.

From C11 6.7.9 Initialization:

4 All the expressions in an initializer for an object that has static or thread storage duration shall be constant expressions or string literals.

That's a constraint and compilers are required to diagnose violations of constraints.

Jens
  • 69,818
  • 15
  • 125
  • 179
  • 1
    This is a bit over my head, so let me check if I understand the reason that (int) "sndserver" works is because it knows ahead of time the address to look for for that int conversion of "sendserver", however when using (int) sndserver_filename, it doesn't know the address ahead of time? – fibpi Apr 06 '17 at 17:10
  • @fibpi Basically yes. String literals like `"sndserver"` are explicitly allowed, as quoted from C11. Casting the address of a string literal to an `int` still is a constant expression so the compiler is fine with `(int)"sndserver"`. – Jens Apr 06 '17 at 17:40
0

Let me try to offer an explanation.

You declare

extern char*    sndserver_filename;

And then use sndserver_filename in a context where a compile-time address (well, link-time really) is required.

But the value of sndserver_filename is not known at compile time. At the start of the program it's NULL. Later you can assign to it:

sndserver_filename = "aaa";

and assign again:

sndserver_filename = "bbb";

This is not useful for compile-time initialization.

What you really need is a statically allocated array, not a pointer:

extern char sndserver_filename[];

Note the square brackets.

This tells the compiler that there is an array of characters that is statically allocated (the size of the array is not known at this time).

In one of the C files, in global scope, you'll need to do something like this:

char sndserver_filename[256];

to actually allocate the memory (and specify size).

Then you can use sndserver_filename as static initializer, and you can set its value by using strcpy (really, strncpy to make sure you don't copy past array size):

strcpy(sndserver_filename, "bbb");

Note that

sndserver_filename = "aaa";

would now be a compile-time error.

  • Thank you so much for the explanation! I can say that this does indeed work and the duplication link also gives some good background. Really appreciate it. – fibpi Apr 06 '17 at 22:44