指针、结构体

一、指针

指针的基本概念:

1、指针变量:

        指针变量:简称指针,它是一种特殊的变量,专门用于存放变量在内存中的起始地址。

        语法:数据类型 *变量名        注:*可与数据类型相接,也可以与变量名相接,也可以两者都

                  不相接,也可以两者都相接,例:char* a;        int *a;        bool * a;        double*a;

                  四种情况都可以表示指针变量a  (一般写作 :例:char* a)。

2、对指针赋值:

        用整型指针存放整型变量的地址;用字符型指针存放字符型变量的地址;用浮点型指针存

        放浮点型变量的地址,用自定义数据类型指针存放自定义数据类型变量的地址。

        语法:指针=&变量名        例:整型变量int a,其存放地址的变量为:int *x = &a,x为任意

                  名,但不能与a或其他变量相同,即不能重名。其中&的作用为取出变量a的起始地址。

                  即若输出变量x,就会输出变量a的地址。

        注:若指针的数据类型与基类型不符,编译会出现警告,但可以强制转换其类型。

                指针指向的诠释:若指针变量存放的是某个对象(这个对象一般来说是内存空间)的地

                址,则称这个指针变量指向该对象。

        指针加法:将一个整型变量加1后,其值将增加1;但是,将指针(地址的值)加1后,增加的

                          量等于它指向的数据类型的字节数(与后文一维数组有联系)

3、使用指针

        若未给指针赋值则无法使用指针,指针内部未乱七八糟的值,VS会直接报错(其他编译器可

        能不报错,但同样无法使用),故使用指针前必须赋初值(为了标准化,建议普通变量也赋

        初值,即使只有警告)。

        指针存放变量的地址,因此,指针名表示的是地址(就像变量名可以表示变量的值一样)。

        *运算符被称为间接值或解除引用(解引用)运算符,将其应用于指针,可以得到该地址的内

        存中储存的值,例如:int a=3; int *p=&a; 若输出p,则为a的地址,若输出*p,则输出变量a

        储存的值3,以此基础上,可以追加*p=8;即将p所指的地址所储存的值变为8,而p所指的地

        址为a的地址,即将a的值重新赋值为8,不再为3。

        注意:多个指针可以指向同一个变量,例:int a=3; int *p=&a;int *p1=&a;这样是可行

        的,且所指地址相同。

4、const修饰指针

        常量指针:

                作用:不能通过解引用(即*)的方法修改内存地址中的值(用原始的变量名可以修改)

                语法:const 数据类型 *变量名 例: const int *a

                注意:

                  ·      指向的变量(对象)可以改变(即之前是指向变量a的,之后可以改为指向变量b)

                  ·      一般用于修饰函数的形参,表示不希望再函数里修改内存地址中的值

                  ·      如果用于形参,虽然指向的对象可以改变,但这么做无意义

                范例:int a=3;  const int *p=&a;   此处可以使用解引用读取值,即*p=3;但不能使

                用*p进行对a值的修改,即:*p=5;此语句是错误的,因为不能给常量赋值,但是可以写

                做:a=5,这样是可以的,因为使用的是原始变量名。
 

        指针常量:

                作用:指向的变量(对象)不可改变。

                语法:数据类型 *const 变量名  例:   int a=3;     int *const p=&a;

                注意:

                  ·      在定义的同时必须初始化,否则没有意义

                  ·      可以通过解引用的方式修改内存地址中的值

                 范例:int a=3,b=5; int *const p=$a;  *p=13;  则a的值变为13,输出*p也为13,但如果 

                            让p重新指向b,则不可行,即追加 p=&b;这行代码,此程序报错

                此处埋坑:C++编译器把指针常量做了一些特殊处理,改头换面,作为引用存在,本质 

                                  还是指针常量,之后学习到记得在此拉一个跳转。
               

        常指针常量:

                作用:指向的变量(对象)不可改变,且不可通过解引用的方法修改内存地址中的值

                语法: const 数据类型 *const 变量名

                此处埋坑:又名常引用,具体请听下回分晓



        总结:

                常量指针:指针指向可以改,指针指向的值不可改

                指针常量:指针指向不可改,指针指向的值可以改

                常指针常量:指针指向不可改,指针指向的值不可改               

                补充:指针指向的诠释:若指针变量存放的是某个对象(这个对象一般来说是内存空 

                           间)的地址,则称这个指针变量指向该对象。(上方同样诠释,防忘)

