c++ 依据输入动态声明数组(一维,二维)

较早的编译器是不同意这样做的,所以一些书籍比方以Tc解说的书本都说数组的下标不能是变量。在vc6.0下亦是如此。

只是在一些较新的编译器如dev c++已经支持了,例如以下代码不会报错

#include <stdio.h>

#include <stdlib.h>

int main()

{

int a; int i;

scanf("%d",&a);

int c[a];

for( i =0 ;i<a;i++)

scanf("%d",&c[i]);

for( i =0 ;i<a;i++)

printf("%d",c[i]);

}

这个和编译器有关,应该是设计到一些标准规范为题吧,不妨不要这样写~使用动态分配内存是比較把握和通用的

一维:

cin>>n;

int* a=new int[n];

if ((a) == NULL)

{}//确保没有动态分配失败

可是new出来的一定不要忘了delete掉

delete []a; // 正确的使用方法

delete a; // 错误的使用方法

后者相当于delete a[0],漏掉了另外n-1个对象。

二维:

设有m行n列

cin>>m>>n;

int **a = new int* [m];

for(int i = 0; i < m; i++)

a[i] = new int [n];

PS:就相当于产生了一个二维数组a[m][n]了,可是对于我们平时声明的数组a[m][n],a[i][j]=a[i*n+j],由于是连续的一片内存,而这样动态声明的数组随意的a[k]都是一个int*类型,即一个地址了,所以仅仅能a[i][j]或者*(*(a+i)+j)来訪问数组的元素,而不能a[i*n+j]这样转换着用了

释放内存:

for(int i = 0; i < m; ++i)

delete []a[i];

delete []a;

PS:事实上对于c++,我们全然能够充分利用它自己强大而方便的容器,比方vector,之所以动态声明数组,相比是大小不确定,声明太大了怕浪费空间,而vector就不用指定大小,当存的数据变多,自己主动扩大容量,比方如果vector默认大小是8,当你再往里存第9个元素时,容器自己主动扩容,变为16,16再不够用,扩为32,2倍2倍的增长,这样就依据须要扩容,不会浪费空间,也能够像普通数组那样直接指定vector的大小,总之普通数组能够的它都能够,普通数组没有的它更有;

一维:

vector<int> a;

a.push_back(k);

k为待存入数组的数,使用方法一样,能够a[i]这样直接取数,还有各种自带的方法,使用方便极了

vector<int> a;

vector<int> a(5); //指定数组大小是5

vector<int> a(5,3); //数组大小为5,并初始化数组全部元素值为3

二维:

cin>>m>>n;//m行n列

vector<vector<int> > a(m, vector<int>(n));

for (i = 0; i < m; i++)

for (j = 0; j < n; j++)

a[i][j] = i*j;

for (i = 0; i < m; i++)

for (j = 0; j < n; j++)

cout<<a[i][j]<<‘ ‘;

cout<<endl;

转自;http://hi.baidu.com/zf2650/blog/item/1cac0e12f87e5f095baf53e7.html

-------------------------------------------------------------------------------------

今天归纳总结了一下,希望以后的朋友能够少走些弯路

一 :关于指针和堆的内存分配

先来介绍一下指针 : 指针一种类型,理论上来说它包括其它变量的地址,因此有的书上也叫它:地址变量。既然指针是一个类型,是类型就有大小,在达内的server上或者普通的PC机上,都是4个字节大小,里边仅仅是存储了一个变量的地址而已。无论什么类型的指针,char * ,int * ,int (*) ,string * ,float * ,都是说明了本指针所指向的地址空间是什么类型而已,了解了这个基本上全部的问题都好象都变的合理了。

在C++中,申请和释放堆中分配的存贮空间,分别使用new和delete的两个运算符来完毕:

指针类型 指针变量名=new 指针类型 (初始化);

delete 指针名;

比如:1、 int *p=new int(0);

它与下列代码序列大体等价:

2、int tmp=0, *p=&tmp;

