C语言进阶13-高级指针

13.1 指向指针的指针的指针

实例:

int i;
int *pi;
int **ppi;

printf( "%d\n", ppi );  //如果ppi是个自动变量,那么它未被初始化,将答应一个随机值。如果是静态变量,打印0.
					    //ppi未初始化之前,他的存储单元的内容不可知
printf( "%d\n", &ppi ); //将ppi的存储单元的地址打印出来,这个值没什么用

*ppi = 5;  //错误,不能对未初始化的指针执行间接访问操作

ppi = π //ppi初始化为指向pi的变量

*ppi = &i; //把pi初始化为指向变量i的指针,pi是通过ppi间接访问得到的。

/*
	下面三条语句具有相同的效果
*/
i = 'a';
*pi = 'a';
**ppi = 'a';

int ***pppi; //访问层次还可以加多,只有当确实需要时,才使用多层访问,不然层序变得更大,更慢,更难维护。

13.2 高级声明

提示:程序中的每个函数都位于内存中的某个位置,所以存在指向那个位置的指针是合法的。

实例:

int *f;         //把表达式 *f 声明为一个整数。进而推断出f是指向整型的指针。
int *a, b;      //这里只有变量a被声明为指针,b为整型变量。

int *k();       //表达式 *K(),先执行函数调用操作符(),因此k是一个函数,它的返回值是一个 指向整型的指针 。

int (*kk)();    //先执行第一个括号聚组,是间接访问在函数调用之前进行。使得kk成为一个函数指针。它所指向的函数返回一个 整型值 

int *(*kkk)();  //kkk也是一个 函数指针 ,它所指向的函数返回值是一个 指向整型的指针 。

int *c[];       //先执行下标操作,c为一个数组,它的元素类型是 指向整形的指针 。

int cc()[];     //非法,函数只能返回标量值,不能返回数组

int ccc[]();    //非法

int (*x[])();   //先对聚组括号内的表达式求值再函数调用符,所以x是一个元素是 函数指针 的数组。指向的函数的返回值是整型值。

int *(*xx[])(); //xx是一个元素为 函数指针的数组,其指向的函数的返回值为 指向整形的指针。

13.3 函数指针

下面介绍两种常见的函数指针的用途:转换表(jump table)和作为参数传递给另一个函数。

注意1: 函数指针和其他指针一样,对函数指针执行间接访问前一定要把它初始化指向某个函数

注意2:函数名被使用时总是由编译器把它转换为函数指针。指针指向函数在内存中的位置。然后 函数调用操作符() 调用该函数,执行开始于这个地址的代码

函数初始化实例:

int f( int );
int (*pf)( int ) = &f; //声明并初始化函数指针pf 
					   //这里的&并不需要,函数名被使用时总是由编译器把它转换为 函数指针。&只是显示说明转换操作。

int ans;
/*
	下面三条语句作用效果一样
*/
ans = f    ( 2 );//函数名f首先被转换为函数指针,指向函数在内存中的位置。然后函数调用操作符调用该函数,执行开始于这个地址的代码
ans = (*pf)( 2 );
ans = pf   ( 2 );

13.3.1 回调函数

先看一个之前单向链表查找特定值的函数:

node *search( node *pnode, int const value )
{
    while( pnode != NULL ){
		if( pnode->value == value )
            break;
        pnode = pnode->link;
    }
    return pnode;
}

上面这种实现方法只能实现查找整数的链表。要使查找函数跟查找的类型无关,这样他就能用于任何类型的值的链表。

/*
	在一个单链表中查找一个指定的值。它的参数一个是指向聊表的第一个节点的指针。一个指向需要查找的指针,和一个函数指针指向用于比较的函数
*/
node *search( node *pnode, void const *value, int (*compare)( void const *, void const *) )
{
    while( pnode != NULL ){
        if( compare( &pnode->value, value ) == 0 ) //相等返回0,为了于标准库中的比较函数兼容。
            break;
        pnode = pnode->link;
    }
    return pnode;
}

int com_ints( void const *a, void const *b )
{
    if( *(int *)a == *(int *)b )
        return 0;
    else 
        return 1;
}

//这样调用
target_node = search( root, &target_value, com_ints );

13.3.2 转移表

例子:

switch( oper ){
    case add:
        result = add( op1, op2 );
        break;
    case sub:
        result = sub( op1, op2 );
        break;
    ...//假设还有很多操作,这里省略掉
       //注:把具体操作和用函数实现,实现具体操作和选择操作分隔开是一种良好的代码风格。
            
}

/*
	为了使用switch语句,表示操作符代码oper必须是整数。如果他是从零开始连续的整数,则可以用转换表来实现。
	转换表就是一个 函数指针数组
	
	创建转换表需要两步。
	1、声明初始化一个函数指针数组,确保这些函数的原型在这个函数指针数组之前声明
	2、使用
*/

double add( double, double );
double sub( double, double );
double mul( double, double );
...

double (*oper_func[])( double, double ) = {
    add, sub, mul,......
};

result = oper_func[oper]( op1, op2 );

**注:**使用函数指针数组时,一定要检查数组是否越界。

13.4 命令行参数

在命令行中编写参数来启动一个程序执行,第一个参数就是程序的名字。

13.4.1 传递命令行参数

c程序的main函数有两个形参。第一个成为argc,表示命令行参数的数目。第二个通常称为argv,他指向一组参数,本质上是数组,里面的元素都是指向一个参数文本的指针。

如果程序需要访问命令行参数,main函数就在声明时加上这些参数argc,argv。

一行命令行命令例子:cc -c -o main.c insert.c -o test argc = 7;

cc就是程序的名字,当程序有几组不同的选项进行启动时,程序对第一个参数进行检查,确定是由哪个名字启动的,选择启动项。

/*
	一个打印其命令行参数的程序,区分这里和上面的命令行的程序
*/
#include 
#include 

int main( int argc, char **argv )
{
    /*
    	打印命令行参数,程序名被跳过
    */
    while( *++argv != NULL ){
        printf( "%s\n", *argv);
    }
    return EXIT_SUCCESS;
}

13.5 字符串常量

当字符串常量出现在表达式中时是个指针常量。而数组名用于表达式中时也是指针常量。我们可以对数组进行下标引用,间接访问,已经指针运算。这些操作对字符串常量也适用。

实例:

"xyz" + 1;   //结果是 y
*"xyz" ;     //结果是 x
"xyz"[2];    //结果是 z

你可能感兴趣的:(C语言,c语言,开发语言,后端)