int a [10]、char name[128]、float price[20]
定义数组的常量表达式不能是变量,因为数组的大小不能动态定义 int a [i] 是错误的
1.1.2 赋值//正常赋值
int a[12] = {1,2,3,4,5,6,7,8,9,10,11,12};
//编译器能够获得数组元素个数
int a[] = {1,2,3,4,5,6,7,8,9,10,11,12};
//前 7 个赋值,后 5 个为 0
int a[12] = {1,2,3,4,5,6,7};
1.2 二维数组
1.2.1 定义
int a [3][4] // 3行4列 数组
可以看做一个特殊的一维数组:二维数组由 A[0]、A[1]、A[2] 三个一维数组组成,以行划分,横着来的,横着读成三个一维数组,几行,就是几个一维数组,存储也是这么存储的。
int a [行][列] = int a [0][] + int a [1][] + int a [2][] + int a [3][] ... (每个一维数组元素有 「列」 个)
1.2.2 赋值// 单个 赋值
a [0][1] = 1
// 整组 赋值
int a [3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}
// 按行 赋值
int a [3][4] = {1,2,3,4},{5,6,7,8},{9,10,11,12}
// 只赋值前 4 个元素,后面元素全为 0
int a [3][4] = {1,2,3,4}
数值元素是左值,可以参与运算 b[1][2] = a[3][4]/2
1.3 字符数组 1.3.1 声明char pWord[11];
1.3.2 赋值1、数组元素逐一赋值,pWord[0] = 'H'
2、聚合方式赋值,char pWord[] = {'H','E','L','L','O'} ,只能在声明的时候使用;
3、字符数组不能给另一个字符数组赋值,a=b 错误,a[0] = b[0] 正常
4、字符数组字符串,char a[] = "HELLO" 等同于 char a[] = "HELLO\0" 没有\0 会有非法字符
1.3.3 字符串处理函数1、strcat 字符串连接函数
① strcat (字符数组名1, 字符数组名2):把 2 链接在1后面,去掉中间的 \0
② 字符数组 1 的长度要足够大
2、strcpy 函数,字符串复制函数
① strcat (字符数组名1, 字符数组名2):把 2 复制到 1 中
② \0 也一同复制一遍
③ 字符数组 1,必须是数组名称;字符数组 2 可以是 字符数组名、也可以是 字符串
3、strcmp 函数,字符串比较函数
① 1=2 输出 0、1>2输出正数、1<2输出负数
② strcmp(str1,str2)、strcmp(str1,“hello”)、strcmp(“how”,“hello”)
4、strlen 函数,测字符长度
① 测字符串的实际长度,不含字符串结束函数 \0
二、指针 2.1 变量与指针
定义一个整型 变量,需要 4 个字节,各个变量连续的存储在内存中,代码中使用变量名,编译器使用地址,实际 *** 作都是通过地址进行的。
此类型的变量,专门存放另一个变量的地址。变量的指针专门编写代码时无法获得,只有在程序运行的时候才可以获取到。
2.1.1 声明数据类型标识符 *指针变量名
int *p ;
float *a,*b ;
2.1.2 赋值
初始化的时候赋值
int i = 100 ;
int * p = &i ;
后期赋值
int i = 100 ;
p = &i ;
① 指针变量名是 p,而不是 *p
② p = &i 取变量 i 的地址,赋值给指针变量 p
③ 声明的时候 *p 用的时候 p,指针变量名是 p
④ 变量是由系统分配内存空间,因此变量的地址不是固定不变的,所以没法直接赋值
⑤ 没有明确指向的指针会引起崩溃,编译器不会出错但是运行有危险
⑥ 指针变量不可以直接赋值 p = 100 编译不过,因为类型不对;*p=100 编译能过,但是指针没有获得地址,因为不能把 *p 当变量使用,输出 p 出错,因为没获得地址。(如果提前给p分配了地址,就可以使用*p=100)
⑦ 强行赋值 p=(int*)100,编译通过,指针能够获得地址,输出 *p 出错,使用指针运算符 * 提取指针所指变量会出错,因为可能系统没分配那个地址出去,或者没有权限,具体的地址是什么每次都不一样,都是系统启动后动态分配的
//编译不过
p = 100
//编译OK,实际没有获得地址
*p = 100;
// 输出地址,出错,因为实际没有获得地址
printf("%d", p);
// 输出指针指向的值,出错,因为实际没有获得地址
printf("%d", *p);
//强转赋值,可以赋值,编译OK
p = (int*)100;
// 输出地址,可以输出
printf("%d", p);
// 输出指针指向的值,出错,没分配或者没权限
printf("%d", *p);
总结:(p=&i 正确)
p = 100 编译不过;
*p =100 编译能过,但是指针没有获得地址,因为不能把 *p 当变量用
p = (int*) 100 编译能过,能获得地址,但是 100 这个地址可能没分配,可能没权限
总结:
printf (%d, p) 输出变量 p 的地址
printf (%d, *p) 输出变量 p 指向的地址内的值
2.1.3 取地址 *** 作、指过去 *** 作
① int *p =&a 等价于 int *(p=&a)
② * 指针运算符--(指过去)、& 取址运算符--(取地址);一对儿,相反的
p(指针):指针地址 *p (整型):指过去--指针地址所指变量
i (整型):变量A &i (指针):取地址-取变量A的地址
2.1.4 指针运算p :1703740
p++:1703744
p-- :1703736
不是加 1,int 加 4,double 加 8,和声明类型有关,类型占多少内存跳多少地址
2.1.5 指向空的指针、空类型指针void *p //空类型的指针变量
可以接受任何类型的数据,使用时,强转所对应的数据类型
int i = 4;
int *pl = NULL;
void *pV = NULL;
Pl = &i;
pV = pl;
cout << "pV 转换类型对结果影响" << *(int*)pV << *(float*)pV << endl
强转对应的 int 能正确显示结果 4
强转其他的 float 字节数目变大,结果未知,5.60519e-045
2.1.6 指向常量的指针、指针常量const 放在标识符前,表示这个数据本身是常量
int* const p = &i;
// int* 是const,无法改变内存指向哪个地址,可以改变地址里面的内容
const int *p = &i;
int const *p = &i;
// 两种表述等价
// 指向的对象是const型,不可以修改(只读指针),但是指针p的指向可以修改。
// int 是const,无法通过指针改变int变量的数值,可以直接通过赋值i=2改变
int const * const p = &i;
// int 是常量,int* 也是常量,指向地址,int内容,都不可以改变
2.2 指针与数组 2.2.1 数组
数组放在连续内存中,同名、同类型、同大小
A[3][4] 可以看做一个特殊的一维数组:二维数组由 A[0]、A[1]、A[2] 三个一维数组组成,以行划分,横着来的,横着读成三个一维数组,几行,就是几个一维数组,存储也是这么存储的。
a[0][0] a[0][1] a[0][2] a[0][3] a[0] -- a[0]+0 a[0]+1 a[0]+2 a[0]+3
a[1][0] a[1][1] a[1[2] a[1][3] a[1] -- a[1]+0 a[1]+1 a[1]+2 a[1]+3
a[2][0] a[2][1] a[2][2] a[2][3] a[2] -- a[2]+0 a[2]+1 a[2]+2 a[2]+3
2.2.2 字符数组char *string = "www.mingri.book";
char *string;
string = "www.mingri.book";
strcat 字符串链接函数
2.3 指针在函数中的应用 2.3.1 传递地址
实参传递进函数后,生成的是副本,函数内改变值不影响实参
但是传递指针,虽然也是副本,但是指向地址都是一样的,所以会改变原变量
2.3.2 指向函数的指针一个函数的入口地址被称为函数指针,可以用一个指针变量指向函数,然后通过该指针变量调用此函数。
int sum (int x, int y);
int *a (int, int);
a = sum;
int c,d;
(*a)(c,d);
2.3.3 空指针调用函数
空类型指针指向任意类型,使用空指针时,强转类型使用即可
2.3.4 从函数中返回指针返回值是 指针
2.4 指针数组数组中元素均为指针的,称为指针数组,
int *p[4]
指针数组中的数组名也是一个指针变量,该指针变量为指向指针的指针
int *p [4];
int a = 1;
*p[0] = &a;
p 是一个指针数组,他的每一个元素是一个指针(值为地址),指针数组第一个值是变量 a 的地址,指针数组的元素
int *(*p) = int **p 指过去两次,第一次*得到 i 的地址,再 * 一次得到 i 的值
**p *p p
4001 1 -- 4005(&i) -- &(*P)
4005 2
4009 3
2.5 安全使用指针当一块内存被销毁时,该区域不可复用,若有指针指向该区域,需要将指针=NUL,或者指向其他区域。申请动态内存分配后,需要使用 delete 销毁
eg:已经销毁的内存不能被指向该内存的指针继续使用:
int* sum(int a,int b)
{
// 返回一个被销毁的内存的指针
c = a+b
pS = &c
return pS;
// 返回一个被销毁的内存的指针
}
int *pl = NULL;
pl = sum(a,b);
// 也许 *pl 还保留着 i 值,但是系统认定 pl 需要销毁
销毁内存时,需要保留一个指向该内存的指针,没有了这个指针,内存无法被销毁,内存泄漏
eg:内存泄漏:
pF = new float;
// 动态申请一块内存,用 pF 去指向
*pF = 4.321f
pF = &f2;
// 让 pF 指向了另一个地址 ,此时上面申请的内存变为不可用状态
不可用,不可回收,内存泄漏
三、引用 3.1 左值引用
数据类型 & 表达式
int a = 10;
int & ia = a;
ia = 2;
引用类似于指针,对引用 *** 作等于对原数据 *** 作
① 只能引用一个,不能再引用另一个对象,不能被重新约束
② 指针可以强转,引用不可以
③ 引用时需要立刻初始化
3.2 右值引用声明赋值
int && k = get() + 4;
// int k = get() + 4; 出错
// int & k = get() + 4; 出错
3.3 引用传递参数
值传递:在函数调用时,将实际参数的值复制一份传递进去,函数中修改了不会影响实际参数
引用传递:调用函数中修改了值,会影响到实际参数
3.4 指针传递参数指针传递:调用函数中修改了值,会影响到实际参数
引用必须被初始化为一个对象,同时不可以变更
3.5 数组做函数参数main(int argc,char *argv[])
argc:获取参数个数
argv:获取具体命令
四、构造数据类型 4.1 结构体 4.1.1 引用及赋值
引用:
声明一个结构体:. 使用成员运算符
声明一个结构体指针:->使用引用运算符,引用结构体指针变量
赋值:
可以单独赋值,也可以统一赋值
struct pinfo
{
int a;
char name[30];
short age;
}pinfo1 = {0, "张三", 20}
4.1.2 内存大小
结构体变量的大小是定义结构体时各成员大小之和
4.2 重命名数据类型typedef int flag; // 给 int 数据类型去一个别名
flag a; // 程序中 flag 就可以作为 int 数据类型来使用
typedef class asdfghjkl... {
成员列表
}myClass, myClassA;
复杂的类型可以用简单的名字, 重新命名
4.2 结构体与函数 4.2.1 结构体变量做函数参数 4.2.2 结构体指针做函数参数 4.2.3 结构体数组声明与引用struct PersonaleInfo
{
int index;
char name[30];
short age;
}Person[5];
struct PersonaleInfo
{
int index;
char name[30];
short age;
}Person[5] = { {1, "张三", 20}
{2, "张四", 21}
{3, "张五", 22}
{4, "张六", 23}
{5, "张七", 24} }
当全部元素进行初始化赋值时,可以不给出数组长度
4.2.4 指针访问结构体数组4.3 共用体 4.3.1 定义与声明
union 共用体类型名
{
成员类型 共用体成员名 1;
成员类型 共用体成员名 2;
成员类型 共用体成员名 3;
}
union myunion
{
int i;
float f;
char ch;
}
myunion u;
与结构体相同,使用 .运算符,引用共用体成员方法
4.3.2 共用体大小① 共用体每个成员分别占有自己的内存单元
② 共用体变量所占的内存长度等于最长的成员的长度
③ 一个共用体变量不能同时存放多个成员的值,某一时刻只能寸其中一个变量的值,就是最后赋值的值
④ 使用共用体的目的是希望用同一个内存段存放不同类型的数据,但是某一时刻只有一种
⑤ 不能对共用体变量名赋值,不能调用变量名来得到一个值,不能在定义时初始化,不能用共用体作为函数参数
4.4 枚举类型 4.4.1 声明
enum weekday
{
Sunday = 0,
Monday = 1,
Tuesday = 2,
Wednesday = 3,
}
enum
{
sun,
mon,
tue,
wed
}
4.5 自定义数据类型
typedef int INTMY;
INTMY a, b;
4.6 异常处理
抛出异常,捕获异常
throw 1; // 抛出异常
catch (int error); // 捕获异常
throw 后面可以使用任何类型,字符串也可以
① throw 构造体,有 id 有 原因字符串
② throw 和 catch 可以在一处们也可以分开, 函数内部 throw 就会直接退出,不想直接退出就需要用 try,
③ 异常呗抛出后,一旦被异常接收器收到后,就会被销毁,异常处理器,应该具备接收任何异常的能力。
④ catch 不需要 break,信号抛出后,会与第一个捕获处结合,执行 catch 后面的语句
⑤ catch... 接收所有异常,catch后面紧跟着一个 throw,就是抛出刚刚接到的异常
⑥ 如果一个 catch 语句忽略了一个异常,那么这个异常将进入更高层的异常处理环境
⑦ 异常匹配会与最近的异常处理模块相匹配,不要求完美匹配,对象、派生、基类都可匹配,如果抛出指针,指针会匹配相应类型,但不会自动转换。基类处理器,可以捕获派生类异常,不会完美匹配。派生类放在前面,基类放在后面。
4.7 宏定义可以替换复杂的数据
#define
① 字符串中不替换,字符串可以用 \ 续行
② #undef 终止宏定义的作用域
③ 带参数的宏定义 #define MUL(x,y) ((x)*(y)),不是直接替换,是直替换计算,记得加括号保证优先级,
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)