对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小。比如int整型变量分配4个字节,char字符型变量分配1个字节等等。被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方。指针作为一个变量用来存放地址,可以通过指针来改动变量。

上图就是一个简单的定义一个一级指针变量和利用指针改变变量数值的过程。int*表示整型指针,*p表示解引用操作,就是利用指针找到a的地址然后再改变a的值。

地址用%p打印,用十六进制表示,在打印时候输入指针变量p和取地址a得出的结果是相同的,证明了指针是用来存放地址的。

指针作为一个变量是有大小的,其大小在32位平台是4个字节,64位平台上是8个字节,大小与指针的类型无关。

上图以32位平台举例子,可以看到无论指针是整型、字符型、浮点型也无论一级指针还是二级指针,其在内存空间所占的大小都是4个字节。

指针有多种类别,按照级数来分便可以分为一级指针,二级指针,三级指针等等

一级指针是最基础的指针,指向的是创建的变量的地址。就类似于上图的前三个sizeof后面所写的。前文讲到指针也是一个变量,是用来存放地址的。既然是一个变量,就也要在内存开辟空间,开辟了空间就也会产生属于指针变量自己的地址。二级指针便是用来存放一级指针地址的。以此类推多级指针也是如此。

指针也可以根据指针指向的变量的数据类型来进行分类,有整型指针,字符指针,数组指针,函数指针等等

整型指针和字符指针

这两个是比较常见和容易理解的指针,依次用int*和char*表示,他们的区别在于指向变量类型不同,内存也不一样,在进行解引用操作时访问的字节大小也因为变量类型的区别会有所差异。整型指针可以访问4个字节,而字符指针只能访问1个字节。也就是说对整型指针变量解引用,一次可以操作一个整型,而对字符变量解引用一次只能操作一个字符。

较为特殊的char*p="hello"这并不是将整个字符串的地址传个了p,而是传了字符穿首元素‘h'的地址,可以通过’h‘的地址来找到整个字符串。此时出现char*p2=“hello”,p2和p代表的是同一处地址,因为hello是常量字符串,没有必要开辟两块不同的空间的来存储它。这是字符指针的一个特性。

void型指针

void型的指针可以接受任何类型的地址,但是不能对void型指针进行解引用操作。解引用操作要有特定的访问字节的数量,比如对整型指针解引用就是访问4个字节,字符型指针解引用就是访问1个字节,而void型指针无法确定访问字节个数,所以不能进行解引用操作。同时void*这种类型的指针也不能进行加减整数的操作,因为无法确定跳过的字节个数。

此图表示了void型指针可以接受任意类型的地址。

数组指针

这是一种指向数组的指针,例如int(*p)[10]这就是一个指向数组的指针,它指向的数组有10个元素,每个元素都是整型。给*p加上括号是因为p和[10]优先结合,这样的话就变成了一个数组而不是指针了。这个数组叫指针数组,int*p[10]这样的写法意思是一个有10个元素的数组,每一个元素都是整型指针,这和数组指针是两个不同的东西。

指向数组的指针里面存放的便是数组的地址,而非数组某个元素的地址,所以在定义数组指针时要用 &+数组名,而不是简单使用 数组名。

上图显示出&arr和arr的不同,虽然起始地址相同,但arr+1只让指针向后移动了一个元素的空间,而&arr+1让指针移动了一个数组的空间。

函数指针

函数指针顾名思义就是指向函数的指针,每个函数都有一个入口,这个入口的地址便是函数指针所指向的地址。函数地址的表示方法为 函数名或 &+函数名。例如一个函数叫Add,&Add和Add都是表示这个函数的地址没有什么差别。函数指针的写法是  函数的返回类型(*)(函数的参数),例如函数Add,其函数指针的写法就是int(*p)(int,int)=Add 。*p要加上括号来保证*和p的优先结合来形成一个指针变量,如果不加括号来优先结合,则会出现int* p(int,int)这样的写法,这就变成了函数的声明,这个函数的返回类型是int*,函数的名字叫p,函数的参数是2个整型和原先的函数指针不是同一个意思。

