可能重复:
文件范围内可变修改的数组
我有一些关于VLA及其行为的概念,我需要澄清一下.
自C99起AFIK可以将VLA声明为本地范围:
int main(int argc, char **argv)
{
// function 'main' scope
int size = 100;
int array[size];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
但它在全球范围内被禁止:
const int global_size = 100;
int global_array[global_size]; // forbidden in C99, allowed in C++
int main(int argc, char **argv)
{
int local_size = 100;
int local_array[local_size];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
上面的代码在C99中声明了一个VLA,因为const修饰符不会创建编译时值.在C++中global_size是一个编译时值,因此,global_array不会成为VLA.
我需要知道的是:我的推理是否正确?我描述的行为是正确的吗?
我也想知道:为什么不允许全球范围内的VLA?在C和C++中是否被禁止?数组在全局和局部范围内的行为有什么不同?
我昨天刚做了一个实验,发现一些令人困惑的事情:
#include <stdio.h>
int main()
{
int j;
scanf("%d",&j);
const int i = j;
int arr[i];
return 0;
}
Run Code Online (Sandbox Code Playgroud)
j从键盘读取数字,它用于arr在堆栈上分配数组.
编译器在编译时甚至不知道数组的大小(将j初始化为0?),但没有编译错误.这怎么可能?
我们可以声明一个可变长度的结构元素吗?
条件如下:
typedef struct
{
uint8_t No_Of_Employees;
uint8_t Employee_Names[No_Of_Employees][15];
}st_employees;
Run Code Online (Sandbox Code Playgroud) 在此代码段中,指向VLA的指针用于更轻松地访问大型查找表:
#pragma GCC diagnostic warning "-Wcast-qual"
char
lookup(int a, int b, int c, char const *raw, int x, int y, int z)
{
typedef char const (*DATA_PTR)[a][b][c];
DATA_PTR data = (DATA_PTR)raw;
return (*data)[x][y][z];
}
Run Code Online (Sandbox Code Playgroud)
GCC 6.2.0扼杀它,而Clang 4.0.0(主干)编译得很好,两者都-Wcast-qual启用了.
In function 'lookup':
warning: cast discards 'const' qualifier from pointer target type [-Wcast-qual]
DATA_PTR data = (DATA_PTR)raw;
^
Run Code Online (Sandbox Code Playgroud)
代码按预期方式运行.
我的猜测是GCC混淆了"指向const元素的VLA的指针"和"指向const VLA的指针",但我达到了......
有没有办法在没有摆弄警告的情况下关闭GCC?这是GCC的错误吗?
EDIT1:
有关实际代码的详细信息:
struct table {
int a;
int b;
int c;
char …Run Code Online (Sandbox Code Playgroud) 您可能知道,VLA 有优点也有缺点,并且在 C11 中它们是可选的。
我认为使 VLA 成为可选的主要原因是:“堆栈可能会爆炸”:
int arr[n]; /* where n = 1024 * 1024 * 1024 */
Run Code Online (Sandbox Code Playgroud)
但是指向 VLA 的指针又如何呢?
int m, n;
scanf("%d %d", &m, &n);
int (*ptr)[n] = malloc(sizeof(int [m][n]));
Run Code Online (Sandbox Code Playgroud)
在这种情况下,不存在炸毁堆栈的风险,而且在我看来它们非常有用。
我的问题是:
委员会是否可以保留指向 VLA 的指针,从而使指向非指针类型的 VLA 成为可选?
或者一件事暗示另一件事?
(原谅我糟糕的英语)
下面的代码应该生成错误,因为在编译期间编译器无法知道数组大小.
int f;
std::cin >> f;
int c[f];
c[100] = 5;
Run Code Online (Sandbox Code Playgroud)
我正在使用gcc(Ubuntu 4.8.2-19ubuntu1)4.8.2进行编译,它不仅仅是编译,而是以某种方式运行.
怎么会发生?
我type_traits在新的C++ 14运行时大小的数组上测试了标题中的一些工具,请考虑以下代码:
int g[]{1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
template <typename T> void print(T &t)
{
std::cout << "Type id: " << typeid(T).name() << '\n';
std::cout << "is_array: " << std::is_array<decltype(T)>::value << '\n';
std::cout << "is_pointer: " << std::is_pointer<decltype(T)>::value << '\n';
std::cout << "extent: " << std::extent<decltype(T)>::value << '\n';
}
int main()
{
print(g);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
静态大小的数组g返回以下输出:
Type id: A11_i
is_array: 1
is_pointer: 0
extent: 11
Run Code Online (Sandbox Code Playgroud)
A11_i我假设的 unmangled …
n3639提出将c99的可变长度数组 s采用到C++ 14中(至少在第一维中是这样).
但最新的我已经能够找到 n3639列表:
C++ 14的第一张CD中的功能,随后被删除到技术规范
这有没有成为技术规范,还是丢失了?
我的问题的原因是,我注意到这段代码:
void f(size_t n) {
int a[n];
for (size_t i = 0; i < n; ++i)
a[i] = 2 * i;
sort(a, a + n);
}
Run Code Online (Sandbox Code Playgroud)
这无法在Visual Studio 2015和gcc中构建(当使用"-pedantic"标志时).
做工精细下gcc5.1,但仍然未能建立的Visual Studio 2015年之下.
这只是gcc在C++ 14中错误地支持c99的可变长度数组,还是以某种方式使它成为C++ 14并且Visual Studio 2015无法将其提取出来?
编辑:看起来gcc已经删除了gcc6.2中的支持:http://coliru.stacked-crooked.com/a/303ae1970fa3f5d2
这个问题源于 Eric Postpischil在另一个帖子中的评论。
我很难理解可变长度数组(VLA)作为函数参数的使用:
sizeof()下面的调用所示;即使完全有可能在堆栈上传递整个数组,就像定义 VLA 时在堆栈上创建一样。那么,如果 VLA 参数没有提供任何优势并且像指针的任何其他数组参数一样进行调整,那么为什么该语言允许使用 VLA 参数声明函数呢?如果语言不使用大小表达式(例如检查实际参数的大小)并且在函数内部无法获得大小表达式(仍然必须为此传递一个显式变量),为什么要对大小表达式进行求值?
为了更清楚地说明我感到困惑的地方,请考虑以下程序(此处为实时示例)。所有函数声明显然都是等效的。但正如 Eric 在另一个线程中指出的那样,函数声明中的参数大小表达式是在运行时评估的评估的。大小表达式不会被忽略。
我不清楚这会带来什么好处,因为大小及其评估没有影响(除了可能的副作用)。特别是,重复一遍,该信息不能被函数内部的代码使用。最明显的变化是在类似堆栈的结构上传递 VLA。毕竟,它们通常也在调用方的堆栈上。但与恒定长度的数组一样,类型在声明时已调整为指针 - 下面的所有声明都是等效的。尽管如此,仍会评估无用且被丢弃的数组大小表达式。
#include <stdio.h>
// Nothing to see here.
extern void ptr(int *arr);
// Identical to the above.
extern void ptr(int arr[]);
// Still identical. Is 1 evaluated? Who knows ;-).
extern void ptr(int arr[1]);
// Is printf evaluated when called? Yes.
// But the array is still …Run Code Online (Sandbox Code Playgroud) 条件运算符中使用的 VM 类型的 C 标准可能存在矛盾。认为:
int f(void) { return 42; }
Run Code Online (Sandbox Code Playgroud)
现在,下面的表达式的类型是什么?
1 ? 0 : (int(*)[f()]) 0
Run Code Online (Sandbox Code Playgroud)
它的值必须等于 NULL,但我不确定类型是什么。是int(*)[f()]一个指向大小为 VLA 的指针f()。f()但是,要完成此 VM 类型,必须评估大小表达式。问题是它属于三元运算符的一个分支,未进行评估。从6.5.15p4开始:
仅当第一个操作数与 0 比较时不等于 0 时,才会计算第二个操作数;仅当第一个操作数比较等于 0 时才计算第三个操作数
条件运算符的规则要求指针的组合是另一个分支6.5.15p60的类型:
...如果一个操作数是空指针常量,则结果具有另一操作数的类型;...
如何解决这个矛盾呢?可能的解决方案是:
int(*)[f()]-f()无论如何都会被评估int(*)[]- 数组类型不完整复合类型的规则表明这可能是 UB,但我不确定这些规则是否适用于这种情况。我寻找引用 C17 规范的答案,但即将推出的 C2X 的措辞也很好。