C++复习 04 数组和指针

来源:互联网 发布:restful接口开发数据库 编辑:程序博客网 时间:2024/06/01 09:57



声明,所有的朋友,如果要转我的帖子,务必注明"作者:黑啤 来源:CSDN博客"和具体的网络地址http://blog.csdn.net/nx500/archive/2007/10/22/1836562.aspx,并且我的所有博客内容,禁止任何形式的 商业用途,请大家尊重我的劳动.谢谢!

目 录

四.数组和指针.
  001 数组和vector的区别在于,数组的长度是固定的.数组一经创建,就不允许动态的修改长度.指针可以像迭代器一样用于遍历和检索数组中的元素.
      现代C++程序应该尽量使用vector和迭代器类型,而避免使用低级的数组和指针.只有在强调速度时,才在类实现的内部使用数组和指针.
  002 数组的缺陷在于没有size()操作,也就是程序员无法知道一个给定数组的长度.
      如果需要更改数组的长度,就必须再声明一个更大的数组,把原来数组的内容copy到新数组中去.
  003 引用不能定义数组.没有所有元素都是引用的数组.
  004 非const变量和要到运行阶段才知道值的const变量都不能用作数组定义的维数.(这两点和C99标准不一样,动态创建参见第021项)
        const unsigned buf_size = 512;  // const 变量.
        int staff_size = 217;           // 非const变量.
        const unsigned sz = get_size(); // 运行时才确定的const变量.
        char buf1[buff_size];           // ok.
        char buf2[buff_size + 1];       // ok, 常量计算表达式.
        double salaries[staff_size];    // error, 非const变量做维数.
        int test_rest[sz];              // error, 运行时才确定的const变量.
  005 默认初始化.
        在函数外定义的内置类型数组,元素都初始化0.
        函数内定义的内置类型数组,元素都无初始化过程.
        如果是类类型的数组,无论在哪里定义,都调用默认构造函数初始化.
  006 显示初始化.
        int ia[3] = {0, 1, 3};
        char ca1[] = {'c', '+', '+'};
        char ca2[] = {'c', '+', '+', '/0'};
        char ca3[] = "c++";  // ca3和ca2有相同的维数和初始化值,含有四个元素.
        char ca4[3] = "c++"  // 这会产生一个编译错误,因为"c++"是一个长度为4的字符串.
        string str_arr[3] = {"hi", "heipi"}; // str_arr[2]是一个值为空的string类型元素.
  007 与vector不同,一个数组不能用另外一个数组初始化,也不能将一个数组赋值给另一个数组.
      个别编译器扩展功能,支持数组复制,但是如果希望编写的程序能在不同的编译器上运行,应该避免这类应用.
  008 数组下标的类型是size_t.
        // 04008.cpp
        #include <iostream>
        
using std::cout;
        
