【语言基础】C++ 基本基础综述(四)

【语言基础】C++ 基本基础综述(四),第1张

一、数组 1.1 一维数组 1.1.1 定义

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)),不是直接替换,是直替换计算,记得加括号保证优先级,

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/707413.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-24
下一篇 2022-04-24

发表评论

登录后才能评论

评论列表(0条)

保存