差别:p所指向的变量是由库操作符new()分配的,位于内存的堆区中,而且该对象未命名。

  

以下是关于new 操作的说明 : 部分引自<<C++面向对象开发>>

1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象,都是通过该指针来间接操作的,而动态创建的对象本身没有名字。

2、一般定义变量和对象时要用标识符命名,称命名对象,而动态的称无名对象(请注意与栈区中的暂时对象的差别,两者全然不同:生命期不同,操作方法不同,暂时变量对程序猿是透明的)。

3、堆区是不会在分配时做自己主动初始化的(包含清零),所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列例如以下:从堆区分配对象,然后用括号里的值初始化该对象。

以下是从堆中申请数组

1、申请数组空间:

指针变量名=new 类型名[下标表达式];

注意:“下标表达式”不是常量表达式,即它的值不必在编译时确定,可以在执行时确定。这就是堆的一个很显著的特点,有的时候程序猿本身都不知道要申请可以多少内存的时候,堆就变的格外实用。

2、释放数组空间:

delete [ ]指向该数组的指针变量名;

注意:方括号非常重要的,假设delete语句中少了方括号,因编译器觉得该指针是指向数组第一个元素的,会产生回收不彻底的问题(仅仅回收了第一个元素所占空间),我们通常叫它“内存泄露”,加了方括号后就转化为指向数组的指针,回收整个数组。delete [ ]的方括号里不须要填数组元素数,系统自知。即使写了,编译器也忽略。<<Think in c++>>上说过曾经的delete []方括号里是必须加入个数的,后来因为非常easy出错,所以后来的版本号就改进了这个缺陷。

以下是个样例,VC上编译通过

#include<iostream>

using namespace std;

//#include <iostream.h>  //for VC

#include <string.h>

void main(){

int n;

char *p;

cout<<"请输入动态数组的元素个数"<<endl;

cin>>n; //n在执行时确定,可输入17

p=new char[n]; //申请17个字符(可装8个汉字和一个结束符)的内存空间strcpy(pc,“堆内存的动态分配”);//

cout<<p<<endl;

delete []p;//释放pc所指向的n个字符的内存空间return ; }

通过指针使堆空间,编程中的几个可能问题

1.动态分配失败。

返回一个空指针(NULL),表示发生了异常,堆资源不足,分配失败。

data = new double [m]; //申请空间

if ((data ) == 0)…… //或者==NULL

2.指针删除与堆空间释放。

删除一个指针p(delete p;)实际意思是删除了p所指的目标(变量或对象等),释放了它所占的堆空间,而不是删除p本身,释放堆空间后,p成了空悬指针,不能再通过p使用该空间,在又一次给p赋值前,也不能再直接使用p。

3.内存泄漏(memory leak)和反复释放。

new与delete 是配对使用的, delete仅仅能释放堆空间。假设new返回的指针值丢失,则所分配的堆空间无法回收,称内存泄漏,同一空间反复释放也是危急的,由于该空间可能已另分配,而这个时候又去释放的话,会导致一个非常难查出来的执行时错误。所以必须妥善保存new返回的指针,以保证不发生内存泄漏,也必须保证不会反复释放堆内存空间。

4.动态分配的变量或对象的生命期。

无名变量的生命期并不依赖于建立它的作用域,比方在函数中建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间,并仅仅能释放一次,在函数内建立,而在函数外释放是一件非常easy失控的事,往往会出错,所以永远不要在函数体内申请空间,让调用者释放,这是一个非常差的做法。你再怎么小心翼翼也可能会带来错误。

类在堆中申请内存 :

通过new建立的对象要调用构造函数,通过deletee删除对象要调用析构函数。

CGoods *pc;

pc=new CGoods; //分配堆空间,并构造一个无名对象

//的CGoods对象;

…….

delete pc; //先析构,然后将内存空间返回给堆; 堆对象的生命期并不依赖于建立它的作用域,所以除非程序结束,堆对象(无名对象)的生命期不会到期,而且须要显式地用delete语句析构堆对象,上面的堆对象在运行delete语句时,C++自己主动调用其析构函数。

正由于构造函数能够有參数,所以new后面类(class)类型也能够有參数。这些參数即构造函数的參数。

但对创建数组,则无參数,并仅仅调用缺省的构造函数。见下例类说明:

class CGoods{

char Name[21];

int Amount;

float Price;

float Total_value;

public:

CGoods(){}; //缺省构造函数。因已有其它构造函数,系统不会再自己主动生成缺省构造,必须显式声明。 CGoods(char* name,int amount ,float price){

strcpy(Name,name);

Amount=amount;

Price=price;

Total_value=price*amount;  }

……};//类声明结束

