彻底搞定C指针

彻底搞定C指针
彻底搞定C指针

彻底搞定C指针

(完全版·修订增补版)

著=姚云飞

修订=丁正宇

前言

姚云飞先生的大作《彻底搞定C指针》是互联网上中文C/C++界内为数不多的专门阐述C指针问题的优秀文献资源之一。

正如书名所示,对于那些学习了C基础知识却始终对C指针不得要领的读者,或者那些已经长期被C指针困扰的读者,作者致力于彻底解决他们在这方面的难题。为了达到这个目的,作者运用了许多生动与亲切的例子,深入浅出地讲透了C指针的原理与机制,并辅以编程实践中最常用的惯例和技巧作为示范。

《彻底搞定C指针》是互联网上下载次数最多的针对C指针问题的中文资源之一。现在,经由修订者的重新修订、编辑与排版,本书的《完全版·修订增补版》全新登场。新版本中的技术用语更加清楚严谨,行文的结构层次更加分明,例子中的程序代码均通过编译以测试其精准性。修订者希望这份新的成果能够令各位读者在C编程方面获得更多的益处,同时也期待着读者们宝贵的反馈信息。

再次向姚云飞先生致敬!

目录

前言 (1)

目录 (2)

修订说明 (3)

A类:规范化 (3)

B类:更正 (3)

C类:明晰化 (4)

D类:编译器 (4)

第壹篇变量的内存实质 (5)

1.先来理解C语言中变量的实质 (5)

2.赋值给变量 (6)

3.变量在哪里?(即我想知道变量的地址) (7)

第贰篇指针是什么? (8)

1.指针是什么东西 (8)

第叁篇指针与数组名 (11)

1. 通过数组名访问数组元素 (11)

2.通过指针访问数组元素 (11)

3.数组名与指针变量的区别 (12)

4.声明指针常量 (13)

第肆篇const int *pi与int *const pi的区别 (14)

1. 从const int i 说起 (14)

2.const int *pi的语义 (15)

3. 再看int *const pi (16)

4.补充三种情况 (18)

第伍篇函数参数的传递 (20)

1.三道考题 (20)

2. 函数参数传递方式之一:值传递 (23)

3. 函数参数传递方式之二:地址传递 (26)

4. 函数参数传递方式之三:引用传递 (27)

第陆篇指向另一指针的指针 (30)

1. 回顾指针概念 (30)

2.指针的地址与指向另一指针地址的指针 (31)

3. 一个应用实例 (32)

第柒篇函数名与函数指针 (37)

1. 通常的函数调用 (37)

2.函数指针变量的声明 (38)

3.通过函数指针变量调用函数 (38)

4.调用函数的其它书写格式 (39)

5.定义某一函数的指针类型 (42)

6. 函数指针作为某个函数的参数 (44)

修订说明

A类:规范化

A1. C程序的代码段,以及行文中的代码的字体,均统一调整为Courier New,例如:- 类型说明符“int”、变量名“a”、地址表达式“&a”、函数名“Exchg1”

等等均作调整。

A2. 行为中的代码段,按一般行文处理缩进;代码段内部规整缩进。

A3. 规整C语句,例如:

- 语句中形如“a=b+c(x,y)”的,将调整为形如“a = b + c(x, y)”的

新样式,即在运算符、用来间隔参数的逗号等的旁边补足空白,令语句的可读

性更强。

- 补全语句结尾的“;”。

A4. 规整行文语序,令其更加通顺。

A5. 规整术语写法,例如:

- “C、C++”调整为“C/C++”。

B类:更正

B1. 更正术语,例如:

- “申明”调整为“声明”。

B2. 规整C技术用语,例如:

- “一个声明一整型指针变量的语句”调整为“一条声明一个指向整型变量

的指针的语句”。

B3. 规整C程序,例如:

-补全定义函数时的类型说明符“void”。

-补全main()程序段中的“return(0);”。

B4. 规整行文,例如:

- “真正有意义上的指针”调整为“具有真正‘指针’意义的变量”。

B5. 更正标点符号,例如:

-将行文里面中文/英文标点符号(全角/半角)混用、前后抵牾的情况进行更正。

