sunwengang blog

C++复合类型之枚举、指针

字数统计: 2.6k阅读时长: 11 min
2019/08/12 Share

枚举enum

C++的enum工具提供了另一种创建符号常量的方式,可以代替const
它还允许定义新的类型,但是必须严格按照格式。
使用enum语法格式和结构相似,例如

enum colorInfo{red, blue, orange};

  • colorInfo是新类型的名称;colorInfo被成为枚举
  • red,blue,orange是符号常量,对应整型树脂0,1,2,这些常量叫做枚举量
    • 默认情况下,将整型数值赋给枚举量,从0开始以此类推
  1. 声明

使用枚举名来声明这种枚举的变量:colorInfo band;

  1. 赋值

只能使用定义枚举量赋值给枚举的变量:

1
2
3
4
5
6
7
8
9
10
11
+ band = red; //将定义枚举中的red值赋给band变量
- band = black; //在枚举中不存在

double *pn;
char *pa;
double *pc;

double bu = 4.2;
pn = &bu;
pa = new char;
pc = new double[10];
  1. 设置枚举的值
  • 使用赋值运算符显式设置枚举的值:

    enum bits {one=1, two=2};

  • 显式定义部分元素(其他的元素以前一个元素作为参照依次+1)

    enum bytes {a, b=100, c} //此时a=0,b=100,c=101

  • 创建多个值相同的枚举量

    enum {zero, null=0, one, numero_uno=1}; //其中zero和null都是0,后面两个都是1,这是合法的

  1. 枚举的取值范围

枚举的最大值最小的2的次幂,将其减去1,得到取值范围的上限。

例如最大值是101,则最小的2的次幂是128,减去1,所以取值的上限是127

如果枚举量的最小值>=0,则取值范围的下限是0;否则同上,取2的最小次幂,减一,加上负号。

例如最小值是-6,则取最小2次幂减一是7,加上负号,所以取值下限是-7


指针

计算机在存储数据的时候必须跟踪的三种基本属性:

  1. 信息存储的位置
  2. 存储的值
  3. 存储的类型

指针是一个变量,其存储的值是值的地址

地址运算符&

地址运算符&可以获取一个常规变量的地址,例如home是变量,&home即是他的地址

address1.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#include<iostream>

using namespace std;

int main() {
int dog = 6;
double food = 4.5;

cout << "dog = " << dog << ", food = " << food <<endl;

cout << "dog address = " << &dog << ", food address = " << &food <<endl;

return 0;
}

打印结果:

1
2
dog = 6, food = 4.5
dog address = 0x7fff81decc1c, food address = 0x7fff81decc20

Notes:

  1. 常用十六进制描述地址(也存在十进制表示法)。 使用常规变量时,值是指定的量,而地址是派生量

  2. 面向对象OOP和面向过程的编程区别在于,OOP强调的是在运行阶段(非编译阶段)进行决策。运行阶段指的是程序正在运行,编译阶段指的是编译器将程序组合起来时。

    运行阶段决策提供了灵活性,可以根据实时情况进行调整。例如声明数组的时候定义长度。

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

    例如:假设manly是一个指针,则manly表示的是一个地址,而*manly表示存储在该处的值。*manly和常规int变量等效。

例如下列代码:

pointer.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include<iostream>
using namespace std;

int main() {
int updates = 6; //变量
int * p_updates; //指针
p_updates = &updates; //指针存储了变量的地址

cout << "value updates = " << updates << ", p_updates = " << p_updates << ", &updates = " << &updates <<endl;

*p_updates = *p_updates + 1;
cout << "Now updates = "<< updates << ", *p_updates = "<< *p_updates << ", p_updates = " << p_updates <<endl;

return 0;
}

执行结果:

1
2
value updates = 6, p_updates = 0x7ffcf520fbcc, &updates = 0x7ffcf520fbcc
Now updates = 7, *p_updates = 7, p_updates = 0x7ffcf520fbcc

声明和初始化

指针声明必须指定指针指向的数据的类型

int * p_updates;
*p_updates的类型是int,由于*被用于指针,因此p_updates变量本身必须是指针,即p_updates指向int类型,或者是指向int的指针,或int*
p_updates是指针(地址);*p_updates是int,而不是指针。

Notes:

