1、指针

1.1、什么是指针

指针的本质

通俗来说 指针是一个变量 (保存变量地址的变量)像常见的:

int a=10;

指针与地址

首先 ,我们通过一个简单 的示意图来说明内存是如何组织的。通常机器都有一系列连续编号或编制的存储单元,这些存储单元可以单个进行操作,也可以连续成组的方式操纵。可以这样理解假定一个存储单元是学校的一个宿舍,每个宿舍都会有自己的门牌号,把宿舍楼当做电脑的存储区,想找的一个人,有两种方式(1)直接叫名字(2)直接找到宿舍对应的床位;
通常情况下,机器的一个字节可以存放一个char类型的数据,两个相邻的字节存储单元可存储一个short (短整型)一个long (长整型)组存储单元(通常是2或4个字节)。因此,如果c的类型是char,并且p是指向c的指针,则可用图1.1表示它们之间的关系。
在这里插入图片描述
那么一元运算符&可用于取一个对象的地址:

p=&c;

将把c的地址赋值给变量p,我们称p为“指向”c的指针。地址运算符&只能应用于内存中的对象,即变量与数组元素。它不能作用于表达式、常量或register类型的变量。

程序中如何声明指针以及如何使用运算符&和*

int x=1,y=2,z[10];
int *ip;//ip是指向int类型的指针
ip=&x;//ip指向x
y=*ip;//修改y的值为1
*ip=0;//x的值为0
ip=&z[0]//ip指向z[0]

1.2、指针有什么作用

指针与函数参数

由于C语言是以传值的方式将参数值传递给被调用函数的,因此,被调用函数不能直接修改主调函数中变量的值。例如,排序函数可能会使用一个名为swap的函数来交换两个次序颠倒的元素。但是,如果将swap函数定义为下列形式:

void swap(int x, int y)
{
    int tmp;
    tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int a = 10, b = 20;
    swap(a, b);
    cout << a << "-----" << b << endl;
    return 0;
}

结果是:
在这里插入图片描述
在这里插入图片描述
可以看出 x y的值会被交换 但是主函数 a b的值不会被修改;
那么,如何实现我们的目标呢?可以使主调程序将指向所要交换的变量的指针传递给被
调用函数:

void swap(int *x, int *y)
{
    int tmp;
    tmp = *x;
    *x = *y;
    *y = tmp;
}
int main()
{
    int a = 10, b = 20;
    cout << a << "-----" << b << endl;
    swap(&a, &b);
    cout << a << "-----" << b << endl;
    return 0;
}

在这里插入图片描述
调动函数前
在这里插入图片描述
调动函数后
在这里插入图片描述
指针还有很多,今儿不讨论这些;

2、引用

2.1、什么是引用

可以这样理解 :引用就是别名;举个例子就是猫可以叫猫也可以叫咪咪;
定义一个变量a赋初值为10;并且给a起一个别名为q;

int a=10;
int &q=a;

在这里插入图片描述
可以看出其实a和q是同一个东西;
引用的底层实现还是指针;

int &q=a;
等价于
int *const q=&a;

证明一下呗;看图。在这里插入图片描述

2.2、引用的规则

1)&在此不是求地址运算,而是起标识作用。
2)类型标识符是指目标变量的类型。
3)声明引用时,必须同时对其进行初始化。
4)引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。ra=1; 等价于 a=1;
5)声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:对引用求地址,就是对目标变量求地址。&ra与&a相等。
6)不能建立数组的引用。因为数组是一个由若干个元素所成的集合,所以无法建立一个数组的别名。
7)不能建立引用的引用,不能建立指向引用的指针。因为引用不是一种数据类型!!所以没有引用的引用,没有引用的指针。

2.3、引用和数组

先看两行代码

int& q[10] ;
//q首先向右结合,所以这个相当于 (int&)q[] q是个数组,其中的元素是引用
//应该叫:引用的数组
int(&p)[10] ;
//p首先和&结合,所以p是引用,引用的对象是数组
//应该叫作数组的引用

眼睛解释的通啊!!!

引用的数组(非法)

可以明确的说明,引用的数组是不能当函数的参数的。再者要说明,这种方式是非法的。
原因:从引用的本质说吧。
首先,引用必须被初始化,而数组并不能被另一个数组初始化或被另一个数组赋值并且因为引用数组的话,就相当于给数组中每个元素引用,没有分配空间。
再次,引用它不支持传统意义的复制,它不占用新的空间。

数组的引用

int main()
{
    int a[10] = { 10,20,30,40,50,60,70,80,90,100 };
   // int& q[10] = a;
    int(&p)[10] = a;
    for (int i = 0; i < sizeof(a) / sizeof(a[0]); i++)
    {
        cout << p[i] << " ";
    }
    cout << endl;
    cout << "size=" << sizeof(p) << endl;
}

