declaring a variable-length array as a global variable in C
Asked Answered
C

4

9

How is it possible to declare a variable-length array as a global variable ?

when variable length array is declared in a function before the length is scanned, it compiles but does not run. it gives segmentation fault. when the same declaration statement is shifted below the scanning statement, it runs fine.

in case we want a variable length array globally available to all functions, how can we do that? problem here is the that the length of the array can only be scanned through some function only.

Calv answered 28/4, 2012 at 3:46 Comment(1)
A variable length array as a global variable does not make sense. A variable length array is sized according to the runtime value of a length variable. If you showed some code then it would help.Placeman
F
3

You can't do that. Here's what the draft of the standard says:

6.7.6.2 Array declarators

2 If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type.

Also,

10 EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or function prototype scope. Array objects declared with the _Thread_local, static, or extern storage-class specifier cannot have a variable length array (VLA) type. However, an object declared with the static storage-class specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.

Fabron answered 28/4, 2012 at 3:55 Comment(0)
S
4

A variable length array (i.e. an array sized with a runtime value) can't be a global variable, because the expression you are using for the size must obviously be computed at compile time. It can only live on the stack. Presumably what you are getting is a static array with a size that depends on where in the code you are defining it (because you are redefining something it depends on).

Why can't you just use a global pointer and realloc() to size it as needed?

Sowens answered 28/4, 2012 at 3:52 Comment(0)
F
3

You can't do that. Here's what the draft of the standard says:

6.7.6.2 Array declarators

2 If an identifier is declared as having a variably modified type, it shall be an ordinary identifier (as defined in 6.2.3), have no linkage, and have either block scope or function prototype scope. If an identifier is declared to be an object with static or thread storage duration, it shall not have a variable length array type.

Also,

10 EXAMPLE 4 All declarations of variably modified (VM) types have to be at either block scope or function prototype scope. Array objects declared with the _Thread_local, static, or extern storage-class specifier cannot have a variable length array (VLA) type. However, an object declared with the static storage-class specifier can have a VM type (that is, a pointer to a VLA type). Finally, all identifiers declared with a VM type have to be ordinary identifiers and cannot, therefore, be members of structures or unions.

Fabron answered 28/4, 2012 at 3:55 Comment(0)
U
3

There's no way to declare a variable length array as a global variable in C as it would have to be allocated before the knowing its size so the compiler can't know how much memory it should allocate for it. What you can (and should) do, however, is to allocate it dynamically:

char* my_dynamic_array = NULL;

void f(unsigned int size)
{
    if(!my_dynamic_array) {
        my_dynamic_array = malloc(size);
    }
    /* do something with the array */
}

int main(void)
{
    f(1024); /* set size dynamically */
    /* do something with the array */
    free(my_dynamic_array); /* free the allocated memory */
    return 0;
}
Uncaredfor answered 28/4, 2012 at 3:56 Comment(0)
J
1

Hum answering this 7 years after opening. Contrary to what has been answered so far there is a hope for the dare devils :).

I came across this need, sharing a global VLA (dyn array, etc...) in a threaded app. Short story, I need my thread to share a global array, I put on the side the synchro/cache problematics here as I just want to show how to share the VLA, this example can be derivated for other needs (for instance an external VLA, etc...)

Here is the code, followed with annotation explaining why it works.

#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>

int gn, gm, *ga;                                                 /* (*2) */

void worker(int tndx, long n, long m, int a[n][m])               /* (*6) */
{ long *np=&n, *mp=&m, i=mp-np, *ap=mp+i;                        /* (*7) */

  *ap=(long)ga;
  /* At this oint the worker can elegantly access the global dyn array.
   * elegantly mean through a[i][j].
   */
  printf("worker %d started\n", tndx);
  for(int j=0;j<m;j++)                        
  { a[tndx][j]+=(tndx*1000);                                     /* (*8) */
  }
}

void *init_thread(void *v)
{ int x[1][1], tndx = (int)(long)v;                              /* (*4) */
  printf("thread #%d started\n", tndx);
  worker(tndx, (long)gn, (long)gm, x);                           /* (*5) */
  return(0);
}

