温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

C语言的指针详细介绍

发布时间:2021-08-11 19:01:19 来源:亿速云 阅读:169 作者:chen 栏目:大数据

这篇文章主要讲解了“C语言的指针详细介绍”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C语言的指针详细介绍”吧!

C语言的指针

C语言最臭名昭著的是就是其指针的使用,也是C语言的精华所在。很多人在学习C语言时,吐槽指针特别难以学习和使用。今天我们来探讨一下C语言指针的问题。

指针的引入

C语言一个特点就是一切皆内存,定义任何类型的变量,都要分配内存。在函数里定义变量,需要在栈内存上非配;定义全局变量,需要在静态内存区分配内存,就连定义函数,也要在静态区分配内存(这就是所谓的代码段存储区域);不固定大小的内存,需要在动态区分配内存。

有了内存,就需要操作内存,也就是读取或者写入内存数据。C语言的最简单的内存操作就是通过赋值操作符(=)实现。比如下面的代码,赋值给变量a,实际就是改变a变量占用的内存的数据; 甚至是用户自定义的结构体变量c,也可以直接使用赋值运算符将b占用的内存块的数据拷贝到c占用的内存块,来完成内存的读写操作。之

int a;
a = 6;

strcut a_t
{
   int m_a;
   char m_b;
   shor m_c;
};

struct a_t b;
a.m_a = 1;
a.m_b = 2;
a.m_c = 3;

struct a_t c;
//使用赋值运算符,将b里面所有成员的值赋给c里面的成员,即完成内存拷贝操作。
c = b;

所以可以这样操作,是因为C语言的变量包含内存的地址(通过&取)和内存大小(通过sizeof取),知道这两个信息,即使没有赋值运算符,也可以使用memcpy实现赋值的功能。比如下面的代码,完全可以不使用赋值运算符,但实现了赋值的目的。

#include <stdint.h>
static int g_test = 0;

int main()
{
    int a = 1;
    //这里我们使用memcpy操作内存,实现赋值的操作
    memcpy(&g_test, &a, sizeof(a));
    
    return 0;
}

但是这里有一个问题,即使C语言的函数参数传递只有值传递,就是说所有的变量通过参数传递,都会产生一份拷贝,唯一的办法就是传递变量的地址,这就产生的了用一个专门的类型来存储变量的内存地址的需求,指针应运而生了,这样做有下面的好处:

  • 用一个专门的类型保存内存地址,比用现有类型(int, unsigned int)更方便,因为指针确定了内存地址的大小。

  • 引入语法,为了方便操作内存:像上面的操作一个语句就可以解决了int *addr = &g_test; *addr = 1;这个可能还不太明显,但是到了结构体有很多成员变量,我们就要自己算每个成员的偏移量了,而指针可以直接找到每个成员的地址, 如指针的->符号操作,也可以使用[]进行内存的偏移操作。

二级指针

C语言引入了指针,又衍生出个二级指针的概念,直接弄晕了一部分新生,其实二级指针只是语法的体现,本质上也是指针,是用来存储内存地址的,这个本质没有变化。比如:

int a = 0;
//p1存储a的地址
int *p1 = &a;
//p2存储p1的地址
int **p2 = &p;

C语言的所有变量都是内存,都可以取地址,指针变量也不例外。不管是几级指针,都可以当做一级指针使用, 只要自己知道当前变量的意义然后处理好就可以了,比如下面的代码:

int alloc_mem(char *p, int size)
{
    char *tmp_p = (char *)malloc(size);
    //*p存储的是`p1`或者`p2`变量的内存地址,用memcpy直接将分配的内存地址付给到p指向的内存
    memcpy(p, &tmp_p, sizeof(char *));
    
    return 0;
}

int alloc_mem1(char **p, int size)
{
    *p = (char *)malloc(size);
        
    return 0;
}

int main()
{
    char *p1 = NULL;
    char *p2 = NULL;
    
    //利用c语言的强转
    alloc_mem((char *)&p1, 2);
    
    //利用二级指针
    alloc_mem1(&p2, 2);
}

上面的alloc_memalloc_mem1效果是一样的,只不过alloc_mem1使用的二级指针的概念。

指针与数组

指针与数组有者本质的区别,指针用来存储一块内存的地址,严格的来说是一块内存的起始地址,至于这块内存有多大,是不知道的,必须使用者显式的指定。比如memcpy这个函数,原型如下:

void *memcpy(void *dst, const void *src, size_t n);

dst指针指的是目标内存的起始地址,但是其指向的内存到底有多大,必须由参数n来给出,负责函数无法知道是否溢出了。而数组本身就是一块内存,包括数组的起始地址大小,比如我们定义一个数组:

//该数组的起始地址为&arr,大小为sizeof(arr)
int arr[32];

arr数组的地址为&a,数组的大小为sizeof(arr)。这里就产生一个问题,就是如何通过函数参数传递数组,C语言是不支持传递数组的,它会默认把数组转换为数组的起始地址的指针。所以C语言函数传递数组,需要额外带上数组的大小,以防止内存溢出的问题。实际上,只要传递指针参数给函数,都要带上这个指针指向内存的大小。

指针难学?

大部分人都认为指针难学,其实不在指针本身,而是指针指向的内容——内存。C语言要保存数据,可以在栈上分配,这部分内存的作用域仅限于函数内部;可以在静态区分配内存,这个区域的内存作用域是全局的,但仅限于确定大小的内存;可以在动态区分配内存,这部分的内存作用域是全局的,支持可变大小的内存。C语言程序存储数据的大部分内存都是在动态区分配的。这就产生了内存管理问题,包括单不限于:

  • 内存的分配

  • 内存的释放

  • 内存大小的检查

  • 内存分配失败的处理

  • 边界的处理:字符总是以0结尾

  • 读越界的处理:在有效内存之外读数据

  • 写越界的处理:在有效内存之外写数据

由于以上问题的存在,导致C语言不像javapython那样使用起来方便,因为这些语言都有垃圾回收器,不用使用者处理关于内存的问题。这才是C语言难学的根本。

结论
  • 指针是存储内存地址的数据类型,C语言可以根据内存地址操作内存。

  • 指针和数组不相同,指针存储的是内存的起始地址不包括内存的大小;数组既有起始地址,也有大小。

  • 通过函数传递指针,除了指针参数本身,还要带上指针指向内存的大小

  • 指针难学并非指针本身,而是内存管理难处理

感谢各位的阅读,以上就是“C语言的指针详细介绍”的内容了,经过本文的学习后,相信大家对C语言的指针详细介绍这一问题有了更深刻的体会,具体使用情况还需要大家实践验证。这里是亿速云,小编将为大家推送更多相关知识点的文章,欢迎关注!

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI