夏天/isummer

Sun of my life !Talk is cheap, Show me the code! 追风赶月莫停留,平芜尽处是春山~

博客园 首页 新随笔 联系 管理

C++中的数组问题

1. 数组赋值与初始化

(1)直接初始化:

int arr[3]={1,2,3};

(2)遍历访问初始化;

for(i = 0;i< 3;i++)
   //直接读入,或者用别的数组,以及别的(i+1)等。

(3)内存操作函数;

memset(arr,3,abs);//abs为另一个已知的数组。

(4)字符串赋值函数,仅限于char型数组。

strcpy(arr,abs) ;abs为一字符串或者char型数组。

2. 数组操作的函数

(1)memset: 作用:Fill block of memory
void * memset ( void * ptr, int value, size_t num );

(2)memcpy: 作用:Copy block of memory
void * memcpy ( void * destination, const void * source, size_t num );

(3)memmove: 作用:Move block of memory
void * memmove ( void * destination, const void * source, size_t num );

  

3. 求数组的长度

    int a[] = {1,2,3,4,5,6};
    int length;
    length = sizeof(a) / sizeof(a[0]);

 4. 函数返回值是数组的问题

一个异常的程序如下:

    #include<stdio.h>
    #define N 5
    int *print()
    {
        int a[N];
        int i;
        for(i=0;i<N;i )
            a[i]=i;

        return a;
    }
    int main()
    {
        int *b,i;
        b=print();
        
        for(i=0;i<N;i )
         printf("%d\n",b[i]);

        return 0;
    }

这个例子就是一个函数返回数组,运行结果是错误的!!!

原因在于: 在函数print()里面 ,数组a[N]是一个局部变量,当你函数执行完之后,会自动释放其空间,所以 return a这句只是返回了一个指向数组a[N]的地值.而在主函数中,b应该接收的是数组a[N]的地址(即数组本身的地址),而它所占用的空间随着函数的调用完 毕也随之被释放掉了,所以得到的答案是不正确的.

程序修改为如下:

    #include<stdio.h>
    #include<stdlib.h>
    #define N 5
    int *print()
    {
        static int a[N];
        int i;
        for(i=0;i<N;i )
            a[i]=i;

        return a;
    }
    int *print1()
    {
        int *a;
        int i;
        a=(int *)malloc(N);
        for(i=0;i<N;i )
        {
            a[i]=i;
        }

        return a;
    }
    int main()
    {
        int *b;
    //    int b[N];
        int i;
        b=print1();
        
        for(i=0;i<N;i )
         printf("%d\n",b[i]);

        return 0;
    }

无论是调用print()函数还是调用print1()函数都能得到正确结果.原因如下

调用print()函数:
     在数组a[N]前面加入了static关键字,它就使得a[N]存放在内存中的静态存储区中,所占用的存储单元不释放直到整个整个程序运行结束.所以当 主函数调用完print()函数后,该空间依然存在.所以main()函数中b指针接收到这个数组的首地值后可以访问数组中的元素.
  
调用print1()函数:
     把数组a[N]换为指针*a,再给这个指针申请空间,也可以正常运行.因为当给指针a申请空间时,给指针分配的空间在堆上,堆上的空间是由程序员自动给 予分配和释放的.若程序员不释放,程序结束时可能由OS释放.所以main函数中b指针也可以接收到这段空间的首地值,得到正确的答案.
  
注意:
  当把main函数中的 int *b注释掉 换成int b[N] 会出现错误:
test.c:30: warning: assignment makes integer from pointer without a cast
或者test.c:30: error: incompatible types when assigning to type ‘int[5]’ from type ‘int *’
  都不能得到正确结果,原因如下:
main 函数中 b得到的返回值是该数组的首地值,如果是*b,就是指针b指向这个数组的首地值,使指针变量向后移动就可以访问该数组中的所有元素.  
如果是b[N]的话,相当于编译器在栈上给数组b[N]分配了N个int空间,所以b指向a的首地值,不能通过这个地址,修改其自身申请的值.
只能通 过一个指针通过这个首地值,让指针向后移动来访问源数据.而且因为b没有进行初始化,所以得到的是随机值
5. 一维数组作为函数参数
实例程序如下:
#include <iostream>
using namespace std;

void fun1(int p[])
{
}

void fun2(int* p)
{
}

void fun3(int p[8])//即使a[10]作为参数传递进来,也不会编译出错,因为传递进来的实际上只是a;
{
}

