芯存猛虎,细嗅蔷薇

芯存猛虎,细嗅蔷薇

数据类型-高级

4、复合类型

4.4、结构简介

struct inflatable
{
	char name[20];
	float vol;
	double price;
};

inflatable vincent;			//C++
struct inflatable goose;	//C

例子

// structur.cpp -- a simple structure
#include <iostream>
struct inflatable   // structure declaration
{
    char name[20];
    float volume;
    double price;
};

int main()
{
    using namespace std;
    inflatable guest =
    {
        "Glorious Gloria",  // name value
        1.88,               // volume value
        29.99               // price value
    };  // guest is a structure variable of type inflatable
// It's initialized to the indicated values
    inflatable pal =
    {
        "Audacious Arthur",
        3.12,
        32.99
    };  // pal is a second variable of type inflatable
// NOTE: some implementations require using
// static inflatable guest =

    cout << "Expand your guest list with " << guest.name;
    cout << " and " << pal.name << "!\n";
// pal.name is the name member of the pal variable
    cout << "You can have both for $";
    cout << guest.price + pal.price << "!\n";
    // cin.get();
    return 0; 
}
/*
Expand your guest list with Glorious Gloria and Audacious Arthur!
You can have both for $62.98!
*/

外部声明可以被其后面的任何函数使用,而内部声明只能被该声明所属的函数使用。通常使用外部声明,这样所有函数都可以使用这种类型的结构。

02-C++数据类型-高级-LMLPHP

变量也可以在函数内部和外部定义,外部变量由所有的函数共享。C++不提倡使用外部变量,但提倡使用外部结构的声明。

结构的赋值

可以使用赋值运算符(=)将结构A赋给另一个同类型的结构B。这样结构中每个成员都将被设置成另一个结构中相应成员的值,即使成员是数组。这种赋值被称为成员赋值。

// assgn_st.cpp -- assigning structures
#include <iostream>

struct inflatable {
	char name[20];
	float volume;
	double price;
};

int main() {
	using namespace std;
	inflatable bouquet = {
		"sunflowers",
		0.20,
		12.49
	};
	inflatable choice;
	cout << "bouquet: " << bouquet.name << " for $";
	cout << bouquet.price << endl;

	choice = bouquet;  // assign one structure to another
	cout << "choice: " << choice.name << " for $";
	cout << choice.price << endl;
	// cin.get();
	return 0;
}
/*
bouquet: sunflowers for $12.49
choice: sunflowers for $12.49
*/

结构体数组

// arrstruc.cpp -- an array of structures
#include <iostream>

struct inflatable {
	char name[20];
	float volume;
	double price;
};

int main() {
	using namespace std;
	inflatable guests[2] = {        // initializing an array of structs
		{"Bambi", 0.5, 21.99},      // first structure in array
		{"Godzilla", 2000, 565.99}  // next structure in array
	};

	cout << "The guests " << guests[0].name << " and " << guests[1].name
	     << "\nhave a combined volume of "
	     << guests[0].volume + guests[1].volume << " cubic feet.\n";
	// cin.get();
	return 0;
}
/*
The guests Bambi and Godzilla
have a combined volume of 2000.5 cubic feet.
*/

结构中的位字段

与C语言一样,C++也允许指定占用特定位数的结构成员,这使得创建于某个硬件设备上的寄存器对应的数据结构非常方便。

struct torgle_register
{
	unsigned int SN :4;
	unsigned int :4;
	bool goodIn:1
	bool goodTorgle:1;
}

4.5、共用体

共同体是一种数据格式,它能够存储不同的数据类型,但只能同时存储其中的一种类型。也就是说,结构可以同时存储int、long、double,共用体只能存储int、long或double。

union one4all
{
	int int_val;
	long long_val;
	double double_val;
};

struct widget
{
	char brand[20];
	int type;
	union id
	{
		long id_num;
		char id_char[20];
	}id_val;
};
widget prize;
if(prize.type==1)
	cin>>prize.id_val.id_num;
else
	cin>>prize.id_val.id_char;

//匿名共用体没有名称,其成员将成为位于相同地址处的变量
struct widget
{
	char brand[20];
	int type;
	union 
	{
		long id_num;
		char id_char[20];
	};
};
widget prize;
if(prize.type==1)
	cin>>prize.id_num;
