如何将可变长度数组声明为全局变量?
如果在扫描长度之前在函数中声明可变长度数组,它将编译但不运行。 它给出了分段错误。 当相同的声明语句移动到扫描语句下方时,它将正常运行。
如果我们想要一个全局可用的可变长度数组,我们该怎么做呢? 这里的问题是数组的长度只能通过某些函数进行扫描。
可变长度数组(即使用运行时值调整大小的数组)不能是全局变量,因为您用于大小的表达式显然必须在编译时计算。 它只能存在于堆栈上。 大概你得到的是一个静态数组,其大小取决于你在代码中定义它的位置(因为你正在重新定义它所依赖的东西)。
为什么不能只使用全局指针和 realloc() 根据需要调整它的大小?
那样做。以下是标准草案的内容:
6.7.6.2 数组声明符
2 如果标识符被声明为具有可变修改的类型,则应为普通标识符(定义见 6.2.3),没有联动,并具有块范围或功能原型范围。如果标识符声明为具有静态或线程的对象存储持续时间,它不应具有可变长度数组类型。
也
10 示例 4 可变修改 (VM) 类型的所有声明都必须在块范围内或函数原型范围。使用 _Thread_local、静态或外部声明的数组对象存储类说明符不能具有可变长度数组 (VLA) 类型。但是,声明的对象静态存储类说明符可以具有 VM 类型(即指向 VLA 类型的指针)。最后,所有使用 VM 类型声明的标识符必须是普通标识符,因此不能是结构或工会。
办法在 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;
}
Hum 在开业 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 startedn", 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 startedn", 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]=%dn",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 failedn",i),1));
}
}
printf("Draining threadsn");
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]=%dn",i,j,a[i][j]);
pthread_exit(NULL);
}
(*1) 在这里我们声明 VLA,运行字符串将指示线程数,以及我们的 2 个暗淡 VLA 的大小,n 行(每个线程 1 行),每行 m 值。
(*2) 我们声明(en setup)我们的全局 VLA,我们将全局 n 和 m(作为 gn, gm)和我们的全局数组公开为指向数组标量类型的指针(这里为 int),我们初始化它以指向 a[0][0]。
(*3) 我们以 a[n][m] (连续整数、0、1、2 等)设置值
(*4) 每个线程都以 init_thread() 开头,请注意,我们声明了一个与我们的 a[n][m] VLA 类型相同的虚拟数组,这里的目的是传递一个符合我们的 worker() API 的数组。
(*5) 我们的工人需要一个 long for n, m(dim)的类型,这在 (*6) 中进行了解释,所以这里我们将全局 n 和 m 传递给我们的工作,以及虚拟数组,我们不关心它,唯一的目的是传递数组 addr 作为参数。
(*6)工作的API,我们有一些参数(如tndx),然后我们有一个VLA,用long,n,long m,int a[n][m]表示。此时 a[][] 是 x[][] 并且没有用。
我们故意使用 long for n 和 m 来修复可能发生的一些堆栈连接意外,然后 n、m 和 a 粘在一起,因为我们取 n 和 m 的地址,寄存器(现代 arch)上的参数被转储到它们的占位符中的堆栈中,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 takeing,stack direction hack等,但这是许多其他代码的工作方式,va_args,alloca等......所以它可能看起来很丑,但这种丑陋可以被隐藏。
干杯斐