指针(2)
Suzhou 18 0
指针运算
指针+-整数
int main()
{
    int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = &a;
    int i = 0;
    int sz = sizeof(a) / sizeof(a[0]);
    for (i = 0; i < sz; i++)
    {
        printf("%d\n", *p);  //1 2 3 4 5 6 7 8 9 10
        //printf("%d\n", *p+1);  可以写成这个形式,去掉下一行代码
        p = p + 1;  //写成指针+整数
    }
    return 0;
}
int main()
{
    int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = &a;
    int i = 0;
    for (i = 0; i < 5; i++)  //每次跳过两个int,循环最多5次,否则指针越界
    {
        printf("%d \n", *p);  //1 3 5 7 9 
            p = p + 2;  //打印时每次跳过两个int
    }
    return 0;
}
int main()
{
    int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = &a[9];  //取得数组最后一位元素的地址
    int i = 0;
    for (i = 0; i < 5; i++)
    {
        printf("%d \n", *p);
            p = p - 2;  //从后向前打印时每次跳过两个int
    }
    return 0;
}
p和*p区别

::: warning printf的运算顺序

void main()
{
    int i = 2;
    printf(" % d, % d, % d, % d, \n", i++, ++i, i, i++);
    printf("% d\n" , i);
}

1、printf函数的执行顺序是由右到左的 2、前自增运算符(++i)先加1,再使用i,此时i已经加了1; 3、后自增运算符(i++)先使用i,再加1。 后自增运算是要在整条语句结束以后才自加1,在所有的运算符都计算完了以后再计算=%d。

::: 指针(2) 指针(2)

  1. *(p++)与 *p++相同:
    int main(int argc, char* argv[])
    {
     int arrayA[4] = { 4, 3, 2, 1 };
     int* p1 = NULL, * p2 = NULL;
     p1 = arrayA, p2 = arrayA;
     printf("第一次:*(p1++)=%d *p2++=%d\n", *(p1++), *p2++);
     printf("第二次:*(p1++)=%d *p2++=%d\n", *(p1++), *p2++);
     return 0;
     /*
     打印结果:
     第一次:*(p1++)=4 *p2++=4
     第二次:*(p1++)=3 *p2++=3
     分析:后缀运算符++优先级高,第一次执行*p1++时,p1++表示先取值后p1再自增,p1取到的值和*结合
     取出p1指向地址的值,即arrayA首地址的值,即4。第二次执行时p1自增后指向arrayA[1]的地址,所以取值为3
     */
    }

  1. *(++p)与 *++p相同:
    int main()
    {
     int arrayA[4] = { 4, 3, 2, 1 };
     int* p1 = NULL, * p2 = NULL;
     p1 = arrayA, p2 = arrayA;
     printf("*(++p1)=%d *++p2=%d *p1=%d *p2=%d\n", *(++p1), *++p2, *p1, *p2);
     return 0;
     /*
     打印结果:*(++p1)=3 *++p2=3 *p1=3 *p2=3
     分析:*(++p1)表示p1先自增1后在取值,p1和p2的实际指向内存空间的位置不变。
     */
    }

3.++p与++(p)相同

int main()
{
    int arrayA[4] = { 4, 3, 2, 1 };
    int* p1 = NULL, * p2 = NULL;
    p1 = arrayA, p2 = arrayA;
    printf("arrayA[0]=%d ++*p1=%d ++(*p2)=%d *p1=%d *p2=%d\n", arrayA[0], ++ * p1, ++(*p2), *p1, *p2);
    return 0;
    /*打印结果:
    arrayA[0]=6 ++*p1=6 ++(*p2)=6 *p1=6 *p2=6
    分析:printf函数打印自右向左,首先执行++(*p2),先对p2指向地址的值进行++运算,即将
    arrayA[0]的值++运算后变成5,因++*p与++(*p)结合规则是相同的,p1也是指向arrayA[0]地址,
    所以++*p1后arrayA[0]的值变成6。
    */
}

  1. (*p)++
    int main()
    {
     int arrayA[4] = { 4, 3, 2, 1 };
     int* p1 = NULL, * p2 = NULL;
     p1 = arrayA;
     printf("arrayA[0]=%d, (*p1)++=%d (*p1)++=%d *p1=%d\n", arrayA[0], (*p1)++, (*p1)++, *p1);
     return 0;
     /*打印结果:
     arrayA[0]=6, (*p1)++=5 (*p1)++=4 *p1=6
     分析:printf函数打印自右向左,(*p1)++先取出p1指向地址的值打印,然后再将p1指向地址的值++运算。
     */
    }

  1. ++复杂用法 ::: warning