else
	cin>>prize.id_char;

4.6、枚举

enum spectnum{red, orange, yellow, green, blue, violet, indigo, yyuu};

spectnum band;
band=blue;			//valid
band=2000;			//invalid

band=orange;		//valid
++band;				//invalid
band=orange + red;	//invalid

int color =blue;	//valid
band =3;			//invalid
color=3+red;		//valid, red converted to int

band = spectnum(3);	//强转

枚举的取值范围

enum bits{one=1, two=2, four=4, eight=8};
bits myflag;
myflag=bits(6);		//valid,because 6 is in bits range

范围,最大值、最小值所对应的2次幂-1(负数:+1)

4.7、指针和自由存储空间

// address.cpp -- using the & operator to find addresses
#include <iostream>

int main() {
	using namespace std;
	int donuts = 6;
	double cups = 4.5;

	cout << "donuts value = " << donuts;
	cout << " and donuts address = " << &donuts << endl;
// NOTE: you may need to use unsigned (&donuts)
// and unsigned (&cups)
	cout << "cups value = " << cups;
	cout << " and cups address = " << &cups << endl;
	// cin.get();
	return 0;
}
/*
donuts value = 6 and donuts address = 0x62fe1c
cups value = 4.5 and cups address = 0x62fe10
*/

指针与C++基本原理

面向对象编程与传统的过程性编程的区别在于,OOP强调的是在运行阶段(而不是编译阶段)进行决策。运行阶段指的是程序正在运行时,编译阶段指的是编译器将程序组合起来时。运行阶段觉得就好比度假时,选择参观哪些景点取决于天气和当时的心情;而编译阶段决策更像不管在什么条件下,都坚持预先设定的日程安排。

总之,使用OOP时,可能在运行阶段吧确定数组的长度。为使用这种方法,语言必须允许在程序运行时创建数组。C++采用的方法是,使用关键字new请求正确数量的农村以及使用指针来跟踪新分配的内存的位置。

在运行阶段做决策并非OOP独有的,但使用C++编写这样的代码比使用C语言要简单很多。

指针表示的是地址。*运算符被称为间接值或解除引用运算符,将其应用与指针,可以得到该地址处存储的值。

// pointer.cpp -- our first pointer variable
#include <iostream>

int main() {
	using namespace std;
	int updates = 6;        // declare a variable
	int *p_updates;         // declare pointer to an int

	p_updates = &updates;   // assign address of int to pointer

// express values two ways
	cout << "Values: updates = " << updates;
	cout << ", *p_updates = " << *p_updates << endl;

// express address two ways
	cout << "Addresses: &updates = " << &updates;
	cout << ", p_updates = " << p_updates << endl;

// use pointer to change value
	*p_updates = *p_updates + 1;
	cout << "Now updates = " << updates << endl;
	// cin.get();
	return 0;
}
/*
Values: updates = 6, *p_updates = 6
Addresses: &updates = 0x62fe14, p_updates = 0x62fe14
Now updates = 7
*/

对于指针,&取地址运算符;*解除引用运算符。有以下的图

02-C++数据类型-高级-LMLPHP

指针初始化

int * p_updates;

int *ptr;		//*ptr是一个int类型的值
int* ptr; 		//指针ptr指向int类型;ptr的类型是指针int的指针

int* p1,p2;		//一个指针(p1)和一个int变量(p2)

double* tax_ptr;//指向double的指针
char* str;		//指向char的指针

**注意:**在C++中,int*是一种复合类型,是指向int的指针

和数组一样,指针都是基于其他类型的。虽然tax_ptr和str指向两种长度不同的数据类型,但这两个变量本身的长度通常是相同的。也就是说,char的地址和double的地址的长度相同。

可以在声明语句中初始化指针。

//被初始化的是指针,而不是它指向的值
int higgens=5;
int* pt=&higgens;
// init_ptr.cpp -- initialize a pointer
#include <iostream>

int main() {
	using namespace std;
	int higgens = 5;
	int *pt = &higgens;

	cout << "Value of higgens = " << higgens
	     << "; Address of higgens = " << &higgens << endl;
	cout << "Value of *pt = " << *pt
	     << "; Value of pt = " << pt << endl;
	// cin.get();
	return 0;
}

