如何将可变长度数组声明为全局变量?
当在扫描长度之前在函数中声明可变长度数组时,它会编译但不会运行。它给出了分段错误。当相同的声明语句转移到扫描语句下方时,它运行正常。
如果我们想要一个可变长度数组全局可用于所有函数,我们该怎么做?问题在于,数组的长度只能通过某些函数进行扫描。
答案 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等...因此它看起来很丑,但是这种丑可以被隐藏
干杯, 皮