1 / 39

§5. Pointers, Arrays, and Structures

§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 * 的作用);

Télécharger la présentation

§5. Pointers, Arrays, and Structures

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. §5. Pointers, Arrays, and Structures

  2. 这一章的内容结构 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(不同类型的变量的有机组合:结构)。

  3. 这次课的内容 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(不同类型的变量的有机组合:结构)。

  4. 5.4 Constants

  5. 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.

  6. 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.

  7. 例: 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.

  8. 例: 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.

  9. 例: 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.

  10. 例: 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.

  11. 例: 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*时,不允许修改的是 *<常量名>及<常量名> [<下标>]对应的值,而不是 <常量名>对应的值。

  12. 例: 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.

  13. 例: 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.

  14. 5.5 References

  15. 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.

  16. 5.5 References • 由于一个引用所表示的是对象的名,所以它不能独立存在,必然要关联于某个已定义的名字,它们对应于同一个对象。 • 关联的方式是:(1)在定义该引用时所声明的初始化;(2)对于被定义为函数的形参(或返回值)的引用,由实参(或返回值表达式)给出对应值。

  17. 一个引用好比 是一个总是用 * 操作的指针常量 例: void g() { int ii = 0; int& rr = ii; rr++; // 等价于:ii++; int* pp = &rr; // pp == &ii; *pp == ii; } rr : ii : &ii 5.5 References 0 1

  18. 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 声明的形参)

  19. 例:使用引用、不易理解的方式 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 声明的形参)

  20. 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. • 这里指的是:当把这样的函数之返回类型定义成引用类型的时候 … ...

  21. 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

  22. 5.7 Structures

  23. 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++中的结构类型实际上是特殊的类(以后会介绍)。

  24. 例: 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

  25. 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

  26. 例: struct Link { Link* previous; Link* successor; }; /* 指针类型变量的存储空间 大小不依赖于对应类型 */ 例: struct No_good { No_good member; }; /* 存在无终止条件的递归, 无法确定对应的存储空间 的大小 */ 5.7 Structures • 用结构类型可以构造对应的数组类型、指针类型、引用类型和结构类型。 • 一个结构类型中的成员类型不能是它自身。

  27. 例: address current; address set_current(address next) { static address prev = current; current = next; return prev; } 5.7 Structures • C++为任意结构类型提供了一个对象整体赋值操作(二进制复制),但不提供与结构类型成员相关的其他整体操作(例如比较操作),这些操作由用户自己定义(以后我们会看到,这些操作所使用的操作符可以与基本类型相同)。

  28. 5.7 Structures • C++提供了取结构类型指定成员值的两个操作: • member selection(using an object). . 返回第一操作数中成员名字与第二操作数相同的成员的值。 • member selection(using a pointer). -> 返回第一操作数所指向的那个对象中,成员名字与第二操作数相同的成员的值。

  29. 5.7 Structures • 用结构类型可定义变量并进行初始化,方式与数组类似(用初始化表的方式)。 • C++为结构类型的对象分配一块连续的存储空间,并按该结构类型各成员的声明顺序和对应类型,对该空间进行划分,但在分配和划分时遵循“字对准”原则(每个成员的起始地址是一个字(Word,其大小与平台相关,常见的是 2 字节或 4 字节)的起点),因此这块空间可能大于各成员类型对应空间之和。

  30. 例: 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

  31. 例: 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.

  32. 5.6 Pointer to Void

  33. 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.

  34. 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* 类型的量所提供的信息是:由于不可能事先约定的原因,所以允许任意的指针类型与之对应(因而通常是函数的形参或返回值)。 • 这样做的可行性在于:在同一平台上任何指针类型的存储空间要求是一致的。

  35. 例: 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* 类型的应用范例:

  36. 要点与引伸 • 常见的自定义常量候选者:定义数据结构时使用的边界量;有统一涵义的、表示状态的函数返回值(如成功代码、出错代码等);指针类型的函数形参。 • 引用类型的实质是起“别名”作用,因此要“追根溯源”。 • 结构类型相当于用成员名作为索引的“异质数组”。 • void* 类型为实现函数的接口通用性与功能灵活性,提供了便利的定义机制的支持,但要求我们有更强的抽象能力。 作业: 5.9.7, 5.9.11, 5.9.12, 5.9.13

  37. 下次课的内容 §6. Expressions and Statements

  38. 这一章的内容结构和阅读要点 • 一个综合体现语言的语法与简单编译过程、程序结构、语句和表达式的程序范例:桌面计算器(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);

  39. 认真读 大致读 回头读 这一章的内容结构和阅读要点 • 一个综合体现语言的语法与简单编译过程、程序结构、语句和表达式的程序范例:桌面计算器(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 节,其他主要靠自学。

More Related