-将程序里面有编程代码意义的符号(如双引号“"”)中被错误地录入为中文标点符号(全角)的,调整为英文(半角)的。

B6. 更正一些外语行文。

C类:明晰化

C1. 初次介绍(不一定是初次出现)专业术语时,用黑体字。

C2. 需要突出重点的地方,用粗体字。

C3. 重整程序,例如:

- 原作中某处的例1中的函数被定义名为“Exchg1()”,例2中的函数被

定义名为“Exchg2()”,那么,将例3中的函数名在定义中调整为

“Exchg3()”,使它们的逻辑关系更为明晰,易于读者阅读和理解。

- 循环体中的“printf("%d", a[i]);”调整为“printf("%d\n",

a[i])”。

C4. 规整行文分段,令其更合乎逻辑。

D类:编译器

D1. 著者声明相关代码“都是在VC6.0上实验”,而修订者则是使用gcc 3.4.2编译器测试相关代码。

第壹篇变量的内存实质

1.先来理解C语言中变量的实质

要理解C指针,我认为一定要理解C中“变量”的存储实质,所以我就从“变量”这个东西开始讲起吧!

先来理解理解内存空间吧!请看下图:

内存地址→ 6 7 8 9 10 11 12 13 ------------------------------------------------------------------------------------------------------- ···| | | | | | | | ···

------------------------------------------------------------------------------------------------------

如上图所示,内存只不过是一个存放数据的空间,就好像我的看电影时的电影院中的座位一样。电影院中的每个座位都要编号,而我们的内存要存放各种各样的数据,当然我们要知道我们的这些数据存放在什么位置吧!所以内存也要象座位一样进行编号了,这就是我们所说的内存编址。座位可以是遵循“一个座位对应一个号码”的原则,从“第1号”开始编号。而内存则是按一个字节接着一个字节的次序进行编址,如上图所示。每个字节都有个编号,我们称之为内存地址。好了,我说了这么多,现在你能理解内存空间这个概念吗?

我们继续看看以下的C/C++语言变量声明:

int i;

char a;

每次我们要使用某变量时都要事先这样声明它,它其实是内存中申请了一个名为i的整型变量宽度的空间(DOS下的16位编程中其宽度为2个字节),和一个名为a的字符型变量宽度的空间(占1个字节)。

我们又如何来理解变量是如何存在的呢。当我们如下声明变量时:

int i;

char a;

内存中的映象可能如下图:

内存地址→ 6 7 8 9 10 11 12 13 -------------------------------------------------------------------------------------------------------

···| | | | | | | | ···

------------------------------------------------------------------------------------------------------

变量名|→i←|→a ←|

图中可看出,i在内存起始地址为6上申请了两个字节的空间(我这里假设了int的宽度为16位,不同系统中int的宽度可能是不一样的),并命名为i。a在内存地址为8上申请了一字节的空间,并命名为a。这样我们就有两个不同类型的变量了。

2.赋值给变量

再看下面赋值:

i = 30;

a = ’t’;

你当然知道个两个语句是将30存入i变量的内存空间中,将“t”字符存入a变量的内存空间中。我们可以利用这样的形象来理解啦:

内存地址→ 6 7 8 9 10 11 12 13 -------------------------------------------------------------------------------------------------------

···| 30| 't'| | | | | ···

------------------------------------------------------------------------------------------------------- |→i←|→ a ←|

3.变量在哪里?(即我想知道变量的地址)

好了,接下来我们来看看&i是什么意思?

是取i变量所在的地址编号嘛!我们可以这样读它:返回i变量的地址编号。你记住了吗?

我要在屏幕上显示变量的地址值的话,可以写如下代码:

printf("%x", &i);

以上图的内存映象为例,屏幕上显示的不是i值30,而是显示i的内存地址编号6了。当然,在你的实际操作中,i变量的地址值不会是这个数了。

这就是我所认为的作为初学者应该能够想象到的变量存储的实质了。请这样理解吧!

最后总结代码如下:

main()

{

int i = 39;

printf(“%d\n”, i); /*①*/

printf(“%d\n”, &i); /*②*/

return(0);

}

现在你可知道①、②两个printf分别在屏幕上输出的是i的什么东西啊?

好啦!下面我们就开始真正进入指针的学习了。

第贰篇指针是什么?

1.指针是什么东西

指针,想说弄懂你不容易啊!我们许多初学指针的人都要这样感慨。我常常在思索它,为什么呢?其实生活中处处都有指针,我们也处处在使用它。有了它我们的生活才更加方便了。没有指针,那生活才不方便。不信?你看下面的例子。

这是一个生活中的例子:比如说你要我借给你一本书,我到了你宿舍,但是你人不在宿舍,于是我把书放在你的2层3号的书架上,并写了一张纸条放在你的桌上。纸条上写着:你要的书在第2层3号的书架上。当你回来时,看到这张纸条,你就知道了我借与你的书放在哪了。你想想看,这张纸条的作用,纸条本身不是书,它上面也没有放着书。那么你又如何知道书的位置呢?因为纸条上写着书的位置嘛!其实这张纸条就是一个指针了。它上面的内容不是书本身,而是书的地址,你通过纸条这个指针找到了我借给你的这本书。

那么我们C/C++中的指针又是什么呢?请继续跟我来吧,下面看一条声明一个指向整型变量的指针的语句:

int *pi;

pi是一个指针,当然我们知道啦,但是这样说,你就以为pi一定是个多么特别的东西了。其实,它也只过是一个变量而已。与上一篇中说的变量并没有实质的区别。不信你看下面图:

内存地址→ 6 7 8 9 10 11 12 13 ------------------------------------------------------------------------------------------------------- ···| 30| 't'| | | | | ···

-------------------------------------------------------------------------------------------------------

变量|→i←|→a←| |→pi←|

(说明:这里我假设了指针只占2个字节宽度,实际上在32位系统中,指针的宽度是4个字节宽的,即32位。)

由图示中可以看出,我们使用“int *pi”声明指针变量——其实是在内存的某处声明一个一定宽度的内存空间,并把它命名为pi。你能在图中看出pi与前面的i、a变量有什么本质区别吗?没有,当然没有!pi也只不过是一个变量而已嘛!那么它又为什么会被称为“指针”?关键是我们要让这个变量所存储的内容是什么。现在我要让pi成为具有真正“指针”意义的变量。请接着看下面语句:

pi = &i;

你应该知道&i是什么意思吧!再次提醒你啦:这是返回i变量的地址编号。整句的意思就是把i地址的编号赋值给pi,也就是你在pi里面写上i的地址编号。结果如下图所示:

内存地址→ 6 7 8 9 10 11 12 13 ------------------------------------------------------------------------------------------------------- ···| 30| 't'| | | 6| ···-------------------------------------------------------------------------------------------------------

变量|→i←|→ a ←| |→pi←|

你看,执行完pi=&i后,在图示中的内存中,pi的值是6。这个6就是i变量的地址编号,这样pi就指向了变量i了。你看,pi与那张纸条有什么区

别?pi不就是那张纸条嘛!上面写着i的地址,而i就是那个本书。你现在看懂了吗?因此,我们就把pi称为指针。所以你要记住,指针变量所存的内容就是内存的地址编号!好了,现在我们就可以通过这个指针pi来访问到i这个变量了,不是吗?看下面语句:

printf("%d", *pi);

那么*pi什么意思呢?你只要这样读它:pi的内容所指的地址的内容(嘻嘻,看上去好像在绕口令了),就是pi这张“纸条”上所写的位置上的那本“书”—— i 。你看,Pi的内容是6,也就是说pi指向内存编号为6的地址。*pi 嘛,就是它所指地址的内容,即地址编号6上的内容了,当然就是30这个“值”了。所以这条语句会在屏幕上显示30。也就是说printf("%d", *pi)等价于printf("%d", i) ,请结合上图好好体会吧!各位还有什么疑问?

到此为止,你掌握了类似&i、*pi写法的含义和相关操作吗?总的一句话,我们的纸条就是我们的指针,同样我们的pi也就是我们的纸条!剩下的就是我们如何应用这张纸条了。最后我给你一道题:程序如下。

char a,*pa;

a = 10;

pa = &a;

*pa = 20;

printf("%d", a);

你能直接看出输出的结果是什么吗?如果你能,我想本篇的目的就达到了。好了,就说到这了。Happy Study! 在下篇中我将谈谈“指针的指针”即对int **ppa;

中ppa的理解。

第叁篇指针与数组名

1. 通过数组名访问数组元素

看下面代码:

int i, a[] = {3,4,5,6,7,3,7,4,4,6};

for (i = 0; i <= 9; i++)

{

printf("%d\n", a[i]);

}

很显然,它是显示a 数组的各元素值。

我们还可以这样访问元素,如下:

int i, a[] = {3,4,5,6,7,3,7,4,4,6};

for (i = 0; i <= 9; i++)

{

printf("%d\n", *(a+i));

}

它的结果和作用完全一样。

2.通过指针访问数组元素

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};

pa = a; /*请注意数组名a直接赋值给指针pa*/

for (i = 0; i <= 9; i++)

{

printf("%d\n", pa[i]);

}

很显然,它也是显示a数组的各元素值。

另外与数组名一样也可如下:

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};

pa = a;

for (i = 0; i <= 9; i++)

{

printf("%d\n", *(pa+i));

}

看pa = a,即数组名赋值给指针,以及通过数组名、指针对元素的访问形

指针常量式看,它们并没有什么区别,从这里可以看出:数组名其实也就是指针。难道它

们没有任何区别?有,请继续。

3.数组名与指针变量的区别

请看下面的代码:

int i, *pa, a[] = {3,4,5,6,7,3,7,4,4,6};

pa = a;

for (i = 0; i <= 9; i++)

{

printf("%d\n", *pa);

pa++; /*注意这里,指针值被修改*/

}

可以看出,这段代码也是将数组各元素值输出。不过,你把循环体{}中的pa

改成a试试。你会发现程序编译出错,不能成功。看来指针和数组名还是不同的。其实上面的指针是指针变量,而数组名只是一个指针常量。这个代码与上面的代码不同的是,指针pa在整个循环中,其值是不断递增的,即指针值被修改了。数组名是指针常量,其值是不能修改的,因此不能类似这样操作:a++。