在这里插入图片描述

2.4、为什么要用引用

1)在引用的使用中,单纯给某个变量取个别名是毫无意义的,引用的目的主要用于在函数参数传递中,解决大块数据或对象的传递效率和空间不如意的问题。
2)用引用传递函数的参数,能保证参数传递中不产生副本,提高传递的效率,且通过const的使用,保证了引用传递的安全性。
3)引用与指针的区别是,指针通过某个指针变量指向一个对象后,对它所指向的变量间接操作。程序中使用指针,程序的可读性差;而引用本身就是目标变量的别名,对引用的操作就是对目标变量的操作。
4)使用引用的时机。流操作符<<和>>、赋值操作符=的返回值、拷贝构造函数的参数、赋值操作符=的参数、其它情况都推荐使用引用。 引用就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样。

3、const修饰符

在C语言中,习惯用#define来定义常量,列如:

#define LIMIT 100

实际上,这种方法只是在预编译阶段进行字符的转换,把程序中出现标识符LIMIT替换为100.在预编译之后,程序不在有LIMIT这个标识符,LIMIT不是变量,没有类型,不占用存储单元,而且容易出错。
C++提供了一种更灵活,更安全的方式来定义常量,即使用const修饰符来定义常量。例如:

const int LIMIT=100;

这个常量是有类型的,占用存储单元,有地址,可以用指针指向它,但不能修改它。
const的作用于#define相似,但是它消除了#define的不安全性。因此c++建议试使用const取代#define定义常量。
说了这么多举个例子呗;

int main()
{
    int a= 1;
    #define T1 a+a
    #define T2 T1-T1
    cout << "T2 is " << T2 << endl;
    return 0;
}

想想答案是多少?0还是2?
在这里插入图片描述
换一下代码试试

int main()
{
    int a= 1;
    int const T1 = a + a;
    int const  T2 = T1 - T1;
    cout << "T2 is " << T2 << endl;
    return 0;
}

在这里插入图片描述
看下面这两个有区别吗?
实际上没有。

const int a=5;
int const a=5;

const会用了吗?继续。。。

3.1、指针与const

(1)通过指针修改值

    int a = 5, b = 10;
    int* q = &a;
    *q = 10;

在这里插入图片描述
在这里插入图片描述
(2)int* const 情况
先搞清楚const限定什么?

int main()
{
    int a = 10, b = 20;
    int* const s = &a;
    s=&b;//错误
    *s=100;//正确
    return 0;
}

const限定的是s的指向也就是说s是个常量;即指向不可以改变不能再去只想别的地址,值可以改变。
继续在来,看代码:

int main()
{
    int a = 10, b = 20;
    int* const s = &a;
    int* p1 = s;
    const int* p2 = s;
    int* const p3 = s;
    const int* const p4 = s;
    return 0;
}

能看出来 那个对那个错吗?
嘿嘿 都对;为什么?
原因就是 s里面存放的是a的地址吧,那也就是说p1他们拿到是a的地址吧,那它有什么错。
(3)const int*
const 限定的是它的值吧即就是*p是个常量吧,和指向没啥子关系;

int main()
{
    int a = 10, b = 20;
    const int *p=&a;
    *p = 100;//错误吧
    p = &b;
    return 0;
}

在这里插入图片描述
在来看看那个错误:

int main()
{
    int a = 10, b = 20;
    const int *s=&a;
    int* p1 = s;//err
    const int* p2 = s;
    int* const p3 = s;//err
    const int* const p4 = s;
    return 0;
}

p1 p3 错误 原因是 p1指向s的话 p1没有被限定 所以说通过*p1就可以改变a的值,*s的能力被扩大化。

3.2、引用与const

int main()
{
    int a = 10;
    int& b = a;
    const int& c = a;
    a += 100;
    b += 100;
    c+=100;//编译出错,因为前面有const进行修饰,所以c不能改变
}

3.3、指针的引用与const 的关系

(1)int *const

int main()
{
    int a = 10, b = 20;
    int* const s = &a;
    int*& p1 = s;//err
    const int*& p2 = s;//err
    int* const& p3 = s;
    const int* const& p4 = s;
    return 0;
}
p1和p2错误的原因一样 就是 s的指向不能修改,而引用就是s的一个别名,所以对p1修改就是对s的修改 s的指向不能被修改但是p1又可以修改 矛盾 ,同是一个东西相煎何太急。。。。

(2)const int *

int main()
{
    int a = 10, b = 20;
    const int* s = &a;
    int*& p1 = s;//err
    const int*& p2 = s;
    int* const& p3 = s;//err
    const int* const& p4 = s;

}

这个不解释 只要 理解引用是它的别名 也就是修改引用和修改它是同一个操作就行了。

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