/*
Value of higgens = 5; Address of higgens = 0x62fe14
Value of *pt = 5; Value of pt = 0x62fe14
*/

指针的危险

在C++中创建指针时,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存。为数据提供空间是一个独立的步骤。

long *fwllow;
*fwllow=23455556;	//invalid

警告

一定要在对指针应用解除引用运算符(*)之前,将指针初始化为一个确定的、适当的地址。

int* pt;
pt=0xB8000000;			//type mismatch
//要将数字值作为地址来使用,应通过强制类型转换将数字转换为适当的地址类型
pt=(int*)0xB8000000;	//type now match

new&delete

  • 可以使用new来分配内存
//方式1
int* pn=new int;

//方式2
int higgens;
int* pt=&higgens;

都是将一个int变量的地址赋给了指针。第2种情况下,可以通过名称higgens来访问该int,在第1种情况下,只能通过该指针进行访问。

// use_new.cpp -- using the new operator
#include <iostream>
int main() 
{
	using namespace std;
	int nights = 1001;
	int *pt = new int;          // allocate space for an int
	*pt = 1001;                 // store a value there

	cout << "nights value = ";
	cout << nights << ": location " << &nights << endl;
	cout << "int ";
	cout << "value = " << *pt << ": location = " << pt << endl;

	double *pd = new double;    // allocate space for a double
	*pd = 10000001.0;           // store a double there

	cout << "double ";
	cout << "value = " << *pd << ": location = " << pd << endl;
	cout << "location of pointer pd: " << &pd << endl;
	cout << "size of pt = " << sizeof(pt);
	cout << ": size of *pt = " << sizeof(*pt) << endl;
	cout << "size of pd = " << sizeof pd;
	cout << ": size of *pd = " << sizeof(*pd) << endl;
	// cin.get();
	return 0;
}
/*
nights value = 1001: location 0x62fe14
int value = 1001: location = 0x26d6710
double value = 1e+07: location = 0x26d6cf0
location of pointer pd: 0x62fe08
size of pt = 8: size of *pt = 4
size of pd = 8: size of *pd = 8
*/
  • 使用delete释放内存
int* ps= new int;		//allocate memory with new
....					//use the memory
delete ps;				//free memory with delete when done

这将释放ps指向的内存,但不会删除指针ps本身。例如,可以将ps重新指向另一个新分配的内存块。一定要配对得使用new和delete。

不要尝试释放已经释放的内存块。另外,不能使用delete来释放声明变量所获得的内存:

int *ps=new int;
delete ps;
delete ps;	//not ok now

int jugs=5;
int* pi=&jugs;	//ok
delete pi;		//not allowed

警告:

智能用delete来释放使用new分配的内存。然而,对空指针使用delete是安全的。

int* ps=new int;
int* pq=ps;
delete pq;

一般来说,不要创建两个指向同一个内存块的指针,因为这将增加错误地删除同一个内存块两次的可能性。

  • 使用new来创建动态数组
int* psome=new int[10];
delete [] psome;
//分配内存的通用格式
type_name* pointer_name =new type_name[size];

使用new和delete时,应遵守以下规则

  1. 不要使用delete来释放不是new分配的内存
  2. 不要使用delete释放同一个内存块两次
  3. 如果使用new[]为数组分配内存,则应使用delete[]来释放
  4. 如果使用new为一个实体分配内存,则应使用delete(没有方括号)来释放
  5. 对空指针应用delete是安全的
// arraynew.cpp -- using the new operator for arrays
#include <iostream>

int main() {
	using namespace std;
	double *p3 = new double [3];  // space for 3 doubles
	p3[0] = 0.2;                  // treat p3 like an array name
	p3[1] = 0.5;
	p3[2] = 0.8;
	cout << "p3[1] is " << p3[1] << ".\n";
	p3 = p3 + 1;                  // increment the pointer
	cout << "Now p3[0] is " << p3[0] << " and ";
	cout << "p3[1] is " << p3[1] << ".\n";
	p3 = p3 - 1;                  // point back to beginning
	delete [] p3;                 // free the memory
	// cin.get();
	return 0;
}
/*
p3[1] is 0.5.
Now p3[0] is 0.5 and p3[1] is 0.8.
*/