前面4、5节中pa[i],*(pa+i)处,指针pa的值是使终没有改变。所以变量指针pa与数组名a可以互换。

4.声明指针常量

再请看下面的代码:

int i, a[] = {3,4,5,6,7,3,7,4,4,6};

int *const pa = a; /* 注意const的位置:不是const int *pa */ for (i = 0; i <= 9; i++)

{

printf("%d\n", *pa);

pa++ ; /*注意这里,指针值被修改*/

}

这时候的代码能成功编译吗?不能。因为pa指针被定义为常量指针了。这时与数组名a已经没有不同。这更说明了数组名就是常量指针。但是……

int *const a = {3,4,5,6,7,3,7,4,4,6}; /*不行*/

int a[]={3,4,5,6,7,3,7,4,4,6}; /*可以,所以初始化数组时必定要这样。*/

以上都是在VC6.0上实验。

第肆篇const int *pi与int *const pi 的区别

1. 从const int i 说起

你知道我们声明一个变量时象这样int i ;这个i是可能在它处重新变赋值的。如下:

int i = 0;

/* . . . */

i = 20; /*这里重新赋值了*/

不过有一天我的程序可能需要这样一个变量(暂且称它变量),在声明时就赋一个初始值。之后我的程序在其它任何处都不会再去重新对它赋值。那我又应该怎么办呢?用const 。

/* . . . */

const int ic =20;

/* . . . */

ic = 40; /*这样是不可以的,编译时是无法通过,因为我们不能对const 修饰的ic重新赋值的。*/

/*这样我们的程序就会更早更容易发现问题了。*/

/* . . . */

有了const修饰的ic 我们不称它为变量,而称符号常量,代表着20这个数。这就是const 的作用。ic是不能在它处重新赋新值了。

认识了const 作用之后,另外,我们还要知道格式的写法。有两种:

const int ic = 20;

int const ic = 20;

它们是完全相同的。这一点我们是要清楚。总之,你务必要记住const 与int哪个写前都不影响语义。有了这个概念后,我们来看这两个家伙:const int *pi

int const *pi

按你的逻辑看,它们的语义有不同吗?呵呵,你只要记住一点:int 与const 哪个放前哪个放后都是一样的,就好比const int ic;与int const ic;一样。也就是说,它们是相同的。

好了,我们现在已经搞定一个“双包胎”的问题。那么

int *const pi;

与前两个语句又有什么不同呢?我下面就来具体分析它们的格式与语义吧!2.const int *pi的语义

我先来说说const int *pi是什么作用(当然int const *pi也是一样的,前面我们说过,它们实际是一样的)。看下面的例子:

/* 代码开始 */

int i1 = 30;

int i2 = 40;

const int *pi = &i1;

pi = &i2; /* 注意这里,pi可以在任意时候重新赋值一个新内存地

i2=*pi

址*/

i2 = 80; /* 想想看:这里能用*pi = 80来代替吗?当然不能!*/

printf("%d\n", *pi); /* 输出是80 */

/* 代码结束 */

语义分析:

看出来了没有啊,pi的值是可以被修改的。即它可以重新指向另一个地址的,但是,不能通过*pi来修改i2的值。这个规则符合我们前面所讲的逻辑吗?当然符合了!

首先const 修饰的是整个*pi(注意,我写的是*pi而不是pi)。所以*pi是常量,是不能被赋值的(虽然pi所指的i2是变量,不是常量)。

其次,pi前并没有用const 修饰,所以pi是指针变量,能被赋值重新指向另一内存地址的。你可能会疑问:那我又如何用const 来修饰pi呢?其实,你注意到int *const pi中const 的位置就大概可以明白了。请记住,通过格式看语义。哈哈,你可能已经看出了规律吧?那下面的一节也就没必要看下去了。不过我还得继续我的战斗!

3. 再看int *const pi

确实,int *const pi与前面的int const *pi会很容易给混淆的。注意:前面一句的const 是写在pi前和*号后的,而不是写在*pi前的。很显然,它是修饰限定pi的。我先让你看例子:

/* 代码开始 */

int i1 = 30;

int i2 = 40;

int *const pi = &i1;

/* pi = &i2; 注意这里,pi不能再这样重新赋值了,即不能再指向

另一个新地址。(第4行的注释)*/

/* 所以我已经注释了它。*/

i1 = 80; /* 想想看:这里能用 *pi = 80; 来代替吗?可以,这里可以通过*pi修改i1的值。(第5行的注释)*/

/* 请自行与前面一个例子比较。 */

printf("%d", *pi); /* 输出是80 */

/* 代码结束 */

语义分析:

看了这段代码,你明白了什么?有没有发现pi值是不能重新赋值修改了。它只能永远指向初始化时的内存地址了。相反,这次你可以通过*pi来修改i1的值了。与前一个例子对照一下吧!看以下的两点分析:

1)pi因为有了const 的修饰,所以只是一个指针常量:也就是说pi值是不可修改的(即pi不可以重新指向i2这个变量了)(请看第4行的注释)。

2)整个*pi的前面没有const 的修饰。也就是说,*pi是变量而不是常量,所以我们可以通过*pi来修改它所指内存i1的值(请看第5行的注释)。

总之一句话,这次的pi是一个指向int变量类型数据的指针常量。

我最后总结两句:

1)如果const 修饰在*pi前,则不能改的是*pi(即不能类似这样:*pi=50;赋值)而不是指pi。

2)如果const 是直接写在pi前,则pi不能改(即不能类似这样:pi=&i;赋值)。

请你务必先记住这两点,相信你一定不会再被它们给搞糊了。现在再看这两个声明语句int const *pi和int *const pi时,呵呵,你会头昏脑胀还是很轻松惬意?它们各自声明的pi分别能修改什么,不能修改什么?再问问

