指针常用的操作(C Primer Plus第六版)

简介: 指针常用的操作(C Primer Plus第六版)

学前服用


动画版的这个听完再去看书上的实例真的很不错!www.bilibili.com/video/BV1MJ…


一、指针操作


总结一下指针的常见操作


实例


#include <stdio.h>
int main(void)
{
    int urn[5] = {100, 200, 300, 400, 500}; //定义一个数组并赋值
    int *ptr1, *ptr2, *ptr3;                //定义三个指针
    ptr1 = urn;     //把数组首元素的地址赋给指针
    ptr2 = &urn[2]; //把数组第2个元素地址赋给指针
    printf("pointer value,dereferenced pointer,pointer address:\n");
    printf("ptr1 = %p,*ptr1 = %d,&ptr1 = %p\n", ptr1, *ptr1, &ptr1); //两个地址,一个值
    //指针的加法
    ptr3 = ptr1 + 4; //urn数组中第4个元素的地址
    printf("\nadding an int to a pointer:\n");
    printf("ptr1 + 4 = %p, *(ptr + 4) = %d\n", ptr1 + 4, *(ptr1 + 4));
    ptr1++; //递增指针
    printf("\nvalues after ptr1++:\n");
    printf("ptr1 = %p,*ptr1 = %d,&ptr1 = %p\n", ptr1, *ptr1, &ptr1);
    ptr2--; //递减指针
    printf("\nvalues after ptr2--:\n");
    printf("ptr2 = %p,*ptr2 = %d,&ptr2 = %p\n", ptr2, *ptr2, &ptr2);
    --ptr1; //恢复为初始值
    ++ptr2; //恢复为初始值
    printf("\nPointers reset to original values:\n");
    printf("ptr1 = %p,ptr2 = %p\n", ptr1, ptr2);
    //一个指针减去另一个指针
    printf("\nsubtracting one pointer from another:\n");
    printf("ptr2 = %p,ptr1 = %p,ptr2 - ptr1 = %td\n", ptr2, ptr1, ptr2 - ptr1);
    //一个指针减去一个整数
    printf("\nsubtracting an int from a poniter:\n");
    printf("ptr3 = %p,ptr3 - 2 = %p\n", ptr3, ptr3 - 2);
    return 0;
}
输出的结果为:
PS D:\Code\C\指针> cd "d:\Code\C\指针\" ; if ($?) { gcc 指针Demo05.c -o 指针Demo05 } ; if ($?) { .\指针Demo05 }
pointer value,dereferenced pointer,pointer address:
ptr1 = 000000000061FE00,*ptr1 = 100,&ptr1 = 000000000061FDF8
adding an int to a pointer:
ptr1 + 4 = 000000000061FE10, *(ptr + 4) = 500
values after ptr1++:
ptr1 = 000000000061FE04,*ptr1 = 200,&ptr1 = 000000000061FDF8
values after ptr2--:
ptr2 = 000000000061FE04,*ptr2 = 200,&ptr2 = 000000000061FDF0
Pointers reset to original values:
ptr1 = 000000000061FE00,ptr2 = 000000000061FE08
subtracting one pointer from another:
ptr2 = 000000000061FE08,ptr1 = 000000000061FE00,ptr2 - ptr1 = 2
subtracting an int from a poniter:
ptr3 = 000000000061FE10,ptr3 - 2 = 000000000061FE08

解析


  • 赋值:可以把地址赋给指针。例如,用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。在该例中,把urn数组的首地址赋给了ptr1,该地址的编号恰好是000000000061FE00。变量ptr2获得数组urn 的第3个元素(urn[2])的地址。注意,地址应该和指针类型兼容。也就是说,不能把 double类型的地址赋给指向int的指针,至少要避免不明智的类型转换。C99/C11已经强制不允许这样做。
  • 解引用: 运算符给出指针指向地址上储存的值。因此,ptr1的初值是100,该值储存在编号为0x7fff5fbff8d0的地址上。
    取址:和所有变量一样,指针变量也有自己的地址和值。对指针而言,s运算符给出指针本身的地址。本例中,ptr1储存在内存编号为000000000061FDF8 的地址上,该存储单元储存的内容是000000000061FE00,即urn的地址。因此&ptr1是指向ptrl的指针,而ptr1是指向utn[0]的指针。
  • 指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。因此ptrl +4与&urn [ 4]等价。如果相加的结果超出了初始指针指向的数组范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
  • 递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。因此,ptr1++相当于把ptr1的值加上4(我们的系统中int为4字节),ptr1指向urn[1]。现在ptr1的值是Ox7fff5fbff8d4(数组的下一个元素的地址),*ptr的值为200(即urn[ 1]的值)。注意,ptr1本身的地址仍是0x7fff5fbff8c8。毕竟,变量不会因为值发生变化就移动位置。