不能修改数组名的值,但指针时变量,可以修改它的值。

4.8、指针、数组和指针算术

指针和数组等价的原因在于指针算术和C++内部处理数组的方式。

C++将数组名解释为地址。

// addpntrs.cpp -- pointer addition
#include <iostream>

int main() {
	using namespace std;
	double wages[3] = {10000.0, 20000.0, 30000.0};
	short stacks[3] = {3, 2, 1};

// Here are two ways to get the address of an array
	double *pw = wages;      // name of an array = address
	short *ps = &stacks[0];  // or use address operator =stacks 
// with array element
	cout << "pw = " << pw << ", *pw = " << *pw << endl;
	pw = pw + 1;
	cout << "add 1 to the pw pointer:\n";
	cout << "pw = " << pw << ", *pw = " << *pw << "\n\n";

	cout << "ps = " << ps << ", *ps = " << *ps << endl;
	ps = ps + 1;
	cout << "add 1 to the ps pointer:\n";
	cout << "ps = " << ps << ", *ps = " << *ps << "\n\n";

	cout << "access two elements with array notation\n";
	cout << "stacks[0] = " << stacks[0]
	     << ", stacks[1] = " << stacks[1] << endl;
	cout << "access two elements with pointer notation\n";
	cout << "*stacks = " << *stacks
	     << ", *(stacks + 1) =  " << *(stacks + 1) << endl;

	cout << sizeof(wages) << " = size of wages array\n";
	cout << sizeof(pw) << " = size of pw pointer\n";
	// cin.get();
	return 0;
}
/*
pw = 0x62fdf0, *pw = 10000
add 1 to the pw pointer:
pw = 0x62fdf8, *pw = 20000

ps = 0x62fdea, *ps = 3
add 1 to the ps pointer:
ps = 0x62fdec, *ps = 2

access two elements with array notation
stacks[0] = 3, stacks[1] = 2
access two elements with pointer notation
*stacks = 3, *(stacks + 1) =  2
24 = size of wages array
8 = size of pw pointer
*/

对于指针的加法

02-C++数据类型-高级-LMLPHP

注意:

将指针变量加1后,其增加的值等于指向的类型占用的字节数。

通常,使用数组表示法时,C++都执行下面的转换

arrayname[i] becomes *(arrayname+i)

如果使用的指针,而不是数组名,则C++也将执行同样的转换

pointername[i] becomes *(pointername+i)

因此,在很多情况下,可以相同的方式使用指针名和数组名。对于它们,可以使用数组方括号表示法,也可以使用解除引用运算符(*)。在多数表达式中,它们都表示地址。区别之一是,可以修改指针的值,而数组名是常量:

pointername=pointername+1;		//valid
arrayname=arrayname+1;			//not allow

另一个区别是,对数组应用sizeof运算符得到的是数组的长度,而对指针应用sizeof得到的是指针的长度,即使指针指向的是一个数组。

数组的指针

short tell[10];
cout<<tell;				//displays &tell[0]
cout<<&tell;			//displays address of whole array

​ 从数字上说,两个地址是相同的;但从概念上说,&tell[0](即tell)是一个2字节内存块的地址,而&tell是一个20字节内存块的地址。换句话说,tell是一个short指针(short*),而&tell是一个这样的指针,即指向包含10个元素的short数组(short( * )[10])。

short(*pas)[10]=&tell;

指针小结

  • 声明指针
typeName* pointerName;
double* pn;
char* pc;
  • 给指针赋值

应将内存地址赋给指针。可以对变量名应用 &运算符,来获得被命名的内存的地址,new运算符返回未命名的内存的地址。

double* pn;
double* pa;
char *pc;
double bubble=3.2;

pn=&bubble;
pc=new char;
pa=new double[30];
  • 对指针解除引用
cout<< *pn;		//3.2
*pc='s';	

另一种对指针解除引用的方法是使用数组表示法,例如,pn[0]与*pn是一样的。决不要对未被初始化为适当地址的指针解除引用。

  • 区分指针和指针所指向的值
int* pt=new int;	//assigns an address to the pointer pt
*pt=5;				//stores the value 5 at that address
  • 数组名

