下载安卓APP箭头
箭头给我发消息

客服QQ:3315713922

C++数组以及指针学习的技巧

作者:课课家教育     来源: http://www.kokojia.com点击数:712发布时间: 2017-07-05 11:00:42

标签: C/C++C语言编程语言

大神带你学编程,欢迎选课

  本文会跟大家详细的介绍C++数组以及指针学习的技巧,那么什么是指针?

  指针其实就是数据存放的地址,因为指针需要能够指向内存中的任意一个位置,因此,指针的长度应该是n位的,32位机器上指针长度就是32位。这和整型的长度是相等的!

  在我个人的理解中,可以将指针理解成int整型,只不过它存放的数据是内存地址,而不是普通数据,我们通过这个地址值进行数据的访问,假设它的是p,意思就是该数据存放位置为内存的第p个字节。

  当然,我们不能像对int类型的数据那样进行各种加减乘除操作,这是编译器不允许的,因为这样错是非常危险的!

C++数组以及指针学习的技巧_C/C++_C语言_编程语言_课课家教育

  数组

  定义及其初始化

  1、数组的维数必须用值大于等于1的常量表达式定义(包括:整型字面值常量、枚举常量、用常量表达式初始化的整型const对象),非const变量以及到运行阶段才知道其值的const变量都不能用于定义数组的维数。

  3、定义数组时没有显式的初始化,则:

  ----在函数体外定义的内置数组,元素均初始化为0

  ----在函数体内定义的内置数组,元素均无初始化

  ----无论在何处定义,若其元素为类类型,则自动调用其默认的构造函数初始化;若无构造函数,则必须为该数组的元素提供显式的初始化

  2、字符数组可以使用字符串字面值进行初始化,但当使用字符串字面值来初始化字符数组时,将在数组后面加入空字符,例如:char A[] = "C++"; //A的维数是4

  3、与vector不同,一个数组不能用另一个数组初始化,也不能将一个数组赋值给另一个数组

  注意:数组长度固定,一旦定义,就无法添加新的元素。但是若要添加,则需要自己更改内存remalloc()以及malloc()函数可以实现

  数组的操作

  下标访问。下标的数据类型:size_t

  指针

  定义及其初始化

  建议:尽量避免使用指针和数组

  指针和数组容易产生不可预料的错误。其中一部分是概念上的问题:指针用于低级操作,容易然生与繁琐细节相关的(book keeping)错误。其他错误则源于使用指针的语法规则,特别是声明指针的语法。

  许多有用的程序都可不使用数组或指针实现,现代c++程序采用vector类型和迭代器取代一般的数组、采用string类型取代C风格字符串。

  指针可能的取值

  一个有效的指针必然是以下三种状态之一:保存一个特定对象的地址;指向某个对象后面的另一对象;或者是0值。若指针保存0值,表明它不指向任何对象。未初始化的指针是无效的,直到给该指针赋值后,才可使用它。

  int ival = 1024;

  int *pi = 0;      // pi initialized to address no object

  int *pi2 = & ival;  // pi2 initialized to address of ival

  int *pi3; // ok, but dangerous, pi3 is uninitialized

  pi = pi2;       // pi and pi2 address the same object, e.g. ival

  pi2 = 0;        // pi2 now addresses no object

  指针初始化和赋值操作的约束

  对指针进行初始化或赋值只能使用以下四种类型的值:

  (1)0值常量表达式。

  (2)类型匹配的对象的地址。

  (3)另一对象之后的下一地址。

  (4)同类型的另一个有效指针。

  把int型变量赋给指针是非法的,尽管此int型变量的值可能为0。

  void*指针

  C++提供了一种特殊的指针类型void*,它可以保存任何类型对象的地址:

  double obj = 3.14;

  double *pd = &obj;

  // ok: void* can hold the address value of any data pointer type

  void *pv = &obj; // obj can be an object of any type

  pv = pd; // pd can be a pointer to any type

  ----void*表明该指针与一地址值相关,但不清楚存储在此地址上的对象的类型。

  ----void*指针只支持几种有限的操作:与另一个指针进行比较;向函数传递void*指针或从函数返回void*指针;给另一个void*指针复制。

  ----不允许用void*指针操纵它所指向的对象。

  指针操作以及指针有关注意事项

  一、解引用操作生成左值

  二、关键概念:给指针赋值或通过指针进行赋值

  对于初学指针者,给指针赋值和通过指针进行赋值这两种操作的差别确实让人费解。谨记区分的重要方法是:如果对左操作数进行解引用,则修改的是指针所指向的值;如果没有使用解引用操作,则修改的是指针本身的值。

  三、指针和引用的比较

  第一个区别在于引用总是指向某个对象:定义引用时没有初始化是错误的。第二个重要区别则是复制行为的差异:给引用赋值修改的是该引用所关联的对象的值,而并不是使引用与另一个对象关联。引用一经初始化,就始终指向同一个特定对象(这就是为什么引用必须在定义时初始化的原因)。

  计算机是如何从内存中进行取指的?