用函数指针调用函数时可以不加*这个解引用符号,因为这个符号将不会在程序运行的时候起到作用。

上图显示了*这个解引用符号在函数指针调用函数时候不起作用,以上的写法都可以用。

根据函数指针的相关知识,可以来看这两段代码。

代码1中间的 void(*)()是一个函数指针类型,将这个函数指针类型放在括号中,是强制类型转换的意思也就是把0强制转换成一个函数指针,强制类型转换这个部分简单写出来就是“(函数指针)0”是将0作为一个函数的地址,而最外层的括号(*函数的地址)()这个是解引用操作,也就是通过0这个地址,找到了0地址处所在的函数,并且进行调用。

代码2   内部的(int,void(*)(int))这一段表示的函数的参数,第一个参数是一个整型,第二个参数是一个函数指针类型,这个函数指针指向的函数的返回类型是void,参数类型是int。而这个函数的名字就是signal。解决了这个部分的内容,剩下的就是void(*)(int),去除里面的signal函数可以很明显地看出来这是一个函数指针。一个函数由三部分组成,返回类型,函数名,函数的参数。也就是说参数和函数名去掉之后,函数声明中就只剩下一个返回类型。此时,函数名和参数已经在前一步分析中得出,剩下的void(*)(int)便就是函数的返回类型,这个函数返回类型是也是一个函数指针。

这两个代码来自于书本《C陷阱和缺陷》。

函数指针和数组的结合实例,简易的计算器,这是函数指针数组的应用

数组传参


数组在传参的时候传的是首元素的地址,数组名表示首元素的地址。函数的形参可以用数组形式表示也可以用指针形式表示。

一维数组的传参比较简单,例如int arr[3]这个数组,形参可以直接使用int arr[]或者int arr[3]用数组形式表示形参,形参处的元素个数可以写也可以不写,因为元素个数在这里不起作用。或者用一级指针表示,int* arr这样就反映了指针传参传的是首元素地址。

二维数组传参相对比较复杂,由数组的知识可以知道,二维数组必须有规定的列数,所以要以数组形式传参的时候列数不能省略。

以指针形式传参,数组名仍然是首元素地址的意思,作为一个二维数组,首元素便是第一行的数组。比如int arr[3][5]这个二维数组的首元素是一个含有5个整型元素的数组,所以在传参的时候传的指针也应该是指向这个数组的指针。所以此时形参应该表示为int (*arr)[5],这表示一个数组指针,指向一个含有5个整型元素的数组,符合正确的传参规则。

回调函数

回调函数是把函数指针作为参数传给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由函数实现方直接调用,而是用另外一方或者特定条件下来调用。

比较常见的例子就是C语言里面的库函数快速排序,这里需要自己实现的比较函数,就用到了回调函数,int_cmp作为函数的指针充当了qsort的参数。

模拟实现qsort快速排序函数,冒泡排序的推广

C语言指针基本知识的更多相关文章

  1. C语言指针入门知识

    C语言指针往往是C语言学习过程中最困难的地方, 最近重新理解了一下C语言的指针知识, 在此整理一下, 如果有错误请留言指正. 对于刚入门的人来说, 指针涉及方方面面, 从简单的数组到结构体, 都会用到 ...

  2. C语言指针和数组知识总结(上)

    C语言指针和数组知识总结(上) 一.指针的基础 1.C语言中,变量的值能够通过指针来改变,打印指针的语句符号可以是:  %08x 2.指针的本质 指针的本质就是变量,那么既然是变量,那么一定会分配地址 ...

  3. C语言-指针

    C指针基础知识 C语言中,指针无疑是最令人头疼的.今天无事就来学学C语言的指针,在此留下点笔记,仅供个人参考. 首先要搞懂的是,指针是什么? 指针:是用来存放内存地址的变量. 不管是什么类型的指针,存 ...

  4. [转]C语言指针学习经验总结浅谈

    指针是C语言的难点和重点,但指针也是C语言的灵魂 . 这篇C语言指针学习经验总结主要是我入职以来学习C指针过程中的点滴记录.文档里面就不重复书上说得很清楚的概念性东西,只把一些说得不清楚或理解起来比较 ...

  5. C语言指针学习

    C语言学过好久了,对于其中的指针却没有非常明确的认识,趁着有机会来好好学习一下,总结一下学过的知识,知识来自C语言指针详解一文 一:指针的概念 指针是一个特殊的变量,里面存储的数值是内存里的一个地址. ...

  6. (转载)c语言指针学习

    前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...

  7. c语言指针学习【转】

    前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...

  8. 关于C语言指针的一些新认识(1)

    Technorati 标签: 指针,数组,汇编,C语言 前言 指针是C语言的精华,但我对它一直有种敬而远之的感觉,因为一个不小心就可能让你的程序陷入莫名其妙的麻烦之中.所以,在处理字符串时,我总是能用 ...

  9. C语言指针转换为intptr_t类型

    1.前言 今天在看代码时,发现将之一个指针赋值给一个intptr_t类型的变量.由于之前没有见过intptr_t这样数据类型,凭感觉认为intptr_t是int类型的指针.感觉很奇怪,为何要将一个指针 ...