image.png

  • 指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。指针必须是第Ⅰ个运算对象,整数是第⒉个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。所以ptr3 - 2与&urn [2]等价,因为ptr3指向的是&arn[4]。如果相减的结果超出了初始指针所指向数组的范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
  • 递减指针:当然,除了递增指针还可以递减指针。在本例中,递减ptr3使其指向数组的第2个元素而不是第3个元素。前缀或后缀的递增和递减运算符都可以使用。注意,在重置ptr1和ptr2前,它们都指向相同的元素urn [ 1]。
  • 指针求差:可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。例如,**ptr2 - ptr1得2,意思是这两个指针所指向的两个元素相隔两个int,而不是2字节。**只要两个指针都指向相同的数组(或者其中一个指针指向数组后面的第1个地址),C都能保证相减运算有效。如果指向两个不同数组的指针进行求差运算可能会得出一个值,或者导致运行时错误。
  • 比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。


小知识点


解引用未初始化的指针 说到注意事项,一定要牢记一点:千万不要解引用未初始化的指针。例如,考虑下面的例子:

int * pt;   //未初始化的指针
*pt = 5;  //严重的错误

为何不行?第2行的意思是把5储存在pt 指向的位置。但是pt未被初始化,其值是一个随机值,所以不知道5将储存在何处。这可能不会出什么错,也可能会擦写数据或代码,或者导致程序崩溃.切记:创建一个指针时,系统只分配了储存指针本身的内存,并未分配储存数据的内存。因此,在使用指针之前,必须先用已分配的地址初始化它。例如,可以用一个现有变量的地址初始化该指针(使用带指针形参的函数时,就属于这种情况)。或者还可以使用第12章将介绍的malloc()函数先分配内存。无论如何,使用指针时一定要注意,不要解引用未初始化的指针!


相关文章
|
6月前
|
编译器 C语言
嵌入式C语言变量、数组、指针初始化的多种操作
嵌入式C语言变量、数组、指针初始化的多种操作
50 0
|
6月前
|
C++ 数据格式
野指针操作导致的崩溃:lua层持有已经被释放的node指针,再次操作导致崩溃
野指针操作导致的崩溃:lua层持有已经被释放的node指针,再次操作导致崩溃
129 0
|
6月前
|
算法 Linux C语言
【Linux系统编程】深入理解Linux目录操作:文件夹位置指针操作函数(telldir,seekdir,rewinddir)
【Linux系统编程】深入理解Linux目录操作:文件夹位置指针操作函数(telldir,seekdir,rewinddir)
83 0
|
5月前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
|
6月前
|
C++
在C和C++中,指针的算术操作
在C和C++中,指针的算术操作
|
6月前
|
存储 安全 编译器
使用unsafe库操作结构体的属性偏移和指针转换
【5月更文挑战第19天】Go语言是类型安全的,但通过`unsafe`包,可以进行结构体属性偏移量计算和指针转换。`unsafe.Offsetof`获取结构体字段的偏移量,`unsafe.Pointer`允许不同类型指针间的转换。然而,这可能导致类型安全屏障被绕过,若使用不当,会引发安全问题或panic。应谨慎使用`unsafe`,因为它不遵循GO 1兼容性准则。
57 0
使用unsafe库操作结构体的属性偏移和指针转换
|
5月前
|
编译器 vr&ar C语言
C primer plus 学习笔记 第10章 数组和指针
C primer plus 学习笔记 第10章 数组和指针
|
存储 Linux 编译器
Linux系统中指针的详细分析与操作
Linux系统中指针的详细分析与操作
135 1
|
6月前
|
C++
来自C++ Primer 5的函数指针的定义,调用等
来自C++ Primer 5的函数指针的定义,调用等
35 0
|
存储 Go
Golang指针的操作以及常用的指针函数
Golang指针的操作以及常用的指针函数
108 0
下一篇
无影云桌面