以下是调用机制 :

void main(){

int n;

CGoods *pc,*pc1,*pc2;

pc=new CGoods(“hello”,10,118000);

//调用三參数构造函数 pc1=new CGoods(); //调用缺省构造函数 cout<<”输入商品类数组元素数”<<endl;

cin>>n;

pc2=new CGoods[n];

//动态建立数组,不能初始化,调用n次缺省构造函数

……

delete pc;

delete pc1;

delete []pc2; }

申请堆空间之后构造函数执行;

释放堆空间之前析构函数执行;

再次强调:由堆区创建对象数组,仅仅能调用缺省的构造函数,不能调用其它不论什么构造函数。假设没有缺省的构造函数,则不能创建对象数组。

---------------------以下我们再来看一下指针数组和数组指针―――――――――――――

假设你想了解指针最好理解下面的公式 :

(1)int*ptr;//指针所指向的类型是int

  (2)char*ptr;//指针所指向的的类型是char

  (3)int**ptr;//指针所指向的的类型是int* (也就是一个int * 型指针)

  (4)int(*ptr)[3];//指针所指向的的类型是int()[3] //二维指针的声明

(1)指针数组:一个数组里存放的都是同一个类型的指针,通常我们把他叫做指针数组。

比方 int * a[10];它里边放了10个int * 型变量,因为它是一个数组,已经在栈区分配了10个(int * )的空间,也就是32位机上是40个byte,每个空间都能够存放一个int型变量的地址,这个时候你能够为这个数组的每个元素初始化,在,或者单独做个循环去初始化它。

样例:

int * a[2]={ new int(3),new int(4) }; //在栈区里声明一个int * 数组,它的每个元素都在堆区里申请了一个无名变量,并初始化他们为3和4,注意此种声明方式具有缺陷,VC下会报错

比如 :

int * a[2]={new int[3],new int[3]};

delete a[0];

delet a[10];

可是我不建议达内的学生这么写,可能会造成歧义,不是好的风格,而且在VC中会报错,应该写成例如以下 :

int * a[2];

a[0]= new int[3];

a[1]=new int[3];

delete a[0];

delet a[10];

这样申请内存的风格感觉比較符合大家的习惯;因为是数组,所以就不能够delete a;编译会出警告.delete a[1];

注意这里 是一个数组,不能delete [] ;

(2) 数组指针 : 一个指向一维或者多维数组的指针;

int * b=new int[10]; 指向一维数组的指针b ;

注意,这个时候释放空间一定要delete [] ,否则会造成内存泄露, b 就成为了空悬指针.

int (*b2)[10]=new int[10][10]; 注意,这里的b2指向了一个二维int型数组的首地址.

注意:在这里,b2等效于二维数组名,但没有指出其边界,即最高维的元素数量,可是它的最低维数的元素数量必需要指定!就像指向字符的指针,即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。

int(*b3) [30] [20]; //三级指针――>指向三维数组的指针;

int (*b2) [20]; //二级指针;

b3=new int [1] [20] [30];

b2=new int [30] [20];

两个数组都是由600个整数组成,前者是仅仅有一个元素的三维数组,每一个元素为30行20列的二维数组,而还有一个是有30个元素的二维数组,每一个元素为20个元素的一维数组。

删除这两个动态数组可用下式:

delete [] b3; //删除(释放)三维数组;