int main(int ac, char **av)
{ int n=atoi(av[1]), m=atoi(av[2]);
  pthread_t tt[n];  /* thread table */                           /* (*1) */
  int a[n][m];      /* dyn array    */                           /* (*1) */

  gn=n, gm=m, ga=&a[0][0]; /* globals setup shared by workers */ /* (*2) */
  for(int i=0, k=0;i<n;i++)for(int j=0;j<m;j++)a[i][j]=k++;      /* (*3) */

  printf("Init  a[][]\n");
  for(int i=0, k=0;i<n;i++)for(int j=0;j<m;j++)
    printf("a[%d][%d]=%d\n",i,j,a[i][j]);
  for(int i=0;i<n;i++)
  { if(pthread_create(&tt[i], NULL, init_thread, (void *)(long)i))
    { exit((printf("pthread_create %d failed\n",i),1));
    }
  }
  printf("Draining threads\n");
  for(int i=0;i<n;i++)
  { pthread_join(tt[i],0);
  }
  printf("Final a[][]\n");
  for(int i=0, k=0;i<n;i++)for(int j=0;j<m;j++)
    printf("a[%d][%d]=%d\n",i,j,a[i][j]);
  pthread_exit(NULL);
}

(*1) Here we declare VLA's, the runstring will dictate the number of threads, along with the size of our 2 dim VLA, n lines (1 per threads) with m values each.

(*2) We declare (en setup) our global VLA, we expose our global n and m (as gn, gm) and our global array as a pointer to the array scalar type (int here), we init it to point to a[0][0].

(*3) we setup values in a[n][m] (consecutives int, 0, 1, 2, ...)

(*4) Each threads are started with the init_thread(), note we declare a dummy array of same type as our a[n][m] VLA, the purpose here is to pass an array that is compliant with our worker() API.

(*5) Our worker require a type long for n, m (the dim) this is explained at (*6), so here we pass the global n and m to our worked, and the dummy array, we don't care about it, the only purpose is to pass an array addr as argument.

(*6) The worked API, we have some args (like tndx) then we have a VLA, denoted by long, n, long m, int a[n][m]. At this point a[][] is x[][] and not usefull.

We used long for n and m on purpose to fix some stack aligment surprise that may occurs, then n, m, and a are glued together, because we take the addr of n and m, thos args that are on register (modern arch) are dumped into the stack in their place holder, i=mp=np take care of defining the stack direction (arg0, arg1, arg2) at this point we are able to access the x[][] base addr and place our global ga in there *ap=(long)ga;

(*8) Now our worked can elegantly access the global (shared) VLA.

Here is a run

VY$ cc -o t2 t2.c -lpthread

VY$ ./t2 3 4
Init  a[][]
a[0][0]=0
a[0][1]=1
a[0][2]=2
a[0][3]=3
a[1][0]=4
a[1][1]=5
a[1][2]=6
a[1][3]=7
a[2][0]=8
a[2][1]=9
a[2][2]=10
a[2][3]=11
thread #0 started
worker 0 started
thread #2 started
worker 2 started
thread #1 started
worker 1 started
Draining threads
Final a[][]
a[0][0]=0
a[0][1]=1
a[0][2]=2
a[0][3]=3
a[1][0]=1004
a[1][1]=1005
a[1][2]=1006
a[1][3]=1007
a[2][0]=2008
a[2][1]=2009
a[2][2]=2010
a[2][3]=2011

Each thread has modified its line my adding its ID*1000.

So we definitly can have a VLA defined globaly.

VLA's are cool, save the need for the learner to read about alloca() etc, yet there is a need for global one and as explained at compile time it is not possible, still GCC (libgcc?) should be able to offer an API to 'patch' a VLA base addr at run time.

I now that many will voice against arg addr taking, stack direction hack, etc, yet this is the way many other code works, va_args, alloca, etc... so it may look ugly, but this ugliness could be hidden.

Cheers, Phi

Jerilynjeritah answered 7/7, 2019 at 16:34 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.