原文:深入理解C指针之三:指针和函数

  理解函数和指针的结合使用,需要理解程序栈。大部分现代的块结构语言,比如C,都用到了程序栈来支持函数的运行。调用函数时,会创建函数的栈帧并将其推到程序栈上。函数返回时,其栈帧从程序栈上弹出。

  在使用函数时,有两种情况指针很有用。一种是将指针作为参数传递给函数,函数可以修改指针所引用的数据,可以高效的传递大块数据。另一种是声明函数指针。

  程序的栈和堆

  程序的栈和堆是C程序的重要运行时元素。程序栈是支持函数执行的内存区域,通常和堆共享一块内存区域。通常程序栈在区域的下部,堆在上部。程序栈存放栈帧(stack frame),栈帧有时候也称为活跃记录或活跃帧。栈帧存放函数参数和局部变量。堆则管理动态内存。

  调用函数时,函数的栈帧被推到栈上,栈向上“长出”一个栈帧。当函数终止时,其栈帧从程序上弹出。栈帧所使用的内存不会被清理,但有可能被另一个栈帧覆盖。

  栈帧由以下几种元素组成:

  * 返回地址。函数完成后要返回的程序内部地址。

  * 局部数据存储。为局部变量分配的内存。

  * 参数存储。为函数参数分配的内存。

  * 栈指针和基指针。运行时系统用来管理栈的指针。

  栈指针通常指向栈顶部。基指针(帧指针)通常存在并指向栈帧内部的地址,比如返回地址,用来协助访问栈帧内部的元素。这两个指针都是运行时系统用来管理程序栈的地址。系统在创建栈帧时,将参数以跟声明时相反的顺序推到帧上,最后推入局部变量。C把块语句当做“微型”函数,会在适当的时候将其推入栈和从栈上弹出。

  参数和局部变量的精确地址可能会变化,不过顺序一般不变。这一点可以解释参数和变量分配内存的相对顺序。将栈帧推到程序栈上时,系统可能会好近内存,这种情况称为栈溢出。要记住每个线程通常都有自己的程序栈,一个或多个线程访问内存中的同一个对象可能会导致冲突。

  通过指针传递和返回数据

  传递参数(包括指针)的时候,传递的是它们的值的副本。当涉及大型数据结构时,传递参数的指针会更高效。传递对象的指针意味着不需要复制对象,但可以通过指针访问对象。

  用指针来传递数据的一个主要原因是函数可以修改数据。如果不希望函数修改数据,可以传递指向常量的指针。

  从函数返回指针有两种情况:一种是在函数内部为指针分配内存并返回指向内存的指针,另一种是函数的调用者负责指针内存的分配和释放。从函数返回指针可能存在几个潜在的问题:

  * 返回未初始化的指针。

  * 返回指向无效地址的指针。

  * 返回局部变量的指针。

  * 返回指针但是没有释放内存。

  设想一下在函数中声明一个指针并为该指针分配内存,但是在调用该函数的时候没有使用一个指针变量来接受函数的返回值,因此我们丢失了该分配内存的地址,导致内存泄露。

  局部数据指针是指当函数返回的时候,(非动态分配的)局部数据的地址也就无效了。动态分配的内存与此不同,存在于堆上,即使函数返回也不受影响。当变量为static,就会在栈帧外部为其分配内存,每次调用函数都会访问同一块内存。

  在将指针作为参数传递时,在使用之前判断指针是否为NULL是个好习惯。重复free一个指针会引发错误,在free之前也应判断指针是否为NULL。而且在释放之后将指针置为NULL。

  传递指针将允许我们修改指针指向的数据,如果我们想修改的是指针怎么办?当然是传递指向指针的指针了。

  函数指针

  函数指针是持有函数地址的指针。通过将函数指针作为参数传递,使我们避免将事件处理程序硬编码进函数里,更加灵活。

void (*foo)();

  void是返回类型,foo是指针变量名,后边的括号里是参数。

int(*fptr1)(int);
int square(int num)
{
return num*num;
}
fptr1 = square;
int n = 5;
printf(" result is %d\n", fptr1(n));

  使用函数的名字直接给函数指针赋值,它会把函数的地址赋给指针。也可以对函数名字使用取地址操作符,但是意思是一样的,在此处上下文中会忽略取地址操作符。可以为函数指针声明一个类型定义,通过类型定义来声明函数指针。

typedef int (*funcptr)(int);
funcptr ptr2;
ptr2 = square;

  传递函数指针只需把函数指针声明为参数即可。

funcptr ptr2;
ptr2 = square;
int compute(funcptr squ, int num)
{
return squ(num);
}
compute(ptr2,n);

  返回函数指针需要把函数的返回类型声明为函数指针。函数指针数组可以基于某些条件选择要执行的函数,声明这种数组只要把函数指针声明为数组的类型即可。

