对于本节的函数内容其实就没什么难点了,但是对于函数这节又涉及到了顺序点的问题,我觉得可以还是忽略吧。

本节知识点:

1.函数中的顺序点:f(k,k++);  这样的问题大多跟编译器有关,不要去刻意追求。  这里给下顺序点的定义:顺序点是执行过程中修改变量值的最后时刻。在程序到达顺序点的时候,之前所做的一切操作都必须反应到后续的访问中。

2.函数参数:函数的参数是存储在这个函数的栈上面的(对于栈可以看上篇文章<内存管理的艺术>),是实参的拷贝。

3.函数的可变参数:

a.对于可变参数要包含starg.h头文件。需要va_list变量,va_start函数,va_arg函数,va_end函数。对于其他函数没什么可说的,只有va_arg函数记得一定是按顺序的接收。这里有一个可变参数使用的小例子,代码如下:

#include <stdio.h>
#include <stdarg.h> float average(char c,int n, ...)
{
va_list args;
int i = 0;
float sum = 0; va_start(args, n); for(i=0; i<n; i++)
{
sum += va_arg(args, int);
} va_end(args);
printf("%c\n",c); return sum / n;
} int main()
{
char c = 'b';
printf("%f\n", average(c,5, 1, 2, 3, 4, 5));
printf("%f\n", average(c,4, 1, 2, 3, 4)); return 0;
}

b.可变参数的缺点:

(1).必须要从头到尾按照顺序逐个访问。

(2).参数列表中至少要存在一个确定的命名参数。

(3).可变参数宏无法判断实际存在的参数的数量。

(4).可变参数宏无法判断参数的实际类型。

(5).如果函数中想调用除了可变参数以外的参数,一定要放在可变参数前面。

注意:va_arg中如果指定了错误的类型,那么结果是不可预期的。

Ps:可变参数就说到这里,可变参数最经典的应用就是printf,等分析printf实现的时候,再好好写写。
4.函数与宏的比较:

注意:宏有一个函数不可取替的功能,宏的参数可以是类型名,这个是函数做不到的!代码如下:

#include <stdio.h>
#include <malloc.h> #define MALLOC(type, n) (type*)malloc(n * sizeof(type)) int main()
{
int* p = MALLOC(int, 5); int i = 0; for(i=0; i<5; i++)
{
p[i] = i + 1; printf("%d\n", p[i]);
} free(p); return 0;
}

5.函数调用中的活动记录问题:包含参数入栈、调用约定等问题。见上篇文章<内存管理的艺术>。

6.递归函数:递归函数有两个组成部分,一是递归点(以不同参数调用自身),另一个是出口(不再递归的终止条件)。

对于递归函数要有一下几点注意:

a.一定要有一个清晰的出口,不然递归就无限了。

b.尽量不要进行太多层次的递归,因为递归是在不断调用函数,要不断的使用栈空间的,很容易造成栈空间溢出的,然后程序就会崩溃的。比如说:对一个已经排好序的结构进行快速排序(因为快排需要使用递归,且对排好顺序的结构排序是最坏情况,递归层数最多),就很容易造成栈空间溢出。一般不同的编译器分配的栈空间大小是不一样的,所以允许递归的层数也是不一样的!

c.利用递归函数,实现不利用参数的strlen函数。代码如下:

/*这是自己实现  strlen*/
/*
#include <stdio.h>
#include <stdlib.h>
#include <assert.h> int my_strlen(const char *str)
{
int num=0;
assert(NULL!=str);
while(*str++)
{
num++;
}
return num;
}
int main(int argc, char *argv[])
{
char *a="hello world";
printf("%d\n",my_strlen(a));
return 0;
}*/ /*这是不用变量 实现strlen 使用递归*/
#include <stdio.h>
#include <stdlib.h>
#include <assert.h> int my_strlen(const char *str)
{
assert(NULL!=str);
return ('\0'!=*str)?(1+my_strlen(str+1)):0; //这里之所以 是加1 不是++ 我是担心顺序点的问题
} int main(int argc, char *argv[])
{
char *a="hello world";
printf("%d\n",my_strlen(a));
return 0;
}

7.使用函数时应该注意的好习惯:

a.如果函数参数是指针,且仅作为输入参数用的时候,应该加上const防止指针在函数体内被以外改变,如:

void str_copy(char *strDestination,const char *strSource);

b.在函数的入口处,应尽可能使用assert宏对指针进行有效性检查,函数参数的有效性检查是十分必要的。不用assert也行,if(NULL == p)也可以。

c.函数不能返回指向栈内存的指针

d.函数不仅仅要对输入的参数,进行有效性的检查 。还要对通过其他途径进入函数体的数据进行有效性的检查 ,如全局变量,文件句柄等。

e.不要在函数中使用全局变量,尽量让函数从意义上是一个独立的模块

f.尽量避免编写带有记忆性的函数。函数的规模要小,控制在80行。函数的参数不要太多,控制在4个以内,过多就使用结构体。

g.函数名与返回值类型在语言上不可以冲突,这里有一个经典的例子getchar,getchar的返回值是int型,会隐藏这么一个问题:

  char c;
c=getchar();
if(XXX==c)
{
/*code*/
}

如果XXX的值不在char的范围之内, 那c中存储的就是XXX的低8位 ,if就永远不会成立。但是getchar当然不会惹这个祸了,因为getchar获得的值是从键盘中的输入的,是满足ASCII码的范围的,ASCII码是从0~127的,是在char的范围里面的,就算是用char去接getchar的值也不会有问题,getchar还是相对安全的。可是对于fgetc和fgetchar就没这么幸运了,他们的返回值类型同样是int,如果你还用char去接收,那文件中的一些大于127的字符,就会造成越界了,然后导致你从文件中接收的数据错误。这里面就有隐藏的危险了!!!对于字符越界问题可以看看这篇文章<c语言深度解剖读书笔记(1.关键字的秘密)>8.陈正冲老师还有一个第七章是讲文件的我觉得总结不多,就写在这里了:

a.每个头文件和源文件的头部 ,都应该包含文件的说明和修改记录 。

b.需要对外公开的常量放在头文件中 ,不需要对外公开的常量放在定义文件的头部。

9.最终的胜利,进军c++(唐老师的最后一课,讲了些c++的知识,总结如下):

a.类与对象:

b.c++中类有三种访问权限:

(1).public  类外部可以自由访问

(2).protected   类自身和子类中可以访问

(3).private     类自身中可以访问

小例子:

#include <stdio.h>

struct Student
{
protected:
const char* name;
int number;
public:
void set(const char* n, int i)
{
name = n;
number = i;
} void info()
{
printf("Name = %s, Number = %d\n", name, number);
}
}; int main()
{
Student s; s.set("Delphi", 100);
s.info(); return 0;
}

注意:上面这段代码要在c++的编译器中进行编译,在gcc中会报错的,因为c标准中是不允许struct中有函数的。
        c.继承的使用,如图:

小例子:

#include <stdio.h>

struct Student
{
protected:
const char* name;
int number;
public:
void set(const char* n, int i)
{
name = n;
number = i;
} void info()
{
printf("Name = %s, Number = %d\n", name, number);
}
}; class Master : public Student
{
protected:
const char* domain;
public:
void setDomain(const char* d)
{
domain = d;
} const char* getDomain()
{
return domain;
}
}; int main()
{
Master s; s.set("Delphi", 100);
s.setDomain("Software");
s.info(); printf("Domain = %s\n", s.getDomain()); return 0;
}

Ps:以上6篇文章终于更新完了,是我对陈正冲老师的<c语言深度解剖>一书和国嵌唐老师c语言课程的一些总结和理解,针对c语言,后面的一点c++仅仅是做个笔记而已,望大牛莫喷~~~