计算机是如何从内存中进行取指的?

  计算机的总线可以分为3种:数据总线,地址总线和控制总线。这里不对控制总线进行描述。数据总线用于进行数据信息传送。数据总线的位数一般与CPU的字长一致。一般而言,数据总线的位数跟当前机器int值的长度相等。例如在16位机器上,int的长度是16bit,32位机器则是32bit。这个计算机一条指令最多能够读取或者存取的数据长度。大于这个值,计算机将进行多次访问。这也就是我们说的64位机器进行64位数据运算的效率比32位要高的原因,因为32位机要进行两次取指和运行,而64位机却只需要一次!

  地址总线专门用于寻址,CPU通过该地址进行数据的访问,然后把处于该地址处的数据通过数据总线进行传送,传送的长度就是数据总线的位数。地址总线的位数决定了CPU可直接寻址的内存空间大小,比如CPU总线长32位,其最大的直接寻址空间长232KB,也就是4G。这也就是我们常说的32位CPU最大支持的内存上限为4G(当然,实际上支持不到这个值,因为一部分寻址空间会被映射到外部的一些IO设备和虚拟内存上。现在通过一些新的技术,可以使32位机支持4G以上内存,但这个不在这里的讨论范围内)。

  一般而言,计算机的地址总线和数据总线的宽度是一样的,我们说32位的CPU,数据总线和地址总线的宽度都是32位。

  计算机访问某个数据的时候,首先要通过地址总线传送数据存储或者读取的位置,然后在通过数据总线传送需要存储或者读取的数据。一般地,int整型的位数等于数据总线的宽度,指针的位数等于地址总线的宽度。

  四、指向指针的指针

  指针本身也是可用指针指向的内存对象。指针占用内存空间存放其值,因此指针的存储地址可存放在指针中。

  int ia[] = {0, 2, 4, 6, 8};

  int *ip =ia; // ip points to ia[0]

  ip = &ia[4]; // ip points to last element in ia

  ip = ia; // ok: ip points to ia[0]

  int *ip2 = ip + 4; // ok: ip2 points to ia[4], the last element in ia

  指针的算数操作只有在原指针和计算出来的新指针都指向同一个数组的元素,或指向该数组存储空间的下一单元时才是合法的。如果指针指向一对象,我们还可以在指针上加1从而获取指向相邻的下一个对象的指针。

  五、C++还支持对这两个指针做减法操作:

  ptrdiff_t n = ip2 - ip;  // ok: distance between the pointers

  结果是4,这两个指针所指向的元素间隔为4个对象。两个指针减法操作的结果是标准库类型ptrdiff_t的数据。与size_t类型一样,ptrdiff_t也是一种与机器相关的类型,在cstddef头文件中定义。size_t是unsigned类型,而ptrdiff_t则是signed_t整型。

  允许在指针上加减0,使指针保持不变。如果一指针具有0值,则在该指针上加0仍然是合法的,结果得到另一个值为0的指针。也可以对两个空指针做减法操作,得到的结果仍是0。

  六、解引用和指针算术操作之间的相互作用

  在指针上加一个整型数值,其结果仍然是指针。允许在这个结果上直接进行解引用操作,而不必先把它赋给一个新指针:

  int last = *(ia + 4);  // ok: initializes last to 8, the value of ia[4]

  加法操作两边用圆括号括起来是必要的。如果写为:

  last = *ia + 4;// ok: last = 4, equivalent to ia[0]+4

  意味着对ia进行解引用,获得ia所指元素的值ia[0],然后加4。

  七、计算数组的超出末端指针

  const size_t arr_size = 5;

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

  int *p = arr;     // ok: p points to arr[0]

  int *p2 = p + arr_size; // ok: p2 points one past the end of arr

  // use caution -- do not dereference!

  C++允许计算数组或对象的超出末端的地址,但不允许对此地址进行解引用操作。而计算数组超出末端位置之后或数组首地址之前的地址都是不合法的。

  可使用此超出末端指针的当做一个哨兵,如同在vector中使用的end变量一般,用于输出和遍历数组,这是一个好习惯

  八、指针和const限定符

  指向const对象的指针

  const double *cptr;    // cptr may point to a double that is const

  const限定了cptr指针所指向的对象类型,而并非cptr本身。也就是说,cptr本身并不是const。

  允许通过给cptr赋值,使其指向一个const对象,但不能通过cptr修改其所指向的对象的值。

  不能使用void*指针保存const对象的地址,而必须使用const void*类型的指针保存const对象的地址:

  const int universe = 42;

  const void *cpv = &universe; // ok: cpv is const

  void *pv = &universe;     // error: universe is const

  不能使用指向const对象的指针修改基础对象,然而如果该指针指向的是一个非const对象,可用其他方法修改其所指的对象。

  九、const指针

  C++语言还提供了const指针——本身的值不能修改:

  int errNumb = 0;

  int *const curErr = &errNumb;    // curErr is a constant pointer

  curErr = curErr;            // error: curErr is a constant pointer

  指向const对象的const指针,既不能修改所指对象的值,也不允许修改指针的指向。

  假设给出以下语句:

  typedef string *pstring;

  const pstring cstr;

  请问cstr变量是什么类型:

  const string *pstring; // wrong interpretation of const pstring cstr

  string *const cstr; // equivalent to const pstring cstr,等价于这句话

  C风格字符串

  C风格字符串的标准库函数(要使用这些标准库函数,必须包含相应的C头文件:cstring)

  strlen(s)       strcmp(s1, s2)      strcat(s1, s2)

  strcpy(s1, s2)    strncat(s1, s2, n)    strncpy(s1, s2, n)

  注意:这些标准库函数不会检查其字符串参数

  永远不要忘记字符串结束符null,调用者必须确保目标字符串具有足够的大小

  如果必须使用C风格字符串,则使用标准库函数strncat和strncpy比strcat和strcpy函数更安全

  char largeStr[16 + 18 + 2]; // to hold cp1 a space and cp2

  strncpy(largeStr, cp1, 17); // size to copy includes the null

  strncat(largeStr, " ", 2);   // pedantic, but a good habit

  strncat(largeStr, cp2, 19); // adds at most 18 characters, plus a null

  对大部分的应用而言,使用标准库类型string,除了增强安全性外,效率也提高了,因此应该尽量避免使用C风格字符串。

  创建动态数组

  动态数组的定义

  int *pia = new int[10];  // array of 10 uninitialized ints

  new表达式返回指向新分配数组的第一个元素的指针

  初始化动态分配的数组

  可使用跟在数组长度后面的一对空圆括号,对数组元素做值初始化:

  int *pia2 = new int[10]();  // array of 10 uninitialized ints

  对于动态分配的数组,其元素只能初始化为元素类型的默认值,而不能像数组变量一样,用初始化列表为数组元素提供各不相同的初值。

  const对象的态数组

  // error: uninitialized const array

  const int *pci_bad = new const int[100];

  // ok: value-initialized const array

  const string *pci_ok = new const int[100]();

  允许动态分配空数组

  char arr[0];       // error: cannot define zero-length array

  char *cp = new char[0];    // ok: but cp can't be dereferenced

  用new动态创建长度为0的数组时,new返回有效的非零指针。该指针与new返回的其他指针不同,不能进行解引用操作,因为它毕竟没有指向任何元素。而允许的操作包括:比较运算,因此该指针能在循环中使用;在该指针上加(减)0;或者减去本身,得0值。