delete [] b2; //删除(释放)二维数组;

再次重申:这里的b2的类型是int (*) ,这样表示一个指向二维数组的指针。

b3表示一个指向(指向二维数组的指针)的指针,也就是三级指针.

(3)二级指针的指针

看下例 :

int (**p)[2]=new (int(*)[3])[2];

p[0]=new int[2][2];

p[1]=new int[2][2];

p[2]=new int[2][2];

delete [] p[0];

delete [] p[1];

delete [] p[2];

delete [] p;

注意此地方的指针类型为int (*),碰到这样的问题就把外边的[2]先去掉,然后回头先把int ** p=new int(*)[n]申请出来,然后再把外边的[2]附加上去;

p代表了一个指向二级指针的指针,在它申请空间的时候要注意指针的类型,那就是int (*)代表二级指针,而int (**)顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间,那首先要定义它的范围:(int(*)[n])[2],n 个这种二级指针,当中的每个二级指针的最低维是2个元素.(由于要确定一个二级指针的话,它的最低维数是必须指定的,上边已经提到)。然后我们又分别为p[0],p[1],p[2]…在堆里分配了空间,尤其要注意的是:在释放内存的时候一定要为p[0],p[1],p[2],单独delete[]
,否则又会造成内存泄露,在delete[]p 的时候一定先delete p[0]; delete p[1],然后再把给p申请的空间释放掉 delete [] p ……这样会防止内存泄露。

(4)指针的指针:

int ** cc=new (int*)[10]; 声明一个10个元素的数组,数组每一个元素都是一个int *指针,每一个元素还能够单独申请空间,由于cc的类型是int*型的指针,所以你要在堆里申请的话就要用int *来申请;

看下边的样例 (vc & GNU编译器都已经通过);

int ** a= new int * [2];     //申请两个int * 型的空间

a[1]=new int[3];        //为a的第二个元素又申请了3个int 型空间,a[1]指向了此空间首地址处

a[0]=new int[4];        ////为a的第一个元素又申请了4个int 型空间,a[0] 指向了此空间的首地址处

int * b;

a[0][0]=0;

a[0][1]=1;

b=a[0];

delete [] a[0]       //一定要先释放a[0],a[1]的空间,否则会造成内存泄露.;

delete [] a[1];

delete [] a;

b++;

cout<<*b<<endl; //随机数

注意 :由于a 是在堆里申请的无名变量数组,所以在delete 的时候要用delete [] 来释放内存,可是a的每个元素又单独申请了空间,所以在delete [] a之前要先delete [] 掉 a[0],a[1],否则又会造成内存泄露.

(5) 指针数组:

我们再来看看另外一种 :二维指针数组

int *(*c)[3]=new int *[3][2];

假设你对上边的介绍的个种指针类型非常熟悉的话,你一眼就能看出来c是个二级指针,仅仅只是指向了一个二维int * 型的数组而已,也就是二维指针数组。

样例 :

int *(*b)[10]=new int*[2][10];//

b[0][0]=new int[100];

b[0][1]=new int[100];

*b[0][0]=1;

cout <<*b[0][0]<<endl; //打印结果为1

delete [] b[0][0];

delete [] b[0][1];

delete [] b;

cout<<*b[0][0]<<endl; //打印随机数

 这里仅仅为大家还是要注意内存泄露的问题,在这里就不再多说了。

假设看了上边的文章,大家预计就会非常熟悉,这个b是一个二维指针,它指向了一个指针数组

另外一种 :

int **d[2];表示一个拥有两个元素数组,每个元素都是int ** 型,这个指向指针的指针:)

   d无论如何变终究也是个数组,呵呵,

   假设你读懂了上边的,那下边的声明就非常easy了:

   d[0]=new int *[10];

   d[1]=new int * [10];

delete [] d[0];

delete [] d[1];

详细的就不再多说了 :)

二 : 函数指针 

关于函数指针,我想在我们可能须要写个函数,这个函数体内要调用还有一个函数,但是因为项目的进度有限,我们不知道要调用什么样的函数,这个时候可能就须要一个函数指针;