void fun4(int (&p)[10])
{
}
int main()
{
    int a[10]={1,5};
    fun1(a);
    fun2(a);
    fun3(a);
    fun4(a);
    return 0;
}

对于数组a[10]作为实参,对所有的以数组名字(数组的首地址的指针)作为实参的函数的调用得用fun1(a),fun3(a),而不能用fun1(a[10])和fun3(a[]),否则出现编译错误,这是由于数组的大小不作为实参的内容传递的原因。

当将void fun4(int (&p)[10])改成void fun4(int (&p)[8])时,编译会出错,提示 error C2664: “fun4”: 不能将参数 1 从“int [10]”转换为“int (&)[8]”

。由此说明只有在fun4函数中,通过引用传递了数组的大小。

fun1,fun2,fun3传递的实际上都是int* p;且数组的大小是不能通过数组名进行传递的。

6. 二维数组作为函数参数

如何将二维数组作为函数的参数传递?写程序的时候要用到二维数组作参数传给一个函数,我发现将二维数组作参数进行传递还不是想象得那么简单,将二维数组当作参数传递的情况如下:

谭浩强先生编著的《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 p[m][n];
  如果要取p[i][j]的值(i>=0 && i<m && 0<=j && j < n),编译器是这样寻址的,它的地址为:
p + i*n + j;

从以上可以看出,如果我们省略了第二维或者更高维的大小,编译器将不知道如何正确的寻址。但是我们在编写程序的时候却需要用到各个维数都不固定的二维数组作为参数,这就难办了,编译器不能识别阿,怎么办呢?不要着急,编译器虽然不能识别,但是我们完全可以不把它当作一个二维数组,而是把它当作一个普通的指针,再另外加上两个参数指明各个维数,然后我们为二维数组手工寻址,这样就达到了将二维数组作为函数的参数传递的目的,根据这个思想,我们可以把维数固定的参数变为维数随即的参数,例如:
    void Func(int array[3][10]);
    void Func(int array[][10]);
  变为:
    void Func(int **array, int m, int n);

   转变后的函数中,array[i][j]这样的式子是不对的(不信,大家可以试一下),因为编译器不能正确的为它寻址,所以我们需要模仿编译器的行为把array[i][j]这样的式子手工转变为:
    *((int*)array + n*i + j);
    在调用这样的函数的时候,需要注意一下,如下面的例子:
    int a[3][3] =
    {
      {1, 1, 1},
      {2, 2, 2},
      {3, 3, 3}
    };
    Func(a, 3, 3);

   根据不同编译器不同的设置,可能出现warning 或者error,可以进行强制转换如下调用:
    Func((int**)a, 3, 3);
   其实多维数组和二维数组原理是一样的,大家可以自己扩充的多维数组,这里不再赘述。

3. 动态申请一个数组与动态释放数组

(1)动态数组中元素是对象本身情况:

  如下操作必然带来内存泄露:

Object是一个类,
Object * pObject = new Object[3];
...
delete pObject ; //problem

这时就已经引发内存泄露了,因为此时释放掉的只是object[0]的内存(编译器以&object[0]的地址为参数调用一次析构函数),
等同于delete[1] pObject

而正确的释放应该下面的操作:

delete[] object;
或者是delete[3] object;

(2)动态数组中元素之对象的指针情况

申请一个数组,数组中存放的是对象的指针Object **object = new Object *[3];

如下操作是异常的,引发内存泄露!!!

Object是一个类,
Object **object = new Object *[3];//只是动态的分配了3个Object对象的指针数组,但它们本身并不是对象
//分配
for(int i = 0;i<3;++i)
{
    object[i] = new Object();
}
...
delete[] object;//problem

其实这样也造成了内存泄露了。此时delete只是指针而非其指向的对象。真正动态产生的对象所占的内存没有被释放掉。

正确操作如下:

for(int i = 0;i<3;++i)
{
    delete object[i] ;
}
delete object;

 (3)动态数组问题

  通过传入数组长度,程序运行时动态指定数组长度

#include "stdafx.h"
#include <iostream>

typedef int* IntPtr;

IntPtr fun(const int size)
{
    IntPtr p = new int[size];
    for (int i = 0; i < size; ++i)
    {
        p[i] = i*5;
    }
    return p;
}


int _tmain(int argc, _TCHAR* argv[])
{
    IntPtr cP = fun(10);
    for (int i = 0; i < 10; ++i)
    {
        std::cout << cP[i] << "   ";
    }
    system("pause");
    return 0;
}

输出:

 

posted on 2015-12-22 22:07  夏天/isummer  阅读(663)  评论(0编辑  收藏  举报