一、函数

函数是一个包含完成一定功能的执行代码段。我们可以把函数看成一个"黑盒子", 你只要将数据送进去就能得到结果, 而函数内部究竟是如何工作的的, 外部程序是不知道的。外部程序所知道的仅限于输入给函数什么以及函数输出什么。函数提供了编制程序的手段,使之容易读、写、理解、排除错误、修改和维护。

计算1-n的和

#include "stdio.h"
void main()
{
//write once only once 只写一次
int i,s=; for(i=;i<=;i++)
{
s+=i;
}
printf("%d \n",s); s=;
for(i=;i<=;i++)
{
s+=i;
}
printf("%d \n",s); s=;
for(i=;i<=;i++)
{
s+=i;
}
printf("%d \n",s);
}

违背“write once only once 只写一次”原则,重复。

#include "stdio.h"

/*
函数定义
int 表示函数的返回值(return),没返回值void
sum 表示函数的名称,符合变量命名
(int n) 参数列表,可以有0-n个
*/
int sum(int n)
{
return n+;
} void main()
{
int x=sum();
x=sum(x);
printf("%d \n",x); //函数调用
printf("%d \n",sum());
}

102

10

#include "stdio.h"

/*
函数定义
int 表示函数的返回值(return),没返回值void
sum 表示函数的名称,符合变量命名
(int n) 参数列表,可以有0-n个
*/
int sum(int n)
{
int i,s=;
for(i=;i<=n;i++)
{
s+=i;
}
return s;
} void main()
{
printf("%d \n",sum());
printf("%d \n",sum());
printf("%d \n",sum());
}

1.1、内置函数

内置函数是指像printf、scanf这类的系统库函数,在编译的过程中,编译器会根据包含的头文件查找相应的库进行连接编译,如果没有包含头文件的话,系统里面有很多库文件,编译器就无法找到对应的文件进行编译。内置函数有许多,可以参考《C语言标准库函数大全.chm》

1.2、自定义函数

1.2.1、函数定义语法

函数类型  函数名(类型 参数名,类型 参数名...)
      {
          函数体;

       }

void show(char c)
{
printf("%c\n",c);
}

1.2.2、函数调用

void main()
{
show('a');
show('b');
show('c');
}

write once only once

#include "stdio.h"

//定义函数
//int 表示函数的返回值,无返回值void,return返回值
//sum 表示函数名称,与变量命名规则相同
//int n表示参数,可以有0-n个,形参
int sum(int n)
{
int s=,i;
for(i=;i<=n;i++)
{
s+=i;
}
return s;
} void p(int n){
printf("%d \t",n);
} void l()
{
printf("\n");
} void main()
{
p(sum());
l();
p(sum());
l();
p(sum());
}

1.3、递归

函数直接或间接的调用自己叫递归。

int f(int n)
{
if(n==) return ;
return f(n-)+n;
} void main()
{
printf("%d",f());
}

f(n)=f(n-1)+n;

斐波那契数列(Fibonacci sequence),又称黄金分割数列

//1 1 2 3 5 8 13 21 34
/* function
f(n)
f(5)=5
f(6)=8
f(7)=13
f(n)=f(n-1)+f(n-2)
f(7)=f(6)+f(5)
13=8+5
*/
int f(int n) //n第几位
{
if(n== || n==) return ;
return f(n-)+f(n-);
} void main()
{
printf("%d",f());
}

二、指针

指针是C语言中非常重要的数据类型,了解指针有助于更加深刻理解C语言。

2.1、指针的概念

指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址。 ”指针是一种保存变量地址的变量“,指针是一个特殊的变量

#include "stdio.h"
void main()
{
int n=;
int *p=&n; printf("%d \n",p); //输入逻辑地址
printf("%p \n",p); //%p 是以16进制的形式输出内存地址
}

2.2、指针的定义

int *p;
char *q;