自己,把你的理解告诉我吧,可以发帖也可以发到我的邮箱(我的邮箱yyf977@https://www.360docs.net/doc/c31681541.html,)!我一定会答复的。

4.补充三种情况

这里,我再补充以下三种情况。其实只要上面的语义搞清楚了,这三种情况也就已经被包含了。不过作为三种具体的形式,我还是简单提一下吧!

情况一:int *pi指针指向const int i常量的情况

/* begin */

const int i1 = 40;

int *pi;

pi = &i1;/* 这样可以吗?不行,VC下是编译错。*/

/* const int 类型的i1的地址是不能赋值给指向int 类型地址的指针pi的。否则pi岂不是能修改i1的值了吗!*/

pi = (int *) &i1; /*这样可以吗?强制类型转换可是C所支持的。*/

/* VC下编译通过,但是仍不能通过 *pi = 80来修改i1的值。去试试吧!看看具体的怎样。*/

/* end */

情况二:const int *pi指针指向const int i1的情况

/* begin */

const int i1=40;

const int * pi;

pi=&i1;/* 两个类型相同,可以这样赋值。很显然,i1的值无论是通过pi还是i1都不能修改的。 */

/* end */

情况三:用const int *const pi声明的指针

/* begin */

int i;

const int * const pi=&i; /*你能想象pi能够作什么操作吗?pi 值不能改,也不能通过pi修改i的值。因为不管是*pi还是pi都是const 的。 */

/* end */

第伍篇函数参数的传递

1.三道考题

开讲之前,我先请你做三道题目。(嘿嘿,得先把你的头脑搞昏才行……唉呀,谁扔我鸡蛋?)

考题一,程序代码如下:

void Exchg1(int x, int y)

{

int tmp;

tmp = x;

x = y;

y = tmp;

printf("x = %d, y = %d\n", x, y);

}

main()

{

int a = 4,b = 6;

Exchg1(a, b);

printf("a = %d, b = %d\n", a, b);

return(0);

}

输出的结果为:

函数指针

方法 指针函数和函数指针的区别 关于函数指针数组的定义 为函数指针数组赋值 函数指针的声明方法为: 数据类型标志符 (指针变量名) (形参列表); 注1:“函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。例如: int func(int x); /* 声明一个函数 */ int (*f) (int x); /* 声明一个函数指针 */ f=func; /* 将func函数的首地址赋给指针f */ 赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。 注2:函数括号中的形参可有可无,视情况而定。 下面的程序说明了函数指针调用函数的方法: 例一、 #include int max(int x,int y){ return(x>y?x:y); } void main() { int (*ptr)(int, int); int a,b,c; ptr=max; scanf("%d%d",&a,&b); c=(*ptr)(a,b); printf("a=%d,b=%d,max=%d",a,b,c); } ptr是指向函数的指针变量,所以可把函数max()赋给ptr作为ptr的值,即把max()的入口地址赋给ptr,以后就可以用ptr来调用该函数,实际上ptr 和max都指向同一个入口地址,不同就是ptr是一个指针变量,不像函数名称那样是死的,它可以指向任何函数,就看你想怎么做了。在程序中把哪个

指向函数的指针详解

指向函数的指针 函数指针是指指向函数而非指向对象的指针。像其他指针一样,函数指针也指向某个特定的类型。函数类型由其返回类型以及形参表确定,而与函数名无关: bool (*pf)(const string &,const string &); 这个语句将pf声明为指向函数的指针,它所指向的函数带有两个const string &类型的形参和bool 类型的返回值。 注意:*pf两侧的括号是必需的。 1.typedef简化函数指针的定义: 函数指针类型相当地冗长。使用typedef为指针类型定义同义词,可将函数指针的使用大大简化: Typedef bool (*cmpfn)(const string &,const string &); 该定义表示cmpfn是一种指向函数的指针类型的名字。该指针类型为“指向返回bool类型并带有两个const string 引用形参的函数的指针”。在要使用这种函数指针类型时,只需直接使用cmpfcn即可,不必每次都把整个类型声明全部写出来。 2.指向函数的指针的初始化和赋值 在引用函数名但又没有调用该函数时,函数名将被自动解释为指向函数的指针。假设有函数: Bool lengthcompare(const string &,const string &); 除了用作函数调用的左操作数以外,对lengthcompare的任何使用都被解释为如下类型的指针:

bool (*)(const string &,const string &); 可使用函数名对函数指针初始化或赋值: cmpfn pf1=0; cmpfn pf2=lengthcompare; pf1=legnthcompare; pf2=pf1; 此时,直接引用函数名等效于在函数名上应用取地址操作符: cmpfcn pf1=lengthcompare; cmpfcn pf2=lengthcompare; 注意:函数指针只能通过同类型的函数或函数指针或0值常量表达式进行初始化或赋值。 将函数指针初始化为0,表示该指针不指向任何函数。 指向不两只函数类型的指针之间不存在转换: string::size_type sumLength(const string &,const string &); bool cstringCompare(char *,char *); //pointer to function returning bool taking two const string& cmpFcn pf;//error:return type differs pf=cstringCompare;//error:parameter types differ pf=lengthCompare;//ok:function and pointer types match exactly 3.通过指针调用函数 指向函数的指针可用于调用它所指向的函数。可以不需要使用解引用

C语言入门学习-C上机实验九要求

上机实验九函数综合练习一 【实验八参考答案见后】 目的和要求: (1)熟练掌握参数传递的要领; (2)掌握全局变量的使用要领; (3)了解静态局部变量的特点。 实验内容: 一、完善程序题 1.求10! 【提示:本程序利用静态局部变量保留每次调用函数的结果。】 #include #define N 10 ________________________________; main() {int i; long f; for(i=1;i<=N;i++) f=____________________________; printf("%d!=%ld\n",N,f); } long JC(int n) {______________________________; jc=jc*n; return jc; } 2.以下程序中的trap函数是一个用梯形法求定积分的通用函数,梯形法求定积分s的公式为: ∑-=+ + + = 1 n 1 i h * h)) *i f(a f(b))/2 ((f(a) s, n b a h - = 其中n为积分小区间数,以下程序调用trap函数求定积分,被积函数是:f(x)=x*x+3*x+2,且n=1000,a=0,b=4。(程序运行结果参考:53.333344)#include #include double mypoly(double x) {return(x*x+3.0*x+2.0); } double trap(double a,double b) {double t,h; int i,n=1000; t=0.5*(mypoly(a)+mypoly(b)); h=______________/(double)(n);

指针函数与函数指针的区别

指针函数与函数指针的区别 一、 在学习arm过程中发现这“指针函数”与“函数指针”容易搞错,所以今天,我自己想一次把它搞清楚,找了一些资料,首先它们之间的定义: 1、指针函数是指带指针的函数,即本质是一个函数。函数返回类型是某一类型的指针 类型标识符 *函数名(参数表) int *f(x,y); 首先它是一个函数,只不过这个函数的返回值是一个地址值。函数返回值必须用同类型的指针变量来接受,也就是说,指针函数一定有函数返回值,而且,在主调函数中,函数返回值必须赋给同类型的指针变量。 表示: float *fun(); float *p; p = fun(a); 注意指针函数与函数指针表示方法的不同,千万不要混淆。最简单的辨别方式就是看函数名前面的指针*号有没有被括号()包含,如果被包含就是函数指针,反之则是指针函数。来讲详细一些吧!请看下面 指针函数: 当一个函数声明其返回值为一个指针时,实际上就是返回一个地址给调用函数,以用于需要指针或地址的表达式中。 格式: 类型说明符* 函数名(参数) 当然了,由于返回的是一个地址,所以类型说明符一般都是int。 例如:int *GetDate(); int * aaa(int,int); 函数返回的是一个地址值,经常使用在返回数组的某一元素地址上。 int * GetDate(int wk,int dy); main() { int wk,dy; do { printf(Enter week(1-5)day(1-7)\n); scanf(%d%d,&wk,&dy); } while(wk<1||wk>5||dy<1||dy>7); printf(%d\n,*GetDate(wk,dy));

C语言基础知识(详细版)

C语言程序的结构认识 用一个简单的c 程序例子,介绍c 语言的基本构成、格式、以及良好的书写风格,使小伙伴对 c 语言有个 初步认识。 例1:计算两个整数之和的c 程序: #include main() { int a,b,sum; /* 定义变量a,b ,sum 为整型变量*/ a=20; /* 把整数20 赋值给整型变量a*/ b=15; /* 把整数15 赋值给整型变量b*/ sum=a+b; /* 把两个数之和赋值给整型变量sum*/ printf( “ a=%d,b=%d,sum=%d\n” ,a,b,sum); /* 把计算结果输出到显示屏上*/ } 重点说明: 1、任何一个c 语言程序都必须包括以下格式: main() { } 这是c 语言的基本结构,任何一个程序都必须包含这个结构。括号内可以不写任何内容,那么该程序将不执行任何结果。 2、main() - 在c 语言中称之为“主函数” ,一个c 程序有且仅有一个main 函数,任何一个c 程序总是从 main 函数开始执行,main 函数后面的一对圆括号不能省略。 3、被大括号{ }括起来的内容称为main 函数的函数体,这部分内容就是计算机要执行的内容。 4、在{ }里面每一句话后面都有一个分号(; ),在c 语言中,我们把以一个分号结尾的一句话叫做一个 c 语 言的语句,分号是语句结束的标志。 5、printf( “ a=%d,b=%d,sum=%d\n” ,a,b,sum); 通过执行这条c 语言系统提供给我们直接使用的屏幕输出 函数,用户即可看到运行结果,本程序运行后,将在显示器上显示如下结果: a=20,b=15,sum=35 6、#include 注意:(1)以#号开头 (2)不以分号结尾这一行没有分号,所以不是语句,在c 语言中称之为命令行,或者叫做“预编译处理命令” 。 7、程序中以/* 开头并且以*/ 结尾的部分表示程序的注释部分,注释可以添加在程序的任何位置,为了提高程序的可读性而添加,但计算机在执行主函数内容时完全忽略注释部分,换而言之就是计算机当做注释部分不存在于主函数中。 C程序的生成过程 C程序是先由源文件经编译生成目标文件,然后经过连接生成可执行文件。 源程序的扩展名为.c ,目标程序的扩展名为.obj , 可执行程序的扩展名为.exe 。

指向函数的指针

指向函数的指针 c/c++ 2010-11-20 13:17:02 阅读41 评论0 字号:大中小订阅首先看这个程序: #include using namespace std; void max(int a, int b) { cout<<"now call max("<b?a:b; cout<

我曾经写过一个命令行程序,有很多命令,于是构着了一个结构的数组,大概是这样 struct{ char *cmd_name; bool (*cmd_fun)(); }cmd_info_list[MAX_CMD_NUM]; 程序中得到一个用户输入的命令字符串后,就匹配这个数组,找到对应的处理函数。 以后每次添加一个命令,只需要加个函数,然后在这个数组中加一个记录就可以了,不需要修改太多的代码。 这可以算是一种用法吧。呵呵。 Windows 中,窗口的回调函数就用到了函数指针。 用VC向导 New Projects ----> Win32 Application ----> A typical "Hello World!" application 其中的WndProc 是WNDPROC 类型的函数typedef LRESULT (CALLBACK* WNDPROC)(HWND, UINT, WPARAM, LPARAM); WndProc 作为窗口的回调函数,用来填充WNDCLASSEX 结构。 WNDCLASSEX wcex; wcex.lpfnWndProc = (WNDPROC)WndProc; void ListTraverse(LinkList L,void (*visit)(int)) { Link p; p=L->next; while(p) { visit(p->data); p=p->next; } return OK; } void print(int c) { printf("%d",c); } ListTraverse(L,print); 这算是个例子吧??? #include #include #include double Add (double x, double y) { return x+y; } double Sub (double x, double y) { return x-y; } double Mul (double x, double y)

C语言指针教学中的知识点分析与总结-最新教育文档

C语言指针教学中的知识点分析与总结 C语言是一门重要的计算机基础课程,指针是C语言的精华。 而指针应用范围广、使用灵活等特点时常让初学者感到困惑。 用指针可以访问各种类型的数据,能够实现动态存储分配,提高编程效率,加深对数据存储方式的理解。本文从指针的基本概念,指针在数组、函数、字符串、动态存储分配等方面的应用入手,剖析指针与各部分基础知识相结合时的教学重点和难点。利用对比的方法指出初学者在学习指针过程中易混的概念及注意事项,有利于初学者对指针的理解和掌握。 1指针基本概念的理解 指针学习首先应掌握其基本概念。指针即地址、地址即指针。 程序运行过程中,变量、数组、函数等都存放在内存的存储单元中,每个存储单元都有地址。使用变量、数组、函数既可以直接访问,又可以利用其存储单元地址进行间接访问,这种间接访问便是借助指针来完成的。 1.1对指针类型的理解 理解指针概念要从指针类型入手,教师在教学中应着重讲述 指针类型的含义,以及与普通变量类型的区别。指针定义时的类型称为指针的基础类型,理解上应区别于普通变量的类型。如定义: 由上表可以看出,普通变量的数据类型决定了其占用内存单 元的字节数以及存放数值的范围。而指针变量不论其基础类型为何种类型,均占用4 个字节的存储空间。并且指针变量与普通变量最大的区别在于,指针变量存地址值,而普通变量存数值。 1.2指针运算符的理解 1.2.1对取地址符“ &”的理解 指针变量定义后应为其赋一个有效地址值,让它指向有效的存储空间。未赋值的指针变量称为“悬空”指针,使用悬空指针非常危险,可能会导致系统崩溃。为指针变量赋值时常要用到取地址运算符“ &”。令

函数指针的使用方法

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数的入口地址)传递给另一个函数,当这个函数指针被用来调用它所指向的函数时,我们就说这个函数是回调函数。 为什么要使用回调函数呢?我们先看一个小例子: Node * Search_List (Node * node, const int value) { while (node != NULL) { if (node -> value == value) { break; } node = node -> next; } return node; } 这个函数用于在一个单向链表中查找一个指定的值,返回保存这个值的节点。它的参数是指向这个链表第一个节点的指针以及要查找的值。这个函数看上去很简单,但是我们考虑一个问题:它只能适用于值为整数的链表,如果查找一个字符串链表,我们不得不再写一个函数,其实大部分代码和现在这个函数相同,只是第二个参数的类型和比较的方法不同。 其实我们更希望令查找函数与类型无关,这样它就能用于查找存放任何类型值的链表了,因此必须改变比较的方式,而借助回调函数就可以达到这个目的。我们编写一个函数(回调函数),用于比较两个同类型的值,然后把一个指向这个函数的指针作为参数传递给查找函数,查找函数调用这个比较函数来执行比较,采用这个方法,任何类型的值得都可以进行比较。 我们还必须给查找函数传递一个指向待比较的值的指针而不是值本身,也就是一个void *类型的形参,这个指针会传递给回调函数,进行最终的比较。这样的修改可以让我们传递指向任何类型的指针到查找函数,从而完成对任何类型的比较,这就是指针的好处,我们无法将字符串、数组或者结构体作为参数传递给函数,但是指向它们的指针却可以。 现在,我们的查找函数就可以这样实现: NODE *Search_List(NODE *node, int (*compare)(void const *, void const *) , void const *desired_value); { while (node != NULL) { if (compare((node->value_address), desired_value) == 0) { break; } node = node->next; } return node; }

C语言基础测试题

C语言基础测试题 一选择题(每题2分) 1. C语言程序的基本单位是( A )。 A. 函数 B. 过程 C. 语句 D. 子程序 2. 对于whil e语句,错误的说法是( C )。 A.用条件控制循环体的执行次数 B.循环体至少要执行一次 C.循环体有可能一次也不执行 D.循环体中可以包含若干条语句 3. 定义语句int a=3;则执行语句a+=a-=a*a后,变量a的值是(C)。 A.3 B.0 C.9 D.-12 4. 关于局部变量和全局变量的叙述中,错误的是(A)。 A.全局变量的重复赋值不会影响局部变量的使用。 B.主函数中定义的变量在整个程序中都是有效的 C.形式参数也是局部变量。 D.不论是局部变量还是全局变量,都以最近的一次赋值为准。 5. 已知:int a=13;那么:printf("%02d",a)结果是(A)。 A.13 B.013 C.01 D.03 6. 在main函数中调用scanf给变量a赋值的方法是错误的,原因是()。 int *p,a; p=&a; printf("input a:"); scanf("%d",*p); A. *p表示的是指针变量p的地址 B. *p表示的是变量a 的值,而不是变量a的地址 C. *p表示的是指针变量p的值 D. *p只能用来说明p是一个指针变量 7.若有以下定义,则对a数组元素地址的正确引用是()。 A.a+5 B.*a+1 C.&a+1 D.&a[0] 8. 若int k=4,a=3,b=2,c=1;则kb); A. 1,3 B. 1,4 C. 2,3 D. 2,4 二.填空题。(每空3分) 1.写一个宏MIN,这个宏输入两个参数并返回较小的一个_________。2.main函数中:for(int i=0; i<3; i++){printf("%d",i);}输出结果是_________。3.设int a=9,b=20;则printf("%d,%d",a--,--b);的输出结果是_________。

