350 likes | 475 Views
第 5 章 指针、数组和结构. p. c. &c. ‘a’. 5.1 指针. 1 、指针的定义 T T* char c=‘a’; char *p=&c; char c2= * p. 间接引用. 各种类型指针: int *pi; char **ppc; int *ap[15]; int (*fp)(char*); int *fp (char *);. 5.1.1 零 0 可以被用于作为任意类型的常量 const int NULL = 0 ; 0 可以被用于指针常量; int *p=NULL;
E N D
p c &c ‘a’ 5.1 指针 • 1、指针的定义 T T* char c=‘a’; char *p=&c; char c2=*p 间接引用
各种类型指针: int *pi; char **ppc; int *ap[15]; int (*fp)(char*); int *fp (char *);
5.1.1零 0可以被用于作为任意类型的常量 const int NULL=0; 0可以被用于指针常量; int *p=NULL; int *p=0;(C++)
5.2 数组 • T T[size] float v[3]; char* a[32]; int b[2][3]; size为常量表达式 void f(int i) { int v1[i];//错误 vector<int> v2(i);//可变界数组 int bad[5,2] ;//错误 }
5.2.1 数组初始化 • 当数组声明中没给出数组大小,有初始化列表,则大小由列表元素个数决定。 int vl[ ]={1,2,3,4} char v2[3]={‘a’,’b’,0}; • 给出大小,则初始化列表给出多于元素就是错误。 char v3[2]= {‘a’,’b’,0}; • 给出大小,如初始化列表的元素太少则由0补。 int v5[8]={1,2,3,4};
5.2.2 字符串文字量 “this is a string” • 长度: • 存储长度:sizeof(“hello”) • 类型:适当个数的const字符的数组 const char[6]
可以将字符串文字量给char *赋值,但不能通过该指针修改字符串文字量。 void f() { char* p=“plato” p[4]=‘e’; }
改为 void f() { char p[]=“zeno”;// char p[5]; p[0]=‘R’; }
字符串文字量是静态分配的,可作为函数返回值。字符串文字量是静态分配的,可作为函数返回值。 const char* error_message(int i) { //… return “range error”; }
字符串文字量可有转义符 “asdsfgf\00sdsad”
5.3 到数组的指针 • 一个数组的名字能够被用作到它的开始元素的指针。 int v[]={1,2,3,4}; int *p1=v; int *p2=&v[0]; int *p3=&v[4]; v 1 2 3 4
从数组名到这个数组的开始元素的隐式转换在C风格中广泛使用从数组名到这个数组的开始元素的隐式转换在C风格中广泛使用 extern “C” int strlen(const char *);//string.h void f() { char v[]=“Annermarie”; char*p=v; strlen(p); strlen(v); v=p; }
5.3.1 在数组里漫游 指向数组的指针运算: p1+1; p3-1; p1++; p3--; • 当两个指针指向同一个数组元素时,指针相减才有定义。 • 两个指针相加是不允许的。
遍历字符数组 void fi(char v[]) { for(int i=0;v[i]!=0;i++) use(v[i]); } void fi(char v[]) { for(char *p=v;*p!=0;i++) use(*(p+i)); }
数组不具有自述性,遍历时需提供元素个数 void fp(int v[],unsigned int size) { for(int i=0;i<size;i++) use(v[i]); } • 大多数C++实现不提供对数组范围的检查。
5.4 常量 • 1、定义——“不变化的值” const int model=90; const int v[]={1,2,3,4}; const int x; 常量必须初始化
将某些东西声明为常量,就保证了其作用域内不能改变它们的值。将某些东西声明为常量,就保证了其作用域内不能改变它们的值。 • 例题: void f() { const int model=90; const int v[]={1,2,3,4}; model=200; v[2]++; }
const改变了类型,但没有描述常量如何分配 void g(const X*p) {*p= ; } void h() { X val; g(&val); }
const最常见的作用是作为数组的界和作为分情况标号。const最常见的作用是作为数组的界和作为分情况标号。 const int max=128; int v[max]; const int a=100; void f(int i) { switch(i){ case a: //… }
5.4.1 指针和常量 • const char *pc; 到const char的指针 char *const cp; 到char 的const 的指针
void f1(char*p) { char s[]="gorm"; const char *pc=s; pc[3]='g'; pc=p; char *const cp=s; cp[3]='a'; cp=p; const char*const cpc=s; cpc[3]='a'; cpc=p;}
在参数中使用指向常量的指针做参数,就禁止了这个函数对改参数的修改。在参数中使用指向常量的指针做参数,就禁止了这个函数对改参数的修改。 char *strcpy(char *p,const char *q);
可以将一个变量的地址赋给一个到常量的指针,但不能将常量的地址赋给一个未加限制的指针。可以将一个变量的地址赋给一个到常量的指针,但不能将常量的地址赋给一个未加限制的指针。 void f4() { int a=1; const int c=2; const int* p1=&c; const int *p2=&a; int *p3=&c;//const_cast<int*>(&c) *p3=7; }
5.5 引用 • 定义和作用: T& 一个引用就是某对象的另一个名字,主要用途描述函数的参数和返回值。 • 引用必需做初始化。 int i=1; int &r1=i;
pp: &ii void g( ) { int ii=0; int &rr=ii; rr++; int *pp=&rr; } rr: 0 ii:
普通T&的初始式必须是一个类型T的左值; • const T&的初始值不必是一个左值,甚至可以不是类型T的左值。 double &dr=1; const double &cdr=1; double temp=double(1); const double &cdr=temp
通过引用描述函数的参数 void increment(int& aa){aa++;} void f() { int x=1; increment(x); std::cout<<x<<‘\n’; }
5.6 指向void的指针 • 任何类型指针都可以赋值给void*的变量(除到函数的指针及到成员的指针); • 可以显式将void*转换到另一个类型指针; • 其他操作均不安全;
void f(int *pi)//判断 {void *pv=pi; *pv; pv++; int *pi2=static_cast<int*>(pv); double *pd1=pv; double *pd2=pi; double *pd3= static_cast<double*>(pi); }
void的最重要的用途是需要向函数传递一个指针,而又不能对对象类型做任何假设,还用就是作为函数的返回值。void的最重要的用途是需要向函数传递一个指针,而又不能对对象类型做任何假设,还用就是作为函数的返回值。 • 一般用于系统中很低的层次。 void *my_alloc(size_t n);
5.7 结构 struct address{ char *name; long int number; ….; }; address jd,*pjd=&jd; jd.name;pjd->name;
结构对象可用初始化列表进行初始化(一般用构造函数)结构对象可用初始化列表进行初始化(一般用构造函数) address jd={“jim Dandy”,61,…}; • 结构类型对象可以被赋值、作为函数参数传递、作为函数返回值返回。
类型的名字在出现之后就可以使用了,不必等到看到完整的声明之后。类型的名字在出现之后就可以使用了,不必等到看到完整的声明之后。 struct link{ link*pre; link*suc; } • 在完整声明被看到之前,不能声明这个结构类型的新对象。 struct No_good{ No_good member;}
struct是类的简单形式 5.7.1类型等价 • 两个结构总是不同类型,即使它们有相同的成员; struct s1{int a;}; struct s2{int a;};//考虑typedef s1 s2; s1 x; s2 y=x;