int a();这个一个函数的声明;

ing (*b)();这是一个函数指针的声明;

让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口參数(本例中參数是空)。注意本例中还没有创建指针变量-仅仅是声明了变量类型。眼下能够用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

unsigned psize = sizeof (int (*) ()); 获得函数指针的大小

// 为函数指针声明类型定义

typedef int (*PFUNC) ();

PFUNC是一个函数指针,它指向的函数没有输入參数,返回int。使用这个类型定义名能够隐藏复杂的函数指针语法,就我本人强烈建议我们大内弟子使用这样的方式来定义;

以下是一个样例,一个简单函数指针的回调(在GNU编译器上通过,在VC上须要改变一个头文件就OK了)

#include<iostream> //GNU 编译器 g++ 实现

using namespace std;

#define DF(F) int F(){ cout<<"this is in function "<<#F<<endl;

return 0;

}

//声明定义DF(F)替代 int F();函数;

DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g); DF(h); DF(i); //声明定义函数 a b c d e f g h i

// int (*pfunc)(); //一个简单函数指针的声明

typedef int(*FUNC)(); //一个函数指针类型的声明

FUNC ff[] = {a,b,c,d,e,f,g,h,i}; //声明一个函数指针数组,并初始化为以上声明的a,b,c,d,e,f,g,h,i函数

FUNC func3(FUNC vv){ //定义函数func3,传入一个函数指针,而且返回一个相同类型的函数指针

vv();

return vv;

}

int main(){

for(int i=0;i<sizeof(ff)/sizeof (FUNC);i++){ //循环调用函数指针

FUNC r=func3(ff[ i ]);

cout<<r()<<endl; //输出返回值,仅仅是返回了0

}

return 0;

}

到眼下为止,我们仅仅讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。很多编译器有几种调用规范。如在Visual C++中,能够在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默觉得_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,參数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及參数传递机制(堆栈,CPU寄存器等)。

好了,先到此为止吧,写这篇文章耗费了基本上快半天的时间了,非常多事情还没有做,等改天有时间再回来整理,全部的源程序都放在openlab3server上我的文件夹下lib/cpp下,大家能够去拿。不知道的登陆openlab3 然后cd ~chengx/lib/cpp就能够看到了。

还有非常复杂的声明可能也是一种挑战 比方<<Think in c++>>里的

int (*(*f4())[10]();的声明,f4是一个返回指针的函数,该指针指向了含有10个函数指针的数组,这些函数返回整形值;不是这个函数有特别之处,而是Bruce Eckel 说的“从右到左的辨认规则”是一种非常好的方法,值得我们去学习,感谢他:)

最后我想应该跟大家说一下,敲代码应该就象JERRY所说的:简单就是美;我们应该遵循一个原则 : KISS (Keep It Simple,Stupid ,尽量保持程序简单 出自 :《Practical C programming》),把自己的程序尽量的简单明了,这是个很很好的习惯。

转自:http://blog.sina.com.cn/s/blog_5041fdd70100w787.html

c++ 依据输入动态声明数组(一维,二维),布布扣,bubuko.com

时间: 2024-10-05 12:09:52

c++ 依据输入动态声明数组(一维,二维)的相关文章

【树状数组】树状数组一维二维模板