5、void关键字

        void表示为无类型,主要有三个用途:

                1)函数的返回值用void,表示函数没有返回值 

void func(int a,int b)
{    
    函数体代码
    return
}

                2)函数的参数填void,表示函数不需要参数(或者让函数列表为空)

int fun(void)     或:  int fun()
{                  {
    函数体代码          函数体代码
    return 0;          return  0;
}                  }

                3)函数的形参用void*,表示接受任意数据类型的指针

                        只关心地址本身,不关心里面的内容,用void*可以存放任意类型的地址

                        注意:

                              ·   不能用void声明变量,它不能代表一个真实的变量。例:void a; 错

                                  误,void为一个抽象概念,不是具体的数据类型         

                             ·   不能对void*指针直接解引用(需要转换成其他类型的指针)例:

char a = 'X';			
void fun(string varname, void* p)							//varname为变量名
{
	cout << varname << "地址为:" << p << endl;				//正确
	//cout << varname << "地址为:" << *p << endl;			//错误:表达式必须是指向完整对象类型的指针
	cout << varname << "地址为:" << *(char*)p << endl;		//正确,已经强制类型转换,有完整数据类型char
}

int main()
{
	fun("a", &a);
	return 0;
}

                             ·   把其他类型的指针赋值给void*指针不需要转换

                             ·   把void*指针赋值给其他类型的指针需要转换

6、二级指针

        定义:二级指针用于存放指针变量的地址;详解:指针是指针变量的简称,也是变量,有变

                   量就有地址。指针用于存放普通变量的地址,二级指针用于存放指针变量的地址。

        语法(二级指针):数据类型** 指针名

        目的:1)传递地址;2)存放动态分配的内存的地址

                    在函数中,如果传递普通变量的地址,形参用指针;传递指针地址的地址,形参用二

                    级指针。

                    把普通变量的地址从传入函数后可以在函数中修改变量的值;把指针的地址传入函数

                    后可以在函数中修改指针的值

7、空指针

        在C和C++中,用 0 或 NULL 都可以表示空指针。

        声明指针后,在赋值之前,让它指向空,表示未指向任何地址。例:int* a=0;或int* a=NULL

        空指针使用指南:

                1)如果对空指针解引用,程序会崩溃。

                2)如果对空指针使用delete运算符,系统会忽略该操作,不会出现异常。所以,内存被

                      释放后也应该把指针指向空

        ·              在函数中,应该有判断形参是否为空指针的代码,目的是保证程序的健壮性。

        访问空指针异常原因:

                NULL指针分配的分区:其范围是从0x00000000到0x0000FFFF。这段空间是空闲的,

                对于空闲的空间而言,没有相应的物理储存器与之相对应,所以对这u的那空间来说,

                任何读写操作都是会引起异常的。空指针是程序无论在何时都没有物理储存器与之对应

                的地址。为了保障“无论何时”这个条件,需要人为划分一个空指针的区域,故有上面的

                NULL指针分区

        C++11的nullptr

                用0和NULL表示空指针会产生歧义,C++11建议用nullptr表示空指针,也就是

                (void*)0;

                NULL在C++中就是0,这是因为在C++中void*类型是不允许隐式转换成其他类型的,所

                以之前C++中用0来代表空指针,但是在重载整形的情况下,会出现上述的问题。所

                以,C++加入了nullptr,可以保证在任何情况下都代表空指针,而不会出现上述的情况,

                因此,建议用nullptr代替NULL,而NULL就当作0使用。

                注意:在Linux平台下,如果使用nullptr,编译需要加-std=c++11参数