函数指针和指针函数的理解

我知道函数指针是指向函数的指针,指针函数还是指一个函数的返回值是一个指针,但下面的几道题还是感觉很迷惑。各位能否讲的详细点呢? (1)float(**def)[10]def是什么? (2)double*(*gh)[10]gh是什么? (3)double(*f[10])()f是什么? (4)int*((*b)[10])b是什么? 这样老感觉有点乱,有什么窍门可以记得并理解的清楚一点么? (1)def是一个指针,指向的对象也是一个指针,指向的指针最终指向的是10个float构成的数组. (2)gh是指针,指向的是10个元素构成的数组,数组的元素是double*类型的指针. (3)f是10个元素构成的数组,每个元素是指针,指针指向的是函数,函数类型为无参数且返回值为double.下面要讲的窍门的例子跟这个很类似. (4)b是指针,指向的是10个元素构成的数组,数组元素为int*类型的指针. 窍门如下: 如果我们碰到复杂的类型声明,该如何解析它?例如: char(*a[3])(int); a到底被声明为什么东东?指针?数组?还是函数? 分析时,从a最接近(按运算符优先级)处开始。我们看到a最接近符号是[]——注意:*比[]的优先级低。a后既然有[],那么a是数组,而且是包含3个元素的数组。 那这个数组的每个元素是什么类型呢?虽然数组a只含有a[0]、a[1]、a[2]三个元素,a[3]实际上已经越界,但在分析数组a的元素的类型时,我们正好需要形式上的元素a[3]。知道了a[3]的类型,就知道了a的元素的类型。a[3]是什么类型?是指针,因为它的前面有*.由此可知,数组a的元素是指针。 光说是指针还不够。对于指针,必须说出它指向的东东是什么类型。它指向的东东是什么,就看*a[3]是什么(a[3]是指针,它指向的东东当然是*a[3])了。继续按优先级观察,我们看到*a[3]后面有小括号,所以可以肯定*a[3]是函数。即数组a的元素是指向函数的指针。 指向的是什么类型的函数?这很明显,是入参为int、返回值为char的类型的函数。 至此解析完毕。

