在过去的几年里,我并没有非常使用过C语言.当我今天读到这个问题时,我遇到了一些我不熟悉的C语法.
显然在C99中,以下语法有效:
void foo(int n) {
int values[n]; //Declare a variable length array
}
Run Code Online (Sandbox Code Playgroud)
这似乎是一个非常有用的功能.有没有关于将它添加到C++标准的讨论,如果是这样,为什么它被省略?
一些潜在的原因:
C++标准规定数组大小必须是常量表达式(8.3.4.1).
是的,当然我意识到在玩具示例中可以使用std::vector<int> values(m);,但这会从堆中分配内存而不是堆栈.如果我想要一个多维数组,如:
void foo(int x, int y, int z) {
int values[x][y][z]; // Declare a variable length array
}
Run Code Online (Sandbox Code Playgroud)
该vector版本变得很笨拙:
void foo(int x, int y, int z) {
vector< vector< vector<int> > > values( /* Really painful expression here. */);
}
Run Code Online (Sandbox Code Playgroud)
切片,行和列也可能遍布整个内存.
看一下comp.std.c++这个问题的讨论很明显,这个问题在争论的两个方面都有一些非常重要的名字引起争议.毫无疑问,a std::vector总是更好的解决方案.
我找到了一个计算数字平方的函数:
int p(int n) {
int a[n]; //works on C99 and above
return (&a)[n] - a;
}
Run Code Online (Sandbox Code Playgroud)
它返回n 2的值.问题是,它是如何做到的?经过一点点测试后,我发现之间是(&a)[k]和/ .这是为什么?(&a)[k+1]sizeof(a)sizeof(int)
为什么我使用以下代码收到错误"可能无法初始化可变大小的对象"?
int boardAux[length][length] = {{0}};
Run Code Online (Sandbox Code Playgroud) 在尝试实现C11解析器(用于教育目的)时,我发现在C11(p.470)中也在C99(p.412)中(感谢Johannes!),直接声明符定义为:
(6.7.6) direct-declarator:
direct-declarator [ type-qualifier-list? * ]
Run Code Online (Sandbox Code Playgroud)
起初,我认为这是语法中的错误(类型列表不应该是可选的).但是,当我在我的参考编译器(clang)中尝试这个时,我得到了一个意外的错误:
int array[*] = { 1, 2, 3 };
// error: star modifier used outside of function prototype
Run Code Online (Sandbox Code Playgroud)
显然,(在clang中)这被称为星形修饰符.
我很快就知道它们只能在函数签名中使用:
void foobar(int array[*])
Run Code Online (Sandbox Code Playgroud)
但是,它们只能用于声明中.尝试在函数定义中使用它也会导致错误:
void foobar(int array[*]) {
// variable length array must be bound in function definition
}
Run Code Online (Sandbox Code Playgroud)
所以据我所知,预期的行为是[*]在函数声明中使用,然后在函数定义中使用固定数字.
// public header
void foobar(int array[*]);
// private implementation
void foobar(int array[5]) {
}
Run Code Online (Sandbox Code Playgroud)
但是,我从未见过它,我也不太了解它的目的.
int[]?int * …我写了一个C程序,它接受来自用户的整数输入,用作整数数组的大小,并使用该值声明给定大小的数组,我通过检查数组的大小来确认它.
码:
#include <stdio.h>
int main(int argc, char const *argv[])
{
int n;
scanf("%d",&n);
int k[n];
printf("%ld",sizeof(k));
return 0;
}
Run Code Online (Sandbox Code Playgroud)
而且令人惊讶的是它是正确的!该程序能够创建所需大小的数组.
但是所有静态内存分配都是在编译时完成的,并且在编译期间,其值n是未知的,那么为什么编译器能够分配所需大小的内存呢?
如果我们能够分配所需的内存就是这样,然后有什么用使用动态分配的malloc()和calloc()?
今天我用一些C代码帮助我的一个朋友,我发现了一些奇怪的行为,我无法解释他为什么会发生这种行为.我们有一个带有整数列表的TSV文件,每行都有一个int.第一行是列表的行数.
我们还有一个非常简单的"readfile"的ac文件.第一行读到n,行数,然后有一个初始化:
int list[n]
Run Code Online (Sandbox Code Playgroud)
最后是一个带有fscanf的n循环.
对于小n(直到~100,000),一切都很好.但是,我们发现当n很大(10 ^ 6)时,会发生段错误.
最后,我们将列表初始化更改为
int *list = malloc(n*sizeof(int))
Run Code Online (Sandbox Code Playgroud)
一切都很好,即使是非常大的n.
有人能解释为什么会这样吗?什么导致了segfault [n]的段错误,当我们开始使用list = malloc(n*sizeof(int))时停止了?
这个问题的目的是提供一个关于如何在C中动态正确分配多维数组的参考.这是一个经常被误解的主题,即使在一些C编程书籍中也很难解释.因此,即使是经验丰富的C程序员也很难做到正确.
我从编程教师/书籍/教程中了解到,动态分配多维数组的正确方法是使用指针指针.
然而,SO上的几个高代表用户现在告诉我这是错误和不好的做法.他们说指针到指针不是数组,我实际上并没有分配数组,而且我的代码不必要地慢.
这就是我教我分配多维数组的方法:
#include <stdlib.h>
#include <stdio.h>
#include <assert.h>
int** arr_alloc (size_t x, size_t y)
{
int** pp = malloc(sizeof(*pp) * x);
assert(pp != NULL);
for(size_t i=0; i<x; i++)
{
pp[i] = malloc(sizeof(**pp) * y);
assert(pp[i] != NULL);
}
return pp;
}
int** arr_fill (int** pp, size_t x, size_t y)
{
for(size_t i=0; i<x; i++)
{
for(size_t j=0; j<y; j++)
{
pp[i][j] = (int)j + 1;
}
}
return pp;
}
void arr_print (int** pp, size_t x, size_t y) …Run Code Online (Sandbox Code Playgroud) c arrays dynamic-arrays dynamic-allocation variable-length-array
使用可变长度数组有一些开销吗?可以在运行时通过命令行参数传递数组的大小吗?与自动和动态分配数组相比,为什么会引入它?
我试图弄清楚alloca()在记忆层面上实际上是如何工作的。来自Linux 手册页:
alloca() 函数在调用者的堆栈帧中分配 size 字节的空间。当调用 alloca() 的函数返回到其调用者时,该临时空间会自动释放。
这是否意味着将按字节alloca()转发堆栈指针n?或者说新创建的内存到底分配在哪里?
这不是与可变长度数组完全相同吗?
我知道实现细节可能留给操作系统之类的东西。但我想知道一般来说这是如何实现的。
我正在移植一些C99代码,这些代码大量使用可变长度数组(VLA)到C++.
我用一个在堆上分配内存的数组类替换了VLA(堆栈分配).业绩受到巨大影响,放缓了3.2倍(见下面的基准). 我可以在C++中使用什么快速的VLA替换?我的目标是在重写C++代码时尽量减少性能损失.
向我建议的一个想法是编写一个数组类,其中包含类中的固定大小的存储(即可以是堆栈分配)并将其用于小型数组,并自动切换到更大数组的堆分配.我的实现是在帖子的最后.它工作得相当好,但我仍然无法达到原始C99代码的性能.为了接近它,我必须将这个固定大小的存储空间(MSL下面)增加到我不熟悉的尺寸.即使对于许多不需要它的小型数组,我也不想在堆栈上分配太大的数组,因为我担心它会触发堆栈溢出.C99 VLA实际上不太容易发生这种情况,因为它永远不会使用比所需更多的存储空间.
我遇到了std::dynarray,但我的理解是它没有被标准接受(但是?).
我知道clang和gcc在C++中支持VLA,但我也需要它与MSVC一起工作.事实上,更好的可移植性是重写为C++的主要目标之一(另一个目标是将程序(最初是命令行工具)转换为可重用的库).
MSL指的是我在其上切换到堆分配的数组大小.我对1D和2D数组使用不同的值.
原始C99代码:115秒.
MSL = 0(即堆分配):367秒(3.2x).
1D-MSL = 50,2D-MSL = 1000:187秒(1.63x).
1D-MSL = 200,2D-MSL = 4000:143秒(1.24x).
1D-MSL = 1000,2D-MSL = 20000:131(1.14x).
增加MSL进一步提高性能,但最终程序将开始返回错误的结果(我假设由于堆栈溢出).
这些基准测试是在OS X上使用clang 3.7,但是gcc 5显示了非常相似的结果.
这是我使用的当前"小向量"实现.我需要1D和2D矢量.我切换到大小超过堆分配MSL.
template<typename T, size_t MSL=50>
class lad_vector {
const size_t len;
T sdata[MSL];
T *data;
public:
explicit lad_vector(size_t len_) : len(len_) {
if (len <= MSL)
data = &sdata[0];
else
data = new …Run Code Online (Sandbox Code Playgroud) c++ arrays performance variable-length-array stack-allocation
c ×8
arrays ×7
c++ ×2
c99 ×2
alloca ×1
function ×1
malloc ×1
memory ×1
parameters ×1
performance ×1
pointers ×1
stack-frame ×1
standards ×1