390 likes | 629 Views
§5. Pointers, Arrays, and Structures. 这一章的内容结构. 5.1 Pointers ( 存储其他变量地址的量:指针 ); 5.2 Arrays (按顺序存储和访问的数据集合:数组); 5.3 Pointers into Arrays (指向数组的指针(指针与数组的对应)); 5.4 Constants (有名字、一经定义即不允许修改值的量:常量); 5.5 References (不用名字来访问或表示值的方式:引用); 5.6 Pointer to Void (指针类型 void * 的作用);
E N D
这一章的内容结构 5.1 Pointers(存储其他变量地址的量:指针); 5.2 Arrays(按顺序存储和访问的数据集合:数组); 5.3 Pointers into Arrays(指向数组的指针(指针与数组的对应)); 5.4 Constants(有名字、一经定义即不允许修改值的量:常量); 5.5 References(不用名字来访问或表示值的方式:引用); 5.6 Pointer to Void(指针类型 void * 的作用); 5.7 Structures(不同类型的变量的有机组合:结构)。
这次课的内容 5.1 Pointers(存储其他变量地址的量:指针); 5.2 Arrays(按顺序存储和访问的数据集合:数组); 5.3 Pointers into Arrays(指向数组的指针(指针与数组的对应)); 5.4 Constants(有名字、一经定义即不允许修改值的量:常量); 5.5 References(不用名字来访问或表示值的方式:引用); 5.6 Pointer to Void(指针类型 void * 的作用); 5.7 Structures(不同类型的变量的有机组合:结构)。
5.4 Constants • 在4.9.6节(Objects and Lvalues)引入的概念: • Object(对象):a contiguous region of storage. • Automatic object:在堆栈中分配空间的对象,其生存期为定义它的那个函数或块的一次运行。 • Static object:在静态存储区中分配空间的对象(在函数或块中定义时须前加 static),其生存期为所在程序的一次运行。 • Lvalue(左值):an object whose address you can take. • Modifiable lvalue:something that can be on the left-hand side of an assignment.
5.4 Constants • 由于存在以下场合,所以引入了用户自定义常量: • Many objects don’t actually have their values changed after initialization. • Symbolic constants lead to more maintainable code than do literals embedded directly in code. • Pointers are often read through but never written through. • Most function parameters are read but not written to.
例: int f(int *ary, int sz) { int normal = 0, suc = 0, err = -1; for (int i=0; i<sz; i++) if ( ary[i] < normal ) return err; return suc; } 例: const int normal = 0, suc = 0, err = -1; int f(int *ary, int sz) { for (int i=0; i<sz; i++) if ( ary[i] < normal ) return err; return suc; } 5.4 Constants • 由于存在以下场合,所以引入了用户自定义常量: • Many objects don’t actually have their values changed after initialization. • Symbolic constants lead to more maintainable code than do literals embedded directly in code. • Pointers are often read through but never written through. • Most function parameters are read but not written to.
例: char str[41]; void clear() { for (int i=0; i<40; i++) str[i] = ’’; str[40] = ’\0’; } void strIn() { for (int i=0; i<40; i++) cin >> str[i]; } 例: const int STRL = 40; char str[STRL + 1]; void clear() { for (int i=0; i<STRL; i++) str[i] = ’’; str[STRL] = ’\0’; } void strIn( ) { for (int i=0; i<STRL; i++) cin >> str[i]; } 5.4 Constants • 由于存在以下场合,所以引入了用户自定义常量: • Many objects don’t actually have their values changed after initialization. • Symbolic constants lead to more maintainable code than do literals embedded directly in code. • Pointers are often read through but never written through. • Most function parameters are read but not written to.
例: const int STRL = 40; char buf[STRL * 10 + STRL]; char* str = &buf; char* str2 = &buf[STRL + 1]; void clear() { memset(str, 32, STRL); memset(str2, 32, STRL); str[STRL] = ’\0’; str2[STRL] = ’\0’; } 例: const int STRL = 40; char buf[STRL * 10 + STRL]; char *const str = &buf; char *const str2 = &buf[STRL + 1]; void clear() { memset(str, 32, STRL); memset(str2, 32, STRL); str[STRL] = ’\0’; str2[STRL] = ’\0’; } 5.4 Constants The pointer is a constant. • 由于存在以下场合,所以引入了用户自定义常量: • Many objects don’t actually have their values changed after initialization. • Symbolic constants lead to more maintainable code than do literals embedded directly in code. • Pointers are often read through but never written through. • Most function parameters are read but not written to.
例: int f(int *ary, int sz) { const int normal = 0, suc = 0, err = -1; for (int i=0; i<sz; i++) if ( ary[i] < normal ) return err; return suc; } 例: int f(const int* ary, const int sz) { const int normal = 0, suc = 0, err = -1; for (int i=0; i<sz; i++) if ( ary[i] < normal ) return err; return suc; } 5.4 Constants The pointer makes the object a constant. • 由于存在以下场合,所以引入了用户自定义常量: • Many objects don’t actually have their values changed after initialization. • Symbolic constants lead to more maintainable code than do literals embedded directly in code. • Pointers are often read through but never written through. • Most function parameters are read but not written to.
例: void g(const char* p) { const int sz = 40; sz = 20; p[0] = ’\0’; /* … */ } void f() { g(”This is a test.”); } 5.4 Constants • 常量分为数值常量和指针常量。 • 数值常量的定义方式: const <类型> <常量名> = <初值>; • 若以函数的形参方式出现,则没有<初值>部分,对应的实参即为<初值>。 • 当<类型>是一个T*时,不允许修改的是 *<常量名>及<常量名> [<下标>]对应的值,而不是 <常量名>对应的值。
例: void f(char* p) { char s[] = ”Gorm”; char *const cp = s; const char* pc = s; cp[3] = ’a’; pc[3] = ’a’; cp = p; pc = p; const char *const cpc = s; *cpc = ’g’; cpc = p; } 5.4 Constants pc is a pointer to a const char. cp is a const pointer to a char. cpc is a const pointer to a const char. • 指针常量的定义方式: <指针类型> const <常量名> = <初值>; • 若以函数的形参方式出现,则没有<初值>部分,对应的实参即为<初值>。 • 不允许修改的是 <常量名>对应的值,而不是 *<常量名>及 <常量名>[<下标>]对应的值。 • Some people find it helpful to read such declarations right-to-left.
例: void f4() { int a=1; const int c=2; const int* p1=&c; //ok const int* p2=&a; //ok int* p3=&c; //error *p3=7; //try to change the value of c } 5.4 Constants • You can assign the address of a variable to a pointer to constant because no harm can come from that. However, the address of a constant cannot be assigned to an unrestricted pointer because this would allow the object’s value to be changed.
5.5 References • A reference is an alternative name for an object. The main use of reference is for specifying arguments and return values for functions in general and for overloaded operators(过载操作符) in particular.(后者以后将介绍) • The notation X& means reference to X. • To ensure that a reference is a name for something (that is, bound to an object), we must initialize the reference.
5.5 References • 由于一个引用所表示的是对象的名,所以它不能独立存在,必然要关联于某个已定义的名字,它们对应于同一个对象。 • 关联的方式是:(1)在定义该引用时所声明的初始化;(2)对于被定义为函数的形参(或返回值)的引用,由实参(或返回值表达式)给出对应值。
一个引用好比 是一个总是用 * 操作的指针常量 例: void g() { int ii = 0; int& rr = ii; rr++; // 等价于:ii++; int* pp = &rr; // pp == ⅈ *pp == ii; } rr : ii : &ii 5.5 References 0 1
void swap(int& a, int& b) { int temp; temp=a; a=b; b=temp; } void swap(int* a, int *b) { int* temp=0; *temp=*a; *a=*b; *b=*temp; } 5.5 References • A reference can be used to specify a function argument so that the function can change the value of an object passed to.(类似于Pascal函数或过程用 var 声明的形参)
例:使用引用、不易理解的方式 void inc(int& aa) { aa++; } void f() { int x = 1; inc(x); // aa、x 对应同一对象 } 例:使用指针、易于理解的方式 void inc(int* p) { (*p)++; } void f() { int x = 1; inc(&x); // x == 2 } 5.5 References • A reference can be used to specify a function argument so that the function can change the value of an object passed to.(类似于Pascal函数或过程用 var 声明的形参)
5.5 References • A reference can also be used to define a function that can be used on both the left-hand and right-hand sides of an assignment. • 这里指的是:当把这样的函数之返回类型定义成引用类型的时候 … ...
struct Pair { string name; // 名字 int val; // 同名字的数量或其他 }; /* vector<Pair>是一个元素类型为Pair、 size可扩张的动态数组类型 */ vector<Pair> pairs; int& value(const string& s) { for (int i=0; i<pairs.size(); i++) if (s == pairs[i].name) return pairs[i].val; Pair p = { s, 0 }; pairs.push_back(p); return pairs[pairs.size()-1].val; } int main() { string buf; /* 从标准输入设备每次读入一字符串 到buf中(以空格分隔),直到读 入了回车为止。对相同的字符串计数 */ while (cin >> buf) value(buf) ++; /* 等价于 value(buf) = value(buf) + 1; 或 value(buf) += 1; */ for (vector<Pair>::const_iterator p = pairs.begin(); // p是循环变量 p != pairs.end(); ++p) cout << p->name << ”: ” << p->val << ”\n”;//从标准输出设备输出 } 5.5 References
5.7 Structures • A struct is an aggregateof elements of the (nearly) arbitrary(任意的)types. (其元素是异质的(heterogeneous)) • 对比:An array is an aggregate(聚集)of elements of the same type.(其元素是同质的(homogeneous)) • 构成一个结构类型的那些元素称为它的成员(Members)。 • 结构类型用来定义有多种类型不同的属性(Attributes)的客观事物(这样的事物是大量存在的)。 • C++中的结构类型实际上是特殊的类(以后会介绍)。
例: struct Pair { string name; // 名字 double val; // 同名字的数量或其他 }; // 这里的分号(;)不能缺少 struct address { // 定义某人的地址 char* name; // 某人的姓名,假定最长为20个字符 long int number;// 门牌号码,假定long为4字节 char* street; // 街名,假定最长为20个字符 char* town; // 城市名,假定最长为14个字符 char state[2]; // 州/省缩写名 long zip; // 邮政编码,假定long为4字节 }; // 这里的分号(;)不能缺少 5.7 Structures
struct address { char* name; // 某人的姓名,假定最长为20个字符 long int number;// 门牌号码,假定long为4字节 char* street; // 街名,假定最长为20个字符 char* town; // 城市名,假定最长为14个字符 char state[2]; // 州/省缩写名 long zip; // 邮政编码,假定long为4字节 }; 5.7 Structures • 结构类型的每个成员都有相应的类型。 • 一个结构类型看上去只是定义了一种事物的一个实例的各个属性(用成员表示),但根据各成员类型的值集可以直接构造出这个结构类型的值集(这是一种典型的抽象)。 • 例如,类型address的值集 Vaddress 是这样构造出来的: Vaddress = V(name)* V(number)* V(street)* V(town)* V(state)* V(zip) = Vchar[21]* Vlong* Vchar[21]* Vchar[15]* Vchar[2]* Vlong |Vaddress| = 256 20 * (2 31-1) * 256 20 * 256 14 * 256 2 * (2 31-1) ≈ 2 510 ≈ 10 155
例: struct Link { Link* previous; Link* successor; }; /* 指针类型变量的存储空间 大小不依赖于对应类型 */ 例: struct No_good { No_good member; }; /* 存在无终止条件的递归, 无法确定对应的存储空间 的大小 */ 5.7 Structures • 用结构类型可以构造对应的数组类型、指针类型、引用类型和结构类型。 • 一个结构类型中的成员类型不能是它自身。
例: address current; address set_current(address next) { static address prev = current; current = next; return prev; } 5.7 Structures • C++为任意结构类型提供了一个对象整体赋值操作(二进制复制),但不提供与结构类型成员相关的其他整体操作(例如比较操作),这些操作由用户自己定义(以后我们会看到,这些操作所使用的操作符可以与基本类型相同)。
5.7 Structures • C++提供了取结构类型指定成员值的两个操作: • member selection(using an object). . 返回第一操作数中成员名字与第二操作数相同的成员的值。 • member selection(using a pointer). -> 返回第一操作数所指向的那个对象中,成员名字与第二操作数相同的成员的值。
5.7 Structures • 用结构类型可定义变量并进行初始化,方式与数组类似(用初始化表的方式)。 • C++为结构类型的对象分配一块连续的存储空间,并按该结构类型各成员的声明顺序和对应类型,对该空间进行划分,但在分配和划分时遵循“字对准”原则(每个成员的起始地址是一个字(Word,其大小与平台相关,常见的是 2 字节或 4 字节)的起点),因此这块空间可能大于各成员类型对应空间之和。
例: address jd = { ”Jim Dandy”, // jd.name 61, // jd.number ”South St”, // jd.street ”New Providence”, // jd.town { ’N’, ’J’ }, // jd.state 7974 // jd.zip }; number street town state zip name not used ”Jim Dandy” ”South St” ” New Providence” 5.7 Structures sizeof(address) 61 ’N’ ’J’ 7974
例: struct S1{int a;}; struct S2{int a;}; // S1 and S2 are two different types. S1 x; S2 y=x; //error: type mismatch S1 x; int l=x; //error: type mismatch 5.7 Structures ● Two structures are different types even when they have the same members.
5.6 Pointer to Void • A pointer to any type of object can be assigned to a variable of type void*, • a void* can be assigned to another void*, • void*s can be compared for equality and inequality, and • a void* can be explicitly converted to another (pointer) type(注意谨慎使用!!). Pointers to functions and pointers to members cannot be assigned to void*s.
5.6 Pointer to Void • The primary use for void* is for passing pointers to functions that are not allowed to make assumptions about the type of the object and for returning untyped objects from functions. • 被定义成 void* 类型的量所提供的信息是:由于不可能事先约定的原因,所以允许任意的指针类型与之对应(因而通常是函数的形参或返回值)。 • 这样做的可行性在于:在同一平台上任何指针类型的存储空间要求是一致的。
例: extern ”C” int qsort( void*, int, int size_t, (int (*fp)(const void*, const void*)) ); // 被排序的数组的起始地址;有效元素个数;数组元素的大小;指定的比较函数的指针 const int TABLE_SIZE = 1000; address ourAddressTable[TABLE_SIZE]; int addrCompare( const void* element1, const void* element2 ) { return strcmp( ((address*)element1)->name, ((address*)element2)->name ); } void mySort() { qsort( ourAddressTable, TABLE_SIZE, sizeof(address), addrCompare ); } 5.6 Pointer to Void • void* 类型的应用范例:
要点与引伸 • 常见的自定义常量候选者:定义数据结构时使用的边界量;有统一涵义的、表示状态的函数返回值(如成功代码、出错代码等);指针类型的函数形参。 • 引用类型的实质是起“别名”作用,因此要“追根溯源”。 • 结构类型相当于用成员名作为索引的“异质数组”。 • void* 类型为实现函数的接口通用性与功能灵活性,提供了便利的定义机制的支持,但要求我们有更强的抽象能力。 作业: 5.9.7, 5.9.11, 5.9.12, 5.9.13
下次课的内容 §6. Expressions and Statements
这一章的内容结构和阅读要点 • 一个综合体现语言的语法与简单编译过程、程序结构、语句和表达式的程序范例:桌面计算器(6.1); • 操作符:操作结果(6.2.1)、求值顺序(6.2.2)、优先级(6.2.3)、位操作符(6.2.4)、自增/自减操作符(6.2.5)、堆操作符(6.2.6)、显式类型转换(6.2.7)、实例生成符(6.2.8); • 声明语句(6.3.1)、选择语句(6.3.2)、迭代(循环)语句(6.3.3)、goto语句(6.3.4); • 注释(6.4);
认真读 大致读 回头读 这一章的内容结构和阅读要点 • 一个综合体现语言的语法与简单编译过程、程序结构、语句和表达式的程序范例:桌面计算器(6.1) • 操作符:操作结果(6.2.1)、求值顺序(6.2.2)、优先级(6.2.3)、位操作符(6.2.4)、自增/自减操作符(6.2.5)、堆操作符(6.2.6)、显式类型转换(6.2.7)、实例生成符(6.2.8); • 声明语句(6.3.1)、选择语句(6.3.2)、迭代(循环)语句(6.3.3)、goto语句(6.3.4); • 注释(6.4); 这一章重点讲授 6.1 节,其他主要靠自学。