++(p)++: 不合法:自增操作数必须是左值 ++( *++p++):不合法:自增操作数必须是左值 ++(++p++): ++(p++): (++p++): :::

int main()
{
    int arrayA[4] = { 4, 3, 2, 1 };
    int* p1 = NULL, * p2 = NULL;
    p1 = &arrayA[1];
    printf("前:arrayA[1]=%d arrayA[2]=%d\n", arrayA[1], arrayA[2]);
    printf("++(*p1++)=%d ++*p1++=%d *p1=%d\n", ++(*p1++), ++ * p1++, *p1);
    printf("后:arrayA[1]=%d arrayA[2]=%d\n", arrayA[1], arrayA[2]);
    return 0;
    /*打印结果:
    前:arrayA[1]=3 arrayA[2]=2
    ++(*p1++)=3 ++*p1++=4 *p1=1
    后:arrayA[1]=4 arrayA[2]=3
    分析(第二行打印代码):
    1.先执行++*p1++:后缀运算符的优先级高于前缀运算符,所以p1++先结合(后进行++运算),p1指向的地址和*
    结合,取出*p1的值3,然后和前面的++结合将该地址的值自增运算,即将arrayA[1]中的数由3变为4,执行完后p1
    执行后缀运算符++,使得p1指向&arrayA[2]。
    2.再执行++(*p1++):()的优先级最高,先执行(*p1++),()中后缀运算符++的优先级高于*,p1++先结合(后进行++运算),
    先取值后自增:(*p1++)先取arrayA[2]地址中的值为2,在和前面的++结合将该地址的自增运算后变为3,执行完后p1
    执行后缀运算符++,使得p1指向&arrayA[3]。
    3.最后执行*p1:*p1的值即&arrayA[3],*p1为1。
    */
}

总结:

#define N_VALUS 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &valus[0]; vp < &valus[N_VALUS];)
    {
        *vp++ = 0;
    }
    return 0;
}

上述代码中* vp++,先执行vp++,后置++最后执行,先执行*vp,取得数组中该位置的值,再执行赋值0的操作,最后执行vp++,指针指向下一个元素。上述代码中可以认为是for循环中循环体语句中的vp++和 *vp = 0写在了一起。 上述代码的作用是把数组values中每一个元素都置为0。 随着内存中数组下标的增长,地址由低到高变化。如下图: 指针(2)

::: warning 上述部分内容引用自CSDN博主“IT笔记”的原创文章,版权声明和原文链接如下: ———————————————— 版权声明:本文为CSDN博主「IT笔记」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:https://blog.csdn.net/u013416923/article/details/120475012 :::

指针减指针
int main()
{
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%d\n", &arr[9] - &arr[0]);
    return 0;
}

指针减指针即是两个地址相减,得到的结果是两个地址之间的元素个数。(如果是小地址减去大地址,得到的结果的绝对值是两个地址之间的元素个数) ::: warning 指针-指针时,两个相减的指针一定要指向同一个内存空间,相减后的结果才有意义。 :::


strlen的模拟实现:

int my_strlen(char* str)
{
    char* start = str;  //start为数组首元素位置
    char* end = str;  //end为数组最后一个元素的位置
    while (*end != '\0')  
    {
        end++;
    }
    return end - start;
}

int main()
{
    char arr[] = "bit";  //数组中的元素:b,i,t,\0。
    int len = my_strlen(arr);
    printf("%d\n", len);  //3
    return 0;
}

上述代码自定义函数中,将数组中每一个位置的地址对应的值依次和“\0”比较,“\0”是数组中最后一个元素,当end值为\0,即end指向数组最后一个元素的位置。 ::: warning *函数传递时,自定义函数中接收时使用int arr[]和int * arr没有区别,都可以操作整个数组的元素。** :::

