|
板凳
楼主 |
发表于 2020-1-28 18:35:16
|
只看该作者
--------------一维数组----------------
数组可以初始化,即在定义时,使它包含程序马上能使用的值。
例如,下面的代码定义了一个全局数组,并用一组Fibonacci数初始化:
int iArray[10]={1,1,2,3,5,8,13,21,34,55); //初始化
void main()
{
//...
}
初始化数组的值的个数不能多于数组元素个数,初始化数组的值也不能通过跳过逗号的方式来省略,这在C中是允许的,但在C++中不允许。
例如,下面的代码对数组进行初始化是错误的:
int arrayl[5]={1,2,3,4,5,6}; //error: 初始化值个数多于数组元素个数
int array2[5]={1,,2,3,4}; //error:初始化值不能省略
int array3[5]={1,2,3,}; //error:初始化值不能省略
int array4[5]={}; //error:语法格式错误
void main()
{
//...
}
初始化值的个数可少于数组元素个数。当初始化值的个数少于数组元素个数时,前面的按序初始化相应值, 后面的初始化为0(全局或静态数组)或为不确定值(局部数组)。
例如,下面的程序对数组进行初始化:
//*********************
//** ch7_2.cpp **
//*********************
#include <iostream.h>
int array1[5]={1,2,3};
static int array2[5]={1};
void main()
{
int arr1[5]={2};
static int arr2[5]={1,2};
int n;
cout <<"global:/n";
for(n=0; n<5; n++)
cout <<" " <<array1[n];
cout <<"/nglobal static:/n";
for(n=0; n<5; n++)
cout <<" " <<array2[n];
cout <<"/nlocal:/n";
for(n=0; n<5; n++)
cout <<" " <<arr1[n];
cout <<"/nlocal static:/n";
for(n=0; n<5; n++)
cout <<" " <<arr2[n];
cout <<endl;
}
运行结果为:
global:
l 2 3 0 0
global static:
1 0 0 0 0
local:
2 23567 23567 23567 23567
local static:
1 2 0 0 0
例中,全局数组和全局静态数组的初始化是在主函数运行之前完成的,而局部数组和局部静态数组的初始化是在进入主函数后完成的。
全局数组arrayl[5]对于初始化表的值按序初始化为1,2,3,还有两个元素的值则按默认初始化为0。
全局静态数组array2[5]与全局数组的初始化情况一样,初始化表值(1)表示第1个元素的值,而不是指全部数组元素都为1。
局部数组arrl[5]根据初始化表值的内容按序初始化, 由于初始化表值只有1个,所以还有4个元素的值为不确定。在这里均为数值23567。
局部静态数组arr2[5]先根据初始化表按序初始化,其余3个数组元素的值默认初始化为0。
2.初始化字符数组
初始化字符数组有两种方法,一种是:
char array[10]={"hello"};
另一种是:
char array[10]={'h','e','l','l','/0'};
第一种方法用途较广,初始化时,系统自动在数组没有填值的位置用,'/0'补上。另外, 这种方法中的花括号可以省略,即能表示成:
char array[10]="hello";
第二种方法一次一个元素地初始化数组,如同初始化整型数组。这种方法通常用于输入不容易在键盘上生成的那些不可见字符。
例如,下面的代码中初始化值为若干制表符:
char chArray[5]={'/t','/t','/t','/t','/0');
这里不要忘记为最后的,'/0'分配空间。如果要初始化一个字符串"hello",那为它定义的数组至少有6个数组元素。
例如,下面的代码给数组初始化,但会引起不可预料的错误:
char array[5]="hello";
该代码不会引起编译错误,但由于改写了数组空间以外的内存单元,所以是危险的。
3.省略数组大小
有初始化的数组定义可以省略方括号中的数组大小。
例如,下面的代码中数组定义为5个元素:
int a[]={2,4,6,8,10};
编译时必须知道数组的大小。通常,声明数组时方括号内的数字决定了数组的大小。有初始化的数组定义又省略方括号中的数组大小时,编译器统计花括号之间的元素个数,以求出数组的大小。
例如,下面的代码产生相同的结果:
static int a1[5]={1,2,3,4,5};
static int a2[]={1,2,3,4,5};
让编译器得出初始化数组的大小有几个好处。它常常用于初始化一个元素个数在初始化中确定的数组,提供程序员修改元素个数的机会。
在没有规定数组大小的情况下,怎么知道数组的大小呢? sizeof操作解决了该问题。 例如,下面的代码用sizeof确定数组的大小:
//*********************
//** ch7_3.cpp **
//*********************
#include <iostream.h>
void main()
{
static int a[]={1,2,4,8,16};
for(int i=0; i<(sizeof(a)/sizeof(int)); i++)
cout <<a <<" ";
cout <<endl;
}
运行结果为:
1 2 4 8 16
sizeof操作使for循环自动调整次数。如果要从初始化a数组的集合中增删元素,只需重新编译即可,其他内容无须更动。
每个数组所占的存储量都可以用sizeof操作来确定! sizeof返回指定项的字节数。sizeof常用于数组,使代码可在16位机器和32位机器之间移植:
对于字符串的初始化,要注意数组实际分配的空间大小是字符串中字符个数加上末尾的,'/0',结束符。
例如,下面的代码定义一个字符数组:
//*********************
//** ch7_4.cpp **
//*********************
#include <iostream.h>
void main()
{
char ch[]="how are you";
cout <<"size of array: " <<sizeof(ch) <<endl;
cout <<"size of string: " <<strlen("how are you") <<endl;
}
运行结果为:
size of array:12
size of string:ll
例中,数组大小为12,而字符串长度为11。
省略数组大小只能在有初始化的数组定义中。
例如,下面的代码将产生一个编译错误:
int a[];//error:没有确定数组大小
在定义数组的场合,无论如何,编译器必须知道数组的大小。
----------------二维数组----------------------------------
一、数组定义和初始化
1: 一维数组初始化:
2: 标准方式一: int value[100]; // value的值不定,没有初始化
3: 标准方式二: int value[100] = {1,2}; // value[0]和value[1]的值分别为1和2,而没有定义的value[i>1]
4: // 则初始化为0
5: 指针方式: int* value = new int[n]; // 未初始化
6: delete []value; // 一定不能忘了删除数组空间
7:
8: 二维数组初始化:
9: 标准方式一: int value[9][9]; // value[j]的值不定,没有初始化
10: 标准方式二: int value[9][9] = {{1,1},{2}}; //value[0][0,1]和value[1][0]的值初始化,其他初始化为0
11: 指针方式一: int (*value)[n] = new int[m][n];
12: delete []value; // n必须为常量,调用直观。未初始化
13: 指针方式二: int** value = new int* [m];
14: for(i) value = new int[n];
15: for(i) delete []value;
16: delete []value; // 多次析构,存储麻烦,未初始化
17: 指针方式三: int * value = new int[3][4]; // 数组的存储是按行存储的
18: delete []value; // 一定要进行内存释放,否则会造成内存泄露
19:
20: 多维数组初始化:
21: 指针方式: int * value = new int[m][3][4]; // 只有第一维可以是变量,其他几维必须都是常量,否则会报错
22: delete []value; // 一定要进行内存释放,否则会造成内存泄露
数组初始化的大括号后面要加“;”来表示结束。
数组访问:
指针形式:如二维数组value[j]的访问:
*(value + j) 或
(*(value + i))[j]
二、数组作为参数传递
1: 一维数组参数传递:
2: void Func(int *value);
3: 或者是
4: void Func(int value[]);
5:
6: 二维数组传递:
7: 定义是 int **value;的传递
8: void Func(int **value);
9: 定义是 int (*value)[n] = new int[m][n];的传递
10: void func(int (*value)[n]); // sizeof(p)=4,sizeof(*value)=sizeof(int)*n;
三、数组与指针关系
1、数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;
2、数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;
3、指向数组的指针则是另外一种变量类型,(在win32平台下,长度为4),仅仅意味着数组存放地址。
4、数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针,而且在其失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。
四、数组的存储格式
多维数组在内存中存储时是按照最低维连续的格式存储的,如二维数组{{1,2},{3,4}}在内存中的位置是这样顺序的“1,3,2,4”,这跟matlab是有区别的,matlab是按列进行存储的。在使用指针进行索引时很有用。
五、字符数组
char类型的数组被称作字符数组,通常用来存储字符串。字符串是附加有特殊字符(串尾标志)的字符序列。串终止字符表明字符串已经结束,该字符由转义序列‘\0’定义,有时被称为空字符,占用一个字节,其中8位全为0。这种形式的字符串通常被称为C型字符串,因为以这样的方式定义字符串是在C语言中推出的,在C++一般使用string,而MFC中则定义了CString类。
字符串中每个字符占用一个字节,算上最后的空字符,字符串需要的字节数要比包含的字节数多一个。如:
char movie_star[15] = “Marilyn Monroe”;
这里字符串是14个字符,但是要定义15个字符串的数组。也可以不指定字符数组的个数。如:
char movie_star[] = “Marilyn Monroe”;
六、内存泄露
我们定义了一个指针,然后给它赋予了一个地址值,然后又不再使用,但是没有delete,那么当给指针赋予其他的地址值时,原来的内存将无法释放,这就叫做内存泄露。
http://hi.baidu.com/%CC%EC%D1%C4 ... 9979123bf3cf59.html
先给出问题:
像下面这样的数组,在函数中如何传参?也就是说如何保证虚参与实参类型一致。
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
函数原型:
void func1( char (*a)[10] )
void func2( char **a )
调用:
func1( str_arr );
func2( str_array);
如果向func2()中传入str_arr会怎么样呢?编译器会警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换。即虚参与实参类型不一致。
同理,也不能向func1()中传入str_array。
我们给出完整的测试程序:
/********二维数组传参测试程序***************/
#include <stdio.h>
void func1( char (*a)[10])
{
int i;
for(i=0;i<3;i++)
printf("%s\n",a);
}
void func2( char **a )
{
int i;
for(i=0;i<3;i++)
printf("%s\n",*(a+i));
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char *str[3] = {"a","b","c"};/*这两种表达效果一样*/
func1(str_arr);
func2(str_array);
return 0;
}
/******************end*******************/
运行结果:
[root@localhost ansi_c]# gcc test.c
[root@localhost ansi_c]# ./a.out
yes
no
uncertain
yes
no
unsure
[root@localhost ansi_c]#
如果将
func1(str_arr);
func2(str_array);
改成:
func1(str_array);
func2(str_arr);
会怎么呢?
[root@localhost ansi_c]# gcc test.c
test.c: 在函数 ‘main’ 中:
test.c:22: 警告:传递参数 1 (属于 ‘func1’)时在不兼容的指针类型间转换
test.c:23: 警告:传递参数 1 (属于 ‘func2’)时在不兼容的指针类型间转换
这两种数组的正确赋值应该如下:
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char (*pa)[10] = str_arr;
char **p = str_array;
pa和p才是和他们相一致的类型。
当然,如果不是传参的话,在main()函数中就不会发生这么多烦恼了。
/*************非传参时的情况************************/
#include <stdio.h>
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
char *str_array[] = {"yes","no","unsure"};
char *str[3] = {"a","b","c"};
int i;
for(i=0;i<3;i++)
printf("%s\n",str_arr);
for(i=0;i<3;i++)
printf("%s\n",str_array);
return 0;
}
/*************************************/
运行结果:
[root@localhost ansi_c]# gcc test1.c
[root@localhost ansi_c]# ./a.out
yes
no
uncertain
yes
no
unsure
[root@localhost ansi_c]#
这说明了一点,在没传参之前,main()函数清楚它们都是二维数组。对于上面给出的两种函数原型:
函数原型:
void func1( char (*a)[10] )
void func2( char **a )
这两种传参方法有什么不同呢?这们对实参有什么要求呢?
上面只是抛出了一个问题,我在这里的主题是想搞清楚二维数组传参有什么奥秘,而非只针对这一个问题提出解决方法。
后面从基础的开始讨论。
我们先看看教材上怎么讲这一块的,
谭浩强的《C程序设计》二维数组作为参数传递,原文如下(略有改变,请原谅):
[原文开始]
可以用二维数组名作为实参或者形参,在被调用函数中对形参数组定义时可以可以指定所有维数的大小,也可以省略第一维的大小说明,如:
void Func(int array[3][10]);
void Func(int array[][10]);
二者都是合法而且等价,但是不能把第二维或者更高维的大小省略,如下面的定义是不合法的:
void Func(int array[][]);
因为从实参传递来的是数组的起始地址,在内存中按数组排列规则存放(按行存放),而并不区分行和列,如果在形参中不说明列数,则系统无法决定应为多少行多少列,不能只指定一维而不指定第二维,下面写法是错误的:
void Func(int array[3][]);
实参数组维数可以大于形参数组,例如实参数组定义为:
void Func(int array[3][10]);
而形参数组定义为:
int array[5][10];
这时形参数组只取实参数组的一部分,其余部分不起作用。
[原文结束]
也就是说多维数组传参要指定第二维或者更高维的大小,可以省略第一维的大小。
像 int array[3][4],要传参的话,函数原型可以为下面三种的任一种:
void func(int a[3][4])
void func(int a[][4])
void func(int (*a)[4])
调用时为:func(array);
同时教材里也说了,如果在型参里不说明列数,则编译器无法决定应为多少行多少列。那么能不能把
int array[3][4]的数组名 array 传给 void func(int **a)呢?
看下面:
/**********************************/
#include <stdio.h>
int main()
{
int array[3][4];
int **p = array;
}
**********************************/
root@localhost ansi_c]# gcc test2.c
test2.c: 在函数 ‘main’ 中:
test2.c:5: 警告:从不兼容的指针类型初始化
[root@localhost ansi_c]#
虽然从本质上讲int array[3][4] 的数组名相当于二级指针,但它不等同于一般的二级指针,因为它还含有数组相关的信息,所以在main函数中:
char str_arr[3][10] = {"yes","no","uncertain"};
for(i=0;i<3;i++)
printf( "%s\n",str_arr+i );
它可以通过下标,每次跳过10个字节来寻址。我们再看看编译器是怎样处理数组的:
对于数组 int p[m][n];
如果要取p[j]的值(i>=0 && i<m && 0<=j && j < n),
编译器是这样寻址的:
p + i*n + j;
我们再看一个例子:
/*********************二维数组传参*****************************/
#include <stdio.h>
void fun( int *a, int m, int n)
{
int i,j;
for( i=0; i<m; ++i)
{
for(j=0;j<n;++j)
{
printf("%d ", *( a+i*n+j ) );
}
putchar('\n');
}
}
void func( int *a, int m, int n)
{
int i,j;
for( i=0;i<m*n;++i)
{
printf("%d ",a);
}
putchar('\n');
}
int main()
{
int a[3][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
};
fun( (int *)a, 3,3);
func( &a[0][0],3,3);
func( (int *)a, 3,3);
return 0;
}
********************end******************************/
[root@localhost ansi_c]# gcc test4.c
[root@localhost ansi_c]# ./a.out
1 1 1
2 2 2
3 3 3
1 1 1 2 2 2 3 3 3
1 1 1 2 2 2 3 3 3
[root@localhost ansi_c]#
我们来看其中的要点,
数组为:
int a[3][3] =
{
{1, 1, 1},
{2, 2, 2},
{3, 3, 3}
};
函数原型和调用为:
原型:
void fun( int *a, int m, int n)
{
.............
printf("%d ", *( a+i*n+j ) );
.............
}
调用:
fun( (int *)a, 3,3);
另一个函数为:
原型:
void func( int *a, int m, int n)
{
.............
printf("%d ",a);
.............
}
调用:
func( &a[0][0],3,3);
func( (int *)a, 3,3);
我们发现这两种方式都能正常执行,我们把一个二级指针,强制转换成了一级指针传了进去,并在函数中模仿编译器数组的寻址方式:*( a+i*n+j )。
我们再看看二维字符数组的例子:
/*******************二维字符数组*******************************/
#include <stdio.h>
void f( char **a, int n)
{
int i;
printf("%c\n",*( (char*)a+0 ) );
printf("%c\n",((char * )a)[n] );
puts("------------OK");
for(i=0;i<3;i++)
printf("%s\n",(char*)a+i*n );
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
f( (char **)str_arr, 10);
return 0;
}
/****************end*************************/
运行结果:
[root@localhost ansi_c]# ./a.out
y
n
------------OK
yes
no
uncertain
[root@localhost ansi_c]#
这里也做了强制类型转换,转换成字符指针,
printf("%s\n",(char*)a+i*n ); 每个字符串的地址就是数组中字符'y'、'n'、'u'的地址,
printf("%c\n",*( (char*)a+0 ) );字符在数组中的排列是顺序的,可以用 *( (char*)a+i )或 ((char * )a) 表示。
当然这个程序也可以改成这样,完全不用二级指针:
/*****************************************************************/
#include <stdio.h>
void f( char *a, int n)
{
int i;
printf("%c\n",*( a+0 ) );
printf("%c\n",(a)[n] );
puts("------------OK");
for(i=0;i<3;i++)
printf("%s\n",a+i*n );
}
int main()
{
char str_arr[3][10] = {"yes","no","uncertain"};
f( (char *)str_arr, 10);
return 0;
}
/*****************************************************************/
归根结底,还是把它转成一级指针来用。
下面做个小结:
数组传参
数组:
int array[4][10];
函数原型:
void func1( int a[][10] );
void func2( int (*a)[10] );
void func3( int *p, int col, int row );
函数调用:
func1( array );
func2( array );
func3( (int *)array, 4, 10 );
容易出错的地方:
int arr[][10];
int **p = arr;
这种方式是错误的.
应该是
int (*p)[10] = arr;
同理,也不能将arr传给fun( int **p)
另外数组传参之后会降级为指针,如:
#include <stdio.h>
void Test(char a[][2])
{
int size = sizeof( a );//4
}
int main(void)
{
char a[3][2] = {'a','b','c','d','e','f'};
int size =sizeof( a );//6
Test( a );
return 0;
}
来源:http://hi.baidu.com/d_life/blog/ ... d363e9ce1b3e2a.html
函数原型:要求传入一个动态二维数组
void func1(int **p,int row, int column)
{
}
调用:
int main()
{
int m,n;
int **b;
cin >> m;
cin >> n;
b = new int *[m];
for(int i=0; i<m; i++)
{
b = new int[n];
};
func1(b,m,n);
return 0;
}
我习惯的做法是不用指针数组,定义一个大块(这种情况主要面向每行行数相同):比如现在有一个W*H的矩阵(H个长度为W的数组),你就直接定义一个float型指针: float* pfBuffer;然后动态分配大小 pfBuffer = new float[W*H];这个buffer在用完之后要调用 delete pfBuffer;来释放.你传递这个float指针,传递行列数之后,你如果要访问y行x列的话,只要算一下它在哪儿, int addr = y*W+x;就是其"地址"了,你要访问它,直接使用pfBuffer[addr]就OK了,实际上我做图象处理的时候全部这样做,因为这样的地址访问很明了,不会给阅读带来不便,而且作为大部分的时候,我们用矩阵比较多,列数不等的情况很少。这只是个人见解。
来源:http://www.sunxin.org/forum/thread/5416.html?page=1
|
|