8、野指针

        野指针就是指针指向的不是一个有效(合法)的地址。在程序中,如果访问野指针,可能

        造成程序的崩溃。

        出现野指针的情况:

                1)指针在定义的时候,如果没有初始化,它的值是不确定的(乱指一气)。

                2)如果用指针指向了动态分配的内存,内存被释放后,指针不会置空,但是,指向的地

                      址已失效

                3)指针指向的变量已超越变量作用域(变量的内存空间按已被系统回收)主要发生在:

                     调用函数时,让指针指向了函数的局部变量,或者把函数的局部变量的地址作为返回 

                     值赋给了指针

        规避方式:

                1)指针在定义的时候,如果没地方指,就初始化为nullptr。

                2)动态分配的内存被释放后,将其置为 nullptr。

                3)函数不要返回局部变量的地址。

                注意:野指针的危害比空指针要大很多,在程序中,如果访问野指针,可能会造成程序

                           的崩溃,是可能,不是一定,程序的表现是不稳定,增加了调试程序的难度。

二、C++内存管理

C++内存模型的基础概念:

1、内存:

        分为内核空间和用户空间,内核空间由操作系统管理,与程序员基本无关,程序员代码写在

        用户空间,用户空间主要分为四个区:栈,堆,数据段,代码段。

2、栈,堆,数据段,代码段四区具体划分:

栈区:存放了程序中的局部变量、函数参数、返回值;

堆区:存放了程序中的动态开辟内存的变量;

数据段:存放了程序中的全局变量和静态变量;

代码段:存放了可执行变成的二进制代码和常量,程序运行后,代码段中的内容是不会改变的,代

               码段数据段很好理解,不细说;

堆和栈:程序处理的数据主要存放在这两个区中 ,栈区效率很高,但空间很小,如果需要处理大

              量的数据,就必须

3、堆和栈的主要区别:

1)管理方式不同:栈是编译器自动管理的,在出作用域时,将自动被释放;堆需手动释放,若程

      序中不释放,程序结束时由操作系统回收。

2)空间大小不同:堆内存的大小受限于物理内存空间;而栈小的可怜,一般只有8M(可以修改系

      统参数)

3)分配方式不同:堆是动态分配;栈由静态分配和动态分配(都是自动释放)

4)分配效率不同:栈是系统提供的数据结构,计算机在底层提供了对栈的支持,进栈和出栈有专

      门的指令,效率比较高;堆是由C++函数库提供的;

5)是否产生碎片:对于栈来说,进栈和出栈都有着严格的顺序(先进后出),不会产生碎片;而

      堆频繁的分配和释放,会造成内存空间的不连续,易产生碎片,太多的碎片会导致性能下降;

6)增长方式不同:栈向下增长,以降序分配内存地址;堆向上增长,以升序分配内存地址

动态分配内存:

1、实现:new和delete运算符

     使用堆区的内存:

        1)声明一个指针;

        2)用new运算符向系统申请一块内存,让指针指向这块内存;

                申请内存语法:new 数据类型(初始值);        //C++11支持{}

                如果申请成功,返回一个地址;如果申请失败,返回一个空地址(暂不考虑)

                注意:new关键字后面的数据类型应和指针的数据类型一样,否则出错:

                例:int* p=new int(5);//正确

        3)通过对指针解引用的方法,像使用变量一样使用这块内存;

        4)如果这块内存不用了,用delete运算符释放它。

                释放内存语法:delete 地址;释放内存不会失败(还钱不会失败)

        注意:

                ·  动态分配出来的内存没有变量名,只能通过指向它的指针来操作内存中的数据。

                ·  如果动态分配的内存不用了,必须用delete释放它,否则有可能用尽系统的内存。

                ·  动态分配的内存生命周期与程序相同,程序退出时若未释放,系统自动回收。

                ·  就算指针的作用域已经失效,所指向的内存也不会释放。详解:指针归指针,内存归

                   内存,多个指针可以指向同一块内存。

                ·  用指针跟踪已分配的内存时,不能跟丢。