(1) *两边的空格是可选的。
int *ptr; 强调*ptr是int类型的变量。
int* ptr; 强调int*是一种指向int的指针。(在C++中,int*是复合类型,是指向int的指针

(2) 在C++中创建指针时,计算机将分配内存用来存储地址的内存,但是不会分配用来存储指针所指向的数据的内存。例如:

long *fellow;
*fellow = 2333; //ERROR
此处没有给2333赋地址。

指针和数字

整数可以加减乘除,而指针表示地址,描述的是位置,将两个地址相乘没有意义。

1
2
3
4
5
6
7
//ERROR 不可以直接赋值,编译器会有错误消息:通用类型不匹配
int* ptr;
ptr = 0xB8000000;

//Right 转换
int* ptr;
ptr = (int*) 0xB8000000;

使用new分配内存

在C++中,除了可以使用C语言的方法malloc()函数分配内存,还可以使用new运算符
例如:
(1) int *ptr = new int;
new int告诉程序需要合适存储int的内存。new运算符根据类型确定需要多少字节的内存,然后找到这样的内存并返回地址。接着将地址赋给ptrptr是被声明为指向int的指针。

(2) int hig;
int *pn = &hig;
这种方法是使用hig名称来访问int

为一个数据对象(可以是结构,或者基本类型等)获得并指定分配内存的通用格式:
typename * poniter_name = new typename;

代码示例:

use_new.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<iostream>

using namespace std;

int main(){
int nights = 1001;
int *pt = new int; //分配内存
*pt = 1001; //赋值

cout << "nights = " << nights << ", its' address = " << &nights <<endl;
cout << "*pt = " << *pt << ", its' address = " << pt <<endl;

double *pd = new double;
*pd = 10000001.0;

cout << "*pd = " << *pd << ", address 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;

return 0;
}

执行结果:

1
2
3
4
5
nights = 1001, its' address = 0x7fff3426f484
*pt = 1001, its' address = 0x150bc20
*pd = 1e+07, address of pointer *pd = 0x7fff3426f488
size of pt = 8 : size of *pt = 4
size of pd = 8 : size of *pd = 8

Note:

变量nightspd的值都存储在栈stack的内存区域;而new堆heap或者自由存储区free store的内存区域分配内存。

使用delete释放内存

使用delete时,后面要加上指向内存块的指针(这些内存块最初是由new分配的)

1
2
3
int *ps = new int;
...
delete ps;

这将会释放ps指向的内存,但是不会删除指针ps本身。例如,可以将ps重新指向另一个新分配的内存块。

一定要配对的使用new和delete,否则会发生内存泄漏 memory leak

只能用delete来释放使用new分配的内存(对空指针使用delete是安全的)

使用delete的关键是,将他用于new分配的内存,这并不意味者要使用用于new的指针,而是用于new的地址。

例如:

1
2
3
4
5
6
7
8
9
10
11
int *ps = new int;
delete ps; //ok
delete ps; //not ok

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

int *pk = new int; //分配内存
int *pq = pk; //声明第二个指针
delete pq; //delete第二个指针,并不会影响pk的值(但是pk的值必须要new分配内存)

使用new创建动态数组

例如下面的语句创建指针,它指向包含十个int值的内存块中的第一个元素:
int *psome = new int[10];

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

将指针变量加一后,增加的量等于它指向类型的字节数(例如int数组一个变量四个字节)

代码:

arraytnew.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include<iostream>

int main() {
using namespace std;
double *p3 = new double[3];
p3[0] = 0.2;
p3[1] = 0.5;
p3[2] = 0.8;

cout << "p3[1] = "<< p3[1] <<endl;

p3+=1;

cout << "Now p3[0] = "<< p3[0] <<endl;
cout << "Now p3[1] = "<< p3[1] <<endl;

p3-=1;

delete []p3; //free memory
return 0;
}

结果:

1
2
3
p3[1] = 0.5
Now p3[0] = 0.5
Now p3[1] = 0.8 //+1之后的数组第一个元素就变成了第二个元素

指针小结

  • 对数组使用sizeof得到的是数组的长度(int4个字节乘以元素数量,单位是字节),而对指针使用sizeof得到的是指针的长度,即指针指向的是一个数组(元素数量)。

    1
    2
    3
    4
    5
    6
    7
    int *pt = new int[10];
    *pt = 5; //pt[0]=5 or pt=5
    pt[0] = 6; //reset

    pt[9] = 44;
    int coats[10];
    *(coats + 4) = 12; //coats[4] = 12
  • 数组名被解释为其第一个元素的地址arrayname,而对数组名应用地址运算符&时,即&arrayname,得到的是整个数组的地址。
    例如:

    1
    2
    3
    short tell[10];
    tell //display &tell[0],一个2字节内存块的地址
    &tell //display address of whole array,一个20字节内存块的地址
  • 对指针接触引用意味着获取指针指向的值,例如*pn,另一种接触引用的方法是使用数组表示法,例如pn[0]

  • 区别指针和指针指向的值,int *pt = new int; *pt = 5;其中pt是指向int的指针,而*pt是完全等同于一个int类型的变量。

  • 数组的静态联编是使用数组声明来创建数组时,数组的长度在编译时设置int tacos[10];

  • 数组的动态联编是使用new[]创建数组,在运行时为数组分配空间,其长度在运行时者之。使用完这种数组后,应该使用delete[]释放占用的内存。

ptrstr.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include<iostream>
#include<cstring>

using namespace std;

int main() {
char animal[20] = "bear";
const char *bird = "wren";
char *ps;

cout << animal << " and " << bird <<endl;
//cout << ps <<endl; //may crash, may garbage, may skip, print nothing

ps = animal;
cout << ps << endl;
cout << (int *)animal << " and " << (int *)ps <<endl;

ps = new char(strlen(animal) + 1); //get new storage返回字符串的长度
strcpy(ps, animal); //copy string to new storage将字符串从一个位置复制到另一个位置
cout << "After using strcpy, animal " << animal << " at "<< (int*)animal <<endl;
cout << ps << " at " << (int*)ps <<endl;

delete []ps;

return 0;
}

执行结果:

1
2
3
4
5
bear and wren
bear
0x7ffde23d00b0 and 0x7ffde23d00b0
After using strcpy, animal bear at 0x7ffde23d00b0
bear at 0x1b2b030
CATALOG
  1. 1. 枚举enum
  2. 2. 指针
    1. 2.1. 地址运算符&
    2. 2.2. 声明和初始化
    3. 2.3. 指针和数字
    4. 2.4. 使用new分配内存
    5. 2.5. 使用delete释放内存
    6. 2.6. 使用new创建动态数组
    7. 2.7. 指针小结