C语言指针在教学中的基础应用-最新教育资料

C语言指针在教学中的基础应用 C Language Pointer in the Teaching of Basic Application CHEN Jing-yan1 , CHEN Yue-bin2 (1.Medical College of Shantou University, Shantou 515041, China; 2.Zengcheng College of South China Normal University, Guangzhou 511363, China) :It can be said that the pointer is the soul of the C language, familiar and flexible use of the pointer, you can effectively represent complex data structures; dynamic memory; the use of string; arrays are very effective. Beginners often an error, how to enable students to acquire and in-depth learning pointer is a difficult and often requires some time. The following combination of experience, discuss and summarize the pointer problem. C语言是一种计算机程序设计语言,是目前最广泛使用的计 算机语言之一,也是各高校专业与非专业主要的计算机教学语 指针是C语言中最为复杂的一个部分,使用起来非常灵活, 因而学习时常出错,必须小心,多思考,多比较,在实践中把其掌握好。 1指针概念 指针变量简称指针,其实它也是变量,只不过里面存放的内

C语言学习笔记之函数指针

C/C++学习笔记之函数指针 函数指针的概念,在潭浩强先生的《C语言程序设计》这本经典的教程中提及过,在大多数情况下我们使用不到,也忽略了它的存在。函数名实际上也是一种指针,指向函数的入口地址,但它又不同于普通的如int*、double*指针,看下面的例子来理解函数指针的概念:view plain int function(int x,int y); int main(void) { int(*fun)(int x,int y); int a=10,b=20; function(a,b); fun=function; (*fun)(a,b);…… } 第一行代码首先定义了一个函数function,其输入为两个整型数,返回也为一个整型数(输入参数和返回值可为其它任何数据类型);后面又定义了一个函数指针fun,与int*或double*定义指针不同的是,函数指针的定义必须同时指出输入参数,表明这是一个函数指针,并且*fun也必须用一对括号括起来;并将函数指针赋值为函数function,前提条件是*fun 和function的输入参数和返回值必须保持一致,否则无法通过编译。可以直接调用函数function(),也可以直接调用函数指针,二者是等效的。 回调函数(callback)是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。简而言之,回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用为调用它所指向的函数时,我们就说这是回调函数。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。请看下面的例子: void f();//函数原型 上面的语句声明了一个函数,没有输入参数并返回void.那么函数指针的声明方法如下:void(*)(); 函数存放在内存的代码区域内,它们同样有地址,我们如何能获得函数的地址呢? 如果我们有一个int test(int a)的函数,那么,它的地址就是函数的名字,这一点如同数组一样,数组的名字就是数组的起始地址。 定义一个指向函数的指针用如下的形式,以上面的test()为例: int(*fp)(int a);//这里就定义了一个指向函数的指针 函数指针绝对不能指向不同类型,或者是带不同形参的函数,在定义函数指针的时候我们很容易犯如下的错误。 int*fp(int a);//这里是错误的,因为按照结合性和优先级来看就是先和()结合,然后变成了一个返回整型指针的函数了,而不是函数指针,这一点尤其需要注意! 例如函数原型为: int fun(int*,int); 则函数指针可以声明为:int(*pf)(int*,int);当然从上述例子看不出函数指针的优点,目的主要是想引出函数指针数组的概念。我们从上面例子可以得知,既然函数名可以通过函数指针加以保存,那们也一定能定义一个数组保存若干个函数名,这就是函数指针数组。正确使用函数指针数组的前提条件是,这若干个需要通过函数指针数组保存的函

C语言基础及指针

C语言基础及指针 我们知道, Android系统是基于linux开发,采用的是linux内核,Android APP开发大部分也要和系统打交道,只是Android FrameWork 帮我们屏蔽了系统操作,我们从Android 系统的分成结构可以看出, Android FrameWork是通过JNI与底层的C/C++库交互,例如:FreeType ,OpenGL ,SQLite ,音视频等等。 做Android为什么需要学习C/C++ ? 1. 企业需要,现在大部分招聘,基本上都会要求会JNI 2. 进阶需要,如果想要研究Android源码,那么不会C/C++ ,行不通 3. 音视频时代到来 (直播) ,音视频处理,很大部分都需要C/C++完成(音视频编解码) 那么下面就一起开始学习C吧 ! let's go C语言中的变量 编写C的时候 , 首先我们需要引入头文件,就像我们写JAVA的时候,需要引入包一样,但C语言他不会帮你自动引入,所有头文件,必须你自己手动引入,最常用的两个头文件是 #include #include C语言中的xxx.h的头文件,里面只有函数声明,没有函数实现,函数实现都在xxx.c里面。 在学习一门语言的时候,我们最先了解的就是变量,变量的定义,变量所占大小,下面我们看看C语言中的变量类型,和变量大小。和JAVA 不同的是, C语言变量的大小,是随着操作系统变化而变化的,不同的操作系统,变量的大小可能不一样。 下面我们来查看C语言的变量类型和变量类型的大小: void main() { int i = 90; printf("int 所占字节:%d\n", sizeof(int)); printf(" i 的值:%d\n", i);

C语言入门_nisy

C语言入门 Nisy 著

有不少人问,学C语言需要什么基础?我想说,你有兴趣吗?你有时间吗?如果两个都准备好了,就可以开始了。数学不太好能学好C语言吗?关系不大,就跟婴儿学说话前是否需要具备数学知识一个道理。 本书是写给C语言初学者的。文中对C语言的语法部分没有过多阐述,因为其他C语言书上已经对C标准讲的很详细了。而是侧重对C语言中一些司空见惯的内容重新进行了剖析,如对C语言的思考、程序是什么的思考、教学顺序的思考、对变量是什么的思考、对模块化程序设计的思考、对递归函数的思考、对指针的思考等。虽没有太多的语法内容,但本书还是拥有一个完整的C语言框架的,对C基础知识有一些了解的朋友可能读起来会更轻松一些。 写这个东西的目的很单纯,就是把自己对C的理解以及C教学方法的一些心得和大家做一个交流。C语言的书籍很多,大都将“Hello,World!”作为见面礼,本书中的第一节内容是先让学习者建立一个内存环境模型,因为我觉得一个C程序员脑海中若没有对内存环境建模是一件很荒唐的事情,C的精髓在于指针,连空间模型都模糊,谈指针又何必。由于时间等原因,书中对一些知识点没有详细讲述,如switch…case…和一维数组的应用等,几句话很难讲透彻,但又不影响初学者对C语言的理解,故本书没有详细阐述。 在本书的阅读上,建议大家还是顺序来读,从第一章开始读,就如同看一幅画,只看局部是没有什么意思的。写这个东西就是一个纯交流,若大家在阅读时发现文中的错误,还望不吝赐教。关于致谢部分,能完成这个文档,我需要感谢的人很多,由于担心文章质量可能会影响到他们的声誉,故这里不再一一写明,只在心中默默感谢。

Keil C51中函数指针使用注意事项

Keil C51中函数指针使用注意事项 在我们的代码中大量使用了函数指针。当函数指针用在Keil C51中时,一定要注意编译器自动生成的函数调用树通常是不正确的,需要手动调整。否则可能造成无法预知的后果。 这是因为,Keil C51编译器并不把函数参数和局部变量压入堆栈中,而是放在寄存器或固定的内存位置。 C51的编译器监视函数调用的嵌套顺序,把几个函数的变量放在同样固定的位置。在C51编译器中连接器会搜索所有函数中变量占用存储区间最多的函数,然后以这个函数的变量的占用空间开辟一片空间,其他函数的变量也放在该空间中,同时实现了变量的覆盖(无相互调用)与地址的共享。例如函数A占10个字节,函数B占20个字节,函数C占15个字节,如果它们之间没有相互调用则仅需20个字节就可以满足45个字节的变量需要。 正是由于所有函数的参数和局部变量的共享一个覆盖区,函数没有相互的调用时,在执行一个函数时,会将另一个函数的变量的存储区覆盖。如果函数有调用,那么不会覆盖原来函数的局部变量的区间。 调用树(call tree)是由Keil链接器自动生成的,用于描述函数的调用关系(调用树可通过编译生成的*.M51文件的OVERLAY MAP OF MODULE部分查看,该部分详细的说明了函数的调用关系以及对覆盖存储区的使用情况)。链接器通过分析调用树来确定哪些寄存器或内存位置是可安全覆盖的。这样两个不同时调用的函数就可以共享同一块内存用于传递参数和存储局部变量。但对于函数指针来说,编译器并不知道函数指针将指向哪个函数。这导致了调用树构造出错的可能,函数的参数和局部变量也可能被错误覆盖(例如,函数A通过函数指针调用了函数B,但编译器并不知道它们之间存在调用关系,所以认为它们是可以共享同一块内存的。这样当函数A调用了函数B,回到函数A后,函数A的参数和局部变量可能已经被改变了,再往下运行就出错了)。 对此,Keil提供了链接器OVERLAY伪指令,可让用户自行修改调用树,调整函数的调用关系。 删除调用关系,命令格式: OVERLAY (sfname-caller ~ sfname-callee) OVERLAY (sfname-caller ~ (sfname-callee, sfname-callee)) 举例:OVERLAY(?PR?_FUNC?DMAIN ~ (?PR?_FUNC_A?DMAIN,?PR?_FUNC_B?DMAIN)) 意思是从FUNC函数中删除对FUNC_A和FUNC_B的调用。 添加调用关系,命令格式: OVERLAY (sfname-caller ! sfname-callee) OVERLAY (sfname-caller ! (sfname-callee, sfname-callee)) 举例:OVERLAY(?PR?_MAIN?DMAIN ! (?PR?_FUNC_A?DMAIN,?PR?_FUNC_B?DMAIN)) 意思是添加FUNC函数对FUNC_A和FUNC_B的调用。 可在链接命令行输入命令。或在Keil集成开发环境中,在“BL51 Misc”-“Overlay”中填入()中的内容。

C语言从入门到精通(吐血分享)4

成功! 结构体、链表、文件 数组、字符串 函数、指针 三种结构化程序设计 三种数据类型、六大表达式 一、简单的程序 #include数学函数命令行 main()/*主函数*/ {/*左花括号,函数体的开始*/ int a,b,c;/*定义语句*/ a=3;/*执行语句*/ b=4; c=a+b; printf("a=%d,b=%d,c=%d\n",a,b,c); }/*右花括号函数体结束*/ 二、vc++6.0使用 1.新建 2.编译 3.组建 4.运行 三、标识符、常量和变量 1.标识符:

1)用途:命名 2)命名规则:a.字母、数字和下划线组成 b.第一个必须是字母或下划线 3)区分大小写 4)分类: a.关键字:代表固定含义,不能另作它用 b.预定义标识符:预先定义并具有特定含义的标识符 库函数的名字(printf)和预编译处理命令(define) 建议不另作它用 c.用户标识符 “见名知意” 2.常量 1)定义:程序运行过程中,其值不能被改变的量。 2)分类:整型常量、实型常量、字符型常量和字符串常量 3)符号常量 #define M5 #define PI 3.14159 s=PI*r*r; 3.变量 1)定义:程序运行过程中,其值可以改变的量 2)实质:一个变量实质上是代表了内存中的某个存储单元 3)原则:变量先定义后使用 四、三种基本数据类型

1.整型数据 1)整型常量 二进制八进制十进制十六进制 十进制--->二、八、十六进制:除n求余 二、八、十六进制--->十进制:按权展开 八进制、十六进制=》二进制 八进制转化二进制 1:0012:0103:0114:1005:1016:110 7:111 十六进制转化为二进制 1:00012:00103:00114:01005:0101 6:01107:01118:10009:1001a:1010 b:1011c:1100d:1101e:1110f:1111 2)整型变量 定义语句的格式 类型名变量名; 整型分类(有符号、无符号) signed unsigned 表2.1 <3>整型数据在内存中的存储 1>最小存储单位:位 2>1字节(B)=8个二进制位 3>在内存中,存储空间右端代表低端,左端代表高端 4>最高位是符号位

C语言指针章节选择题(新)

1. 若已定义: int a[]={0,1,2,3,4,5,6,7,8,9},*p=a,i; 其中0≤i≤9,则对a数组元素不正确的引用是 A) a[p-a] B) *(&a[i]) C) p[i] D) a[10] D 2. 已知指针p的指向如下图所示,则执行语句*--p;后*p的值是 a[0] a[1] a[2] a[3] a[4] ┌──┬──┬──┬──┬──┐ │10 | 20 | 30 │40 | 50 | └──┴──┴──┴──┴──┘ p↑ A) 30 B) 20 C) 19 D) 29 B 3. 下面程序运行时,如果从键盘上输入3,5<回车>,程序输出的结果是________。 main( ) { int a,b,*pa,*pb; pa=&a; pb=&b; scanf("%d,%d",pa,pb); *pa=a+b; *pb=a+b; printf("a=%d,b=%d\n",a,b); } A) a=13,b=13 B) a=8,b=8 C) a=8,b=13 D) 出错 C 4. 下面程序段的输出结果是________。 main( ) { char string1[20],string2[20]={"ABCDEF"}; strcpy(string1,string2); printf("%s\n",string1+3); } A) EF B) DEF C) CDEF D) ABCDEF B 5. 下列程序执行后的输出结果是________。 main()

{ int a[3][3],i,*pmul; pmul=&a[0][0]; for(i=0;i<9;i++) pmul[i]=i+1; printf("%d\n",a[1][2]); } A) 3 B) 6 C) 9 D) 随机数 B 6. 有如下程序段 int *p,a=10,b=1; p=&a; a=*p+b; 执行该程序段后,a的值为________。 A) 12 B) 11 C) 10 D) 编译出错 B 8. 以下函数返回a所指数组中最小值所在的下标值fun( int *a,int n) { int i,j=0,p; p=j; for(i=j;i

函数指针和指针函数

在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。 1.函数指针定义 函数类型(*指针变量名)(形参列表); “函数类型”说明函数的返回类型,由于“()”的优先级高于“*”,所以指针变量名外的括号必不可少,后面的“形参列表”表示指针变量指向的函数所带的参数列表。 例如: int (*f)(int x); double (*ptr)(double x); 在定义函数指针时请注意: 函数指针和它指向的函数的参数个数和类型都应该是—致的; 函数指针的类型和函数的返回值类型也必须是一致的。 2.函数指针的赋值 函数名和数组名一样代表了函数代码的首地址,因此在赋值时,直接将函数指针指向函数名就行了。 例如, int func(int x); /* 声明一个函数*/ int (*f) (int x); /* 声明一个函数指针*/ f=func; /* 将func函数的首地址赋给指针f */ 赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,

因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。

3.通过函数指针调用函数 函数指针是通过函数名及有关参数进行调用的。 与其他指针变量相类似,如果指针变量pi是指向某整型变量i的指针,则*p等于它所指的变量i;如果pf是指向某浮点型变量f的指针,则*pf 就等价于它所指的变量f。同样地,*f是指向函数func(x)的指针,则*f 就代表它所指向的函数func。所以在执行了f=func;之后,(*f)和func代表同一函数。 由于函数指针指向存储区中的某个函数,因此可以通过函数指针调用相应的函数。现在我们就讨论如何用函数指针调用函数,它应执行下面三步: 首先,要说明函数指针变量。 例如:int (*f)(int x); 其次,要对函数指针变量赋值。 例如:f=func; (func(x)必须先要有定义) 最后,要用(*指针变量)(参数表);调用函数。 例如:(*f)(x);(x必须先赋值) 【例】任意输入n个数,找出其中最大数,并且输出最大数值。 main() { int f(); int i,a,b; int (*p)(); /* 定义函数指针*/ scanf('%d',&a);

实验8 c语言非常棒还详细,适合初学者学习

实验8:善于使用指针 综合性实验 实验名称:善于使用指针 实验编号:C_8 小组成员:(姓名) 实验日期: 仪器设备:计算机 实验地点: 实验目的: 1、通过实验进一步掌握指针的概念,会定义和使用指针变量。 2、学会使用指针作为函数参数的方法。 3、能正确使用数组的指针变量。 4、能正确使用字符串的指针和指向字符串的指针变量。 5、能正确使用多维数组的指针变量。 6、能正确使用指向函数的指针变量。 7、能正确使用指针数组变量。 实验内容:空 实验原理: 定义和引用指针的方法;指针作为函数参数。 指针引用数组和字符串的方法。 多维数组的指针、指向函数指针和指针数组。 实验步骤: 实验原理记录及数据处理:(将所运行的程序代码填写在此处。) 结果与结论:程序是否能正常运行,如果不能存在哪些问题。 实验内容(一) 编程序并上机调试运行以下程序(都要求用指针处理)。 (1)输入3个整数,按由小到大的顺序输出,然后将程序改为:输入3个字符串,按由小到大的顺序输出。 ①先编写一个程序,以处理输入3个整数,按由小到大的顺序输出。运行程序,分析 结果。 ②把程序改为能处理3个字符串,按由小到大的顺序输出。运行此程序,分析结果。 ③比较以上两个程序,分析处理整数与处理字符串有什么不同?例如: (a)怎样得到指向整数(或字符串)的指针。 (b)怎样比较两个整数(或字符串)的大小。 (c)怎样交换两个整数(或字符串)。 (2)写一函数,求一个字符串的长度。在main函数中输入字符串,并输出其长度。 分别在程序中按以下两种情况处理: ①函数形参用指针变量; ②函数形参用数组名。 作分析比较,掌握其规律。 (3)将n个数按输入时顺序的逆序排列,用函数实现。 ①在调用函灵敏时用数组名作为函数实参。 ②函数实参改为用指向数组首元素的指针,形参不变。

相关文档
最新文档