随机推荐

  1. JDBC的操作步骤和实例()

    加载JDBC驱动程序 提供JDBC连接的URL 创建数据库的连接 创建一个Statement 执行SQL语句 处理结果 关闭JDBC对象 实例JdbcUtils 创建一个JDBC程序包含7个步骤: 1 ...

  2. Phoenix踩坑填坑记录

    Phoenix踩坑填坑记录 Phoenix建表语句 如何添加二级索引 判断某表是否存在 判断索引是否存在 Date类型日期,条件判断 杂项 记录Phoenix开发过程中的填坑记录. 部分原文地址:ph ...

  3. netty写Echo Server & Client完整步骤教程(图文)

    1.创建Maven工程 1.1 父节点的pom.xml代码(root pom文件) 1 <?xml version="1.0" encoding="UTF-8&qu ...

  4. Scala类型参数(泛型)与隐式转换

    package com.yz9 import org.junit.Test import scala.collection.mutable.ListBuffer class test { @Test ...

  5. P3164 [CQOI2014]和谐矩阵(高斯消元 + bitset)

    题意:构造一个$n*m$矩阵 使得每个元素和上下左右的xor值=0 题解:设第一行的每个元素值为未知数 可以依次得到每一行的值 然后把最后一行由题意条件 得到$m$个方程 高斯消元解一下 bitset ...

  6. P4755 Beautiful Pair (分治 + 主席树)

    题意:1e5的数组 计算有多少对 ai * aj <= max(ai ai+1...aj-1 aj) 题解:在处理这种涉及到区间极值的题时 好像是个套路分治 从级值中间分成两个区间 从区间短的那 ...

  7. Codeforces Round #582 (Div. 3) C. Book Reading

    传送门 题意: 给你n,k.表示在[1,n]这个区间内,在这个区间内找出来所有x满足x%k==0,然后让所有x的个位加到一起(即x%10),输出. 例如:输入10 2 那么满足要求的数是2 4 6 8 ...

  8. Educational DP Contest E - Knapsack 2 (01背包进阶版)

    题意:有\(n\)个物品,第\(i\)个物品价值\(v_{i}\),体积为\(w_{i}\),你有容量为\(W\)的背包,求能放物品的最大价值. 题解:经典01背包,但是物品的最大体积给到了\(10^ ...

  9. 数位dp整理 && 例题HDU - 2089 不要62 && 例题 HDU - 3555 Bomb

    数位dp: 数位dp是一种计数用的dp,一般就是要统计一个区间[li,ri]内满足一些条件数的个数.所谓数位dp,字面意思就是在数位上进行dp.数位的含义:一个数有个位.十位.百位.千位......数 ...

  10. 抓包 127.0.0.1 (loopback) 使用 tcpdump+wireshark

    直接使用 wireshark无法抓取 127.0.0.1环回的数据包,一种解决方法是先传到路由器再返回,但这样可能造成拥塞. Linux 先使用tcpdump抓包并输出为二进制文件,然后wiresha ...