C++指针

指针

C++指针

 

 

C++ 中内存单元内容与地址

内存由很多内存单元组成,这些内存单元用于存放各种类型的数据。

计算机对内存的每个内存单元都进行了编号,这个编号就称为内存地址,地址决定了内存单元在内存中的位置。

记住这些内存单元地址不方便,于是C++语言的编译器让我们通过名字来访问这些内存位置。

举例

C++指针

 

 如果使用地址那我们编程会很不方便,使用变量名代替地址

C++指针

 

 指针的定义和间接访问操作

指针定义的基本形式:指针就是一个变量,其符合变量定义的基本形式,它存储的是值的地址。对类型T,T*是“到T的指针”类型,一个类型为T*的变量能保存一个类型T的对象的地址

int a = 112; float c = 3.14;
int* d = &a; float* e = &c; // 指针变量保存的是地址,使用取地址符&

通过一个指针访问它所指向地址的过程称为间接访问(indirection)或者引用指针(dereferencing the point)

这个用于执行间接访问的操作符是单目操作符*

cout << (*d) << endl;  cout << (*e) << endl;

 

一个变量有三重要信息:

  • 变量的地址位置
  • 变量所存的信息
  • 变量的类型

指针变量也是变量,其特殊之处在于所存的信息是地址和类型

指针变量是一个专门用来记录变量的地址的变量,通过指针变量可以间接访问另一个变量的值

 

C++的原生指针

数组与指针

数组名对应一个地址空间

 

int main()
{
    char aStr[] = {"helloworld"};
    char aStr1[] = "helloworld" ; // 同aStr1

    char* pStr = (char*)"helloworld";


    pStr = aStr; // 指针变量的值允许改变
    // aStr = pStr; // 数组变量的值不允许改变
    aStr[0] = 'H'; // aStr[index]的值可以改变

}

pStr[index]的值可变不可变,取决于所指区间的存储区域是否可变。

 

左值与右值

概念:

一般说法,编译器为其单独分配了一块存储空间,可以取其地址的,左值可以放在赋值运算符左边

右值指的是数据本身,不能取到其自身地址,右值只能赋值运算右边。

左值最常见的情况如函数和数据成员的名字

右值是没有标识符、不可以取地址的表达式,一般也称之为“临时对象”。

比如:a=b+c

&a是允许的操作,而&(b+c)不能通过编译,因此a是一个左值,而(b+c)是一个右值。

 

 一般类型指针T*

T泛指任何一种类型

*的两种意义:

  • 变量声明中使用,表示该变量是一个储存地址的变量,即指针
  • 在表达式中使用,表示取该地址对应的内存单元的值

 

指针的数组

array of pointers

一组指针,数组的元素是指针

T* t[]

可看成

T* (t[])

 

数组的指针

a pointer to an array

指针指向一个数组

T(*T)[]

 

int main()
{
    // 4字节的最小的整数,-1,0,4字节最大的整数
    int c[4] = {0x80000000, 0xFFFFFFF, 0x00000000, 0x7FFFFFFF};
    int* a[4]; // 指针的数组 存的是指针
    int(*b)[4]; // 数组的指针
    b = &c; // b指向数组c
    for (unsigned int i = 0; i < 4; i++)
    {
        a[i] = &(c[i]); // 将每个元素的地址赋值给指针的数组中的元素,即指针
    }
    cout << *(a[0]) << endl; // -2147483648
    cout << (*b)[3] << endl; // 2147483647 (*b)取出数组,然后[3]对数组进行访问
}

 

const与指针

  • const pointer
  • pointer to const

const修饰

  • 看左侧最近的部分
  • 左侧没有,则看右侧
int main()
{
    char strH[] = { "helloworld" };
    char const* pStr1 = "helloworld"; // 左侧最近是char,修饰char,pStr1指针指向的地址可变,但是该地址下字符值不可变
    const char* pStr11 = "helloworld"; // 左侧没有看右侧,等价于pStr1
    char* const pStr2 = (char*)"helloworld"; // 左侧最近是*,修饰指针,pStr2一旦指向某个地址,就不能再指向其他地址,该指向就不许变
    char const* const pStr3 = "helloworld"; // 指向和所指向的值都不可变
    pStr1 = strH;
    // pStr2 = strH; // pStr2不可修改
    // pStr3 = strH; // pStr3不可修改
}

 