三、一维数组和指针

1、数组的地址:

        数字在内存中占用的空间是连续的;C++将数组名解释为数组第0哥元素的地址;数组第0个

        元素的地址和数组首地址的取值是相同的;数组第n个元素的地址是:数组首地址+n;C++编

        译器把数组名[下标]  解释为    *{数组首地址+下标}

2、数组的本质

        数组是占用连续空间的一块内存,数组名被解释为数组第0个元素的地址。C++操作这块内存

        有两种方法:数组解释法和指针表示法,它们是等价的。

        数组解释法:

int a[5] = {3,6,5,8,9};

for(int ii=0;ii<5;ii++)
{
    cout << "a" << ii << "的值为" << a[ii] << endl;
}

        指针表示法:

int a[5] = {3,6,5,8,9}
int*p =a;
for(int ii=0;ii<5;ii++)
{
    cout << "p" << ii << "的值为" << *(p+ii) << endl;
}

3、数组名不一定会被解释为地址

        在多数情况下,C++将数组名解释为数组的第0个元素的地址,但是,将sizeof运算符用于数   

        据名时,将返回整个数组占用内存空间的字节数。

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

4、一维数组作为函数的参数

        一维数组作为函数的参数时,只能传数组的地址,并且必须把数组长度也传进去,除非数组

        中有最后一个元素的标志

        书写方式:void func(int* arr,int len);        或void func(int arr[],int len);

5、用new动态创建一维数组

        普通数组再栈上分配内存,栈很小;如果需要存放更多的内存,必须再堆上分配内存

        动态创建一维数组的语法:数据类型 *指针=new 数据类型[数组长度]

        释放一维数组的语法:delete[] 指针;

        注意:

                ·    动态创建的数组没有数组名,不能用sizeof运算符

                ·    可以用数组表示法和指针表示法两种方式使用动态创建的数组

                ·    必须使用delete[]来释放动态数组的内存(不能只用delete,若只用,则只释放首地址

                     所指的内存空间)

                ·    不要用delete[]来释放不是new[]分配的内存

                ·    不要用delete[]释放同一个内存块两次(否则等同于操作野指针)

                ·    对空指针使用delete[]是安全的(释放内存后,应该把指针置空nullptr)

                ·    声明普通数组的时候,数组长度可以用变量,相当于在栈上动态创建数组,并且不需

                     要释放

                ·    如果内存不足,调用new会产生异常,导致此程序种植;如果在new关键字后面加一

                     个(std::nothrow)选项,则返回nullptr,不会产生异常

                ·    系统会自动跟踪已分配数组的尺寸,故使用delete[]释放数组时不需要指定数组大小

三、结构体

1、结构体基本概念

        结构体属于用户自定义的数据类型,允许用户储存不同的数据类型

2、结构体定义和使用

语法: struct 结构体名(结构体成员列表)

        通过结构体创建变量的三种方法:

                ·    struct 结构体名 变量名

                ·    struct 结构体名 变量名={成员1值,成员2值......}

                ·    定义结构体时顺便创建变量

举例:

//结构体定义
struct Student
{
    string Name;
    int Age;
    int Score;
};        //注意:此大括号后要加分号

//通过学生类型创建具体学生
struct Student s1;         //创建学生s1

struct Student s2={ ... }  //创建学生s2,并赋初值
例:
struct Student s2={ s2.Name="BOB"; s2.Age=20; s2.Score=100; }

//定义结构体时顺便定义变量s3
struct Student
{
    string Name;
    int Age;
    int Score;
} s3;

        

        

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值