C语言指针

什么是指针:

    1、指针是一种数据类型,使用它可以定义指针变量,简称指针。

    2、指针变量中存储的是内存地址(整数,便签序号),可以通过它访问对应的内存

    3、以32位系统为例,指针变量的需要占用4字节内存空间,取值范围是:0x00000000~0xffffffff,每个整数代表一个字节,最多可以访问4G内存(64位系统的指针变量则需要占用8字节内存空间)。

    4、可以使用printf+%p 显示指针变量的值。

如何使用指针:

    定义指针变量:类型* 指针变量名;

        1、指针变量与普通变量的使用方法不同,因此为区别与普通变量,一般指针变量以p结尾。

        2、指针变量不能连续定义,一个*只能定义出一个指针变量。

            int* p1,p2; //  p1是指针变量,p2普通的int类型变量

            int *p1,*p2; // p1,p2都是指针变量

        3、指针变量中存储的整数只代表一个字节,当通过指针变量访问内存时,具体访问多少个字节由指针变量的类型决定,也就是说指针变量中存储的是一块内存的首地址。

        4、指针变量的默认值与普通变量一样是随机的(野指针),一般为了安全要给指针变量初始化,如果没有合适的内存地址可以先初始化NULL(空指针)。

    给指针变量赋值:指针变量 = 内存地址;

        // 定义指针变量时初始化

        int* p = # // 获取变量的地址赋值给指针变量,num变量的类型要与指针变量相同,否则编译器会产生警告,在解引用时也可能产生段错误

        // 定义完指针变量后再赋值

        p = malloc(4); // 把堆内存的地址赋值给指针变量,堆内存的使用后续会详细讲解

    根据指针变量中存储的内存地址访问内存(解引用):*指针变量

        int num = 1234;

        int* p = #

        *p <=> num; // *p 等价于 num

    指针变量解引用时,具体访问多个少字节数由指针变量的类型决定:

            char* p; // *p 访问一个字节

            short* p; // *p 访问两个字节

            double* p; // *p 访问4个字节

    指针变量解引用时可能出现段错误的原因:

        1、指针变量中存储的是无效的内存地址(不在maps文件里的地址范围内)。

        2、指针变量中存储的是具有只读权限的内存地址,通过指针变量修改内存时也会产生段错误。

        const int num = 1234;   // num使用的是代码段内存

        int main()

        {

            int* p = (int*)&num;

            *p = 6666; // 强行修改只读权限的内存,会产生段错误

        }

        3、指针变量中存储的是NULL,解引用时肯定产生段错误。

        总结:当指针变量解引用时产生段错误,要在指针变量赋值时改正。

为什么要使用指针:

    1、使用指针可以在函数之间共享变量

        使用全局变量可以在函数之间共享变量,但使用全局变量容易造成命名冲突,并且在程序运行期间全局变量所使用的data、bss、text内存不会被释放,这多使用全局变量容易造成内存浪费,尽量少使用全局变量,最好不用。

        当函数需要返回多个执行结果时,需要在函数之间共享变量,也就是共享变量地址,例如:scanf函数。

    2、适当的使用指针可以提高函数的传参效率

        c语言的函数传参是值传递(赋值、内存拷贝),比如:double,long double,long long,自定义类型(结构、联合、类),它们的字节数 > 4,如果直接传递变量的值则最少需要拷贝8字节内存数据,而传递变量的地址,只需要拷贝4字节内存数据。

        // 以下代码传递变量的地址比直接传递变量的值要节约一半的时间

        void func(long double* p)

        {

        }

        int main()

        {

            long double f = 3.14;

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

            {

                func(&f);

                f+=1;

            }

        }

    3、使用堆内存必须与指针变量配合

        当 int num; 语句时,系统会分配4字节内存(text、data、bss、stack),并让这4字节内存与变量名num建立联系,在之后的代码中使用num就相当于4字节内存,我们把这种功能叫做给内存取名字。

        而堆内存无法取名字,当向系统申请一块内存时,系统会返回这块内存的首地址,这块内存无法与变量名建立联系,也就是无法取名字,需要指针变量存储内存的地址以便之后使用这块堆内存,所以说堆内存必须与指针变量配合使用。

指针的运算:

    指针变量中存储的是代码内存地址的整数,理论所有整数能进行的运算,指针变量也能够运算,但只有以下三种运行对指针变量来说是有意义:

        注意:指针变量的进步值就是指针变量解引用时所访问的字节数。

        指针+n => 指针变量中的整数+进步值*n

        int* p = 0;

        p+3 得到的结果是12

        指针-n => 指针变量中的整数-进步值*n

        short* p = 8;

        p - 3 得到的结果2

        指针-指针 => (指针变量中的整数-指针变量中的整数)/进步值

        double *p1 = 36 ,*p2 = 20;

        p1 - p2 得到的结果是2

        指针变量加减整数相当于指针变量前后移动,指针减指针可以计算出两地址之间相隔多个对象(两个指针变量的类型必需相同)。