typedef int (*operation)(int,int);
operation operations[128] = {NULL};//使用NULL来初始化所有数组元素

  我们可以用相等和不等操作符来比较函数指针。不同的函数指针的长度不一定相等。虽然可以将一种函数指针转换为另一种函数指针,但是应该谨慎使用这种方法,因为运行时系统不会验证函数指针所用的参数是否正确。不应该把函数指针转换成void*指针。基本指针用作占位符,用来交换函数指针的值。一定要确保给函数指针传递的参数是正确的,否则会造成不确定行为。

  总体来说,函数指针允许应用程序根据不同的条件执行不同的函数,对于控制应用程序内的执行序列很有用。

  

深入理解C指针之三:指针和函数的更多相关文章

  1. callback回调函数理解 相当于this指针

    1.callback函数在微软的官方手册中是这样定义callback函数的:“callback函数是由应用程序定义而由操作系统调用的函数”.   凡是由用户设计而却由windows系统调用的函数,统称 ...

  2. 图说js中的this——深入理解javascript中this指针

    没搞错吧!js写了那么多年,this还是会搞错!没搞错,javascript就是回搞错! ………… 文章来源自——周陆军的个人网站:http://zhoulujun.cn/zhoulujun/html ...

  3. 函数指针与指针函数以及typedef

    c难于理解的是指针,其魅力之处也是指针,函数方法结构,化繁为简可以理解为:返回值 函数名(形参表),具体来说: 返回值:1.可以为空void 2.基本数据类型char short int long f ...

  4. 终于懂了:Delphi的函数名不是地址,取地址必须遵守Object Pascal的语法(Delphi和C的类比:指针、字符串、函数指针、内存分配等)good

    这点是与C语言不一样的地方,以前我一直都没有明白这一点,所以总是不明白:函数地址再取地址算怎么回事? ------------------------------------------------- ...

  5. [C++]函数指针与指针函数

    函数指针与指针函数,之前总是分不清,今天就好好把它两认认清楚. 先从概念说起,简单是理解. 函数指针:是指针.一个指向某一个函数入口地址的指针. 指针函数:是函数.一个返回值是指针的函数. 记忆: 从 ...

  6. 【转】 C++易混知识点2. 函数指针和指针函数的区别

    我们时常在C++开发中用到指针,指针的好处是开销很小,可以很方便的用来实现想要的功能,当然,这里也要涉及到指针的一些基本概念.指针不是基本数据类型,我们可以理解他为一种特殊类型的对象,他占据一定空间, ...

  7. [ 随手记 2 ] C/C++ 数组/指针/传数组到函数/指针数组/数组指针

    1.=================================================================== 1,数组是一块内存连续的数据.2,指针是一个指向内存空间的变 ...

  8. C++中的指针,指针函数和函数指针

    指针是C或C++中的一大难题,因此弄懂指针对C和C++的学习有很大的帮助,最近一直在研究指针,因此写一篇随笔把心得记录一下. 简单来说指针也是一种变量,只不过指针变量所存储的不是我们直观上看到的,而是 ...

  9. c++指向指针的指针与 c++指针作为函数参数传递问题

    一直搞不明白,c++中指针到底是个啥东西,今天遇到到c++,指向指针的指针的问题,突然有点开窍了. 举个例子: int main(int argc, char** argv){ int a[5]={1 ...

随机推荐

  1. SDUTOJ 1298 活动选择

    #include<iostream> #include<memory.h> using namespace std; int a[105],b[105],c[105],d[10 ...

  2. Mac下一个svn提交.a文件

    Mac在版本控制工具Versions非常方便.一些库有.a文件必须提交svn,commit它发现被忽略的,不能提交没有这个文件.然后通过设置.VEIW->SHOW IGNORED ITEMS选, ...

  3. ZOJ 2412 Farm Irrigation(DFS 条件通讯块)

    意甲冠军  两个农田管内可直接连接到壳体  他们将能够共享一个水源   有11种农田  管道的位置高于一定  一个农田矩阵  问至少须要多少水源 DFS的连通块问题  两个相邻农田的管道能够直接连接的 ...

  4. Apple Watch开发了一些细节和总结

    本文旨在总结最近Watch在发展中遇到的问题和细节 1.左右Watch真机调试问题 一般的情况下,你为IOS主应用创建了一个extention,比方说Today Extension .Xcode都会自 ...

  5. 十天学会php第五天

    学习目标:学会读取数据    先看两个函数:    1.mysql_query    送出一个 query 字符串. 语法   : int mysql_query(string query, int ...

  6. Android 应用程序启动过程源代码分析

    本文转自:http://blog.csdn.net/luoshengyang/article/details/6689748 前文简要介绍了Android应用程序的Activity的启动过程.在And ...

  7. swift 它们的定义TabBarItem

    1.效果图     2.NewsViewController.swift // // NewsViewController.swift // NavigationDemo // // Created ...

  8. table在 点击线条颜色

    效果图: <html> <head> <meta http-equiv="Content-Type" content="text/html; ...

  9. bonecp使用数据源

    bonecp.properties jdbc.driverClass=oracle.jdbc.driver.OracleDriver jdbc.jdbcUrl=jdbc:oracle:thin:@19 ...

  10. SQL Server审计功能入门:SQL Server审核 (SQL Server Audit)

    原文:SQL Server审计功能入门:SQL Server审核 (SQL Server Audit) 介绍 Audit是SQL Server 2008之后才有的功能,它能告诉你"谁什么时候 ...