"C语言-高级指针话题&预处理器"

  "C语言基础复习八"

Posted by Xu on January 26, 2018

高级指针话题

high_pointer&pre_compile

高级声明

  • int f();
  • int *f();//()优先级高于*,所以f首先是一个函数,然后返回值是一个指向整型值的指针
  • int (*f)();//首先f是一个指针,它指向一个返回int的函数
  • int *(*f)(int,float);//f是一个指针,指向一个返回指向整型值指针的指针,函数的参数为整型、浮点型
  • int f[];//整型数组
  • int *f[];//[]优先级高,所以f首先是一个数组,其中的元素为指向整型值的指针
  • int f()[];//非法,含义为f为一个函数返回一个整型数组,但函数只能返回标量值
  • int f[]();//非法,含义为f为一个数组,数组中的元素为返回int的函数,数组中的元素长度应该相等
  • int (*f[])();//含义为f为一个数组,数组中的元素为指针,这些指针指向返回整型的函数。由于数组中的元素都是指针,长度相等所以合法
  • int *(*f[])();//这个同上,只不过指针指向一个返回指向int指针的函数

函数指针

初始化:函数名被使用时默认为函数指针

int f(int);
int (*pf)(int) = &f;//&可以省略,因为函数名被使用时默认为函数指针

利用函数指针调用函数:

int ans;
ans = f(25);
ans = (*pf)(25);
ans = pf(25);//函数名默认为函数指针

函数指针的应用:

  • 回调函数:针对不同类型的数据,需要使用不同的函数来处理,虽然功能类似,这种情况下我们可以将函数指针作为参数传递给函数作为回调函数
  • 转换表:当参数类型相同时,但具体操作不同,我们可以用转换表来对应不同的操作函数,转换表也就是一个函数指针数组,利用下标可以指向不同的函数

命令行参数

int main(int argc,char **argv)//argc表示参数个数,argv指向参数数据(一维指针数组,每个指针指向具体参数,该数组的末尾是null指针,用来确定参数的个数)

argv

字符串常量

字符串常量实际上是一个指向该字符串首地址的指针常量

  • “xyz”+1;//实际上指向y
  • *“xyz”;//指向首地址的第一个字符‘x’
  • “xyz”[2];//指向‘y’

两个例子:

//打印星号,进度条
void print_star(int value)
{
  value +=5;
  value /=10;
  print("%s\n","**********"+10-n);
}

//将二进制打印为16进制字符
void binary_to_ascii(unsigned int value){
     unsingned int quo;
     quo = value/16;
     if(quo != 0){
         binary_to_ascii(quo);
     }
     putchar("123456789ABCDEF"[value%16]);//利用字符串常量代表字符指针来输出字符
     

}

预处理器

编译c程序的第一个步骤:预处理阶段

  • 删除注释
  • 插入#include文件内容
  • 定义和替换#define指令的符号

预定义符号

  • _FILE_ :进行编译的源文件名称
  • _LINE_ :文件当前的行号
  • _DATE_ :文件被编译的日期
  • _TIME_ :文件被编译的时间
  • _STDC_ :编译器是否遵循ANSI C

#define

#define name replace_stuff//当符号name出现时都会被替换成replace_stuff

将参数替换到文本中称为宏定义

#define name(parameter-list) stuff

对于宏定义我们需要用括号保证替换后运算的结合性:

#define DOUBLE(x)  ((x)+(x))

由于在宏的定义中,邻近字符串常量自动连接的特性可以让我们很容易将字符串分为几段,所以我们可以利用该特性来将参数插入到字符串中:

#define PRINT(FORMAT,VALUE) print ("the value is" FORMAT "\n",VALUE)//字符串可以自动连接

对于参数进行#argument,会被替换为argument字符串:

#define PRINT(FORMAT,VALUE) printf("The value of " #VALUE " is " FORMAT "\N",VALUE)
...
PRINT("%d",x+3);
//输出结果为:the value of x+3 is 25

对于##则是用于将两边的符号连接成一个符号

#define ADD_TO_SUM(sum_number,value)  sum##sum_number +=value
ADD_TO_SUM(5,25);//这句话的含义是将25加到sum5变量中去

宏和函数的比较:

  • 用于调用和从函数返回的代码比宏的代码要大
  • 宏与类型无关,函数的参数要是固定的类型
  • 一些任务无法用函数实现,只能用宏如:#define MALLOC(n,type) ((type *)malloc((n)*sizepf(type)))//这里传入的参数是类型,函数无法实现

带副作用的宏参数

当宏参数在宏定义中出现两次是就会出现副作用,因为宏是完全替代,所以一个运算可能会在两次参数出现的位置进行重复计算

命名约定:一般宏定义全部大写

#undef

#undef name

移除一个宏定义,如果一个现存的名字需要被重新定义,那么它的旧定义首先必须用#undef移除

命令行定义

-Dname=stuff//将宏name替换成stuff

条件编译

我们可以选择某条语句或某组语句进行翻译或被忽略,比如一些调试代码就不需要在正式产品中执行,我们又不想删除这些代码,因为要方便以后的调试维护,这里就需要使用到条件编译

#if constant-expression
     statements
#elif constant-expression
     other-statements
#else 
     other-statements             
#endif     

其中constant-expression由预处理器求值,为非零值,则statements部分进行正常编译,否则被忽略

测试是否被定义

//1.
#if defined(symbol)
//等同于
#ifdef symbol
//2.
#if !defined(symbol)
//等同于
#ifndef sumbol

文件包含

  • 函数库包含:#include//明确指出是函数库文件
  • 本地文件包含:#include “filename” //在本地文件找不到时,会在函数库中找

多重嵌套包含

当我们#include两个头文件a和b时,这两个头文件又同时#includ一个头文件x,这时x.h被包含两次,为了避免这种情况发生,我们在定义头文件的时候尽量采用下面模式可以消除这种多重包含:

#ifdef _HEADERNAME_H
#define _HEADERNAME_H 1
/*
**声明你头文件中的内容
*/
#endif