使用指针要注意的问题:

    空指针:值为NULL的指针变量叫空指针,系统规定不能对空指针解引用,否则就会产生段错误。

    如何避免空指针产生的段错误:对来历不明的指针解引用前,先判断是否是空指针。

        1、函数的返回值如果是指针类型,当函数执行出错、失败时会返回空指针。

        2、当函数的参数是指针时,我们无法保证调用者传递的指针是否是空指针,所以在解引用前要先判断。

        注意:大多数系统的NULL是0地址,但也有少数系统的NULL是1地址,所以判断空指针时

        if(!p) // 不通用,容易让人误会p是布尔类型

        {

        }

        if(p == NULL) // 当少写一个=时,编译器不会报错,就变成赋值NULL指针了,接下来肯定会产生段错误

        {

        }

        if(NULL == p) // 完美

        {

        }

    野指针:指针变量中存储的地址不知道是否"合法",这种指针叫野指针。

        对野指针解引用可能产生的后果:

            1、段错误

            2、脏数据

            3、一切正常

        无法判断一个指针变量是否是野指针,并且野指针产生的错误具有隐藏性、潜伏性、随机性,所以野指针比空指针的危害更大。

        如何避免野指针产生的危害:

            前提:所有野指针都是人为制造出来的,所以只要不制造野指针就能避免野指针产生的危害。

            1、定义指针变量时一定要初始化,宁可赋值为NULL,也不要产生野指针。

            2、函数不要返回局部变量的地址,因为随着函数的执行结束,属性局部变量的内存会被释放,,因此这种情况返回的地址就是野指针。

            3、当堆内存被释放后,与它配合的指针变量要及时赋值为NULL。

const与指针:

    const与指针配合使用一共有5种写法,3种功能:

    功能1:保护指针变量不被修改

        int* const p;

    功能2:保护指针变量指向的内存不被修改

        const int* p;

        int const * p;

    功能3:既保护指针变量也保护指向变量所指向的内存不被修改

        const int* const p;

        int const * const p;

指针与数组名:

    数组名:数组名就是个地址,所以数组作为函数的参数时才能蜕变成指针,它是数组内存块的首地址,也就是第一个元素的内存首地址,所以数组名可以使用解引用的方式访问数组中的每个元素。

    int arr[] = {1,2,3,4,5}; // arr就是int*类型的地址

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

    {

        printf("%d ",*(arr+i));

    }

    *(arr+i) <=> arr[i] 这两种写法是等价的。

    数组名可以使用指针的语法,指针也可以使用数组名的语法。

    int arr[] = {1,2,3,4,5}; // arr就是int*类型的地址

    int* p = arr;

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

    {

        printf("%d ",p[i]);

    }

    数组名与指针的相同点:

        1、它们都是地址,代表着一块内存的首地址。

        2、它们都可以使用 *、[] 访问内存中的数据。

    数组名与指针的不同点:

        1、数组名是常量,而指针是变量。

        2、指针变量有4字节的存储空间,而数组名就是地址。

            arr == &arr // 地址相同,但类型不同

        3、指针与它指向的内存之间是指向关系,数组名与内存之间是映射关系,数组名会替换成地址。

通用指针:

    通用指针就是void类型的指针,它能与任意类型的指针进行转换,缺点是不能解引用,必须先转换成其它类型的指针才能解引用,进步值是1。

    为什么使用通用指针:

        不同类型的指针不能互相赋值(因为它们解引用时所访问的内存字节数不同),编译器会产生警告。

        但不同类型的指针和数组会有一些通过操作,比如:清理内存、内存拷贝、数组排序等,当实现这类功能的函数时调用者可能提供任意类型指针或数组,这种情况就需要使用void类型的指针作为函数的参数。

指针函数:

    返回值是指针类型的函数,没有什么需要重点关注,主要为了防止别人装13。

函数指针:

    前提:一个函数就是一段代码,它会被翻译成二进制指令,存储在text内存段中,函数名就是它在text内存的首地址,我们只要知道函数的地址和函数的类型就能调用这个函数。

    函数指针:专门存储函数地址的指针变量。

    如何定义函数指针:

        1、照抄函数的声明。

        2、用小括号包括一个函数名。

        3、在函数名前面加*,末尾加fp,防止命名冲突。

    如何使用函数指针:

        1、用函数名给函数指针赋值。

        2、函数指针() 就可以调用函数。

    函数指针有什么用:

        可以把函数像数据一样在函数之间传递,这样可以做到让旧函数调用新函数,这种函数的调用模式叫回调模式,例如:qsort函数。

    void qsort(void *base, size_t nmemb, size_t size,

                  int (*compar)(const void *, const void *));

    1、在设计sqort函数时预留一个函数指针的参数compar,在qsort函数内会用compar调用函数,用于比较数组中的每个元素。

    2、当调用qsort函数时,调用者需要提数组首地址、数组长度、每个元素的字节数,根据这三个参数把数组以size字节为单位切割成nmemb个元素,然后调用compar对这些元素进行比较,根据比较的结果对数组进行排序。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

  • 指针是C语言中广泛使用的一种数据类型。 运用指针编程是C语言最主要的风格之一。利用指针变量可以表示各种数据结构; ...
    朱森阅读 8,848评论 3 44
  • 1.语言中变量的实质 要理解C指针,我认为一定要理解C中“变量”的存储实质, 所以我就从“变量”这个东西开始讲起吧...
    金巴多阅读 5,878评论 0 9
  • 这里对 C 语言的指针进行比较详细的整理总结,参考网络上部分资料整理如下,希望能对大家有所帮助。 指针概念 计算机...
    Veeupup阅读 1,792评论 0 0
  • 前言:复杂类型说明 要了解指针,多多少少会出现一些比较复杂的类型,所以我先介绍一下如何完全理解一个复杂类型,要理解...
    mingguang阅读 1,432评论 0 0
  • 1. 变量 不同类型的变量在内存中占据不同的字节空间。 内存中存储数据的最小基本单位是字节,每一个字节都有一个内存...
    C语言学习阅读 5,086评论 0 4

友情链接更多精彩内容