C语言深度解剖读书笔记(6.函数的核心)的更多相关文章

  1. C语言深度解剖读书笔记

    开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有一段时间了,一直没有时间来写这篇博客.正巧还刚刚看完了国嵌唐老师的C语言视频,觉得两者是异曲同工,所以就把两者一起记录下 ...

  2. 【转】 C语言深度解剖读书笔记(1.关键字的秘密)

    本文出处:http://blog.csdn.net/mbh_1991/article/details/10149805 开始本节学习笔记之前,先说几句题外话.其实对于C语言深度解剖这本书来说,看完了有 ...

  3. c语言深度解剖(笔记)

    1.1最宽恒大量的关键字----auto 函数内部变量,限制作用域为这个 1.2.1最快的关键字---- register函数. 关键字请求编译器尽可能的将变量存在 CPU 内部寄存器中 1.2.2使 ...

  4. 深度学习读书笔记之RBM(限制波尔兹曼机)

    深度学习读书笔记之RBM 声明: 1)看到其他博客如@zouxy09都有个声明,老衲也抄袭一下这个东西 2)该博文是整理自网上很大牛和机器学习专家所无私奉献的资料的.具体引用的资料请看参考文献.具体的 ...

  5. 《神经网络算法与实现-基于Java语言》的读书笔记

    文章提纲 全书总评 读书笔记 C1.初识神经网络 C2.神经网络是如何学习的 C3.有监督学习(运用感知机) C4.无监督学习(自组织映射) Rreferences(参考文献) 全书总评 书本印刷质量 ...

  6. 《数据结构与算法分析:C语言描述》读书笔记

    我们数据结构的课用了这本英文教材,作者是Mark Allen Weiss.总体来说比<算法导论>简单很多,但内容上交集非常大.其实是因为去掉了大多数证明和数学,对于没有耐心看符号和公式的人 ...

  7. 《Go语言实战》读书笔记

    <Go语言实战>中文版pdf 百度网盘: https://pan.baidu.com/s/1kr-gMzaPAn8BFZG0P24Oiw 提取码: r6rt 书籍源码:https://gi ...

  8. 《C语言深度解剖》学习笔记之函数

    第6章 函数 1.编码风格 [规则6-1]每一个函数都必须有注释 [规则6-2]每个函数定义之后以及每个文件结束之后都要加若干个空行 [规则6-3]在一个函数体内,变量定义与函数语句之间要加空行 [规 ...

  9. JavaScript语言精粹读书笔记 - JavaScript函数

    JavaScript是披着C族语言外衣的LISP,除了词法上与C族语言相似以外,其他几乎没有相似之处. JavaScript 函数: 函数包含一组语句,他们是JavaScript的基础模块单元,用于代 ...

随机推荐

  1. javascript入门视频第一天 小案例制作 零基础开始学习javascript

    JavaScript 是我们网页设计师必备的技能之一.我们主要用javascript来写的是网页特效.我们从零基础开始学习javascript入门. 但是,好的同学刚开始不知道怎么学习,接触js,因此 ...

  2. 用Swift完成不同View Controller之间的切换

    之前用objective-c开发时,页面之间的切换很容易.其实用swift没有很大的变化,如果你是用storyboard完成的界面,基本上是同样的方式,只不过在代码部分写成swift风格的就行了. 今 ...

  3. java socket线程通信

    关于socket线程通信的一些知识整理 一般我们需要要让两台机子进行通信,需要创建一个Server 类,一个Client类,还需要创建一个线程类 server public class Server ...

  4. 文件操作中的FLAG(O_RDONLY,O_WRONLY )的值

    #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> void main(void) { ...

  5. SQLyog 注册码

    用户名: 随意填写 秘钥: ccbfc13e-c31d-42ce-8939-3c7e63ed5417a56ea5da-f30b-4fb1-8a05-95f346a9b20ba0fe8645-3916- ...

  6. 一、Nginx配置文件详解

    配置文件介绍 主要有两部分:分别是 main:主体部分 http{}:虚拟主机配置部分 配置指令主要以分号结尾:配置语法:directive value1 [value2 ....] 支持使用的变量 ...

  7. Linux -FHS 标准

    FHS (Filesystem Hierarchy Standard),其目的是让用户可以了解以安装软件通常放在那个文件下面. /bin 放置的是单用户维护模式下,能够被调用的命令.主要有cat,ch ...

  8. svn回滚版本1

    我刚开始使用SubVersion时, 想把svn代码库回滚到以前某个版本,  上网找来找去都找不到(那时文档比较少), 让我郁闷了好一阵子.  现在记录一下 SubVersion回滚的方法: 第一种方 ...

  9. jsp截取字符串

    <%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn"%> < ...

  10. <Win32_18>平滑的人物走动 —— 解决闪屏

    今天咋一看,发现很久没写博客了 的确,开学之后,写博客的时间越来越少了…… 今天来做一个比较实用的小应用——平滑的人物走动,同时解决常见的闪屏问题.实现透明位图 这些技术在游戏开发中是很常见的 --- ...