1

I need to include some typedef definition in two source files:

typedef double mat[MATSIZE][MATSIZE] ;

so I created def.h which includes:

#ifndef DEF_H
#define DEF_H
typedef double mat[MATSIZE][MATSIZE] ;
#endif

and in both .c files I included it by:

in the first file processed:

#define MATSIZE 4
#include "def.h"

in the second .c file:

extern int MATSIZE;
#include "def.h"

But I get

error: variably modified ‘mat’ at file scope

What did I do wrong?

qwerty
  • 175
  • 4
  • 15
  • You have to define `MATSIZE` also. See http://stackoverflow.com/questions/1712592/variably-modified-array-at-file-scope – tuergeist Dec 26 '11 at 16:08
  • I have ' #define MATSIZE 4 #define MATLENGTH MATSIZE*MATSIZE #include "def.h" ' in one of these .c files which is processed first, and ' extern int MATSIZE; #include "def.h" 'in the second – qwerty Dec 26 '11 at 16:10
  • Where? Why don't you #define it in def.h, where it belongs? – wildplasser Dec 26 '11 at 16:13
  • I've understood it's better to define in source files and not header – qwerty Dec 26 '11 at 16:17
  • 1
    One .c file says MATSIZE is a constant. The other says it is an (external) int. One of them is lying... – wildplasser Dec 26 '11 at 16:18
  • in file1 MATSIZE is constant. For file2 to know what it is, it has to declare it as extern, no? – qwerty Dec 26 '11 at 16:22
  • 2
    No. In file1 it is a *preprocessor* definition. It instructs the preprocessor to replace the string 'MATSIZE' by the string '4' every time that it will occur. (so the compiler will effectively see `typedef double mat[4][4];` when scanning the.h file). The second .c file declares an (external) int with the **name** MATSIZE (and yet unknown value), so the compiler will effectively see `typedef double mat[MATSIZE][MATZIZE];` when scanning the .h file, and think that MATSIZE is an (external) int with unknown value; this is not acceptable. – wildplasser Dec 26 '11 at 16:28
  • If you are using two different .c files defining it in .h is fine. – Ahmed Masud Dec 26 '11 at 16:29
  • So, the only way for implementing this is to define both MATSIZE and typedef in header file and include it in both source files? – qwerty Dec 26 '11 at 16:34
  • 1
    Yes, more or less. What you want, is a guarantee that the typedef for 'mat' yields **exactly the same type every time its is included** by another (.c) file. The simplest way to do that is to keep the `#define MATSIZE xxx` in the same (.h file). – wildplasser Dec 26 '11 at 16:40
  • It isn't a good duplicate target (it is for Objective-C). The one for C is *[Variably modified array at file scope in C](https://stackoverflow.com/questions/13645936/variably-modified-array-at-file-scope-in-c)*. – Peter Mortensen Jul 29 '23 at 09:34
  • 1
    Does this answer your question? [Variably modified array at file scope in C](https://stackoverflow.com/questions/13645936/variably-modified-array-at-file-scope-in-c) – Toby Speight Jul 29 '23 at 12:06

3 Answers3

2

The concept of Variable Length Arrays (VLAs) is "new" to C99.

Before C99 you could only use real constants for specifying sizes of arrays. The following code was illegal either in block scope or file scope.

const int size = 42; /* size is not a real constant */
int boo[size];

C99 introduced VLA for block scope. The example code above is legal C99, provided it happens in block scope. Your definition is at file scope and therefore invalid.


Also it is a really bad bad idea to have the same typedef refer to two different types.

pmg
  • 106,608
  • 13
  • 126
  • 198
  • There are no "variably modified arrays" in C. There are variable length arrays, though. The error message does not say something about the latter, it would seem. – Jens Dec 26 '11 at 16:27
  • Ooops ... got carried away by the original message. I meant variable length arrays. Post edited. Thank you @Jens – pmg Dec 26 '11 at 16:29
2

When arrays are defined outside a block (at file scope or global scope), the size must be known at compile time. That means that the each dimension on the array must be a constant integral value (or, for the first dimension, it could be implied by the initializer for the array).

If you used a C89 compiler, you might get a message about non-constant array dimensions. GCC 4.6.1 gives the 'variably modified mat at file scope' message.

C99 added Variable Length Arrays to the repertoire, but they can only appear inside a block or an argument list, where the size can be determined at runtime.

So, in a function, you could legitimately write:

extern int MATSIZE;

extern void func(void);

void func(void)
{
    typedef double mat[MATSIZE][MATSIZE];
    // ...
}

(The function declaration is needed to avoid the warnings like:

warning: no previous prototype for ‘func’ [-Wmissing-prototypes]

since I habitually compile with -Wmissing-prototypes.)

The other issue is that in one file, MATSIZE is a compile-time (#defined) constant; in the other, there is apparently an integer variable MATSIZE. These are completely unrelated. The types therefore are different.


typdef is block scoped

wildplasser is concerned about whether typedef is block-scoped or global. It is block-scoped, as this otherwise execrable code demonstrates:

#include <stdio.h>

static void function(void)
{
    typedef int i;
    i j = 1;
    printf("j = %d\n", j);
    {
    typedef double i;
    i j = 2.1;
    printf("j = %f\n", j);
    }
    {
    typedef char i[12];
    i j = "this works";
    printf("j = %s\n", j);
    }
}

int main(void)
{
    function();
    return(0);
}

If that was present to me for code review, it would be summarily rejected. However, it amply demonstrates a point.

Community
  • 1
  • 1
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • IMHO typedefs have file scope, even when issued from inside a function. Are you trying to confuse the poor boy? – wildplasser Dec 26 '11 at 16:48
  • IMO (and in the opinion of the C standard), you are incorrect; `typedef` is block scoped. For instance, C99 §6.7.7 Type definitions says in ¶2 _If a typedef name specifies a variably modified type then it shall have block scope_. – Jonathan Leffler Dec 26 '11 at 16:52
  • In that case you managed to confuse even me ;-] Never used it. It looks like a Pascalism, anyway... Ah, I see: only for VLA. That makes sense. – wildplasser Dec 26 '11 at 16:55
  • Any `typedef` can be block scoped; a variably modified typedef can only be block scoped (it cannot be file scope). – Jonathan Leffler Dec 26 '11 at 17:13
  • Well: your *code review* part makes perfect sense: its only *valid* use is related to VLA's. Apart for VLAs, I cannot think of any *sane* use of block-scoped typedefs. (eg in c89 / c90 code) – wildplasser Dec 26 '11 at 17:30
  • Block-scoped typedefs are seldom used (that I'd readily grant you), but they could be useful in a function if you need a type that will only be used by the one function for its internal processing and not relayed to other functions. It isn't easy to produce a compelling example, especially not in a comment. _However_, the original point I was rebutting was 'typedefs have file scope when placed inside a function', which is wrong. Whether they're useful or not is somewhat separate. Nevertheless, typedefs are block scoped when they appear inside a block (such as a function body). – Jonathan Leffler Dec 26 '11 at 18:38
1

MATSIZE is not known. Which is why you are getting this issue.

#ifndef DEF_H
#define DEF_H

#define MATSIZE 100 /* or whatever */

typedef double mat[MATSIZE][MATSIZE]

#endif 
Ahmed Masud
  • 21,655
  • 3
  • 33
  • 58
  • Why did you change the include guard macro form `DEF_H` to `_DEF_H`? The original is perfect, while yours invokes undefined behavior. Basically, names starting with underscores are in the implementation name space and you should not use them in your application. – Jens Dec 26 '11 at 16:24
  • @jens heh force of habit... you are right, i'll update to fix. – Ahmed Masud Dec 26 '11 at 16:26
  • @querty Put the MATSIZE in the def.h ! – Ahmed Masud Dec 26 '11 at 16:29