将可变长度数组声明为C中的全局变量

时间:2012-04-28 03:46:22

标签: c global-variables variable-length-array

如何将可变长度数组声明为全局变量?

当在扫描长度之前在函数中声明可变长度数组时,它会编译但不会运行。它给出了分段错误。当相同的声明语句转移到扫描语句下方时,它运行正常。

如果我们想要一个可变长度数组全局可用于所有函数,我们该怎么做?问题在于,数组的长度只能通过某些函数进行扫描。

4 个答案:

答案 0 :(得分:4)

可变长度数组(即使用运行时值调整大小的数组)不能是全局变量,因为显然必须在编译时计算用于大小的表达式。它只能存在于堆栈中。据推测,你得到的是一个静态数组,其大小取决于你定义代码的位置(因为你正在重新定义它所依赖的东西)。

为什么不能只使用全局指针和realloc()来根据需要调整大小?

答案 1 :(得分:3)

你不能这样做。 以下是标准草案的内容:

  

6.7.6.2数组声明符

     

2如果标识符被声明为具有可变修改类型,则为   应为普通标识符(如6.2.3中所定义),否则   链接,并具有块范围或函数原型范围。如果   标识符声明为具有静态或线程的对象   存储持续时间,它不应具有可变长度的数组类型。

另外,

  

10示例4可变修改(VM)类型的所有声明必须在块范围或   功能原型范围。使用_Thread_local,static或extern声明的数组对象   存储类说明符不能具有可变长度数组(VLA)类型。但是,声明了一个对象   静态存储类说明符可以具有VM类型(即指向VLA类型的指针)。最后,全部   用VM类型声明的标识符必须是普通标识符,因此不能是其成员   结构或工会。

答案 2 :(得分:2)

无法在C中将变量长度数组声明为全局变量,因为在知道其大小之前必须先分配它,因此编译器无法知道应为其分配多少内存。但是,您可以(而且应该)做的是动态分配它:

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;
}

答案 3 :(得分:0)

嗡嗡声在打开后的7年内回答。与迄今为止的回答相反,敢于冒险的恶魔有希望:)。

我遇到了这种需要,在线程应用程序中共享了全局VLA(dyn数组等)。 简而言之,我需要线程共享一个全局数组,我将同步/缓存问题放在一边,因为我只想展示如何共享VLA,此示例可以派生用于其他需求(例如,外部VLA等)

这里是代码,后跟注释解释其工作原理。

#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)在这里我们声明VLA,运行字符串将指示线程数,以及我们2个暗淡VLA的大小,n行(每线程1条),每行m个值。

(* 2)我们声明(设置)我们的全局VLA,将我们的全局n和m(作为gn,gm)和我们的全局数组公开为指向数组标量类型的指针(此处为int),我们将其初始化指向a [0] [0]。

(* 3)我们在a [n] [m]中设置值(连续的int,0、1、2,...)

(* 4)每个线程都以init_thread()开始,请注意,我们声明了一个与a [n] [m] VLA类型相同的虚拟数组,此处的目的是传递一个与我们的兼容的数组worker()API。

(* 5)我们的工作人员需要一个长为n,m(暗)的类型,这在(* 6)处得到了解释,因此在这里,我们将全局n和m传递给我们的工作对象,并且将伪数组传递给不必理会,唯一的目的是将数组地址作为参数传递。

(* 6)工作的API,我们有一些参数(如tndx),然后有一个VLA,表示为long,n,long m,int a [n] [m]。此时,a [] []是x [] []而不有用。

我们故意用了n和m的时间来解决可能发生的一些堆栈冲突问题,然后将n,m和a粘合在一起,因为我们将n和m的加法器取在寄存器中(现代拱门)被放到其占位符中的堆栈中,i = mp = np请注意定义堆栈方向(arg0,arg1,arg2),此时我们可以访问x [] []基本地址和位置我们的全球ga * ap =(long)ga;

(* 8)现在,我们的工作人员可以优雅地访问全局(共享)VLA。

这里是奔跑

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

每个线程都通过添加ID * 1000来修改其行。

因此,我们可以最终定义一个VLA全局变量。

VLA很酷,省去了学习者阅读alloca()等的需要,但是仍然需要全局的,而且正如编译时所解释的那样,不可能,GCC(libgcc?)仍然应该能够提供一个API在运行时“修补” VLA基本地址。

我现在很多人会反对arg addr的使用,堆栈方向修改等,但这是许多其他代码的工作方式,va_args,alloca等...因此它看起来很丑,但是这种丑可以被隐藏

干杯, 皮