使用new运算符创建一个用户输入大小的数组

8

我有几个关于数组的问题。 我学过,数组大小必须在声明时是常量,编译器必须知道其值。 但是,在使用GNU GCC编译器(C ++ 11标准过滤器)时,当以动态方式声明该数组(使用new)时,我可以使用变量作为数组大小完美地编译和运行程序。

int num;
cout << "How big an array? ";
cin >> num;
int *arr = new int [num];

问题1:这是标准的吗?我的教授们说法不一。

问题2:如果它是标准的,那么是否可以在创建后扩展数组(或任何数组)的大小?

问题3:同样地,如果这个表达式是标准的,那么是否可以在函数中使用它——例如使用一个函数来创建这样的数组?如果可以,应该怎么做?

(附言:你好,我是新来的,也是C++的初学者)


2
https://dev59.com/8m445IYBdhLWcg3wia2V#4984228 - Patashu
3
你的“教授”在动态数组这么简单的问题上意见不一致? - chris
1
@chris:我怀疑教授们只是对问题的理解不同。 - Greg Hewgill
@GregHewgill,这就是我想的。我敢打赌,他们认为可变长度数组是该主题(或至少之一)。 - chris
5个回答

7

问题1)这是否被认为是标准的?我的教授们持有不同的观点。

是的,这是完全有效的。请注意,您需要使用运算符delete[]显式删除arr指向的内存。最好使用std::vector<int>,它将为您执行内存管理。

您可能会将可变长度数组(VLA)与其混淆,在C++中不允许使用:

// same num as the one in your example
int arr[num]; // ERROR

这在C中是有效的,但在C++中无效(虽然C++14将包括可变长数组,不过与C的可变长数组会有一些差异)。

问题2)如果它是标准的,那么创建后是否可以扩展数组(或任何数组)的大小?

不行,你不能扩展它。你可以分配一个更大的数组,复制元素并删除先前的数组。再次提醒,如果你使用的是std::vector<int>,这个过程会自动完成。

问题3)同样,如果此表达式是标准的,那么是否可以在函数中使用它-例如使用函数创建这样的数组?(如果可以,如何操作?)

当然可以:

int *allocate(size_t size) {
    return new int[size];
}

但是再次使用std::vector

int num;
cout << "How big an array? ";
cin >> num;
std::vector<int> arr(num); // num elements, all of them initialized to 0
// insert 42 at the end. reallocations(if any) are done automatically
arr.push_back(42); 

谢谢!然而,使用allocate函数时,我的IDE将size标记为关键字。因此,使用(size_t size)而不是(size_t num)(int num)有什么特殊意义吗? - Shisa
@user2480471,使用“size”作为参数名称没有任何问题。你的IDE这样做很奇怪。当然,你可以更改参数的名称,这并不会改变语义。此外,“operator new”需要一个“size_t”作为其参数,因此最好使用这种类型而不是“int”,以避免错误/警告。 - mfontanini

3
我研究过,数组的大小必须在声明时是固定的,或者编译器必须知道它的值。
这是正确的,但仅适用于静态或自动数组。你正在堆上分配一个动态数组,这是不同的。
静态数组
在全局范围内声明的数组必须具有常量大小。
int arr[5];

自动数组

在函数内部自动分配的数组必须拥有恒定的大小(有例外情况,请见下文)。

void f() {
    int arr[5];
}

动态数组

使用new在堆上分配的动态数组可以是任何大小,可以是常量也可以是变量。

new int[5];
new int[n * 4];

GCC扩展

唯一的例外是,GCC允许使用一个变量来声明自动数组的大小:

void f(int n) {
    int arr[n];
}

然而,这种用法并不标准。

0

作为其他答案的补充:

向量

(同意使用向量进行动态调整大小):

std::vector<int> v;
v.push_back(1);
v.push_back(1);
v.resize(v.size()+10, 5); // Greater resized
v.resize(v.size()-1);     // Lower resized
  • 如果新的大小大于旧的大小,则会初始化额外的元素为5(或0,如果未使用第二个参数,则为int),而古老的元素保持不变。
  • 如果新的大小小于旧的大小,则会被截断。

数组

附注:(关于堆栈分配)

处理数组的方式可能会导致显着不同的结果(请参见这个非常有趣的讨论):

// Create 500 bytes on the heap
char *pBuffer = new char[500];      // or malloc in C

// Create 500 bytes on the stack
char buffer[500];                  

我本以为不传递第二个参数给 resize 会插入 0(或者是 T 的默认构造函数创建的任何值)。 - cHao
谢谢,我还不熟悉向量,所以所有的信息都非常感谢。 - Shisa

0

问题1 - 运算符“new”用于进行动态分配,也就是说,当您不知道数组大小时,没有问题,您可以这样做!我认为您的教授可能会混淆C语法,其中“new”既不存在,也不允许像int p [n]这样的操作。

问题2 - 不,无法增加使用运算符“new”创建的数组的大小。您必须分配另一个数组并复制数据。您可以考虑使用向量来轻松完成此操作。

问题3 - 我不知道为什么要这样做,但是这是可能的。

int* createarray(int size)
{
   return new int[size]; 
}


int main()
{
    int *p = createarray(10);
}

0

问题1:这是否被认为是标准的?

给定定义 int n = 42, new float[n][5] 是良好形式(因为n是noptr-new-declarator的表达式),但new float[5][n]是不良形式(因为n不是常量表达式)。 --5.3.4.6,N3242

如果分配的类型是数组类型,则分配函数的名称为operator new[],释放函数的名称为operator delete[]。 --5.3.4.8,N3242

new T[5]导致调用operator new[](sizeof(T)*5+x)。在这里,x和y是非负未指定的值,表示数组分配开销; --5.3.4.12,N3242

问题2:如果它是标准的,在这种情况下,是否可能在创建后扩展数组(或任何数组)的大小?

部分情况下不行,或者不推荐。
当分配函数返回一个非空指针时,它必须是指向已经保留了对象空间的存储块的指针。大多数情况下在堆上进行分配,可能没有更多连续的内存用于数组,这是很重要的。
如果必须这样做,并且具有内存池,请使用定位new运算符进行部分操作,但现在所做的就是分配器设计者所做的事情,存在破坏内部内存存储的风险。

Q3:是否使用函数创建这样的数组?(如何创建?)

通过 new 表达式创建的实体具有动态存储期(3.7.4)。[注意:这种实体的生存期并不一定限制在创建它的作用域内。—注]--5.3.4.1, N3242

其余的内容是如何设计这样的功能以满足你的需求,甚至可以使用模板。

 1 template<typename T>T* foo(std::size_t size){
 2         return new T[size] ;
 3 }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接