zoukankan      html  css  js  c++  java
  • (C/C++学习笔记) 十二. 指针

    十二. 指针

    ● 基本概念

    变量的地址就是指针,存放指针的变量就是指针变量(因而又叫作地址变量 address variable); 这个地址编号本身就是一个无符号的整数,32位系统下为4字节(8位十六进制数),64位系统下为8字节(16位十六进制数)

     

    内存地址是用16进制表示的, 内存地址不占用内存空间

     

    指针本身是一种数据类型, 它可以指向int, char, float double等不同类型的变量, 仔细揣摩下图:

     

    指针加1的时候(在CPU32位的情况下),要根据指针的类型来前进相应字节数。比如int类型的指针前进四个字节,char型的指针前进一个字节,void类型指针前进一个字节……

     

    8位十六进制数占多少字节,比如0x12345678这是多少字节?

    一位十六进制代表四位二进制,0x12345678转换成二进制就是0001-0010-001-0100-0101-0110-0111- 1000,而每八位二进制占一个字节,所以8位十六进制数占4字节。这也是指针变量的占用4个字节的原因。

     

    注意如下英语表达:

    int *p;

    p is the address of an int

    (dereference p is an integer)

     

    int v;

    p = &v;

    p stores the address of v

     

    int x, y, *p;

    p = &x; /* p gets the address of x */

    y = *p; /* y gets the value point to by p*/

    y = *(&x); /* same as y = x*/

     

    C Pointer is used to allocate memory dynamically i.e. at run time.

    //定义指针变量的几种形式:

    //形式1:

    int a = 10; //定义一个的int型变量aa赋值为10

    int *p;

    p = &a; //定义一个int型指针变量pp指向int型变量a并赋值为(be set to the value ofint型变量a的地址, &a; 这一语句表示的是对指针进行初始化

    //形式2:

    int a = 10;

    int *p = &a; //同上,也就是说int *p等价于p

    //形式3:

    int *p, i = 10, j;    //如果是int *p, *i表示定义两个指针变量pi

    p = &i; // 以上是定义一个指针变量

    j = *p; // p所指的内存单元,即变量"i"的内容(整数10)赋予变量j;*p代表p所指的变量i, 其内容是数据

    // 上述的定义和语句等价于:

    int *p; // 此时,p还是一个野指针

    int i = 10;

    int j;

    p = &i; // 此时,p不再是一个野指针

    j = *p;

     

    //判断正误:

    int *p;

    *p = &a; //语法错误,因为int *p表示定义一个指针变量;*p代表p所指的变量a的具体数据

    //—————————————————————————

    int *p;

    p = 10; //语法错误,因为不可以把一个数赋值给一个指针变量

    //—————————————————————————

    int *p = 10; // 语法错误,int *p后面只能是地址

    //—————————————————————————

    int *p = a; // 这里分两种情况:如果a是一个变量,那么语法错误;如果我们先定义了一个名为a的数组,例如:int a[10];,那么该语句合法,因为一个数组名就是一个指针常量(指针常量就是一个地址值),这里"常量指针"是指"指针本身是常量,而非指针指向的对象是常量

    //—————————————————————————

    char *p = "world"; // 语法正确,因为字符串"world"就相当于一个数组,指针变量存储的是字符'w'的地址。

    //—————————————————————————

    int **pp = &p; //语法正确,表示取指针地址--定义一个int型指针变量pp, pp指向指针变量p并赋值为指针变量p的地址

    #include <stdio.h>

     

    int main01() // 这里main后面如果加了个01,表示这个main函数停止,而执行下面一个main函数

    {

        int a = 10;

        int *p = &a;

        int b = *p; //定义一个的int型变量b"*指针变量"是引用指针变量的形式,其含义是引用指针变量所指向的值

        printf("%d ", b);

        return 0;

    }

    // 结果为10

     

    int main()

    {

        int a = 10;    //也可以是int a; 此时变量a没有初始化, 里面是垃圾值

        int *p = &a;

        *p = 20; //通过指针变量p间接地修改它指向的变量的值

        printf("%d ", a);

        return 0;

    }

    // 结果为20

     

    ● 二级指针/二重指针/指向指针的指针

    三重及以上的指针统称为多重指针。

    int **p, *s, k = 20;

    s = &k;

    p = &s;

    // *s代表存储单元k,*p代表存储单元s,因此**p也代表存储单元k;p→s→k(20)

    例如:

    int **pp;

    定义了一个二级指针变量,等效于下列定义方式:

    typedef int * P;

        P *p;

    二级指针常用来指向一个指针数组。

    例①

        int a[2][3]={1,2,3,4,5,6}; //声明并初始化二维数组

        int *p_a[3]; //声明整型指针数组

        p_a[0]=a[0]; //初始化指针数组元素

        p_a[1]=a[1];

        int **pp;

        pp=p_a;

        

    注意:在使用二级指针时经常容易犯两类错误。

    (1)类型不匹配

    例如:

        pp=a; //错误,因为pp是一个int**型变量,a是一个int[2][3]型的地址

        pp=&a; //错误,pp是一个int**型变量,&a是一个(*)int[2][3]型的地址

        pp=&a[0]; //错误,pp是一个int**型变量,&a[0]是一个(*)int[3]型的地址

     

    (2)指针没有逐级初始化

    例如:

        int i=3;

        int **p2;

        *p2=&i;

        **p2=5;

        cout<<**p2;

    虽然上述程序段编译、连接均没有错误,但运行时出错。其原因在于int **p2;只是定义了一个指针变量,变量中的内容(地址)是一个无意义的地址,而*p2=&i是对无意义的内存单元赋值,这样是错误与危险的。正确的作法是从第一级指针开始,逐级初始化。

     

    逐级初始化多级指针

    例:

        int i=3;

        int **p2;

        int *p1;

        p1=&i; //初始化一级指针

        p2=&p1; //初始化一级指针

        **p2=5; //通过指针给变量i赋值

        cout<<**p2; //结果为5

        上述两个二级指针在内存中的分布下图所示。

     

    ● 指针的简单例子

    #include <iostream.h>

    #include <stdio.h>

     

    int main()

    {

        int a=1;

        printf("&a=%p ",&a);

        int *p=&a;    //输出变量a的地址

        printf("p=%p ",p);    //同上

        printf("p=%x ",p);    //同上

        printf("*p=%d ",*p);    //*p相当于它指向的整型变量a, 输出变量a的值

        printf("&p=%p ",&p);    //输出指针变量p的地址

        int **pp=&p;        //二级指针, 相当于int *(*pp)=&p;(指针运算符是自右向左结合的)

        printf("pp=%p ",pp);    //输出指针变量p的地址

        printf("*pp=%p ",*pp);    //输出指针变量p的内容, *pp相当于(它指向的指针变量 np)指向(vp)的(relative clause)整型变量的地址

        printf("**pp=%d ",**pp);    //输出指针变量pp最终指向的变量a的值

        printf("&pp=%p ",&pp);        //输出指针变量pp的值

        return 0;

    }

     

    &*两个运算符

    &----address-of operator (取地址运算符)( (reference)):to initialize the pointer variable with the address of a variable that you've already declared

    *----①pointer operator (指针运算符)( dereference): to declare that the variable after it is a unsign-int-type variable which stores the address of another variable

    indirection operator (间接访问运算符) : to access the value of the variable that the pointer is pointing to

    单目运算符"*"必须出现在运算对象的左边,其运算对象是存放地址的指针变量或地址

    单目运算符"&"必须出现在运算对象的左边,其运算对象是任何已经被声明的变量(包括在指针变量)

    &*和*&的区别

    /*

    int a;

    int *p=a;

    &*p*&a的区别: &*的优先级相同,结合性为自右向左, 因此:

    ① &*p先进行*p运算, 再进行&*p运算, *p相当于它p指向的变量a, 所以 &*p相当于取变量a的地址;

    ② *&a先进行&a运算, 再进行*&a运算, &a相当于p, 所以*&a相当于变量a

    */

    #include<stdio.h>

    main()

    {

        long i;

        long *p;

        printf("please input the number: ");

        scanf("%ld",&i);

        p=&i;

        printf("the result1 is: %p ",&*p);                     /*输出变量i的地址*/

        printf("the result2 is: %p ",&i);                     /*输出变量i的地址*/

    }

     

     

     

    ● 有关*p

    *p: ① 作为左值, 相当于它指向的变量, 向指针变量p所指的变量赋值;

    ② 作为右值, 相当于它指向的变量的值, 从指针变量p所指的变量取值

    如果*p 前面有int, char等数据类型, 那就一起构成了指向int型变量的指针,指向char型变量的指针等. (相比整型指针变量/字符型指针变量更标准的读法)

    #include <iostream.h>

     

    int main()

    {

        int i=1,j;

        int *p=&i;

        *p=2;

        j=*p;

        cout<<i<<endl;

        cout<<j<<endl;

        return 0;

    }

    #include<stdio.h>

     

    main()

    {

        long i;

        long *p;

        printf("please input the number: ");

        scanf("%ld",&i);

        p=&i;

        printf("the result1 is: %ld ",*&i);                 /*输出变量i的值*/

        printf("the result2 is: %ld ",i);                    /*输入变量i的值*/

        printf("the result3 is: %ld ",*p);                 /*使用指针形式输出I的值*/

    }

     

    ● 野指针, 垂悬指针/迷途指针, 空指针

    ① 野指针 (wild pointer) : 定以后没有初始化的指针, 例如:

    int *p;

    或者没有指向任何有效地址的指针也叫野指针, 例如:

    int *p=10;

    ② 悬垂指针/迷途指针 (Dangling pointer): 当所指向的对空间/对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针); 此时, 应该将该指针赋值为NULL

    ③ 空指针(NULL pointer)

    一个指向NULL的指针,我们称之为空指针,意味着这个指针不指向任何一个变量(points to nothing; NULL"stdio.h"等头文件中被宏定义为0 (严格来说是#define NULL ((void *)0), (void *)0 就是将0强制转化为(void *)类型的指针), 所以程序在编译时将NULL替换成0; 0表示指向地址为0的单元(这个单元一般是不能使用的).

    也就是说, 空指针事实上指向了内存的零地址,但是操作系统并没有使用零地址附近的空间,这样做是为了使指针指向一个已知的地方防止成为野指针. 例如:

    int *pointer = NULL;

    ④ 空类型指针(void pointer)

    定义一个指针变量,但不指定它指向具体哪种数据类型。不同类型的指针不能互相赋值, 例如:

    char *p1="helloworld";

    int *p2=p1;    //错误

    但是, 空类型指针可以赋值给任何类型的指针, 例如:

    void *p1="helloworld";

    int *p2=p1;    //不推荐的做法, 推荐的做法是进行指针类型的强制转换, 例如:

    char *p1="helloworld";

    int *p2=p1;

    int *p2=(int *)p1;

    然而, 指针的强制转换没有意义, 因为我们只是改变了一个变量的地址, 不能强制转换指针指向的变量的类型

    另外, 不允许对void型指针做加减法运算.

     

    ● 常量指针/指针常量

    常量指针(指向常量的指针变量)&指针常量(常量性质的指针 / 指针常量指向变量)

    关键: *是一个单目运算符,它的运算对象在其右边(不运算其左边的对象)

    常量()指针 constant pointer(指针指向的是常量, 指向的不可变, 但它本身可变)

    const char *p;        //从右向左读, * 读作a pointer to

    p is a pointer to const char;

    等价于:

    char const * p;     //同上因为C++里面没有const*的运算符,所以const只能属于前面的类型。

     

    //C++标准规定,const关键字放在类型或变量名之前等价的。

    const int n=5; //same as below

    int const m=10;

     

    • 数组名就是常量指针:

    int a[10];

    int *p=a;

    a++;        //不合法, 因为a是指针p指向的常量, 因为a是地址值, 当然是常量

    p++;

    其实这就像我们经常看到的char *p="ABC", 指针p指向的也是字符A的地址

    指针()常量(指向的是变量, 指向的int变量可变, 但它本身不可变)

    char *const p;

    p is a const pointer to char

    △似乎下面这个声明本身就是错的, 编译器不通过

    指针()常量(如果指向的是常量, 指向的不可变, 它本身也不可变)

    char *const *p;

    p is a const pointer to a const char

    /*看变量声明的时候应该从右至左看,以如下为例:

    char *const *p

    先看从右至左第一个符号*,这表明变量p是指针,再看第2个符号const,它修饰的是*p,*p表示p指向的内容,所以,p指向的内容是常量,下一个符号是*,这就表示该常量为指针,然后是char,就表示指向的内容是char*/

    口诀:

    const(*号)左边放,我是指针变量指向常量;

    const(*号)右边放,我是指针常量指向变量;

    const(*号)两边放,我是指针常量指向常量;

    指针变量能改指向,指针常量不能转向!

    要是全都变成常量,锁死了,我不能转向,你也甭想变样!

    //常量指针的案例

    #include<stdio.h>

     

    int main()

    {

        int a=1, b=2;

        int const *p=&a;

        //*p=3;

        //printf("%d ",a);    //出错, 提示"l-value specifies const object"

        printf("%d ",*p);    //结果为1

        p=&b;        //b的地址赋给p

        printf("%d ",*p);    //结果为2

        return 0;

    }

    _____________________________________________________

    //指针常量的案例

    #include<stdio.h>

     

    int main()

    {

        int a=1, b=2;

        int *const p=&a;

        *p=3;

        printf("%d ",a);    //a的值变为3

        printf("%d ",*p);    //结果为3

        //p=&b;

        //printf("%d ",*p);    //出错,因为指针p的值是个常量,不可改变,编译器会提示l-value specifies const object

        return 0;

    }

    // 如果是    char *const p= "hello";*p='b'; 编译通过, 但会运行出错, 因为"hello"是字符串常量

    // 如果是    char *const p= 'h';*p='b'; 编译不通过, 提示: cannot convert from 'const char' to 'char *const (字符指针常量)'

    ________________________________________________________

     

    #include<stdio.h>

     

    int main()

    {

        int a=1, b=2;

        int *const *p=&a;        //出错, 提示cannot convert from 'int *' to 'int *const * '

        //*p=3;

        //printf("%d ",a);        //出错

        //p=&b;

        //printf("%d ",*p);    //出错

        return 0;

    }

     

    char p[]="abc123ABC"; 以及char *p="abc123ABC";

    • char p[]="abc123ABC"; 以及char *p="abc123ABC";的区别:

    const在前,定义为常量指针;*在前,定义为指针常量。

    • const 在前,定义为常量指针;*在前,定义为指针常量

    char p[]是一个数组,分配了内存, "abc123ABC" 复制到了该内存里面,这个内存可读可写, 保存在栈空间数组里

    char *p是一个指针,没有分配内存(除了给这个指针变量本身分配的四个字节), 指针p指向的是字符串常量"abc123ABC", 其地址是第一个字符a的地址(也可以只指向一个字符), 保存在静态数据区. 另外, p=这样的赋值是可以的,也就是a指向了另外的地址。

    char *p C++03中不推荐使用(deprecated), 而应该写成const char *p = "abc123ABC ", 它等同于char const * p= "abc123ABC ", 又等价于:

    char *p;

    p="abc123ABC";

    • 总之,死记:字符串常量代表的是一个地址值, 字符串常量和数组一样,它也是当成指针来对待的,它的值就是字符串首个字符的地址.
    • 另外, 除了char buffer[20]= "hello ";strcpy(p, "hello ");这两个情况外,程序中出现的字符串都应该被视为字符串常量.

    #include <stdio.h>

    int main()

    {

        char *p = "abc";

        printf("%p ", p);

        printf("%p ", "abc");

        printf("%p ", &(p[0]));    //p[0]代表字符'a'

        printf("%p ", &(p[1]));    //p[1]代表字符'b'

        printf("%p ", &(p[2]));    //p[2]代表字符'c'

        printf("%c ", p[3]);    //p[3]代表转义字符空字符''(NULL), ASCII码为0

        printf("%p ", &(p[3]));

        return 0;

    }

    #include <stdio.h>

     

    int main()

    {

        char *p = "abc";    //指针变量p在栈区,字符串"abc"在常量区,即"abc"时字符串常量

        *p = 'd'; //*p代表存储a的内存空间,但这个内存空间存储的值不可改变

        //*p相当于p[0], 因此p[0] = 'd';同样会出现编译通过但结果出错的情况

        printf("%s ", p);

        return 0;

    }

    //编译、连接都通过,但是运行出现"该内存不能为'written'"的应用程序错误, 因为"abc"是字符串常量;

    //另外注意,如果char *p = "a"; 同样的,编译通过,但程序会出错。不能写成char *p = 'a'; 因为字符常量不能代表一个地址,编译器会提示:cannot convert from 'const char' to 'char *'

     

    //正确的程序应该是:

     

    #include <stdio.h>

    int main()

    {

        char p[] = "abc";    //字符数组名p在栈区,字符串"abc"也在栈区,即"abc"不是字符串常量

        printf("%s ", p);

        *p = 'd';    //*p也代表存储a的内存空间,这个内存空间存储的值可以改变

        printf("%s ", p);

        return 0;

    }

     

    //注意如下相似案例:

    #include <stdio.h>

     

    int main()

    {

        char *p = "abc";

        p = 'd';    //编译不通过, 提示cannot convert from 'const char' to 'char *'

        printf("%s ", p);

        return 0;

    }

     

    #include <stdio.h>

     

    int main()

    {

        char *p = "abc";

        p = "def";    //编译通过,字符型常量是一个没有命名的常量, 这里将指针变量p指向了另一个字符串"def"

        printf("%s ", p);    //仔细琢磨一下, p是一个指针, 它的值就是一个字符串常量, 而字符串常量就相当于一个地址值

        return 0;

    }

     

     

    #include <stdio.h>

    int main()

    {

        char p[10] = "abc";

        printf("%s ", p);

        *p = "def";    

        printf("%s ", p);

        return 0;

    }

    //编译不通过,提示:cannot convert from 'char [4]' to 'char'

    图解:

    char a[] = "hello";

    char *p = "world";

     

    ● 指针与一维数组

    指针与一维数组:

    //定义数组元素的指针

    int a[10];    //也可以对其初始化, int a[10]={0};

    int *p;        //上两句等价于:int *p, a[10];

    p=&a[0];        //上两句等价于int *p=a; int *p=&a[0]; 不能是上两句等价于int *p=&a

     

    一维数组的结构是线性的, 如图:

     

    对于一维数组及其指针:

    int a[5]={1,2,3,4,5};

    int *p;

    p=a;    //a是指向数组起始元素的指针常量, 是右值, 不能

    一维数组首地址

    (一维数组第一个元素的地址)

    一维数组第n个元素的地址

    (从0开始计数)

    a

    a+n, 如a+4

    &a

    /

    &a[0]

    &a[n], 如&a[4]

    &(*(a+0)), 即&(*(a))

    &(*(a+n)), 如&(*(a+4))

    p, 即&(*p)

    &(*p+4), 如&(*(p+4))

    ● 不能用&(a+4), 否则提示error C2102: '&' requires l-value(有地址的值), &的运算只能是已经被声明的变量;

    *(a+4)之所以正确, 是因为*的运算对象除了可以是指针变量, 还可以是地址

     

    一维数组第0个元素的值

    一维数组第n个元素的地址

    (从0开始计数)

    a[0]

    a[0+n], 如a[4]

    *(&a[0])

    *(&a[0+n]), 如*(&a[4])

    *(a+0), 即*(a)

    *(a+n), 如*(a+4)

    *p

    *(p+4), 如*(p+4)

    #include<stdio.h>

     

    int main()

    {

        int a[5]={1,2, 3, 4, 5};

        int *p;

        p=a;

        printf("%d ",a);

        printf("%d ",a+2);

        

        printf("%d ",&a);

        //printf("%d ",&(a+2));    //不合法, 因为&的运算对象只能是已经被声明的变量

        

        printf("%d ",p);

        printf("%d ",p+2);        //加的是两个sizeof(int), 而不是两个sizeof(p)

    }

    //通过指针变量获取一维数组元素的地址和值(简单案例)

    #include <stdio.h>

     

    void main()

    {

    int a[5]={1,2,3,4,5};

        int *p;

        p=a;

        printf("%p ", a);

        printf("%p ", &a[0]);

        printf("%p ", &(*(a)));

        printf("%p ", &(*p));

        printf("----------- ");

     

        printf("%p ", a+4);

        printf("%p ", &a[4]);

        printf("%p ", &(*(a+4)));

        printf("%p ", &(*(p+4)));

        printf("----------- ");

     

        printf("%d ", a[0]);    //下标法

        printf("%d ", *(&a[0]));    //地址法

        printf("%d ", *(a+0));        //地址法, *(a+0)相当于*(a)

        printf("%d ", *p);        //指针法

        printf("----------- ");

     

        printf("%d ", a[4]);    //下标法

        printf("%d ", *(&a[4]));    //地址法

        printf("%d ", *(a+4));        //地址法

        printf("%d ", *(p+4));        //指针法

    }

    //通过指针变量获取一维数组的元素(复杂案例)

    #include <iostream>

    using namespace std;

    void main()

    {

        int i,a[10];

        int *p;

        //利用循环,分别为10个元素赋值

        for(i=0;i<10;i++)

            a[i]=i;

        //将数组中的10的元素输出到显示设备

        p=&a[0];    //即p=a;

        for(i=0;i<10;i++,p++)

            cout << *p << endl;        //也可将后两句写成for(i=0; i<10; i++); cout<<*(a+1)<<endl;    //a+i表示数组a中的第i个元素的地址

    }

     

    ● 指针与二维数组

    二维数组用矩阵方式存储元素, 在内存中仍然是线性结构:

     

    对于二维数组的指针及其元素的值:

    int a[3][2]={{1,2},{11,12},{21, 22}};

    int *p;

    p=*a;

    二维数组首地址

    (二维数组第一个元素的地址)

    具体含义

    数据类型

    二维数组第n个元素的地址

    a

    0一维数组指针的地址

    int (*)[2]

    *(a+2)+1

    &a[0]

    0一维数组指针的地址

    int (*)[3][2]

    *(pp+2)+1

    &a

    整个二维数组指针的地址

    int (*)[2]

    /

    &a[0][0], 等同于:

    &(a[0][0]), &(*(a[0]+0)),

    &(*(*(a+0)+0))

    0行第0列数组元素的地址

    int *

    &a[2][1]

    a[0]

      

    int *

    /

    *a

    0行第0列数组元素的地址

    int *

    &a[0][0]+2*2+1

    *a+2*2+1

    p

    指向第0行第0列数组元素的指针

    int *

    p+5

     

    //通过指针变量获取二维数组的元素(简单案例)

    #include <stdio.h>

     

    void main()

    {

     

        int a[3][2]={{1,2},{11,12},{21, 22}};

        int *p;

        p=*a;    //p=a[0]; p=&a[0][0];

        int (*pp)[2]=a;        //定义一个int (*)[2]类型的一维数组()指针pp, pp指向一个包含5个元素的一维数组

        int (*ppp)[3][2]=&a;    //定义一个int (*)[3][2]类型的二维数组()指针

        

        printf("%p ", a);

        printf("%p ", &a[0]);

        printf("%p ", &a);

        printf("%p ", &a[0][0]); //最易理解, 常用

        printf("%p ", a[0]);

        printf("%p ", *a);

        printf("%p ", p);

        printf("----------- ");

     

        printf("%p ", *(a+2)+1);    //使用一维数组(型)地址的首地址

        printf("%p ", *(pp+2)+1);    //使用一维数组(型)指针指向的首地址

        //不能使用二维数组(型)指针引用数组元素

        printf("%p ", &a[2][1]);    //使用第0行第0列数组元素的地址; //最易理解, 常用

        printf("%p ", &a[0][0]+2*2+1);    //使用第0行第0列数组元素的地址

        printf("%p ", *a+2*2+1);    //使用第0行第0列数组元素的地址

        printf("%p ", p+5);        //使用第0行第0列数组元素的指针

        printf("----------- ");

     

        printf("%d ", *(*a+0)+0);    //使用一维数组(型)地址

        printf("%d ", *(*a));        //使用第0行第0列数组元素的地址

        printf("%d ", *(*pp+0)+0);    //相当于printf("%d ", *(*pp)

        //不能使用二维数组()指针来取数组元素的值

        printf("%d ", a[0][0]);    //最易理解, 常用

        printf("%d ", *(a[0]));

        printf("%d ", *p);

     

        printf("----------- ");

     

        printf("%d ", *(*(a+2)+1));

        printf("%d ", *(*(pp+2)+1));

        printf("%d ", a[2][1]);    //最易理解, 常用

        printf("%d ", *(p+4));    

        //不能用a[5], *(&a[5]))取值

    }    

     

    //通过指针变量获取二维数组的元素(复杂案例)

    #include<iostream>

    using namespace std;

    void main()

    {

        int i,j;

        int a[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};

        cout << "the array is: " << endl;

        for(i=0;i<4;i++)//行

        {

            for(j=0;j<3;j++)//列

                cout <<*(*(a+i)+j) << endl;

        }

    }

     

    ● 熟记下表

    int a[3][5]={0};

    a

    二维数组名称,数组首地址

    int (*p)[5]=&a;

    定义一个指向int [5]类型的指针变量p并初始化, p指向一个包含5个元素的一维数组

    a[0], *(a + 0), *a

    0行,0列元素地址

    a + 1

    第1行首地址

    a[1], *(a + 1)

    第1行,0列元素地址

    a[1] + 2, *(a + 1) + 2, &a[1][2]

    第1行,2列元素地址

    *(a[1] + 2), *(*(a + 1) + 2), a[1][2]

    第1行,2列元素的值

     

    ● 字符串与指针

    //赋值字符串str1并命名为str2

    #include<stdio.h>

     

    main()

    {

        char str1[]="you are beautiful",str2[30],*p1,*p2;

        p1=str1;

        p2=str2;

        while(*p1!='')

        {

            *p2=*p1;

            p1++;                                /*指针移动*/

            p2++;

        }

        *p2='';                                     /*在字符串的末尾加结束符*/

        printf("Now the string2 is: ");

        puts(str1);                                 /*输出字符串*/

    }

     

    ● 数组指针&指针数组

    数组指针:a pointer to an array,即指向数组的指针

    指针数组(一维的较常见)array of pointers,即用于存储指针的数组,也就是数组元素都是指针

    ([]的优先级大于*)

    int (*p)[4];     //数组指针, p is a pointer to an integer array of size 4

    int *p[4];     //指针数组, Array of 4 pointers to int

    int

    int (*p[8])[5]; //p is an array of pointers to integer array of size 5

    #include<stdio.h>

     

    main()

    {

        int a=2;

        int b=3;

        int *p[4];

        p[0]=&a; //不能是*p[0]=&a;

        p[1]=&b;

        int **pp=p;    //p就是一个数组名, 代表第一个元素的地址; pp是指向指针数组p首元素的指针(指向指针的指针)

        //也可以写成int *(*pp)=p, 因为*的运算方向是从左至右, 故可以省略括号

        printf("%d ", *(p[0]));

        printf("%p ",&a);        //变量a的地址

        printf("%p ",p[0]);    //数组p0个元素的值是变量a的地址    

        printf("%p ",&(p[0]));    //数组p0个元素的地址, 等同于printf("%p ",&p[0]);

        printf("%p ",p);        //同上

        printf("%p ",pp);        //pp这个指针变量里面存储的是数组p的首地址

        printf("%p ",*pp);        //pp这个指针变量指向的另一个指针变量的值是变量a的地址

        //printf("%p ",*(pp)); //pp存储的内容是指针数组首元素的地址, 所以

    }

     

     

     

     

     

    //指针数组的指针元素指向不同的整形变量

    #include<stdio.h>

     

    int main()

    {

    int a=1;

    int b=2;

    int c=3;

    int *tab[3]={&a,&b,&c};

    int i;

    for (i=0; i<3;i++)

        {

                printf("%d ", *(tab[i]));

        }

    return 0;

    }

     

    //指针数组的指针元素指向不同的数组

    #include<stdio.h>

     

    int main()

    {

    int t1[4]={0,1,2,3};

    int t2[4]={4,5,6,7};

    int t3[4]={8,9,10,11};

    int *tab[3]={t1,t2,t3};

    int i,j;

    for (i=0; i<3;i++)

        {

            for (j=0; j<4; j++)

            {

                printf("%d ", *(tab[i]+j));

            }

        }

    return 0;

    }

     

     

    //指针数组的指针元素指向不同的字符串

    #include <iostream>

       

    using namespace std;

    const int MAX = 4;

       

    int main ()

    {

    char *names[MAX] = {        //也可以不用宏定义MAX的值, 在方括号内什么数字也不填

    "Zara Ali",

    "Hina Ali",

    "Nuha Ali",

    "Sara Ali",

    };

     

    for (int i = 0; i < MAX; i++)

    {

    cout << "Value of names[" << i << "] = ";

    cout << names[i] << endl;

    }

    return 0;

    }

     

     

    ● 指针数组作main()函数参数

    int main(int argc, char *argv[])    //等价于int main(int argc, char **argv)

    //argc:参数个数(argument count); argv: 参数向量(argument vector);

    这些参数不能在程序中得到, 因为main()函数是操作系统调用的, 所以实参只能由操作系统给出.

    例如在DOS系统的操作命令状态下, 在命令行中包括了命令名和需要传给mian()函数的参数, 参数个数包括命令.

    命令行的一般形式为:

    命令名    参数1    参数2...参数n

    例如:

    d:debug1.exe hello world

    #include <stdio.h>

    #include <stdlib.h>

     

    int main(int argc, char **argv)

    {

        int i;

        

        printf("共有%d个参数 ", argc);

        

        for(i=0; i<argc; i++)

        {

            printf("%d个参数是%s ",i+1, argv[i]);

        }

     

        system("PAUSE");

        return 0;

    }

    //得到可执行文件E: estDebug est.exe

    按Win键+R打开运行,在打开的运行中输入CMD打开命令行程序cmd.exe

     

     

    ● 指针的算术运算

    对指针作算术运算有两种:

    1. 对指针加减整数, 相当于对指针指向的变量的地址作运算, 这种运算与指针指向的变量的数据类型的大小有关.
    2. 指针互减(其它运算都不行)

    #include <iostream>

    using namespace std;

    void main()

    {

        int a=100;

        int *p_a=&a;

        printf("address:%d ",p_a);

        p_a++;

        printf("address:%d ",p_a);

        char b='A';

        char *p_b=&b;

        printf("address:%d ",p_b);

        p_b--;

        printf("address:%d ",p_b);

        int c[5]={0};

        int *p_c1=&c[0];

        int *p_c3=&c[3];

        //两个类型相同的指针允许作减运算

        printf("%d ",p_c3-p_c1);

        int *pp[5];

        printf("%d ",sizeof(pp));

    }

     

    ● 指针与函数

    函数存放在代码区(code area), 在编译时被分配给一个入口地址, 这个地址就是函数的指针.

    可以用一个指针变量指向函数, 然后通过该指针变量调用这个函数.

    ① 指针形参(指针变量作函数参数)

    数据类型 函数名(数据类型 *pt1, 数据类型 *pt2, ...)

    ② 函数型指针(指向函数的指针)

    数据类型 (*函数指针名)(形参表)

    ※回调函数: 通过函数指针而被调用的函数

    1. 指针型函数(返回指针值的函数)

    数据类型 *函数名(参数表)

    {

        函数体

    }

     

    ● 指针变量作函数参数

    //指针形参(指针变量作函数参数)

    #include <stdio.h>

    void func(int *a)

    {

        (*a)++;

    }

     

    int main()

    {

        int a = 1;

        func(&a); //通过函数的形参间接地修改实参的值, 之所以实参为地址, 是因为子函数func的形参类型为整形指针变量

        printf("%d ", a);

        return 0;

    }

     

    void func1(int *p)

    {

        p = NULL;

    }

     

    int main()

    {

        int a = 1;

        int *p = &a;

        func1(p);    //虽然p的值的&a的值相同, 但本质上这还是一个单向的值传递, 不是按址传递

        // p指向a的地址还是NULLp还是指向了a的地址

        return 0;

    }

     

    voud func1(int **p)        //形参必须是二级指针, 因为下面的实参&p是取的指针的地址

    {

        *p = NULL;

    }

     

    int main()

    {

        int a = 1;

        int *p = &a;

        func1(&p);    //按址传递

        // p指向a的地址还是NULL?指向了NULL

        return 0;

    }

     

    ● 函数型指针(指向函数的指针)

    /*

    int function(int xint y); 声明一个函数

    int (*f) (int xint y); 声明一个函数指针

    f=function; function函数的首地址赋给指针

    */

    #include <iostream>

    using namespace std;

    int per(int a,int b);

     

    void main()

    {

        int width=10,lenght=30,result;

        int (*f)(int x,int y);

        f=per;//定义函数指针

     

        result=(*f)(width,lenght);    //f是指向avg函数的函数指针, 调用f就像调用avg函数

        cout << result <<endl;

    }

     

    int per(int a,int b)

    {

        return (a+b)*2;

    }

     

     

    ● 指针型函数(返回指针值的函数)

    #include <iostream.h>

    int *per(int a,int b);

    int perimeter;

     

    void main()

    {

        int width=10, length=30, *result;

        result=per(width,length);    //per这个指针函数被调用后返回一个地址值, 将这个地址值赋给指针变量result

        cout<<per(width,length)<<endl;    //per()函数被调用后, 返回指向全局变量perimeter的指针p

        cout<<result<<endl;

        cout<<*result<<endl;

     

    }

     

    int *per(int a,int b)

    {

        perimeter=(a+b)*2;    //如果在子函数中定义的一个变量并且改变量将用于其它函数, 那这个变量一定要是全局变量, 如果是局部变量, 局部变量的内存空间在每次函数调用时分配,在函数执行完时释放, 并得到函数的返回地址, 就算是静态局部变量, 即使在函数执行完时释放, 内存空间没有被释放, 它的作用域也只是在函数内.

        int *p= &perimeter;

        return p;

    }

     

     

  • 相关阅读:
    ld: library not found for -lstdc++.6
    WebViewJavascriptBridge 进行js 与native通信。
    invalid active developer path (/Library/Developer/CommandLineTools), missing xcrun at: /Library/Developer/CommandLineTools/usr/bin/xcrun
    OC 与 js 界面JSBridge交互
    react-native init的时候出现问题:npm WARN React-native@0.35.0 requires a peer of react@~15.3.1 but none was
    react-无状态组件
    2018年末--积极拥抱h5.转载 大前端时代来临,我们何去何从?
    /Users/macbook/Library/Developer/Xcode/DerivedData/MapViewDemo: No such file or direc
    EXCEL中如何获得工作表(sheet)的名称
    DELPHI中Showmodal与Show的区别
  • 原文地址:https://www.cnblogs.com/ArrozZhu/p/8377912.html
Copyright © 2011-2022 走看看