以下模板都是点更新,区间查询,如果是区间更新点查询,只需将利用lowbit的循环方向倒过来 一维: inline int lowbit(int x) { return x & -x; } void add(int x, int val) { for(int i = x; i <= n; i += lowbit(i)) C[i] += val; } int sum(int x) { int ret = 0; for(int i = x; i > 0; i -= lowbit(i)) re

一维动态数组和二维动态数组的创建和使用

#include<stdio.h> #include<malloc.h> void main(){ int *a,n=10,i; /* calloc()函数的原型是:(void *)calloc(unsigned n,unsigned size) calloc()函数用于向系统动态申请n个,每个占sizege字节的内存单元,函数返回值为所申请的内存空间首地址 malloc和calloc主要区别在于,当系统的内存只剩下一些非常小的碎片时,用calloc函数设计的动态数组的时间效率优于

java数组(一维数组,二维数组)

数组初始化: 1,动态初始化:数组定义与为数组分配空间和赋值的操作分开进行 2,静态初始化:在定义数组的同时就为数组元素分配空间并赋值 3,默认初始化:数组是引用类型,它的元素相当于类的成员变量,因此,数组分配空间之后 每个元素也被按照成员的规则被隐士的初始化值 ------------------------------------------------. 一维数组: package day06; import java.util.Date; /*** * 数组演示 * 数组的声明: * t

C# 数组、一维数组、二维数组、多维数组、锯齿数组

C#  数组.一维数组.二维数组.多维数组.锯齿数组 一.数组: 如果需要使用同一类型的对象,就可以使用数组,数组是一种数据结构,它可以包含同一类型的多个元素.它的长度是固定的,如长度未知的情况下,请使用集合. 二.一维数组: 声明及初始化: class Program { static void Main(string[] args) { //方法一 int[] num = new int[3];//声明一个长度为3的值类型的数组: num[0] = 3;//为数组赋值: num[1] = 5

java基础中的一维数组和二维数组

1.数组:具有相同数据类型的一组数据的集合. 一维数组 2.创建一维数组 a.数组作为对象允许使用new关键字进行内存分配,使用数组之前,必须首先定义数组变量所属类型,即声明数组.  声明数组: 语法:数组元素类型   数组名字[]; 数组元素类型[]  数组名字: 注释:数组元素类型:决定了数组的数据类型,包括基本数据类型和费基本数据类型. 数组名字:为一个合法的标识符,自己去定义 []:指明该变量是一个数组类型变量,单个"[]"表示要创建的是一维数组. 例如:int arr[];

C#一维数组,二维数组,多维数组

所谓的数组,就是相同数据类型的元素的元素按一定顺数排列的集合,就是把有限个类型相同的变量用一名字命名,然后用编号区分他们的变量的集合,这个名字成为数组名,编号称为下标.组成数组的各个变量成为数组的分量,也称为数组的元素,有时也称为下标变量,数组是在程序设计中,为了处理方便,把具有相同类型的若干变量按有序的形式组织起来的一种形式.这些按序排列的同类数据元素的集合成为数组. 数组又分为一维数组,二维数组,多维数组 一维数组用一个整数来索引,多维数组用两个或多个整数来索引. 一.数据类型[] 变量名

16.10.20 4th 1蛤蟆爬井 2一维数组 3二维数组

摘要 1蛤蟆爬井 2一维数组 3二维数组 例子 井深10米, 蛤蟆白天爬5m,晚上落4米,求几天爬出来. //思路,用循环语句来做,for因为是未知次数所以排除,while 先判断后运行排除, dowhile,先爬在判断所以可以 int gaodu = 0; int tianshu = 0; boolean tianse = true; do{ if(tainse){ //白天爬5米 gaodu+=5; //爬完后改黑天 tianse = false; //天数+1 tianshu +=1; }

一维数组,二维数组,三维数组,数组与指针,结构体数组,通过改变指针类型改变访问数组的方式

 打印数组中的每个元素,打印每个元素的地址: #include <stdio.h> #include <stdlib.h> void main(void) { int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; for (int *p = a; p < a + 10;p++)  //指针类型决定4个字节 { printf("\n%p,%d", p, *p); } getchar(); } 指针数组 #inclu

7、一维数组和二维数组耳朵创建,输出,Arrays.fill()替换

package com.xxx.xxx; import java.util.Arrays; public class demo8 { /** * 1.初始化一维数组 使用一维数组 * 2.初始化二维数组 使用二维数组 * 3.填充替换数组元素 * @param args */ public static void main(String[] args) { //1.第一种初始化方式 int arr[] = new int[]{1,2,3,4,5,6}; //一维数组 //1.1.第二种初始化方式