一维数组的声明与字符数组
C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合。数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量。
数组的声明并不是声明一个个单独的变量,比如 number0、number1、...、number99,而是声明一个数组变量,比如 numbers,然后使用 numbers[0]、numbers[1]、...、numbers[99] 来代表一个个单独的变量。数组中的特定元素可以通过索引访问。
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。
声明数组(三要素:类型、数组名、元素数)
在 C++ 中要声明一个数组,需要指定元素的类型和元素的数量,如下所示:
type arrayName [ arraySize ];
这叫做一维数组。arraySize 必须是一个大于等于1的整数常量表达式,type 可以是任意有效的 C++ 数据类型,即内置数据类型或类类型。除引用之外,数组元素的类型还可以是任意的复合类型。数组的定义中不能引用,即没有引用数组,这是因为应用不能赋值,而数组中的元素必须可以被赋值。
int& a[10]; //定义错误
虽然没有引用数组,但数组可以引用。
int a[6] = {0,2,4,6,8,10};
int (&p) [6] = a;
如上,p是数组a的引用。数组的引用可以用于函数实参传递。此时可确保传过来的数组长度合乎要求。
当数组的大小未知,需要动态申明一维数组,申明格式如下:
int* a = new int[n];
当数组使用完毕,需要释放内存空间
delete []a;
数组的引用
在 C++中,数组永远不会按值传递,它是传递第一个元素,准确地说是第 0个 的指针。
例如,如下声明 :
void putValues( int[ 10 ] );
被编译器视为
void putValues( int* );
数组的长度与参数声明无关,因此,下列三个声明是等价的:
// 三个等价的 putValues()声明
void putValues( int* );
void putValues( int[] );
void putValues( int[ 10 ] );
因为数组被传递为指针 所以这对程序员有两个含义:
1. 在被调函数内对参数数组的改变将被应用到数组实参上而不是本地拷贝上,当用作实参的数组必须保持不变时,程序员需要保留原始数组的拷贝函数可以通过把参数类型声明为 const 来表明不希望改变数组元素。
void putValues( const int[ 10 ] );
2. 数组长度不是参数类型的一部分,函数不知道传递给它的数组的实际长度,编泽器也不知道,当编译器对实参类型进行参数类型检查时,并不检查数组的长度。例如:
void putValues( int[ 10 ] ); // 视为 int*
int main()
{
int i, j[ 2 ];
putValues( &i ); // ok: &i 是 int*; 潜在的运行错误
putValues( j ); // ok: j 被转换成第 0 个元素的指针
// 实参类型为 int*: 潜在的运行错误
return 0;
}
参数的类型检查只能保证putValues()的两次调用都提供了int*型的实参,类型检查不能检验实参是一个 10元素的数组 。习惯上, C风格字符串是字符的数组,它用一个空字符编码作为结尾。但是所有其他类型,包括希望处理内含空字符的字符数组必须以某种方式在向函数传递实参时使其知道它的长度。
一种常见的机制是提供一个含有数组长度的额外参数。例如:
void putValues( int[], int size );
int main()
{
int i, j[ 2 ];
putValues( &i, 1 );
putValues( j, 2 );
return 0;
}
另外一种机制是将参数声明为数组的引用
当参数是一个数组类型的引用时,数组长度成为参数和实参类型的一部分,编译器检查数组实参的长度与在函数参数类型中指定的长度是否匹配。
// 参数为 10 个 int 的数组
// parameter is a reference to an array of 10 ints
void putValues( int (&arr)[10] );//不能写成&arr[10],因为下标操作符的优先级较高
int main() {
int i, j[ 2 ];
putValues( i ); // 错误: 实参不是 10 个 int 的数组
putValues( j ); // 错误: 实参不是 10 个 int 的数组
return 0;
}
C风格字符串与字符数组
1.C风格字符串(两种)
1)字符串常量:以双引号括起来的字符序列。为了兼容C语言,C++中所有字符串常量都由编译器自动在末尾添加一个空字符。字符常量‘A’表示单个字符A,字符串常量"A"表示A和空字符('\0')两个字符。如char ca1 = "C++";
2)末未添加了‘\0’的字符数组。即末尾显式的添加null字符('\0'),如char ca1 = {'C','+','+','\0'};
可见,C风格字符串末尾必须有一个字符‘\0’(结束符null)。
2.字符数组字符串输出遇'\0'终止:
char c[5] = {'a','b','\0','c','\0'}
printf("%s",c)
运行结果:ab。
二维数组
二维数组的声明与初始化
int a[2][3] = {
{0,1,2},
{3,4,5}
};
C++中规定,在声明和初始化一个二维数组时,如果对所有的元素赋值,则第一维(行数)可以省略,但第二维不能省略。同样,声明更高维的数组时,也只有第一维可以省略。
int array[][3] = {1,2,3,4,5,6};//正确
int disp(int a [][]);//错误,不能省略列数
int a[3][2] = {(0,1),(2,3),(4,5)}; //等价于int a[3][2] = {1,3,5};
int* p = a[0];
printf("%d",p[0]);
上述代码,用到了“逗号运算符”。赋值运算符比逗号运算符优先级高。
逗号运算符是指,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
在C/C++中,二维数组按照行优先顺序连续存储。
行优先存储二维数组向一维数组的转化,a[x][y] = b[x*列数+y]
较早的编译器是不允许这样做的,所以一些书籍比如以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;
关于C++ const 的全面总结
2、指针使用CONST(1) 指针本身是常量不可变 char* const pContent;
(2) 指针所指向的内容是常量不可变 const char *pContent;
(3) 两者都不可变 const char* const pContent;
(4) 还有其中区别方法,沿着*号划一条线:
如果const位于*的左侧,则const就是用来修饰指针所指向的变量,即指针指向为常量;
如果const位于*的右侧,const就是修饰指针本身,即指针本身是常量。
数组指针、指针数组与数组名的指针操作
1.指针运算——算术运算、关系运算
算术运算的两种形式:
1)指针+/-整数 (用于指向数组中某个元素的指针,+1即指向下一个元素)
2)指针-指针(两个指针都指向同一个数组的元素,以数组元素的长度为单位) 关系运算(前提:两个指针都指向同一个数组的元素) 2.指针数组与数组指针
指针数组:一个数组里面装着指针,即也是一个数组指针数组
int *a[10];
数组指针:指向一个数组的指针,它其实还是指针,只不过它指向一个数组。其中,[]的优先级比*高,必须添加(*p)。
int (*p)[10];
二维数组的数组名是一个数组指针
int a[4][10];
int (*p)[10];
p = a;//a的类型是int(*)[10]
int a[10];
int (*p)[10] = &a;//注意此处是&a,不是a,一维数组的数组名a的类型是int*,&a的类型是int(*)[10],即指向数组的指针
int *q = a;
这里p是指向有10个元素整型数组的指针,*p的大小是20个字节,故p+1跳过40个字节;而q是指向整型的指针,*q的大小是4个字节,故q+1跳过4个字节。&a[0]=a,即首元素的地址。注意数组的首地址是常量,不能进行赋值,a+=2编译错误。
二维数组其实是每个元素本身都是数组的数组,以int a[4][5];为例,有一下总结:
&a的类型为int(*)[4][5];
a+i的类型为int(*)[5];
*(a+i)的类型为int*;
*(*(a+i)+j)的类型为int;
*(a+i)= a[i];
*(*(a+i)+j) = *(a[i]+j) = a[i][j]。