using std::endl;

        
int main(){
        
const int MAX = 10;
        
int ia[MAX] = {0,1,2,3,4,5,6,7,8,9};
          
for(size_t ix = 0; ix < MAX; ++ix)
            cout 
<< ia[ix] << " ";
          cout 
<< endl;
          
return 0;
        }

        // 编译及运行结果
        Nan@linux-g4n6:~/Documents> g++ -o o 04008.cpp
        Nan@linux-g4n6:~/Documents> ./o
        0 1 2 3 4 5 6 7 8 9
        Nan@linux-g4n6:~/Documents>
      程序员必须自己注意,防止数组越界,因为数组没有类似vector中size()的函数支持查询数组的长度.
      其实可以考虑这个办法计算数组长度: size_t ia_size = sizeof(ia)/sizeof(*ia);
  009 指针用于指向对象,具体的说,指针保存的是另一个对象的地址.
      与迭代器不同:指针用于指向单个对象,通过解引用(*)访问元素,而迭代器只能用于访问容器内的元素.
        string s("Hello heipi");
        string *sp = &s; // sp指向了string s.
        string s_arr[2] = {"Hello", "heipi"};
        sp = s_arr;      // sp指向了string数组的第一个元素"Hello"
      要理解指针声明语句,请从右向左阅读.
  010 指针定义时,必须初始化(指向具体的对象,或者是初始化为0, 或NULL,NULL定义在cstdlib头文件中).
      对指针进行初始化或者赋值的的四种类型:
        1.0值"常量"表达式,注意,用值为0的int变量来初始化一个指针是错误用法.
        2.类型匹配对象的地址.
        3.匹配对象的下一个地址.
        4.同类型的另一个有效的指针.
  011 void*指针,可以保存任何类型对象的地址.他表明该指针与一个地址相关,但不清楚存储在此地址上的对象的类型.
      const void* 指针,可以用于保存const类型对象的地址.
        double pi = 3.14;
        double *dp = &pi;
        void *vp = &pi;
        vp = dp;
  012 指针通过*操作获取对象,可以访问,也可以修改.
        cout << *dp;
        *dp = 9999;
        cout << *dp;
  013 指针和引用:引用总是指向某个固定对象,定义时必须初始化,不可将引用修改为指向别的对象.
      对引用的修改,是修改其指向对象的值.对指针的修改,使指针指向新的对象.
        double &dp1 = pi;
        double obj1 = 2.34;
        dp1 = obj1;    // 此时pi变为了2.34,和obj1值相同.
        dp = obj1;     // 此时dp不再指向pi, pd指向了obj1.
  014 指向指针的指针.
        double **dpp = &dp;
        cout << **dpp;
  015 指向数组的指针.
        const int MAXSIZE = 5;
        int ia[MAXSIZE] = { 0, 2, 4, 6, 8};
        int *ip = ia;  // 在表达式中使用数组名时,该名字会自动转换为指向数组的第一个元素的指针.
        ip = &ia[3]    // ip指向元素6.
  016 指针的算术运算.
        int *ip2 = ip + 1;         // ip2 指向元素8.
        ptrdiff_t dist = ip2 - ip; // ptrdiff定义在cstddef头文件中.
        cout << *(ip + 1);          // 等同于cout << *ip2, 需要注意括号的应用.
  017 C++允许指针指向数组或对象的超出末端的地址,但不允许对此地址进行解引用操作.
        int *ip_head = ia;
        int *ip_end  = ip_head + MAXSIZE; // 可以引用此地址,但不能解引用其中的值(无意义的值).
        for (int *tmp = ip_head; tmp != ip_end; ++tmp){ // 这里,指针就是数组的迭代器.
          cout << *tmp << std::endl;
        }
  018 指向const对象的指针也必须是const类型的,不能通过解引用修改const指针指向的对象的值.
        const int *ip3 = &MAXSIZE; // ip3指向了MAXSIZE,但是不能通过*ip3修改MAXSIZE的值.
        int iTmp = 9;
        ip3 = &iTmp;               // iTmp虽然是非const类型,ip3可以指向iTmp,但仍然不能通过*ip3来修改.
      把一个const对象的地址赋给一个普通的,非const对象的指针也是错误的.
        int *ip4 = &MAXSIZE;       // error, ip4不是const对象的指针.
      指向const对象的指针常用于形式参数,用来防止意外的修改实际变量.
  019 const指针,意味着指针本身不能修改,const在*后边.
        int counter = 9;
        int *const ip5 = &counter; // ip5是指向counter的指针.
        *ip5 = 999;                // ok,可以通过*ip5修改其值.
        ip5  = ia;                 // error,就是不能将ip5指向其他类型.
        ip5  = ip5;                // error,嘿嘿,ip5赋给自己也不行,别费心思了.
      指向const对象的const指针.
        const int *const ip6 = &MAXSIZE; // ip6指向MAXSIZE,可以通过*ip6引用MAXSIZE的值,别无他用.
      特别注意const与typedef结合使用容易出错的地方.
        typedef string *pstring;   // pstring是一个指向string类型的指针.
        const pstring cstr;        // cstr是一个string *const的变量,也就是指向string类型的const指针.
                                   // 有人误解这是个const string *的类型,还记得么?const是说明cstr的!
        pstring const cstr1;       // ok,这个声明和上边cstr的声明是相同的含义.
  020 字符串字面值的类型,就是const char类型的数组.C++中,不应该继续使用这种类型,因为它是大量安全问题的根源.
        const char *cp = "some value";
        while(*cp){
          cout << *cp;
          ++cp;
        }
      C风格字符串的标准函数库,连接和复制的操作都必须保证s1有足够大的空间,否则后果将极度危险.
        #include<cstring>;
        strlen(s);                // 返回s的长度,不包括字符串结束符.
        strcmp(s1,s2);            // 字典顺序比较s1 s2两个字符串的大小,返回0表示相同,正数表示s1大于s2.
        strcat(s1,s2);            // 将s2连接到s1后,并返回s1.
        strcpy(s1,s2);            // 将s2复制到s1中.
        strncat(s1,s2,n);         // 将s2的前n个字符,连接到s1后边,并返回s1.
        strncpy(s1,s2,n);         // 将s2的前n个字符,复制到s1中,并返回s1.
      永远也不要忘记字符串结束符NULL.
  021 用于存放动态分配对象的内存空间称为自由存储区或"堆".
      c使用malloc和free分配,而c++用new和delete.
        定义动态数组.
          int *ipa1 = new int[10];     // 由于int是内置类型,所以ipa是没有初始化过的.
          int *ipa2 = new int[10]();   // ipa2都初始化为0.
          string *spa = new string[5]; // spa分配5个string空间,每个string对象都初始化为empty.
        new可以创建长度为0的指针,此时返回的地址是有效的,可以比较,但是不能解引用.
          size_t num = get_size();     // 通get_size()函数返回数组的长度.
          int *ipa3 = new int[num]();  // ok .
        const对象的动态数组.
          const int *pi1 = new const int[10];   // error,这样是不行的,必须有初始化.
          const int *pi2 = new const int[10](); // ok, 都初始化为0.
        用delete [] 释放数组空间,如果遗漏了空方括号,将导致程序运行时的错误.这样会导致少释放了内存空间,产生内存泄漏.
          delete [] ipa1;
  022 对string类型,可以使用c_str函数返回const char类型的数组.
        const char *str = s.c_str();
  023 使用数组初始化vector对象.
        const size_t arr_size = 6;
        int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};
        vector<int> ivec1(int_arr, int_arr + arr_size); // ivec1包含全部int_arr的元素.
        vector<int> ivec2(int_arr + 1,int_ary + 3);     // ivec2包含1,2,3三个元素.
  024 多维数组.在C++中,也就是数组的数组.
      下边两个初始化的结果是相同的.
        int ia1[3][4] = {{0,1,2,3},{4,5,6,7},{8,9,10,11}};
        int ia2[3][4] = {0,1,2,3,4,5,6,7,8,9,10,11};
      指向多维数组的指针.
        int *ip[4];          // 指向整数的指针数组, ip[0],ip[1],ip[2],ip[3]都是指向整数的指针.
        int (*ip)[4] = ia;   // ip是一个指针,指向含有四个整数类型元素的数组指针.
        // 下边两个定义合起来等同于上边的一条定义.
        typedef int int_array[4];
        int_array *ip = ia;
        for(int_array *p = ia; p!= ia + 3; ++p)
          for(int *q = *p; q != *p + 4; ++q)
            cout << *q << endl;
 
原创粉丝点击