用new动态创建长度为0的数组时,new返回有效的非零指针。该指针与new返回的其他指针不同,不能进行解引用操作,因为它毕竟没有指向任何元素。而允许的操作包括:比较运算,因此该指针能在循环中使用;在该指针上加(减)0;或者减去本身,得0值。

  动态空间的释放

  动态分配的内存最后必须进行释放,否则,内存最终将会逐渐耗尽。

  使用数组初始化vector对象

  const size_t arr_size = 6;

  int int_arr[arr_size] = {0, 1, 2, 3, 4, 5};

  // ivec has 6 elements: each a copy of the corresponding element in int_arr

  vector ivcec(int_arr, int_arr + arr_size);

  多维数组

  严格地说,C++中没有多维数组,通常所指的多维数组其实就是数组的数组。

  代码::

  #include

  main()

  {

  static  int  a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}}; /* 定义一个3行4列的二维数组 */

  int  *p;

  printf ("%d %d/n",a,*a);

  printf ("%d %d/n",a[0],*(a+0));

  printf ("%d %d/n",&a[0],&a[0][0]);

  printf ("%d %d/n",a[0][0],*(*(a+0)+0));

  for  (p=a[0];p

  {

  if ((p-a[0])%4==0) /* 利用整数指针变量p减去当前地址判断出是不是已经显示出了四个值,换行回车 */

  {

  printf ("/n");

  }

  printf ("%4d",*p); /* 打印出元素的值 */

  }

  printf ("/n");

  }

  /*

  第 5行中的a和*a打印出来的值,就会让人弄不明白我们知道数组传递的地址那么a表示这个数组的其实地址为什么*a却不是实际值呢?原因是在多维数组中 a+0表示的是第0行的首地址,a+1表示是第一行的首地址,那么*a其实就是*(a+0),那么第一个元素其实是a[0][0]

  而*(a+0) 仅仅是把一个3行4列的二维数组拆为了三个一维数组,*(a+0)显然表示的不是物理位置也就不可能得到第一个元素的值了,它仅仅是一个内存地址也就是第 0行的启始地址,再看8行中的*(*(a+0)+0),它表示的则是第0行第0列个元素的值也就是a[0][0],再次强调*(a+0)表示的是数组第一行的首地址,也就是第二行的*(a+1),而元素值要得到显然就是*(*(a+0)+0)了如果是第0行第1个也就是*(*(a+0)+1)。

  本文分享结束,还有很多关于指针与数组的相关知识将在课课家教育网呈现,多级指针在数组中的应用也会更加深入的介绍。

赞(25)
踩(0)
分享到:
华为认证网络工程师 HCIE直播课视频教程