在多数情况下,C++将数组名视为数组的第1个元素的地址

int tacos[10];
  • 指针算术

C++允许将指针和整数相加。加1的结果等于原来的地址值加上指向的对象占用的总字节数。还可以将一个指针减去另一个指针,获得两个指针的差。

仅当两个指针指向同一个数组(也可以指向超出结尾的一个位置)时,这种运算才有意义。

int tacos[10]={5,2,8,2,3,4,5,5,6,7};
int* pt=tacos;
pt=pt+1;
int* pe=&tacos[9];
pe=pe-1;
int diff=pe-pt;
  • 数组的动态联编和静态联编
//static
int tacos[10];

//dynamic
int size;
cin>>size;
int *pz=new int[size];
...
delete[]pz;
  • 数组表示法和指针表示法

使用方括号数组表示法等同于对指针解除引用

tacos[0]
tacos[3]

数组名和指针变量都是如此。因此对于指针和数组名,即可以使用指针表示法,也可以使用数组表示法

int *pt=new int[10];
*pt=5;
pt[0]=6;
pt[9]=44;

int coats[10];
*(coats+4)=12;

指针和字符串

注意:

在cout和多数C++表达式中,char数组名、char指针以及用括号扩起来的字符串常量都被解释为字符串第一个字符的地址。

// ptrstr.cpp -- using pointers to strings
#include <iostream>
#include <cstring>              // declare strlen(), strcpy()

int main() {
	using namespace std;
	char animal[20] = "bear";   // animal holds bear
	const char *bird = "wren";  // bird holds address of string
	char *ps;                   // uninitialized

	cout << animal << " and ";  // display bear
	cout << bird << "\n";       // display wren
	// cout << ps << "\n";      //may display garbage, may cause a crash

	cout << "Enter a kind of animal: ";
	cin >> animal;              // ok if input < 20 chars
	// cin >> ps; Too horrible a blunder to try; ps doesn't
	//            point to allocated space

	ps = animal;                // set ps to point to string
	cout << ps << "!\n";       // ok, same as using animal
	cout << "Before using strcpy():\n";
	cout << animal << " at " << (int *) animal << endl;
	cout << ps << " at " << (int *) ps << endl;

	ps = new char[strlen(animal) + 1];  // get new storage
	strcpy(ps, animal);         // copy string to new storage
	cout << "After using strcpy():\n";
	cout << animal << " at " << (int *) animal << endl;
	cout << ps << " at " << (int *) ps << endl;
	delete [] ps;
	// cin.get();
	// cin.get();
	return 0;
}
/*
bear and wren
Enter a kind of animal: fox
fox!
Before using strcpy():
fox at 0x62fdf0
fox at 0x62fdf0
After using strcpy():
fox at 0x62fdf0
fox at 0x2576cd0
*/

通过使用strcpy()和new,将获得"fox"的两个独立副本

经常需要将字符串放到数组中。初始化数组时,请使用=运算符;否则应使用strcpy()或strncpy()。

char food[20]="carrots";		//initalization
strcpy(food,"flan");			//otherwise

对于strncpy()

strncpy(food,"a picnic basket filled with many goodies",19);
food[19]='\0';

这样最多将19个字符复制到数组中,然后将最后一个元素设置成空字符。如果该字符串少于19个字符,则strncpy()将在复制完该字符串之后加上空字符,以 标记该字符串的结尾。

使用new创建动态结构

02-C++数据类型-高级-LMLPHP

提示

如果结构标识符时结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符。

另一种访问结构成员的方法是,如果ps是指向结构的指针,则*ps就是被指向的值——结构本身。由于*ps是一个结构,因此(*ps).price是该结构的price成员。
// newstrct.cpp -- using new with a structure
#include <iostream>

struct inflatable { // structure definition
	char name[20];
	float volume;
	double price;
};

