C语言指针基础知识实例讲解

对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小。比如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语言指针基本知识实例讲解的文章就介绍到这了,更多相关C语言指针基本知识内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C语言指针基础知识实例讲解

    对程序进行编译的时候,系统会把变量分配在内存单位中,根据不同的变量类型,分配不同的字节大小.比如int整型变量分配4个字节,char字符型变量分配1个字节等等.被分配在内存的变量,可以通过地址去找到,内存区每一个字节都有一个编号,地址也可以形象的理解成我们生活中的住址,通过住址找到每一个人所在的地方.指针作为一个变量用来存放地址,可以通过指针来改动变量. 上图就是一个简单的定义一个一级指针变量和利用指针改变变量数值的过程.int*表示整型指针,*p表示解引用操作,就是利用指针找到a的地址然后再改

  • R语言基础画图实例讲解

    1.plot函数 plot(x,y,xlim=c(0,100),ylim=c(0.4,1), type="o",lwd=2,col=2,pch=24,cex=1.5, yaxs="i",xaxs="i", xlab="Sample Ration(%)",ylab="Accuracy") x,y : 要画图的x轴内容和y轴内容 xlim,ylim: x轴和y轴的范围 type: 表示图的形状,是点.线.点线.

  • C语言实现多线程定时器实例讲解

    1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 每个任务都可以有自己的定时器,并且可以选择是否要重复执行 定义方便的任务函数实现接口 定时器可以由用户自定义何时启动和停止 提供等待功能,保证任务列表中的所有任务执行完成 提供任务列表的传参功能 2. API库介绍 void setTick(int val); 设置定时间的间隔时间tick,若设置tick为1000,且任务的定时器时间为1000,则任务会在1秒后执行,默认tick为1秒,

  • C语言指针应用简单实例

    C语言指针应用简单实例 这次来说交换函数的实现: 1. #include <stdio.h> #include <stdlib.h> void swap(int x, int y) { int temp; temp = x; x = y; y = temp; } int main() { int a = 10, b = 20; printf("交换前:\n a = %d, b = %d\n", a, b); swap(a, b); printf("交换

  • Webshell基础知识深入讲解

    一.什么是Webshell? 顾名思义,"web"的含义是显然需要服务器开放web服务,"shell"的含义是取得对服务器某种程度上操作权限.webshell常常被称为入侵者通过网站端口对网站服务器的某种程度上操作的权限.由于webshell其大多是以动态脚本的形式出现,也有人称之为网站的后门工具. webshell就是以asp.php.jsp或者cgi等网页文件形式存在的一种代码执行环境,也可以将其称做为一种网页后门.黑客在入侵了一个网站后,通常会将asp或php

  • R语言的xtabs函数实例讲解

    今天在做一个列联表独立性检验的时候,总是无法处理好要求的数据类型,偶然的机会,看到了xtabs()函数,感觉很适合用来做列联表,适合将一列数据转换成列联表. shifou <- c("yes","yes","no","no") xinbie <- c("nan","nv","nan","nv") freq <- c(34,38,2

  • R语言绘制空间热力图实例讲解

    先上图 R语言的REmap包拥有非常强大的空间热力图以及空间迁移图功能,里面内置了国内外诸多城市坐标数据,使用起来方便快捷. 开始首先安装相关包 install_packages("devtools") install_packages("REmap") library(devtools) library(REmap) 我们来试试其强大的城市坐标获取功能 city<- c("beijing","上海") get_geo_

  • R语言写2048游戏实例讲解

    2048 是一款益智游戏,只需要用方向键让两两相同的数字碰撞就会诞生一个翻倍的数字,初始数字由 2 或者 4 构成,直到游戏界面全部被填满,游戏结束. 编程时并未查看原作者代码,不喜勿喷. 程序结构如下: R语言代码: #!/usr/bin/Rscript #画背景 draw_bg <- function(){ plot(0,0,xlim=c(0,0.8),ylim=c(0,0.8),type='n',xaxs="i", yaxs="i") for (i in

  • R语言格式化输出sprintf实例讲解

    用%s替代字符串 name <- 'max' sprintf('my name is %s',name) [1] "my name is max" 用%d替代整数 age <- 18 sprintf('age:%d',age) [1] "age:18" d前面添加数字n,可以添加n-替代数字位数的空格 sprintf('age:%3d',age) [1] "age: 18" d前面添加0加上数字n,可以添加n-替代数字位数的0 spr

  • R语言向量知识点及实例讲解

    有常见的六种基本的向量类型 创建向量 设定recursive = T,c函数可以从其他数据结构中递归形成向量 > v <- c(.295, .300, .250, .287, list(.102, .200, .303), recursive = T) > v [1] 0.295 0.300 0.250 0.287 0.102 0.200 0.303 > typeof(v) [1] "double" > v <- c(.295, .300, .250

随机推荐