cpp-变量
1.枚举类型
枚举类型是用户自定义的类型,在定义时要列举出该枚举类型所有的数值。
枚举的定义
定义格式如下:
[enum] enumName {val1, val2, val3}
- 其中的通常为较易懂的字符,这也是枚举类型的使用目的:增强易读性
- 按照顺序,枚举值依次与1,2,3等整数一一对应。在定义时也可以指出枚举值对应的数值,只是要指出的需要在左边,剩下未指出的的将会按照递增顺序。如:
enum Day {sun = 7, mon = 1, tue = 2, wed, thu, fri, sat}
对枚举类型的操作
- 枚举类型与整数值一一对应,所以可以进行计算。
- 可以把枚举量赋值给整型变量;整型变量通过强制类型转换可以赋值给(在其值范围内的)枚举变量
- 枚举变量可以进行大小比较
- 对枚举变量的输出结果是其对应的整数值
2.数组
数组是一组组成元素类型相同的,在内存存储连续的序列。
数组的定义
一维数组的定义
dataType arrayName[memNum]
typedef
在用于数组定义时有种特殊的写法:typedef int A[10];
。这种写法将int [10]
这样的数组定义作了A
,其特殊之处在于新的名字夹在组成类型的中间
初始化表
指在定义数组时,同时给出其值的表,格式为:int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
。
- 若使用初始化表而没有给出初值/给出的初值不够,则会用
0
作为其默认值进行补全 - 使用初始化表且完整给出所有值则可以省略数组定义时的个数:
int a[] = {1, 2, 3, 4, 5}
字符串数组
字符串数组是指形如char s[num]
的数组。
所有的字符串数组在计算长度时都需要-1
,因为要在末尾添加字符串的结束标志\0
(新标准下数组长度似乎不再需要为结束标志留位置,且定义时给出的num下标即为最大可访问的下标)。在输出时会检测这个\0
作为结束标志,而不会将这个标志输出,这种字符串叫asciiz串
。
字符串数组的初始化
字符串数组可以用如下的方式进行初始化:
char s[] = {"hello"};
char s[] = {'h', 'e', 'l', 'l', 'o'};
char s[] = "hello"
这些方式都会在其末尾加上\0
,而不需要考虑太多。若要求用户在运行时为字符串赋值,则要特别注意在用户输入后向末端添加\0
。
二维数组
二维数组的定义
格式:dataType arrayName[index1][index2];
,其中的index1对应行,index2对应列,可访问下标范围为0-index1 - 1
, 0-index2 - 1
-
使用typedef来简化:
typedef int nickName[index1][index2];
-
可以用初始化表来为二维数组定义
int a [2][3] = {{2, 3}, {3, 4}}
:这样初始化后的二维数组对应为2, 3, 0; 3, 4, 0
,即是说可以用大括号内嵌套大括号的方式指定
二维数组的使用
- 在定义函数时若涉及二维数组的调用,则必须指明列数;行数可以通过用户传入的参数来界定,以此获得较灵活的使用。如:
int max (int array[ ][5], int row)
:这种方式定义的函数,可以传入所有列为5的二维数组,再通过传入的行参数即可访问该二维数组。即是说,定义参数时,必须指出列数,而行数可以省略(但必须通过其他方式指出,以防止越界)
- 将二维数组作为一维数组使用
- 在逻辑上二维数组与现实生活情景有更加明确的关联,但是一二维数组在实现上没有本质差别,在内存中均为申请首地址后,一段连续的地址。
- 可以将二维数组传给调用参数为一维数组的函数,如:
- 有函数定义为
max(int x[ ], int max)
- 现有一二维数组
array[5][6]
- 则可以通过
max( array[0], 5*6)
来调用这个使用一维数组的函数
- 有函数定义为
3.结构类型
结构类型的定义
struct someName {
int val1;
char str[10];
};
//结合typedef完成定义
typedef struct someName{
int val1;
char str[10];
} newName;
结构类型的初始化
newName a = {11, "hello"};
结构类型的使用
通过成员访问符.
来调用其中的变量。如:a.val1
,a.str
4.联合类型
联合类型的定义与结构类似,但是联合定义的变量公用一块内存。即是说,一段时间内只能使用其中的一个变量类型,若同一时间要访问多个联合内的变量,会失败。
联合适用于一次性使用的、不在同一时间内使用的大变量,以来节省空间的使用。
联合类型的定义
union someName{
int a;
double b;
char c;
}
5.指针
指针的定义
dataType *pointerName
- 指针定义与一般变量定义的区别在于变量名前有一个
*
,这个符号在书写时可以跟着dataType
也可以跟着pointerName
,但编译器将符号的附着点判断在变量名pointerName
上。如int* a, b
:申请了一个整形变量b,和一个整形指针a,即是说按照int *a, b
理解。 - 指针应该尽量赋予初值。对于没有初值的变量应当使用空指针变量
NULL
来完成时初始化。NULL
是stdio.h
中定义的常量,其值为0
。 - 指针的数值一般为整型值,代表着内存编号。
指针的操作
基本操作
- 取地址
int *a = &valA
:符号&
将变量的地址赋给了指针 - 间接访问
*a = 5
:符号*
用于指针时,等价于使用对应的变量val
用于数组时的操作
-
指针的值常初始化做数组的第一个元素,以便于访问,如下。
int * a, b[10] = {}; a = &b[0]
-
此时指针可以与整数进行加减运算,表示指针指向当前元素的上一个、下一个元素
-
若有两个同类型的指针分别指向同一个数组,那么这两个指针也可以进行减法运算,表示计算出指针间相隔的元素个数。
用于结构变量时的操作
-
若指针指向一个结构变量,那么对于其中的成员变量,可以额外有一种特殊的访问方式,如下:
struct sth { int a; double b; } sth a; sth * p; cout << (*p).a;//常规方式 cout << p->b; //特殊的方式
指针变量的输出
-
当指针变量直接输出时,会输出变量对应的整形值,数值常为十六进制。如:
int x = 1, *y = &x; cout << y; //输出的是y的值,亦即x对应的内存地址
-
当指针变量指向字符串/字符数组时,会直接输出字符串。如:
char str[] = "string", *p = str; cout << p; // 输出结果是string cout << *p; //输出结果是s
-
若要字符串输出的为地址,则要在输出前进行类型转换,换做一个其它类型的指针
cout << (void *)p; //输出的为字符串的地址
指针作为传递的参数
使用指针作为函数传递的参数,可以提高传输效率。常用在数组作为参数时使用。如:
int max(int *x, int num){
//形参为一个指针
.......;
}
int main(void){
int a [10];
....;
max(&a[0], 10);
//传回数组地址
}
这样避免了传递整个数组的低效。
指针常量与指向常量的指针
使用指针,会有两个效果:
1. 提高参数传递的效率
2. 用形参改变了实参的值(调用的函数可能改变原有的、传入的实参的值)
指针常量
而第二个效果常常会产生副作用,为了避免这个副作用,可以使用关键词const
构成指针常量
。指针常量
只能改变所指向的内存(即地址值),但不能通过间接访问改变所指向内存的数值(指地址所指向的内容)。这样就避免了形参改变实参,同时还保留了传递参数的功能。
指针常量
的声明:dataType *const pointerName
即是在*
前加了关键词const
以申明指针常量。
需要与指针常量
区别的是指向常量的指针常量
指向常量的指针常量
指向常量的指针常量
是一个常量,即不能改变它指向的值,也不能通过间接访问改变它所指向的内存。
指向常量的指针常量
的声明:const dataType *const pointerName
在指针常量前也加上关键词const
,就构成了指向常量的指针常量。
对比
指针常量:dataType *const pointerName
指向常量的指针常量:const dataType *const pointerName
6.动态变量
动态变量是指,在程序在静态的未运行阶段无法确定的变量,只有在程序运行时他才根据程序的需要产生、消亡。
动态变量的创建
动态变量的申请有两种方式
a.关键字new
-
使用关键字
new
,会生成一个指针/地址 -
申请单个动态变量:
new dataType
int *p;
p = new int;
*p = 1;
- 申请数组,数组可以为多维的:
new dataType[num]
int *p;
int n;
//一维数组
p = new int[n];
//二维数组
p = new int[n][20];
//应当注意高维数目一定得指定,最低一维可以不指定
b.函数malloc()
- 函数
malloc()
返回一个void *
变量,即void
型变量。在使用前需要对其进行类型转换。 - 申请单个动态变量:
(dataType *)malloc(sizeof(dataType) )
int *p;
p = (int *)malloc(sizeof(int) );
- 申请数组,数组亦可以为多维的:
(dataType *)mallloc(sizeof(dataType) * num)
int *p;
p = (int *)malloc(sizeof(int) *n);
//申请了一个一维数组,可以放(0,n-1)个数
p = (int *)malloc(sizeof(int) *n * 20);
//申请了一个二维数组,且同上:高维必须指出,低维可以不指出
c.new
与malloc()
的区别
主要在于:
new
会自动调用构造函数,而malloc()
则不会new
自动计算需要的内存,而malloc()
需要手动指出malloc()
需要进行类型转换
动态变量的销毁
a.销毁使用new
创建的变量
使用new
创建的变量最好使用关键字delete
销毁
格式:delete pointerName
//销毁单个动态变量
delete p;
//销毁指向动态数组的变量
delete [ ]p;
b.销毁使用malloc()
创建的变量
使用malloc()
创建的变量最好使用函数free()
销毁
格式:free(pointerName)
//free( )因为是函数,所以只有一种使用方法,而没有多种格式
free(p);
c.delete
与free()
的区别
主要区别如下:
- 关键字
delete
会调用相关类型的析构函数,而函数free( )
则不会
d.动态变量造成的内存泄漏
当一个指向动态变量的指针改变指向的变量后,便无法完成对动态变量的访问了,无法对其访问也无法回收对之分配的内存,这种现象叫做内存泄漏
7.函数指针
函数指针的定义
dataType (* pointerName)(argsType)
dayaType
是指返回值类型,(argsType)
是参数列表,这个参数按序填入即可- 可以使用
typedef
重命名,格式为:typedef dataType (* newName)(argsType)
。即新的类型名为newName
,在原来pointerName
的位置
函数指针的赋值与调用
- 赋值
有两种格式,如下方演示
int (* fp)(int, double);
int demo(int a, double b){
return a;
}
//方法1
fp = demo;
//方法2
fp = &demo;
- 调用
这里调用即是指用函数指针
完成对函数的调用,也是函数指针的用处
。调用也有两种格式
int (* fp)(int, double);
int demo(int a, double b);
//方法1
(*fp)(10, 2.8);
//方法2
fp(10, 2.9);