int main() {
	using namespace std;
	inflatable *ps = new inflatable;  // allot memory for structure
	cout << "Enter name of inflatable item: ";
	cin.get(ps->name, 20);            // method 1 for member access
	cout << "Enter volume in cubic feet: ";
	cin >> (*ps).volume;              // method 2 for member access
	cout << "Enter price: $";
	cin >> ps->price;
	cout << "Name: " << (*ps).name << endl;              // method 2
	cout << "Volume: " << ps->volume << " cubic feet\n"; // method 1
	cout << "Price: $" << ps->price << endl;             // method 1
	delete ps;                        // free memory used by structure
	// cin.get();
	// cin.get();
	return 0;
}
/*
Enter name of inflatable item: Fab
Enter volume in cubic feet: 1.4
Enter price: $27.99
Name: Fab
Volume: 1.4 cubic feet
Price: $27.99
*/
  • 使用new和delete
// delete.cpp -- using the delete operator
#include <iostream>
#include <cstring>      // or string.h
using namespace std;
char *getname(void);    // function prototype

int main() {
	char *name;         // create pointer but no storage

	name = getname();   // assign address of string to name
	cout << name << " at " << (int *) name << "\n";
	delete [] name;     // memory freed

	name = getname();   // reuse freed memory
	cout << name << " at " << (int *) name << "\n";
	delete [] name;     // memory freed again
	// cin.get();
	// cin.get();
	return 0;
}

char *getname() {       // return pointer to new string
	char temp[80];      // temporary storage
	cout << "Enter last name: ";
	cin >> temp;
	char *pn = new char[strlen(temp) + 1];
	strcpy(pn, temp);   // copy string into smaller space

	return pn;          // temp lost when function ends
}

/*
Enter last name: Fredlkin
Fredlkin at 0x726cd0
Enter last name: Pook
Pook at 0x726cd0
*/

自动存储、静态存储和动态存储

  • 自动存储

​ 在函数内部定义的常规变量使用自动存储空间,被称为自动变量,这以为着它们在所属的函数被调用时自动产生,在该函数结束时消亡。

​ 实际上,自动变量是一个局部变量,其作用域为包含它的代码块。

​ 自动变量通常存储在栈中。这意味着执行代码块时,其中的变量依次加入到栈中,而离开代码块时,将按相反的顺序释放这些变量。这被称为后进先出(LIFO)。因此,在程序执行的过程中,栈不断地增大和缩小。

  • 静态存储

​ 静态存储是整个程序执行期间都存在的存储方式。使变量称为静态的方式有两种:一种是在函数外面定义它;另一种是在声明变量时使用关键字static:

static double fee=56.50;
  • 动态存储

​ new和delete运算符提供了一种比自动变量和静态变量更灵活的方法。它们管理了一个内存池,这在C++中被称为自由存储空间(free store)或堆(heap)。该内存池同用于静态变量和自动变量的内存时分开的。

​ new和delete让我们能够在一个函数中分配内存,在另一个函数中释放它。因此,数据的生命周期不完全受程序或函数的生存时间控制。

栈、堆和内存泄漏

​ 如果使用了new运算符在自由存储空间(或堆)上创建变量后,没有调用delete,将会发生什么?

​ 如果没有调用delete,则即使包含指针的内存由于作用域规则和对象生命周期的原因而被释放,在自由存储空间上动态分配的变量或结构也将继续存在。实际上,将会无法访问自由存储空间中的结构,因为指向这些内存的指针无效。这将导致内存泄漏。被泄漏的内存将在程序的整个生命周期内都不可使用;这些内存被分配出去,但无法收回。

4.9、类型组合

//结构定义
struct antarctica_year_end
{
	int year;
};

antarctica_year_end s01,s02,s03;		//创建变量
s01.year=1998;							//使用成员运算符访问成员

antarctica_year_end=&s02;				//创建指向这种结构的指针
pa->year=1999;							//使用间接成员运算符访问成员

antarctica_year_end trio[3];			//创建结构数组
trio[0].year=2003;						//使用成员运算符访问成员
(trio+1)->year=2004;					//使用间接成员运算符

const antarctica_year_end* arp[3]={&s01,&s02,&s03};		//创建指针数组
std::cout<<arp[1]->year<<std::endl;						//使用间接运算符

const antarctica_year_end** ppa=arp;					//创建上面数组的指针 ppa=arp
std::cout<<(*ppa)->year<<std::endl;					//*ppa为&s01,式子为s01的year成员
std::cout<<(*(ppb+1))->year<<std::endl;

例子

