精华内容
下载资源
问答
  • C++野指针及c++指针使用注意点

    千次阅读 多人点赞 2019-08-29 16:11:33
    野指针及c++指针使用注意点 避免野指针的产生 “野指针”的成因主要有: 1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时...

    内容转自(部分已被本人编辑):https://www.cnblogs.com/mrlsx/p/5419030.html

    野指针及c++指针使用注意点

    避免野指针的产生

    “野指针”的成因主要有:

    1)指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。

    char *p; //此时p为野指针
    

    2)指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针.

    char *p=new char[10];  //指向堆中分配的内存首地址,p存储在栈区
    cin>> p;
    delete []p; //p重新变为野指针
    

    3)指针操作超越了变量的作用范围。          

    char *p=new char[10]; //指向堆中分配的内存首地址
    cin>> p;
    cout<<*(p+10); //可能输出未知数据
    

    指针的注意点:

    a.指针指向常量存储区对象

    char *p="abc";

    此时p指向的是一个字符串常量,不能对*p的内容进行写操作,如srtcpy(p,s)是错误的,因为p的内容为“abc”字符串常量,该数据存储在常量存储区,但可以对指针p进行操作,让其指向其他的内存空间。

    b.资源泄漏

    问题:

    1 #include<iostream>
    2 using namespace std;
    3 void main()
    4 {
    5     char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    6     p="ab";             //此时改变p的指向,p指向了常量“ab”,而不再是new char分配的内存空间了,new char[3]开辟的三个字符空间没有被释放,从而造成了资源泄漏。
    7     delete []p;         //释放时报错,此时p指向的是字符串常量
    8 }

    结果:卡死

     

    改进:

    1 #include<iostream>
    2 using namespace std;
    3 void main()
    4 {
    5     char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    6     strcpy(p,"ab");      //将"ab"存储到p指向的内存空间,而不是改变p的指向
    7     delete []p;         //ok
    8 }

    结果:正确

     

    c.内存越界

    1 char *p=new char[3];  //分配三个字符空间,p指向该内存空间
    2 strcpy(p,"abcd");    //将abcd存处在分配的内存空间中,由于strlen("abcd")=4>3,越界
    3 delete []p;         //ok
    

    d.返回值是指针

    问题:数组p[]中的内容为“hello world”,存储在栈区,函数结束时内容被清除,p变为野指针,可能导致乱码

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char p[]="abc";
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     改进:

    1.加static限定,延长数组生存期

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     static char p[]="abc";  //此时数组为静态数组,存储在全局/静态区,生存期到程序结束,因此函数结束时不会销毁p
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     

    2.定义成指针型数组

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char *p="abc";    //"abc"存储在文字常量区,p是指向常量的指针,生存期到程序结束
     6     return p;
     7 }
     8 void main()
     9 {
    10     cout<<f()<<endl;
    11 }

    结果:

     

    3.动态分配存储空间,存储在堆区

     1 #include<iostream>
     2 using namespace std;
     3 char *f()
     4 {
     5     char *p=new char[5];  //动态分配存储空间,p指向堆区
     6     strcpy(p,"abc");   // 这里不能用p="abc",前面已经说明
     7     return p;
     8 }
     9 void main()
    10 {
    11     cout<<f()<<endl;
    12 }

    结果:

     e.指针做形参

    即所谓的地址传递,我们都知道地址传递的方式,形参的改变会导致实参的改变,但要注意的是,这里的改变是指指针所指内容的改变,而不是指针值的改变。因此,当形参改变会导致实参改变时,指针所指的内容是非const类型的,否则会出错。

    1.改变指针内容:

    void swap(int *a,int *b)   //交换的是*a,*b,即指针的内容,而不是指针a,b
    {
       int t;
       t=*a;
       *a=*b;
       *b=t;
    }

    2.改变指针值:

    #include<iostream>
    using namespace std;
    void fun(char *p)
    {
        p="cba";    //“cba”存放在文字常量区,让p指向常量"abc",这里改变的是指针值,实参并不会改变
    }
    void main()
    {
        char *p="abc";  //“abc”存放在文字常量区,p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //输出"abc",而不是"cba"
    }

    结果:

    如果对于这里不容易理解:笔者作如下解释:

    改写代码:(注意看注释)

    #include<iostream>
    using namespace std;
    void fun(char *a)  //给形参换个名字
    {
        cout<<a<<endl;   //abc
        a="cba";         //“cba”存放在文字常量区,让a指向常量"cba"
        cout<<a<<endl;   //cba
    
        //证明这里只改变了指针a的指向,即改变的是指针值a的值,先前是“abc”的地址,后来是“cba”的地址。实参并不会改变。
    }
    void main()
    {
        char *p="abc";  //“abc”存放在文字常量区,p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //输出"abc",而不是"cba"
    
        p="cba";
    	cout<<p<<endl;  //输出“cba”    实参p发生改变
        这就证明:在fun函数中,只是改变了形参的值。
    }

    main函数和fun函数中的"abc"、"cba"都是存放在文字常量区,是具有各自的编号地址的。可以自己调试用printf("%p\n",p);打印出地址,可以看到两个不同的地址。

     

    继续看下面的情况,修改指针的内容:

    #include<iostream>
    using namespace std;
    void fun(char *p)
    {
        p[0]='c';    //改变p的内容,即修改p[0]
    }
    void main()
    {
        char *p="abc";  //p指向常量"abc"
        fun(p);
        cout<<p<<endl;   //error,p所指内容为常量,不能修改
    }

    结果:

    注:p="ab"和strcpy(p,"ab"),含义不一样,前者指针p指向常量“ab”存储区域的首地址,改变了p最开始指向的new申请的内存空间;而后者是将“ab”分配到new申请的内存空间中;

    展开全文
  • C++指针详解-史上最全

    千次阅读 多人点赞 2019-03-11 04:30:39
    C++指针详解-史上最全pointer定义array 与 pointerpointer 初始化pointer 的计算pointer 与 conststring和pointerpointer 指向 pointervoid pointer(泛型指针)非法指针和空指针pointer 和 function 写在前面:这我...

    写在前面:这我第一次写blog,同时也是为了即将到来的C++面试进行复习。以后会不定期更新我的复习笔记,希望大家一起交流学习。如果有错误欢迎指正。
    本篇笔记内容绝大部分是基于下面链接内容进行了翻译和整理,加上自己理解的部分:
    Pointers - C++ Tutorials
    (由于我是在海外学习,有些专业术语我直接用了英文请谅解。如果由不清楚的地方可以留言评论。)

    **未经允许禁止转载**

    pointer定义

    1. 一个变量储存了一个数据对象的内存地址称为该数据对象的指针。
    2. & 是address符号,可理解为 “address of”
    3. * 是dereference 符号, 我们可以把它理解为 “value pointed to by” 由…所指向的值

    例子:

          int* foo; //声明一个名为foo的int类型的指针
          int bet = 25;
          int baz;
          foo = &bet;  //把bet的地址存入foo指针内,使得foo指向bet的地址
          baz = *foo;  // baz现在等于了foo指针指向地址中所储存的数值(25)
    

    注意⚠️:int* foo 这里的int* 是一个整体的关键字。
    和dereference operator * 不是一个东西

    例子:

    int main(){
          int firstvalue, secondvalue;
          int* mypointer; //声明一个int pointer
          mypointer = &firstvalue;//变量名称为mypointer的int* 等于&firstvalue 的地址
          *mypointer = 10; //使用了dereference符号,the value pointed by mypointer is now equals to 10. mypointer的地址的值被*access到了并且赋值为10
          mypointer = &secondvalue;
          *mypointer = 20;
          cout << "firstvalue is " << firstvalue << '\n';
          cout << "secondvalue is " << secondvalue << '\n';
          firstvalue is 10
          secondvalue is 20  
    }
    

    >output:
    >>firstvalue is 10
    >>secondvalue is 20
    另外注意声明的时候
    >这是两个pointer:
    int * p1, * p2;
    >这是一个pointer 一个integer
    int * p1, p2;


    array 与 pointer

    1. array 的本质是开辟一定的内存空间给一串地址,它本身与pointer类似
    2. array与pointer的区别在于pointer可以任意更改所指向的地址,而array只能固定指向开辟的数组
    3. array总是指向第一个元素
      int myarray[20];
      int* mypointer;
      mypointer = myarray;//根据上面的描述,他们都是指针,指向地址。
      
    
    1. 深度理解array
      array[ ]的中括号与dereference * 一样,是等价的。被称为offset opreator。
      在其中加入index表明需要dereference的地址是哪一个
      例如
      a[5] = 0;       // a [偏移5位地址] = 0
      *(a+5) = 0;     // pointed to by (a+5) = 0 
    

    >以上两个是等价的,再看下面的例子:

     // more pointers
    int main ()
    {
          int numbers[5];
          int * p;
          p = numbers;  *p = 10;//因为numbers是一个指向array内第0位的指针
          p++;  *p = 20; //number+1的值。指针指向了下一位
          p = &numbers[2];  *p = 30;//正常的access第二位
          p = numbers + 3;  *p = 40;//与p++同理 ie 0+3 = 3
          p = numbers;  *(p+4) = 50;//指向第把指针右移四位并且dereference它的值改为50
          for (int n=0; n<5; n++)
            cout << numbers[n] << ", ";
          return 0;
    }
    

    pointer 初始化

    int myvar;
    int *foo = &myvar;
    int *bar = foo;
    

    >pointer可以指向一个地址 或者指向另一个pointer


    pointer 的计算

    在pointer中,只有加法和减法是被允许的。
    根据指针所指向的数据类型大小,加法和减法的效果是不同的

    char *mychar;
    short *myshort;
    long *mylong;
    ++mychar;
    ++myshort;
    ++mylong;
    

    假设一个系统的char占1bit,short占2bit,long占4bit。
    char所在的地址为1000,short为2000,long为3000
    那么经过上面的opreation(自加):
    现在mychar指向了1001(它原先占用了1000这个1bit)
    myshort指向了2002
    mylong指向了3004

    • dereference * 优先级要低于自加自减。但是同时要检查是pre-fixed还是post-fixed;
    • 如果是post-fixed,就算优先级要高于*但也是statement之后才加。(*dereference 到 没被增加的那个地址里
    • 如果++放到*前面,则代表已经进行过dereference的操作了 所以是对值进行加1
    *p++   // same as *(p++): increment pointer, and dereference unincremented address
    *++p   // same as *(++p): increment pointer, and dereference incremented address
    ++*p   // same as ++(*p): dereference pointer, and increment the value it points to
    (*p)++ // dereference pointer, and post-increment the value it points to 
    

    例如*p++ = *q++;这里意味着*q的值赋予了*p,赋值过程结束后,指针地址加1。
    注意这里* 和 ++都是对指针p或者q进行操作的
    基本上等同于:

    *p = *q;
    ++p;
    ++q;
    

    pointer 与 const

    指针可以通过获取一个变量的地址来对其储存的值进行更改操作。但有些时候我们只想读取,并不想改值。
    这时候我们需要声明被指针指向的类型为const

    int x;
    int y = 10;
    const int * p = &y;
    x = *p;          // ok: 进行只读取*p的操作
    *p = x;          // error: 更改了p,p是由const修饰的,不容更改。
    

    详细分析一下,这里p指向了一个变量, 但是是以const的形式所指向的。
    这就意味着他只能读这个指向的值而不能对其进行更改。
    同时注意:这里面的&y是一个int* 类型,但是它被用于赋值给了一个const int*类型。这是允许的:
    一个指向非const类型的指针可在内部自动转换为一个指向const类型指针。但是不能反过来。
    因为就安全性而言,把一个const类型转换为非const是不安全的。
    例:

    #include <iostream>
    using namespace std;
    
    void increment_all (int* start, int* stop)
    {
      int * current = start;
      while (current != stop) {
        ++(*current);  // 增加指针所指向的值
        ++current;     // 增加指针本身,改变指针所指向的地址,而不是改变指向的值
      }
    }
    
    void print_all (const int* start, const int* stop)
    {
      const int * current = start;
      while (current != stop) {
        cout << *current << '\n';
        ++current;     // 增加指针本身,改变指针所指向的地址,而不是改变指向的值
      }
    }
    
    int main ()
    {
      int numbers[] = {10,20,30}; //回顾下array的本质
      increment_all (numbers,numbers+3);
      print_all (numbers,numbers+3);
      return 0;
    }
    

    注意print_all (const int* start, const int* stop)这个方法中,const是用来修饰指针所指向的值是const的,
    所指向的值是只读的并且无法被修改的。但是指针本身(start和stop)它们是可以被更改的。指针本身是可以被再次赋值(自加,自减)
    或者用新的指针来给它们赋值。例如:

      int * const p3 = &x;  // const pointer to non-const int
    const int * const p4 = &x;  // const pointer to const int
    

    最后,以下这两种情况是完全相同的:

    const int * p2a = &x;  //      non-const pointer to const int
    int const * p2b = &x;  // also non-const pointer to const int
    

    string和pointer

    string的本质是一个包含了自身所有的char,并且以’\0’(terminating null character)结尾的array
    ,并且每个元素都是const char因为它们是只读的。
    例:

    const char* word = "hello"; //指针指向h,回顾array
    *(word+4) // output:指针指向’o‘所在的地址,所以这里是'o'
    

    pointer 指向 pointer

    指针指向指针,其实是指针指向它所指向的指针的地址。
    这个其实不难理解。例如:

    char a;
    char * b;
    char ** c;
    a = 'z';
    b = &a;
    c = &b;
    

    这里指针b储存了char a所在的地址,而指针的指针c储存了指针b所在的地址。
    假设a的地址是7230,b的地址是8092,那么这里他们分别的值就应该是:

    • c 是 char** 类型,所包含的值为 8092
    • *c 是char* 类型,所包含的值为 7230
    • **c char 类型,所包含的值为 ‘z’

    void pointer(泛型指针)

    void指针是指针的一种。void pointer是指向了一个没有类型的值的指针。
    因此它的长度和dereferencing的值也是不确定的。

    这样的好处是void pointer可以有很大的自由度:它可以指向任何类型如char,int,float等等。
    但是同时也会产生一些限制,比如我们无法直接使用dereference*。因此,在void pointer中的地址需要被转换进入其他的拥有具体类型的pointer中。

    常规的用法就是作为泛型参数,例如:

    #include <iostream>
    using namespace std;
    
    void increase (void* data, int psize)
    {
      if ( psize == sizeof(char) ){
        char* pchar;
        pchar=(char*)data; //类型转换
        ++(*pchar); 
        }
      else if (psize == sizeof(int) ){
        int* pint; 
        pint=(int*)data; 
        ++(*pint); 
        }
    }
    
    int main ()
    {
      char a = 'x';
      int b = 1602;
      increase (&a,sizeof(a));
      increase (&b,sizeof(b));
      cout << a << ", " << b << '\n';
      return 0;
    }
    

    非法指针和空指针

    理论上来讲,指针可以指向任何地址,包括一个不包含任何元素或者非法元素的地址
    例如:

    int * p;               // 未背初始化的指针
    
    int myarray[10];
    int * q = myarray+20;  // 指向了超出array范围的位置
    

    如上代码并不会产生错误,因为指针是可以指向任何地址的。
    但是当你尝试*dereference q的时候就会出现错误。

    当你需要一个不指向任何位置的pointer时,可以使用如下方式:

    int * p = 0;
    int * q = nullptr;
    int * e = NULL; //较老版本使用
    

    注意区分null pointer和void pointer 的区别。


    pointer 和 function

    pointer是可以指向一个function的。常用的情况就是使用一个function来作为另一个function的参数。

    // pointer to functions
    #include <iostream>
    using namespace std;
    
    int addition (int a, int b)
    { return (a+b); }
    
    int subtraction (int a, int b)
    { return (a-b); }
    
    int operation (int x, int y, int (*functocall)(int,int))//function作为一个参数传入
    {
      int g;
      g = (*functocall)(x,y);
      return (g);
    }
    
    int main ()
    {
      int m,n;
      int (*minus)(int,int) = subtraction; //注意这里是一个function整体赋值给了另一个function,
                                            //名为minus的方法和subtraction有一样的功能。
      m = operation (7, 5, addition);
      n = operation (20, m, minus);
      cout <<n;
      return 0;
    }
    

    如果你看到这里了还没有晕,那么恭喜你pointer已经被你安排的明明白白了?
    有任何问题欢迎评论区留言讨论。

    **未经允许禁止转载**

    展开全文
  • C++指针详解

    2017-09-29 14:33:45
    C++指针详解 C++指针详解 C++指针详解 C++指针详解 C++指针详解 C++指针详解
  • C++ 指针

    千次阅读 多人点赞 2021-05-03 10:51:12
    C++ 指针介绍 (对象指针 & this 指针)

    概述

    指针 (pointer) 是一个变量, 其指为另一个变量的地址. 即内存位置的直接地址.
    在这里插入图片描述

    指向对象的指针

    在建立对象时, 编译系统会为每一个对象分配一定的存储空间, 以存放其成员.
    在这里插入图片描述
    我们可以定义一个指针变量, 用来存放对象的指针. 例如:

    Time time1;
    Time *p;  // 定义指针, 格式: 类名 *对象指针名
    p = &time1;  // 将指针指向Time类对象
    

    我们可以通过对象指针访问对象和对象的成员. 例如:

    int main() {
    
        Time time1;
        Time *p;  // 定义指针, 格式: 类名 *对象指针名
        p = &time1;  // 将指针指向Time类对象
        
        p->hour; // 等同于time1.hour
        p->show_time();  // 等同于time1.show_time()
    
        return 0;
    }
    

    指向对象数据成员的指针

    对象中的成员也有地址, 存放对象成员地址的指针变量就是指向对象成员的指针变量.
    在这里插入图片描述

    定义指向对象数据成员的指针变量的方法和定义指向不同变量的指针变量方法相同. 例如:

    int main() {
    
        Time time1;
        int *p;  // 定义指针, 格式: 类名 *对象指针名
    
        p = &time1.hour;  // 将指针指向time1对象的hour成员
        
        return 0;
    }
    

    通过指向对象数据成员的指针变量访问成员. 例如:

    int main() {
    
        Time time1;
        int *p;  // 定义指针, 格式: 类名 *对象指针名
    
        p = &time1.hour;  // 将指针指向time1对象的hour成员
        cout << *p << endl;  // *p等同于time1.hour
    
        return 0;
    }
    

    this 指针

    每个对象都可以利用一个自己的特殊指针 this, 即指向当前对象的指针.
    在这里插入图片描述
    Box 类:

    #ifndef PROJECT1_BOX_H
    #define PROJECT1_BOX_H
    
    class Box {
    private:
        double height;
        double width;
        double length;
    public:
        Box();
        Box(double h, double w, double l);
        double volume();
    };
    
    #endif //PROJECT1_BOX_H
    

    Box.cpp:

    #include "Box.h"
    
    Box::Box() : height(-1), width(-1), length(-1) {}
    
    Box::Box(double h, double w, double l) : height(h), width(w), length(l) {}
    
    double Box::volume(){
        return (height * width * length);
    }
    

    mian:

    #include "Box.h"
    #include <iostream>
    using namespace std;
    
    int main() {
    
        Box a(2,2,2);
        double volume = a.volume();
    
        cout << "Box 体积: " << volume << endl;
    
        return 0;
    }
    

    this 指针的作用

    调用 a.volume(), this 值为对象 a 起始地址, 实际执行:

    return ((*this).height * (*this).width * (*this).length);
    return (this -> height) * (this -> width) * (this - >length)
    return (a.height) * (a.width) * (a.length)
    

    在这里插入图片描述

    this 指针的实现

    C++ 在处理时, 会在成员函数的形参列中增加一个 this 指针. 调用时, 将对象的地址给形参 this 指针, 然后按 this 的指向去引用其他成员.

    程序中的调用:
    a.volume();  
    
    实际的调用方式是:
    a.volume(&a);
    
    展开全文
  • 二级指针指针指针) 3.1 定义与初始化 3.2间接数据访问 3.2.1.改变一级指针指向 3.2.2改变 N-1 级指针的指向 3.2.3二级指针的步长 四. 指针与数组 4.1 指针与数组名 4.1.1 通过数组名访问数组元素 4.....

    目录

    一.变量的内存实质到

    1.1变量的实质

    1.2 赋值给变量

    1.3 变量在哪里?

    二. 指针是个什么东西?

     三. 二级指针(指针的指针)

    3.1 定义与初始化

    3.2 间接数据访问

    3.2.1 .改变一级指针指向

    3.2.2 改变 N-1 级指针的指向

    3.2.3 二级指针的步长

    四. 指针与数组

    4.1 指针与数组名

    4.1.1 通过数组名访问数组元素

    4.1.2 通过指针访问数组元素

    4.1.3 数组名与指针变量的区别

    4.2 指针数组( 字符指针数组 )

    4.2.1 定义

     4.2.2 代码实例

    4.3 二级指针与指针数组 

    4.3.1 .指针数组名赋给二级指针的合理性

    4.3.2 完美匹配的前提(小尾巴 NULL)


    在风起云涌的编程世界中,C/C++作为编程界的扛把子,以霸主地位而屹立不倒,究其原因,它有其他语言无法相媲美的“底牌”而存在,那就是——指针。指针被称为是C/C++中的精髓,也有人说由于指针的存在让C/C++变得更加难学,难懂,难消化。果真是这样吗?本篇文章让我们一起来揭开指针的真正面纱。

    一.变量的内存实质到

    1.1变量的实质

    要理解指针,首先就要理解“变量”的存储实质,如果你已经深入理解“变量”的存储实质,直接跳过本小节……

    首先我们来看一下内存空间图:

    如图所示,内存只不过是一个存放数据的空间,可以理解为装鸡蛋的篮子,装水果的箱子,或是装RMB的钱包,随便啦,反正就是这么一个很随意的玩意……现在我们把它想象成电影院的座位,电影院中的每个座位都要编号,而我们的内存要存放各种各样的数据,当然我们要知道我们的这些数据存放在什么位置吧!所以内存也要象座位一样进行编号了,这就是我们所说的内存编址(为内存进行地址编码)。座位可以是遵循“一个座位对应一个号码”的原则,从“第 1 号”开始编号。而内存则是按一个字节接着一个字节的次序进行编址,如上图所示。每个字节都有个编号,我们称之为内存地址

    内存编址:

    当我们在程序中写下了这样的语言声明:

    int i;
    char a;

    时,它其实是内存中申请了一个名为 i 的整型变量宽度空间(DOS 下的 16 位编程中其宽度为 2 个字节),和一个名为 a 的字符型变量宽度的空间(占 1 个字节)。

    内存中的映象如下图所示:

    图中可看出,i 在内存起始地址为 6 上申请了两个字节的空间(我这里假设
    了 int 的宽度为 16 位,不同系统中 int 的宽度可能是不一样的,最常用的win32环境下为4个字节),并命名为 i。
    a 在内存地址为 8 上申请了一字节的空间,并命名为 a。阿欧……这样我们就有两个不同类型的变量了。变量有了接下来我们考虑的就是如何给变量进行赋啦。

    1.2 赋值给变量

    再看下面赋值:
    i = 30;
    a = ’t’;
    你当然知道个两个语句是将 30 存入 i 变量的内存空间中,将“t”字符存入 a 变量的内存空间中。我们可以利用这样来形象理解:

    我们将30存在了以地址6(真正的地址可不是这样子哦,真正的地址如:0016FD14)为起始地址的两个字节空间里,a 在内存地址为 8 上申请了一字节的空间存入了‘t’,那么变量i和a在哪呢???

    1.3 变量在哪里?

    接下来我们来看看&i 是什么意思?是取 i 变量所在的地址编号嘛!我们可以这样大声读出来:返回 i 变量的地址编号。你记住了吗?如果没有,在读一遍:返回 i 变量的地址编号

    以一小段简单代码来弱弱看下:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	system("pause");
    	return 0;
    }

    输出结果为:

    输出的 &i 的值 0016FD14就是我们图示中内存空间编码为6的内存地址。接下来就进入我们真正的主题——指针。

    二. 指针是个什么东西?

    指针,想说弄懂你不容易啊!我常常在思索它,为什么呢?其实生活中处处都有指针,我们也处处在使用它。有了它
    我们的生活才更加方便了。没有指针,那生活才不方便。不信?你看下面的例子。

    比如有天你说你要学习C++,要借我的这本 C++ Primer Plus,我把书给你送过去发现你已经跑出去打篮球了,于是我把书放在了你桌子上书架的第三层四号的位置。并写了一张纸条:你要的书在第 三 层 四号的书架上。贴在你门上。当你回来时,看到这张纸条,你就知道了我借与你的书放在哪了。你想想看,这张纸条的作用,纸条本身不是书,它上面也没有放着书。那么你又如何知道书的位置呢?因为纸条上写着书的位置嘛!聪明!!!其实这张纸条就是一个指针了。它上面的内容不是书本身,而是
    书的地址,你通过纸条这个指针找到了我借给你的这本书。

    那么我们 C/C++中的指针又戴上了啥面具呢?让我们拭目以待。下面看一条声明一个指向整型变量的指针的语句:
    int *pi;

    pi 是一个指针,当然我们知道啦,但是这样说,你就以为 pi 一定是个多么特别的东西了。其实,它也只过是一个变量而已。与上一篇中说的变量并没有实质的区别。好了,这就是指针。仅此而已,就这么简单。不信你看下图:

    (说明:这里我假设了指针只占 2 个字节宽度,实际上在 32 位系统中,指针的宽度是 4 个字节宽的,即 32 位。)
    由图示中可以看出,我们使用“int *pi”声明指针变量 —— 其实是在内存的某处声明一个一定宽度的内存空间,并把它命名为 pi。你能在图中看出pi 与前面的 i、a 变量有什么本质区别吗?没有,当然没有!肯定没有!!真的没有!!!pi 也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让 pi 成为具有真正“指针”意义的变量。请接着看下面语句:
    pi = &i;
    你应该知道 &i 是什么意思吧!刚刚大声读的那句话,如果忘了,回头去在大声读,记住了!!!那这句代码怎么读呢?这样大声读:把 i 地址的编号赋值给 pi。并记下来。也就是你在 pi 里面写上 i 的地址编号。结果如下图所示:

    你看,执行完 pi=&i 后,在图示中的内存中,pi 的值是 6。这个 6 就是i 变量的地址编号,这样 pi 就指向了变量 i 了。你看,pi 与那张纸条有什么区别?pi 不就是那张纸条嘛!上面写着 i 的地址,而 i 就是那本厚书C++ Primer Plus。你现在看懂了吗?因此,我们就把 pi 称为指针。所以你要牢牢记住:指针变量所存的内容就是内存的地址编号 ! 本篇文章看完啥都可以没记住,这18个红字切记,切记,切记要牢牢刻在脑子里,也许可能或许大概在将来某个面试中人家问你指针是啥?这18个字足以得满分。也会随着你不断的学习对这句话会理解的越来越深。切记记住这18个红字!!好了废话太多了,现在我们就可以通过这个指针 pi 来访问到 i 这个变量了,请看代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	int *pi = &i;
    	std::cout << "*pi = " << *pi << std::endl;
    
    	system("pause");
    	return 0;
    }
    

    输出结果如下: 

    那么程序中*pi 什么意思呢?你只要这样大声读它:pi 内容所指的地址的内容(读上去好像在绕口令了),就是 pi 这张“纸条”上所写的位置上的那本 “书”—— i 。你看,Pi 的内容是 6,也就是说 pi 指向内存编号为 6 的地址。*pi嘛,就是它所指地址的内容,即地址编号 6 上的内容了,当然就是 30 这个“值”了。所以这条语句会在屏幕上显示 30。请结合上图好好体会吧!由于本人水平有限,对“指针是个什么东西?”的理解仅限于此,有问题我们在随时探讨喽。真想说:指针是个什么东西,这么难理解,脑仁疼疼的。
    到此为止,你已经能大声读出类似&i、*pi 写法的含义了。也知道了具体的相关操作。总结一句话:我们的纸条就是我们的指针,同样我们的 pi 也就是我们的纸条!剩下的就是我们如何应用这张纸条了。如何用?大声读出下面的代码并正确理解含义。

    char a,*pa;
    a = 10;
    pa = &a;
    *pa = 20;

    假设你已经完全掌握在此之前的所有内容,我们接着看看“指针的指针”它是个什么东西?即对int **ppa;中 ppa 的理解。

     三. 二级指针(指针的指针)

    二级指针,是一种指向指针的指针。我们可以通过它实现间接访问数据,和改变一级指针的指向问题。

    3.1 定义与初始化

    以一张简图说明问题:(看不懂看第二节)

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	std::cout << "&i = "<< &i << std::endl;
    	std::cout << "i =  " << i << std::endl;
    
    	int *pi = &i;
    	std::cout << "*pi = " << *pi << std::endl;
    
    	int **ppi = &pi;
    	std::cout << "**ppi = " << **ppi << std::endl;
    
    	system("pause");
    	return 0;
    }

     输出结果:

     思考:**pi 怎么读?代表的含义是什么?

    3.2 间接数据访问

    3.2.1 .改变一级指针指向

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	int i = 30;
    
    	int *pi = &i;
    	std::cout << "一级指针*pi = " << *pi << std::endl;       //一级指针
    
    	int **ppi = &pi;
    	std::cout << "二级指针**ppi = " << **ppi << std::endl;   //二级指针
    
    	*pi = 20;
    	std::cout << "改变一级指针内容: *pi = " << *pi << std::endl;  //改变一级指针值
    	std::cout << "一级指针*pi = " << *pi << std::endl;       //二级指针
    
    	int b = 10;
    	*ppi = &b;
    	std::cout << "改变一级指针指向*pi = " << *pi << std::endl;   //改变一级指针的指向
    	std::cout << "二级指针**ppi = " << **ppi << std::endl;   
    
    	system("pause");
    	return 0;
    }
    

    输出结果:

    3.2.2 改变 N-1 级指针的指向

    • 可以通过一级指针,修改 0  级指针(变量)的内容。
    • 可以通过二级指针,修改一级指针的指向。
    • 可以通过三级指针,修改二级指针的指向。
    •  ·····
    • 可以通过 n  级指针,修改 n-1 

    3.2.3 二级指针的步长

    所有类型的二级指针,由于均指向一级指针类型,一级指针类型大小是 4,所以二级指针的步长也是 4,这个信息很重要

    四. 指针与数组

    4.1 指针与数组名

    4.1.1 通过数组名访问数组元素

    看下面代码:
    int i, a[] = {3,4,5,6,7,3,7,4,4,6};
    for (i = 0; i <= 9; i++)
    {
        std::cout << a[i] std::endl;
    }
    很显然,它是显示 a 数组的各元素值。
    我们还可以这样访问元素,如下:
    int i, a[] = {3,4,5,6,7,3,7,4,4,6};
    for (i = 0; i <= 9; i++)
    {
    std::cout << *(a+i) << std<<endl;;
    }
    它的结果和作用完全一样。

    4.1.2 通过指针访问数组元素

    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a; /*请注意数组名 a 直接赋值给指针 pa*/
    for (i = 0; i <= 9; i++)

    {
     std::cout <<  pa[i] << std::endl;
    }
    很显然,它也是显示 a 数组的各元素值。

    另外与数组名一样也可如下:
    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a;
    for (i = 0; i <= 9; i++)
    {
       std::cout <<  *(pa+i) << std::endl;
    }

    4.1.3 数组名与指针变量的区别

    请看下面的代码:
    int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};
    pa = a;
    for (i = 0; i <= 9; i++)
    {
    printf("%d\n", *pa);
    pa++; /*注意这里,指针值被修改*/
    }

    可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的 pa改成 a 试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而 数组名只是一个指针常量。

    4.2 指针数组( 字符指针数组 )

    4.2.1 定义

    指针数组的本质是数组,数组中每一个成员是一个指针。定义形式如下:
    char * pArray[10];
    语法解析:pArray 先与“[ ]”结合,构成一个数组的定义,char *修饰的是数组的内容,即数组的每个元素。

    图示:

     4.2.2 代码实例

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
    	for(int i=0; i<sizeof(pArray)/sizeof(*pArray); i++)
    	{
    		std::cout << pArray[i] << std::endl;
    	} 
    
    	system("pause");
    	return 0;
    }

    输出结果:

    4.3 二级指针与指针数组 

    4.3.1 .指针数组名赋给二级指针的合理性

    二级指针与指针数组名等价的原因:
    char **p 是二级指针;
    char* array[N]; array = &array[0]; array[0] 本身是 char*型;
    char **p = array;

    例子代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	char * pArray[] ={"apple","pear","banana","orange","pineApple"};
    	std::cout << "**********pArray[i]************" << std::endl;
    	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
    	{
    		std::cout << pArray[i] << std::endl;
    	}
    		
    	char **pArr = pArray;
    	std::cout << "**********pArr[i]************" << std::endl;
    	for(int i=0; i<sizeof(pArray)/ sizeof(*pArray); i++)
    	{
    		std::cout << pArr[i] << std::endl;
    	}
    	system("pause");
    	return 0;
    }

    输出结果:

    4.3.2 完美匹配的前提(小尾巴 NULL)

    数组名,赋给指针以后,就少了维度这个概念,所以用二级指针访问指针数组,需要维度,当然了,也可以不用需要。

    实例代码:

    #include <iostream>
    #include <stdlib.h>
    
    using namespace std;
    
    int _tmain(int argc, _TCHAR* argv[])
    {
    	//演绎 1
    	std::cout << "******演绎 1*****" << std::endl;
    	int arr[10] = {1};
    	for(int i=0; i<10; i++)
    	{
    		std::cout << arr[i] << std::endl;
    	}
    
    	int *parr = arr;
    	for(int i=0; i<10; i++)
    	{
    		std::cout << *parr++ << std::endl;
    	}
    
    
    	//演绎 2
    	std::cout << "*****演绎 2*****"<<std::endl;
    	char *str = "china";
    	while(*str)
    	{
    		std::cout << *str++ << std::endl;
    	}
    
    	char * pArray[] ={"apple","pear","banana","orange","pineApple",NULL};
    	char **pa = pArray;
    	while(*pa != NULL)
    	{
    		std::cout << *pa++ << std::endl;
    	}
    	system("pause");
    	return 0;
    }

    输出结果:

    【下一篇:】C/C++指针详解之提高篇(史上最全最易懂指针学习指南!!!!)

    展开全文
  • c++指针和内存结构总结

    千次阅读 2017-07-04 16:05:14
    c++指针、内存模型、函数指针
  • 练习题:C++指针练习

    千次阅读 2020-09-24 13:59:24
    C++中,const作用域指针时,可以看做是对指针权限的限制。这里我们先把指针的权限归为两种,分别为指向权限和修改权限。(ps:以上是为了理解方便,实际并没有如此规定) int a = 10, b = 20; int* p = &a; ...
  • c++指针的用法

    千次阅读 2020-03-03 14:21:14
    c和c++都有指针的概念,指针其实就是记录数据地址的变量 一、指针的定义和使用:指针定义的语法:数据类型 *指针变量名; 假设 int a = 10; int * p; 让指针记录变量a的地址:p = &a; 使用指针的时候需要解引用...
  • C++指针访问数组遍历

    千次阅读 2020-05-11 16:57:50
    C++指针访问数组遍历 目录 C++指针访问数组遍历 1、注意点 2、指针代码变量数组 1、注意点 int a[10] = { 1,2,3,4,5,6,7,8,9,10 }; int *p = a; 数组名字就是数组第一个元素的地址。 通过++p可以逐个访问...
  • C++ 指针常量、常量指针和常指针常量 2014年11月26日 22:12:41 To-Big_Fish 阅读数:4324 https://blog.csdn.net/yujin753/article/details/41523005 1、指针常量 如果在定义指针变量时候,指针变量前用const...
  • C++ 指针引用 *&

    千次阅读 2019-04-13 17:52:36
    文章目录指针引用的作用示例仅一级指针实现二级指针实现指针引用实现 指针引用的作用 指针引用 *& 可以 替代 二级指针 **; 使用时,更简单; 可以被重新赋值。 示例 如下示例,要求在 func( p)中,m_value值...
  • 今天小编就为大家分享一篇python调用c++返回带成员指针的类指针实例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • C++指针详解1、指针的定义初始化以及回收2、常量与指针1)常量指针(常指针)2)指针常量3)总结3、引用与指针 1、指针的定义初始化以及回收 指针是一个地址,这个地址内存放数据,这些数据就叫做“指针所指的数据”...
  • C++指针的强制类型转换

    千次阅读 2019-09-04 16:28:56
    c语言中,指针是就是内存地址,因此使用指针可以方便的操作内存数据。指针的类型标志着这个指针指向数据的类型,有两个作用: 1、告诉了编译器需要从这个地址开始对**多少字节(n)**的数据进行操作, 以及操作模式 2...
  • 指针有时候真的不是很好懂下面下面我将利用调试的方式讲解一下指针到底是个什么东西 这里我使用了链表结构题 指针传递传递 这张图片我们可以看出head指针的地址与head指针所指向的内容的地址也就是值的地址如下图...
  • c++指针数组,int *,char *,string *

    千次阅读 多人点赞 2019-07-03 18:59:36
    c++指针数组 指针数组≠数组指针,指针数组归根结底是个数组,数组的元素是指针。数组指针归根结底是个指针,指向数组的第一个元素。我们以int型、char型、str型指针数组为例。 整型指针数组 #include<iostream&...
  • c++ 指针指针数组

    千次阅读 2017-09-16 01:19:38
    一、指针数值和数组指针 首先解释下指针数组及数组指针区别: 指针数组:array of pointers,即用于存储指针的数组,也就是数组元素都是指针 数组指针:a pointer to an array,即指向数组的指针 int s[6] = {...
  • 对空指bai针的定义稍有不同 ...但这并不是指针造成的,指针本身的意义是一样的,只是C++的数据类型要比C的丰富,所以指针类型也较C的丰富了。 指针 引用 数组 string 结构体 共用体 枚举 位域 ...
  • C++指针地址和指针的值

    千次阅读 2018-12-28 19:57:49
    C++指针 学过C++ 都知道,指针是一个很重要的概念,他是C++的灵魂,在上学的时候从书山了解到,指针就是指向内存的地址,然,今天和同事讨论指针传递和引用传递的时候,我说:指针传递其实就是引用传递。打脸了。...
  • C++指针的用处

    千次阅读 2018-08-12 23:34:53
    一、C++中的类和结构使用指针定义成员作用   1、多态的要求。 如果要使用同一接口,多态设计,那么就一定要使用基类指针。只有指针才能在实力化后动态的判断使用哪个方法。 2、代码复用。有一些程序...
  • c++指针初始化问题

    千次阅读 多人点赞 2018-12-13 11:47:18
    c++中的指针是一个很经典的用法,但是也是最容易出错的,比如定义了一个指针,必须对其进行初始化,不然这个指针指向的是一个未知的内存地址,后续对其操作的时候,会报错。这只是其次,最让人头疼的就是指针错误...
  • C++指针数组的简单用法

    千次阅读 2020-08-02 00:18:31
    流程:先用指针获得数组的首地址,通过解指针获得相应数组元素变量,因为地址后移四个字节(整型数组),对应整型指针则表示为+1 若要循环遍历数组,加个for循环就行了和普通数组一样。 #include<iostream> ...
  • c++指针

    千次阅读 2018-04-04 14:33:30
    c++ *号和&都是取地址符号,&类似get;*类似set方法,但*号还有更重要的意义,首先*是计算机允许开辟最大类型Bit数(这个内存中用于存放地址,如果内存容不下地址Bit数,就相当于double转float,地址出问题...
  • C++ 指针类型

    千次阅读 多人点赞 2017-04-13 20:58:19
    指针,用来描述内存地址,并通过提供指针操作来实现与内存相关的程序功能。 1. 定义  * ;  类型决定了指向的内存空间的大小。  指针变量也是一种变量,有着自己的内存空间,该空间上存储的是另一个变量的内存空间...
  • 原文地址,感谢原作者 ... 展示一下使用指针指针指针的引用修改传递给方法...当我们把一个指针做为参数传一个方法时,其实是把指针的复本传递给了方法,也可以说传递指针指针的值传递。 如果我们在方法内...
  • C++ 指针与取地址&

    千次阅读 2019-06-09 03:29:34
    C++相比于其他的语言,比较有特点的就是指针的内容。因为涉及到内存地址和地址当中的存储内容,比较容易混淆。其中的关键点是,当我们定义一个指针p,比如说int *p时,指针p必须指向一个地址,而*p则表示该地址中...
  • C++指针的初始化和赋值

    千次阅读 2020-02-27 09:47:41
    指针的初始化和赋值 进度: 指针的初始化和赋值 指针变量的初始化 语法形式 存储类型 数据类型 *指针名=初始地址; 例: int *pa = &a; 注意事项 用变量地址作为初值时,该变量必须在指针初始化之前已声明过,...
  • c++指针(一)——指针的定义以及写法

    万次阅读 多人点赞 2018-05-08 08:26:31
    一、c++中指针的定义以及写法二、定义变量时的内存分配三、指针与数组名的关系四、常量指针与常量指针五、指针的指针六、指针和引用好了,按照这个顺序开启探索c++指针之旅~一、c++中指针的定义int *p; //*号前有...
  • C++指针作为形参时的问题

    千次阅读 2018-12-29 15:14:33
    指针作为形参时,1、可以改变指针指向的内容,2、但是无法改变指针本身的地址,3、除非在改变指针地址时,采用返回值。 文章目录 ##1、可以改变指针指向的内容 void test(int* s){ *s = 100; } int main(){ int a ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 677,141
精华内容 270,856
关键字:

c++指针

c++ 订阅