指向指针的指针

int main()
{
    int a = 123;
    int* b = &a;
    int** c = &b;
}

C++指针

 

 *操作符具有从右向左结合性

**这个表达式相当于*(*c),必须从里向外逐层求值

*c得到的是c指向的位置,即b

**c相当于*b,得到变量a的值

C++指针

野指针和空指针

未初始化和非法的指针

int main()
{
    int *a; // a指向哪里?发送什么?
    *a = 12;
}

运气好的话,定位到一个非法地址,程序会出错,从而终止

最坏的情况,定位到一个可以访问的地址,无意修改了它,这样的错误难以捕捉,引发的错误可能与原先用于操作的代码毫不相干,无法跟踪。

指针一定要初始化并被恰当赋值。

 

Null指针

一个特殊的指针变量,表示不指向任何东西。

int* a = NULL;

NULL指针的概念非常有用,它给了一种方法,来表示特定的指针目前未指向任何东西

注意事项:

对于一个指针,如果已经知道将被初始化为什么地址,那么请赋给它这个地址,否则请把它设为NULL。在对一个指针进行间接引用前,请先判断这个指针的值是否为NULL。

int main()
{
    int a = 123;
    int* p = NULL;
    p = &a;
    if (p != NULL)
    {
        cout << *p << endl;
    }LL
    p = NULL; // 不用时置为NULL

    return 0;
}

 

杜绝野指针

指向“垃圾”内存的指针,if等判断对它们不起作用,因为没有置NULL

一般有三种情况:

  • 指针变量没有初始化
  • 已经释放不用的指针没有置为NULL,如delete和free之后的指针
  • 指针操作超越了变量的作用范围

指针使用注意事项:

  • 没有初始化的,不用的或超出范围的指针请置为NULL。

指针基本运算

&与*操作符

char ch = 'a'; char* cP = &ch;

C++指针

 

 &ch取地址,会将ch的地址取出来,放到一个空间中(不是cp变量),我们不知道该空间地址,不能使用&,不能作为左值,&ch可以取到地址,但是存储这个地址的空间我们得不到,只能作为右值。

char ch = 'a';
char* cP = &ch;

C++指针

 

 

int main()
{
    char ch = 'a';

    // &
    // &ch = 97; // &ch左值不合法
    char* cp = &ch; // &ch右值
    // &cp = 97; // &cp左值不合法
    char** cpp = &cp; // &cp右值

    // *
    *cp = 'a'; // *cp左值取变量ch位置
    char ch2 = *cp; // *cp右值取变量ch存储的值
    // *cp + 1 = 'a'; // *cp + 1 左值不合法的位置
    ch2 = *cp + 1; // *cp + 1 右值取到字符做ASCII码+1操作
    *(cp + 1) = 'a'; // *(cp+1)左值语法上合肥,取ch后面位置
    ch2 = *(cp + 1); // *(cp + 1)右值语法上合法,取ch后面位置的值
    return 0;
}

 

++与--操作符

C++指针

 

 C++指针

 

 C++指针

 

 

关于++++,----等

C++指针

 

 C++指针

 

 

 

数组名和指针