// mixtypes.cpp --some type combinations
#include <iostream>

struct antarctica_years_end
{
    int year;
 /* some really interesting data, etc. */
};

int main()
{
    antarctica_years_end s01, s02, s03; 
    s01.year = 1998;
    antarctica_years_end * pa = &s02;
    pa->year = 1999;
    antarctica_years_end trio[3]; // array of 3 structures
    trio[0].year = 2003;
    std::cout << trio->year << std::endl;
    const antarctica_years_end * arp[3] = {&s01, &s02, &s03};
    std::cout << arp[1]->year << std::endl;
    const antarctica_years_end ** ppa = arp; 
    auto ppb = arp; // C++0x automatic type deduction
// or else use const antarctica_years_end ** ppb = arp; 
    std::cout << (*ppa)->year << std::endl;
    std::cout << (*(ppb+1))->year << std::endl;
    // std::cin.get();
    return 0;
}
/*
2003
1999
1998
1999
*/

4.10、vector、array

vector

模板类vector类似于string类,也是一种动态数组。可以在运行阶段设置vector对象的长度,可以在末尾附加新数据,还可以再中间插入新数据。

声明创建一个名为vt的vector对象,它可以存储n_elem个类型为typeName的元素

vector<typeName> vt(n_elem)
//其中参数n_elem可以是整型常量,也可以是整型变量。

#include<vector>
using namespace std;
vector<int>vi;
int n;
cin>>n;
vector<double> vd(n);

array

​ vector类的功能比数组强大,但代价是效率较低。如果需要长度固定的数组,使用数组是更佳的选择,但代价是不那么方便和安全。array,也位于名称空间std中。

​ 与数组一样,array对象的长度也是固定的,也使用栈(静态内存分配),而不是自由存储区,因此其效率与数组相同,但更方便、安全。

array<typeName,n_elem> arr;
//声明创建一个名为arr的array对象,它包含n_elem个类型为typeName的元素。
//n_elem不能是变量

#include<array>
....
using namespace std;
array<int,5> ai;
array<double,4>ad={1.2, 2.1, 3.43, 4.3};

对比

// choices.cpp -- array variations
#include <iostream>
#include <vector>   // STL C++98
#include <array>    // C++0x

int main() {
	using namespace std;
// C, original C++
	double a1[4] = {1.2, 2.4, 3.6, 4.8};
// C++98 STL
	vector<double> a2(4);   // create vector with 4 elements
// no simple way to initialize in C98
	a2[0] = 1.0 / 3.0;
	a2[1] = 1.0 / 5.0;
	a2[2] = 1.0 / 7.0;
	a2[3] = 1.0 / 9.0;
// C++0x -- create and initialize array object
	array<double, 4> a3 = {3.14, 2.72, 1.62, 1.41};
	array<double, 4> a4;
	a4 = a3;     // valid for array objects of same size
// use array notation
	cout << "a1[2]: " << a1[2] << " at " << &a1[2] << endl;
	cout << "a2[2]: " << a2[2] << " at " << &a2[2] << endl;
	cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
	cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
// misdeed
	a1[-2] = 20.2;
	cout << "a1[-2]: " << a1[-2] << " at " << &a1[-2] << endl;
	cout << "a3[2]: " << a3[2] << " at " << &a3[2] << endl;
	cout << "a4[2]: " << a4[2] << " at " << &a4[2] << endl;
	//  cin.get();
	return 0;
}

/*
a1[2]: 3.6 at 0x62fdf0
a2[2]: 0.142857 at 0x25b6720
a3[2]: 1.62 at 0x62fdb0
a4[2]: 1.62 at 0x62fd90
a1[-2]: 20.2 at 0x62fdd0
a3[2]: 1.62 at 0x62fdb0
a4[2]: 1.62 at 0x62fd90
*/

首先,无论是数组、vector对象还是array对象,都可以使用标准数组表示法来访问各个元素。

其次,从地址中可知,array对象和数组存储在相同的内存区域(即栈)中,而vector对象存储在另一个区域(自由存储区或堆)中。

第三,可以将一个array对象赋给另一个array对象;对于数组,必须逐个元素复制数据。

数组越界无法禁止。对于vector和array对象,可以禁止,但仍然不安全。

08-16 14:12