指针的关系运算

将数组values中每一个元素的值都变为0:

#define N_VALUS 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &valus[N_VALUES]; vp > &valus[0];)
    {
        *--vp = 0;
    }
    return 0;
}

上述代码的另一种写法:

#define N_VALUS 5
int main()
{
    float values[N_VALUES];
    float* vp;
    for (vp = &valus[N_VALUES-1]; vp > &valus[0];vp--)
    {
        *vp = 0;
    }
    return 0;
}

第二种写法不推荐使用。 ::: danger 允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。 如下图数组arr内部指针p1只可以与p2比较,不可以与p3进行比较。 指针(2) :::


指针和数组
数组名
int main()
{
    int arr[] = { 0 };
    printf("%p\n", arr);
    printf("%p\n", &arr[0]);
    return 0;
}
000000CA285AFB54
000000CA285AFB54

由上可得,数组名是数组的首元素地址。(绝大多数情况下) ::: warning 两个例外情况:

  1. &arr:&数组名,数组名不是首元素地址,表示整个数组,&数组名 取出的是整个数组的地址。
  2. sizeof(arr):sizeof(数组名),数组名不是首元素地址,表示整个数组,sizeof(数组名)计算的是整个数组的大小。 ::: &+数组名:
    int main()
    {
     int arr[5] = { 0 };
     printf("%p\n", arr);
     printf("%p\n", arr + 1);
     printf("%p\n", &arr[0]);
     printf("%p\n", &arr[0] + 1);
     printf("%p\n", &arr);
     printf("%p\n", &arr + 1);
     return 0;
    }
    0000003159AFF5F8
    0000003159AFF5FC
    0000003159AFF5F8
    0000003159AFF5FC
    0000003159AFF5F8
    0000003159AFF60C
    上述代码中虽然arr和&arr[0]和&arr的值相同,但表示&arr的意义和其他两个不一样。 将三种取地址的方式都+1,arr和&arr[0]取出的地址相同,表示的意义是首元素地址+1,是下一个元素的地址,+1即跳过一个int类型,地址+4字节。 &arr + 1是数组地址+1表示跳过这个数组,地址+4*4即16字节。 ::: tip

整型地址+1表示跳出一个整型,数组地址+1表示跳过整个数组,以此类推。 ::: 既然可以把数组名当成地址存放到一个指针中,我们使用指针来访问一个就成为可能。即数组可以通过指针访问。 如下:

int main()
{
    int arr[10] = { 0 };
    int* p = arr;  //arr数组可以通过指针p进行访问
    int i = 0;
    for (i = 0; i < 10; i++)
    {
        *(p + i) = i;  //指针形式操作把元素重新赋值
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);  //数组形式打印元素

    }
    printf("\n");
    for (i = 0; i < 10; i++)
    {
        arr[i] = i + 1;  //数组形式操作把元素重新赋值
    }
    for (i = 0; i < 10; i++)
    {
        printf("%d ", *(p+i));  //指针形式打印元素

    }
    return 0;
}
0 1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8 9 10

二级指针
int main()
{
    int a = 10;
    int* pa = &a;  //pa是一级指针变量,int*是一级指针类型
    int** ppa = &pa;  //ppa是二级指针变量,int**是二级指针类型
    printf("%p\n", pa);  //三级等以后以此类推
    printf("%p\n", ppa);
    **ppa = 20;
    printf("%d", **ppa);  //解引用 
    return 0;
}
00000016B4B6F6B4
00000016B4B6F6D8
20

上述代码,pa类型中最右边的 * 表示pa是指针变量,前面的int表示pa所指向的对象a的类型是int。ppa类型中最右边的 * 表示ppa是指针变量,前面的int*表示ppa所指向的对象pa的类型是int * 。 如下图: 指针(2)


指针数组

指针数组是存放指针的数组。 如下可以将指针存放进指针数组中,并可以通过指针的解引用访问到原来的变量值。

int main()
{
    int a = 10;
    int b = 20;
    int c = 30;
    int* arr[3] = { &a,&b,&c };
    int i = 0;
    for (i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);
    }
    return 0;
}
10 20 30

指针(2)

评论区

索引目录