揭密数组名 https://www.cnblogs.com/fenghuan/p/4775023.html
现在到揭露数组名本质的时候了,先给出三个结论:
(1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;
(2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量;
(3)指向数组的指针则是另外一种变量类型(在WIN32平台下,长度为4),仅仅意味着数组的存放地址!
1、数组名指代一种数据结构:数组
现在可以解释为什么第1个程序的这一行:sizeof(str);的输出为10的问题,根据结论1,数组名str的内涵为一种数据结构,即一个长度为10的char型数组,所以sizeof(str)的结果为这个数据结构占据的内存大小:10字节。
再看:

1.int intArray[10];
2. cout << sizeof(intArray) ;

第2行的输出结果为40(整型数组占据的内存空间大小)。
如果C/C++程序可以这样写:

1. int[10] intArray;
2. cout << sizeof(intArray) ;

我们就都明白了,intArray定义为int[10]这种数据结构的一个实例,可惜啊,C/C++目前并不支持这种定义方式。
2、数组名可作为指针常量
根据结论2,数组名可以转换为指向其指代实体的指针,所以程序1中的第5行数组名直接赋值给指针,程序2第7行直接将数组名作为指针形参都可成立。
下面的程序成立吗?

1. int intArray[10];
2. intArray++;

读者可以编译之,发现编译出错。原因在于,虽然数组名可以转换为指向其指代实体的指针,但是它只能被看作一个指针常量,不能被修改。 
而指针,不管是指向结构体、数组还是基本数据类型的指针,都不包含原始数据结构的内涵,在WIN32平台下,sizeof操作的结果都是4。
顺 便纠正一下许多程序员的另一个误解。许多程序员以为sizeof是一个函数,而实际上,它是一个操作符,不过其使用方式看起来的确太像一个函数了。语句 sizeof(int)就可以说明sizeof的确不是一个函数,因为函数接纳形参(一个变量),世界上没有一个C/C++函数接纳一个数据类型(如 int)为"形参"。
3、数组名可能失去其数据结构内涵 
到这里似乎数组名魔幻问题已经宣告圆满解决,但是平静的湖面上却再次掀起波浪。请看下面一段程序:

#include <iostream>
using std::cout;
using std::endl;
void arrayTest(char str[])
{
     cout << "sizeof(str):" << sizeof(str) << endl;
}
int main()
{
    char str1[10] = "I love U.";
    arrayTest(str1);

    system("pause");
    return 0;
}

程序的输出结果为:

C++指针

一个可怕的数字,前面已经提到其为指针的长度!

结论1指出,数组名内涵为数组这种数据结构,在arrayTest函数体内,str1是数组名,那为什么sizeof的结果却是指针的长度?这是因为:

(1)数组名作为函数形参时,在函数体内,其失去了本身的内涵,仅仅只是一个指针;

(2)很遗憾,在失去其内涵的同时,它还失去了其常量特性,可以作自增、自减等操作,可以被修改。

所以,数组名作为函数形参时,其全面沦落为一个普通指针!它的贵族身份被剥夺,成了一个地地道道的只拥有4个字节的平民。

以上就是结论4。

 

 

 

 

 

指针和引用

指针对于一个类型T,T*就是指向T的指针类型,也即一个T*类型的变量能够保存一个T对象的地址,而类型T是可以加一些限定词的,如const、volatile等等。见下图,所示指针的含义

char c = 'a';

char *p = &c; //p里面存放的是c的地址

C++指针

引用是一个对象的别名,主要用于函数参数和返回值类型,符号X&表示X类型的引用。见下图,所示引用的含义:

int i=1;

int &r = i;    //此时i=r=1;

若执行r=2;//此时i=r=2;

int *p = &r;  //p指向r;

C++指针

 

 

指针和引用的区别

1.首先,引用不可以为空,但指针可以为空。前面也说过了引用是对象的别名,引用为空——对象都不存在,怎么可能有别名!故定义一个引用的时候,必须初始化。因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象,i.e.,你的设计不允许变量为空,这时你应该使用引用。如下图中,如果定义一个引用变量,不初始化的话连编译都通不过(编译时错误)

而声明指针是可以不指向任何对象,也正是因为这个原因,使用指针之前必须做判空操作,而引用就不必。

2.其次,引用不可以改变指向,对一个对象"至死不渝";但是指针可以改变指向,而指向其它对象。说明:虽然引用不可以改变指向,但是可以改变初始化对象的内容。例如就++操作而言,对引用的操作直接反应到所指向的对象,而不是改变指向;而对指针的操作,会使指针指向下一个对象,而不是改变所指对象的内容。

3.再次,引用的大小是所指向的变量的大小,因为引用只是一个别名而已;指针是指针本身的大小,4个字节

4.最后,引用比指针更安全。由于不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,因此引用很安全。对于指针来说,它可以随时指向别的对象,并且可以不被初始化,或为NULL,所以不安全。const 指针虽然不能改变指向,但仍然存在空指针,并且有可能产生野指针(即多个指针指向一块内存,free掉一个指针之后,别的指针就成了野指针)

总之,用一句话归纳为就是:指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名,引用不改变指向。

原文链接: https://www.cnblogs.com/aidata/p/13023157.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    C++指针

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/353002

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年3月2日 上午7:50
下一篇 2023年3月2日 上午7:50

相关推荐