"*"是一个说明符,用来说明这个变量是个指针变量,是不能省略的,但它不属于变量名的一部分
前面的类型标识符表示指针变量所指向的变量的类型,而且只能指向这种类型的变量

2.3、指针的初始化

// 定义int类型的变量a
int a = ; // 定义一个指针变量p
int *p; // 将变量a的地址赋值给指针变量p,所以指针变量p指向变量a
p = &a;
// 定义int类型的变量a
int a = ; // 定义一个指针变量p
// 并将变量a的地址赋值给指针变量p,所以指针变量p指向变量a
int *p = &a;

2.4、指针运算符

char a = ;
printf("修改前,a的值:%d\n", a); // 指针变量p指向变量a
char *p = &a; // 通过指针变量p间接修改变量a的值
*p = ; printf("修改后,a的值:%d", a);

取出指针所指向变量的值

char a = ;

char *p;
p = &a; char value = *p;
printf("取出a的值:%d", value);

练习:定义一个函数,使用指针完成两个数交换。

/* Note:Your choice is C IDE */
#include "stdio.h"
void main()
{
int n=;
int *p=&n; //定义指针p,可以指向int类型变量的地址
//&n是取出n的地址 printf("%d\n",p);
printf("%p\n",p); printf("%d \n",*p); //取出地址对应的值 *p=; //将p指向的地址对应的值修改为55 printf("%d\n",n); printf("%d \n",*p); //取出地址对应的值 }

2.5、指针与数组

int array[10]={0,1,2,3,4,5,6,7,8,9},value;

array就是数组的地址。

value=array[0];//也可写成:value=*array; 
value=array[3];//也可写成:value=*(array+3); 
value=array[4];//也可写成:value=*(array+4);

int array[10]; 
int (*ptr)[10];   //指向地址的地址
ptr=&array; 
上例中ptr是一个指针,它的类型是int (*)[10],他指向的类型是int [10] 
,我们用整个数组的首地址来初始化它。在语句ptr=&array中,array代表数组本 
身。

int arrays[3]={1,2,3};
int (*ptr)[3];
ptr=&arrays;

printf("%d\n",**ptr+3);  //指向地址的地址

三、宏

C语言中提供的宏定义命令,其主要目的是为程序员在编程时提供一定的方便,并能在一定程度上提高程序的运行效率。

3.1、简单宏定义

[#define指令(简单的宏)]  #define 标识符替换列表

替换列表是一系列的C语言记号,包括标识符、关键字、数、字符常量、字符串字面量、运算符和标点符号。当预处理器遇到一个宏定义时,会做一个 “标识符”代表“替换列表”的记录。在文件后面的内容中,不管标识符在任何位置出现,预处理器都会用替换列表代替它。

#define STE_LEN 80
#define TRUE 1
#define FALSE 0
#define PI 3.14159

3.2、带参数的宏

带参数的宏定义有如下格式:
[#define指令—带参数的宏]  #define 标识符(x1, x2,…,xn)替换列表

其中x1, x2,…,xn是标识符(宏的参数)。这些参数可以在替换列表中根据需要出现任意次。
在宏的名字和左括号之间必须没有空格。如果有空格,预处理器会认为是在定义一个简单的宏,其中(x1,x2,…,xn)是替换列表的一部分。

例如,假定我们定义了如下的宏:

#define MAX(x,y)    ((x)>(y) ? (x) :(y))
#define IS_EVEN(n) ((n)%2==0)

现在如果后面的程序中有如下语句:

i = MAX(j+k, m-n);
if (IS_EVEN(i)) i++;

预处理器会将这些行替换为

i = ((j+k)>(m-n)?(j+k):(m-n));
if (((i)%==)) i++;

200

#define PRINT_INT(x)    printf("%d\n", x)

3.3、C语言中常用的宏

01: 防止一个头文件被重复包含
#ifndef COMDEF_H
#define COMDEF_H
//头文件内容
#endif

02: 重新定义一些类型
防止由于各种平台和编译器的不同,而产生的类型字节数差异,方便移植。

typedef unsigned char boolean; /* Boolean value type. */
typedef unsigned long int uint32; /* Unsigned 32 bit value */
typedef unsigned short uint16; /* Unsigned 16 bit value */
typedef unsigned char uint8; /* Unsigned 8 bit value */
typedef signed long int int32; /* Signed 32 bit value */
typedef signed short int16; /* Signed 16 bit value */
typedef signed char int8; /* Signed 8 bit value */

//下面的不建议使用
typedef unsigned char byte; /* Unsigned 8 bit value type. */
typedef unsigned short word; /* Unsinged 16 bit value type. */
typedef unsigned long dword; /* Unsigned 32 bit value type. */
typedef unsigned char uint1; /* Unsigned 8 bit value type. */
typedef unsigned short uint2; /* Unsigned 16 bit value type. */
typedef unsigned long uint4; /* Unsigned 32 bit value type. */
typedef signed char int1; /* Signed 8 bit value type. */
typedef signed short int2; /* Signed 16 bit value type. */
typedef long int int4; /* Signed 32 bit value type. */
typedef signed long sint31; /* Signed 32 bit value */
typedef signed short sint15; /* Signed 16 bit value */
typedef signed char sint7; /* Signed 8 bit value */

03: 得到指定地址上的一个字节或字

#define MEM_B(x) (*((byte *)(x)))
#define MEM_W(x) (*((word *)(x)))

04: 求最大值和最小值

#define MAX(x,y) (((x)>(y)) ? (x) : (y))
#define MIN(x,y) (((x) < (y)) ? (x) : (y))
05: 得到一个field在结构体(struct)中的偏移量

#define FPOS(type,field) ((dword)&((type *)0)->field)

06: 得到一个结构体中field所占用的字节数
#define FSIZ(type,field) sizeof(((type *)0)->field)
07: 按照LSB格式把两个字节转化为一个Word

#define FLIPW(ray) ((((word)(ray)[0]) * 256) + (ray)[1])

08: 按照LSB格式把一个Word转化为两个字节
#define FLOPW(ray,val) (ray)[0] = ((val)/256); (ray)[1] = ((val) & 0xFF)
09: 得到一个变量的地址(word宽度)

#define B_PTR(var) ((byte *) (void *) &(var))
#define W_PTR(var) ((word *) (void *) &(var))

10: 得到一个字的高位和低位字节
#define WORD_LO(xxx) ((byte) ((word)(xxx) & 255))
#define WORD_HI(xxx) ((byte) ((word)(xxx) >> 8))

11: 返回一个比X大的最接近的8的倍数
#define RND8(x) ((((x) + 7)/8) * 8
12: 将一个字母转换为大写

#define UPCASE(c) (((c)>='a' && (c) <= 'z') ? ((c) – 0×20) : (c))
13: 判断字符是不是10进值的数字

#define DECCHK(c) ((c)>='0' && (c)<='9')
14: 判断字符是不是16进值的数字

#define HEXCHK(c) (((c) >= '0' && (c)<='9') ((c)>='A' && (c)<= 'F') \
((c)>='a' && (c)<='f'))

15: 防止溢出的一个方法
#define INC_SAT(val) (val=((val)+1>(val)) ? (val)+1 : (val))

16: 返回数组元素的个数
#define ARR_SIZE(a) (sizeof((a))/sizeof((a[0])))

17: 返回一个无符号数n尾的值MOD_BY_POWER_OF_TWO(X,n)=X%(2^n)
#define MOD_BY_POWER_OF_TWO( val, mod_by ) ((dword)(val) & (dword)((mod_by)-1))

18: 对于IO空间映射在存储空间的结构,输入输出处理
#define inp(port) (*((volatile byte *)(port)))
#define inpw(port) (*((volatile word *)(port)))
#define inpdw(port) (*((volatile dword *)(port)))
#define outp(port,val) (*((volatile byte *)(port))=((byte)(val)))
#define outpw(port, val) (*((volatile word *)(port))=((word)(val)))
#define outpdw(port, val) (*((volatile dword *)(port))=((dword)(val)))

19: 使用一些宏跟踪调试
ANSI标准说明了五个预定义的宏名。它们是:
__LINE__
__FILE__
__DATE__
__TIME__
__STDC__

C++中还定义了 __cplusplus
如果编译器不是标准的,则可能仅支持以上宏名中的几个,或根本不支持。记住编译程序也许还提供其它预定义的宏名。
__LINE__ 及 __FILE__ 宏指示,#line指令可以改变它的值,简单的讲,编译时,它们包含程序的当前行数和文件名。
__DATE__ 宏指令含有形式为月/日/年的串,表示源文件被翻译到代码时的日期。
__TIME__ 宏指令包含程序编译的时间。时间用字符串表示,其形式为: 分:秒
__STDC__ 宏指令的意义是编译时定义的。一般来讲,如果__STDC__已经定义,编译器将仅接受不包含任何非标准扩展的标准C/C++代码。如果实现是标准的,则宏__STDC__含有十进制常量1。如果它含有任何其它数,则实现是非标准的。
__cplusplus 与标准c++一致的编译器把它定义为一个包含至少6为的数值。与标准c++不一致的编译器将使用具有5位或更少的数值。

可以定义宏,例如:当定义了_DEBUG,输出数据信息和所在文件所在行
#ifdef _DEBUG
#define DEBUGMSG(msg,date) printf(msg);printf(“%d%d%d”,date,_LINE_,_FILE_)
#else
#define DEBUGMSG(msg,date)
#endif
20: 宏定义防止错误使用小括号包含。
例如:
有问题的定义:#define DUMP_WRITE(addr,nr) {memcpy(bufp,addr,nr); bufp += nr;}
应该使用的定义: #difne DO(a,b) do{a+b;a++;}while(0)
例如:
if(addr)
DUMP_WRITE(addr,nr);
else
do_somethong_else();
//宏展开以后变成这样:
if(addr)
{memcpy(bufp,addr,nr); bufp += nr;};
else
do_something_else();

更多

C语言 第八章 函数、指针与宏的更多相关文章

  1. Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针

    Atitit java方法引用(Method References) 与c#委托与脚本语言js的函数指针   1.1. java方法引用(Method References) 与c#委托与脚本语言js ...

  2. 函数指针玩得不熟,就不要自称为C语言高手(函数指针是解耦对象关系的最佳利器,还有signal)

    记得刚开始工作时,一位高手告诉我说,longjmp和setjmp玩得不熟,就不要自称为C语言高手.当时我半信半疑,为了让自己向高手方向迈进,还是花了一点时间去学习longjmp和setjmp的用法.后 ...

  3. C语言中的函数指针

    C语言中的函数指针 函数指针的概念:   函数指针是一个指向位于代码段的函数代码的指针. 函数指针的使用: #include<stdio.h> typedef struct (*fun_t ...

  4. 利用C语言中的函数指针实现c++中的虚函数

    C语言中的函数指针 #include<stdio.h> int fun1(int a) { return a*a; } int fun2(int a) { return a*a*a; } ...

  5. C语言学习019:函数指针

    在C语言中,函数名也是指针变量,比如创建了一个add(int n,int m)的函数的同时也创建了一个名为add的指针变量,因此我们可以把函数指针当作一种类型为它赋值.当作参数传递等操作 C语言创建函 ...

  6. 策略设计模式与c语言中的函数指针

    在C语言中有一个非常重要的概念-函数指针,其最重要的功能是实现回调函数(指函数先在某处注册,而它将在稍后某个需要的时候被调用)在java语言中没有指针的概念,但是可以利用接口和指针实现类似的功能,具体 ...

  7. 1、C语言中的函数指针

    一 通常的函数调用 void MyFun(int x); //此处的申明也可写成:void MyFun( int ); int main(int argc, char* argv[]) { MyFun ...

  8. C语言复杂的函数指针声明

    复习C语言ING,发现复杂的函数指针声明看不懂,百度半天终于略知一二. 讲的比较详细的一篇blog: http://blog.csdn.net/megaboy/article/details/4827 ...

  9. C语言编程中函数指针的定义及使用

    C语言中函数指针的定义: typedef int (*funcPtr)(int, int)表示定义了一个函数指针funcPtr,这个函数指针只能指向如下: int add(int, int).int ...

  10. C语言笔记 08_函数指针&回调函数&字符串&结构体&位域

    函数指针 函数指针是指向函数的指针变量. 通常我们说的指针变量是指向一个整型.字符型或数组等变量,而函数指针是指向函数. 函数指针可以像一般函数一样,用于调用函数.传递参数. 函数指针变量的声明: / ...

随机推荐

  1. Jenkins部署配置简介

    前段时间研究了一下自动化测试,因而接触到了Jenkins,今天有时间进行一下Jenkins部署配置相关知识的总结分享 前言:由于本次只是实验性研究,采用Windows环境,因此Jenkins可以通过下 ...

  2. Python之路第一课Day7--随堂笔记(面向对象编程进阶...未完待续 )

    本节内容: 面向对象高级语法部分 静态方法.类方法.属性方法 类的特殊方法 反射 异常处理 Socket开发基础 作业:开发一个支持多用户在线的FTP程序 面向对象高级语法部分 一.静态方法 通过@s ...

  3. USACO翻译:USACO 2012 JAN三题(3)

    USACO 2012JAN(题目三) 一.题目概览 中文题目名称 放牧 登山 奶牛排队 英文题目名称 grazing climb lineup 可执行文件名 grazing climb lineup ...

  4. POS与EPOS区别

    本文分文三个部分来介绍,第一部分是两个概念(POS与EPOS),第三部分是 POS与EPOS的区别. 一.epos ( electronic point of sale ) 俗称电话pos机: .EP ...

  5. ABP理论学习之SignalR集成

    返回总目录 本篇目录 介绍 安装 建立连接 内置功能 你自己的SignaR代码 介绍 Abp.Web.SignalR 使得在基于ABP的应用程序中使用 SignalR相当容易.查看SignalR文档获 ...

  6. ABP理论学习之EntityFramework集成

    返回总目录 本篇目录 Nuget包 创建DbContext 仓储 仓储基类 实现仓储 自定义仓储方法 阅读其他 ABP可以使用任何ORM框架工作,并且已经内置了EntityFramework集成.这篇 ...

  7. 一小时包教会 —— webpack 入门指南

    什么是 webpack? webpack是近期最火的一款模块加载器兼打包工具,它能把各种资源,例如JS(含JSX).coffee.样式(含less/sass).图片等都作为模块来使用和处理. 我们可以 ...

  8. RxJava 备注

    RxJava是一个采用观察者模式的异步框架,本文给出几个基本的使用例子. 1.配置依赖: compile 'io.reactivex:rxjava:1.0.14' compile 'io.reacti ...

  9. .NET平台下对C3D文件的读写

    [题外话] 最近实验室要我修改C3D(The 3D Biomechanics Data Standard)文件,虽然从网上找到了一个叫c3d4sharp的类库,这个类库单纯读取C3D文件的话还可以,但 ...

  10. Chrome 控制台新玩法-console显示图片以及为文字加样式

    有兴趣的同学可以文章最后的代码复制贴到控制台玩玩. Go for Code 在正常模式下,一般只能向console 控制台输出简单的文字信息.但为了把信息输出得更优雅更便于阅读,除了cosole.lo ...