C和指针---第十三章:高级指针话题

13.1 进一步探讨指向指针的指针

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
	int i = 5;
	int *pi = &i;
	int **ppi;

	ppi = &pi;
	//如果没有ppi = &pi;则这是错误的赋值方法,因为ppi还未被初始化。如果ppi被初始化,则*ppi = pi毫无作用。
//	*ppi = pi;			


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

	return 0;
}
13.2 高级声明
int f;    //一个整型变量
int *f;   //一个指向整型的指针
int *f();
f是一个函数,返回值类型是一个指向整型的指针。

备注:用于声明变量的表达式和普通的表达式在求值时所使用的规则相同

int (*f)();
第一对括号的作用:迫使间接访问在函数调用之前进行,使f成为一个函数指针(即我们必须传递函数的地址进去,进行*f后成为一个函数,然后调用这个函数),它所指向的函数返回一个整型值。

则下面这个表达式容易理解:

int *(*f)();
函数指针,返回一个指向整型的指针。
int *f[];
[]的优先级高于*,所以首先f是个数组,数组内部的元素为*,而类型为int,所以数组的元素类型是指向整型的指针。

以下两个表达式均非法:

1.

int f()[];

f是个函数,它返回一个整型数组---但是是非法的,函数只能返回标量值,不能返回数组。

2. 

int f[]();

f是个数组,返回值为整型的函数---但是是非法的,因为数组元素必须具有相同的长度,但不同的函数显然可能具有不同的长度。

所以下面的声明是合法的:

int (*f[])();
f是一个数组,数组元素的类型是函数指针,它所指向的函数的返回值是一个整型值。
int *(*f[])();
创建一个指针数组,指针所指向的类型是返回值为整型指针的函数。

13.3 函数指针

简单声明一个函数指针并不意味着它马上就可以使用。和其他指针一样,对函数执行间接访问之前必须把它初始化为指向某个函数。

int f( int );
int ( *pf )( int ) = &f;
初始化表达式中的&操作符是可选的,因为函数名被使用时总是由编译器把它转换为函数指针。
int ans;
ans = f( 25 );
ans = ( *pf )( 25 );
ans = pf( 25 );
第一条语句简单的使用名字调用函数f。但首先f被转换为函数指针,该指针指定函数在内存中的位置。然后,函数调用操作符调用该函数,执行开始于这个地址的代码。

第二条语句对pf执行间接访问操作,它把函数指针转换为一个函数名。但接着又被转换为函数指针来执行函数。

第三条语句直接调用函数指针来执行函数。

13.3.1 回调函数

这里有个简单的函数,它用于在一个单链表中查找一个值。它的参数是一个指向链表第一个节点的指针以及那个需要查找的值:

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

	return node;
}
这个函数的一大缺点是:只能查找整型值,所以我们可以通过回调函数的技巧来达到查询任何类型的值。

备注:参数类型声明为void*, 表示“一个指向未知类型的指针”

#include <stdio.h>

node *search_list( node *node, void const *value, int ( *compare )( void const *, void const * ) )
{
	while ( NULL != node ){
		if ( compare( &node->value, value ) == 0 ){
			break;
		}
		node = node->link;
	}

	return node;
}
所以,当我们想比较整型值的时候,我们可以这样编写比较函数:
int compare_int( void const *a, void const *b )
{
	if ( *( int * )a == *( int * )b ){
		return 0;
	}

	return 1;
}
然后,我们可以这样调用:
desired_node = search_list( root, &desired_value, compare_int );
13.3.2 转移表

假设有个计算器程序:

switch ( oper ){
	case ADD:
		result = add( op1, op2 );
		break;
	case SUB:
		result = sub( op1, op2 );
		break;
	case MUL:
		result = mul( op1, op2 );
		break;
	case DIV:
		result = div( op1, op2 );
		break;
......
}
如果操作符很多,则switch则很长很长。

我们可以用函数指针来转换:

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

result = oper_func[ oper ]( op1, op2 );
请参考《代码大全2》.

13.4 命令行参数

假设我们输入:

prog -a -b -c name1 name2 name3
则下面代码执行这个命令:
#include <stdio.h>
#define TRUE 1

void process_standard_input( void );
void process_file( char *file_name );

int option_a, option_b;

void main( int argc, char **argv )
{
	while ( *++argv != NULL && **argv == '-' ){
		switch( *++*argv ){
			case 'a':
				option_a = TRUE;
				break;
			case 'b':
				option_b = TRUE;
				break;
		}
	}

	if ( NULL == *argv ){
		process_standard_input();
	}
	else{
		do{
			process_file( *argv );
		}while( *++argv != NULL );
	}
}
但如果命令行是:
prog -abc name1 name2 name3
则我们只需要修改while循环即可:
while ( ( opt = *++*argv ) != '\0' ){
	switch( opt ){
		case 'a':
			option_a = TRUE;
			break;
.....
	}
}

13.5 字符串常量

#include <stdio.h>

int main(void)
{
	printf("%c\n", *( "xyz" + 1 ) );

	return 0;
}
程序输出字符:‘y’

当一个字符串常量出现于表达式中时,它的值是个指针常量。编译器把这些指定字符的一份拷贝存储在内存的某个位置,并存储一个指向第一个字符的指针。但是,当数组名用于表达式时,它们的值也是指针常量。我们可以对它们进行下标引用,间接访问以及指针运算。

一个神秘的函数:

#include <stdio.h>

void mystery( int n )
{
	n /= 10;
	printf("%s\n", "**********" + 10 - n );
}

int main(void)
{
	int i = 0;
	while ( i++ <= 100 ){
		mystery( i );
	}

	return 0;
}
如果参数为0,则打印0个星号,如果为100,则打印10个星号。但是当n等于0的时候,星号就10个,不是存在越界情况吗?实际上我们打印的是字符'\0',故个人推荐,在星号后面增加一个(‘ ’)空格。

打印十六进制也可以简化成下面的代码:

putchar( "0123456789ABCDEF"[ value % 16 ] );
知道python的人应该对这个不陌生,其实python的源码是用C写的时候,你就懂得了。

习题:

1.

#include <stdio.h>
#include <ctype.h>


int main(void)
{
	int (*func[])(int value) = {
		iscntrl, isspace, isdigit, islower, isupper, ispunct, isprint
	};
	int	n_count[8] = {0, 0, 0, 0, 0, 0, 0, 0};
	char	ch;
	int	i = 0;
	while ( ( ch = getchar() ) != EOF ){
		n_count[7]++;
		for ( i = 0; i < 7; i++ ){
			if ( func[i]( ch ) ){
				n_count[i]++;
				continue;
			}
		}
	}
	printf("cntrl:%.2f%%\n", n_count[0] * 100.0 / n_count[7] );
	printf("space:%.2f%%\n", n_count[1] * 100.0 / n_count[7] );
	printf("digit:%.2f%%\n", n_count[2] * 100.0 / n_count[7] );
	printf("lower:%.2f%%\n", n_count[3] * 100.0 / n_count[7] );
	printf("upper:%.2f%%\n", n_count[4] * 100.0 / n_count[7] );
	printf("punct:%.2f%%\n", n_count[5] * 100.0 / n_count[7] );
	printf("no print:%.2f%%\n", ( n_count[7] - n_count[6] ) * 100.0 / n_count[7] );

	return 0;
}

程序输出:

2.

#include <stdio.h>
#include <stdlib.h>

typedef struct Node{
	struct Node *next;
	char		data;
} Node;

void func( Node *node, void (*commonFunc)( Node* node ) )
{
	Node *current = node;
	while ( current ){
		commonFunc( current );
		current = current->next;
	}
}

void print_list( Node *node )
{
	printf("%c-->", node->data );
}

int main(void)
{
	Node d = { 0, 'd' };
	Node c = { &d, 'c' };
	Node b = { &c, 'b' };
	Node a = { &b, 'a' };

	func( &a, print_list );
	printf("NULL\n");

	return 0;
}

程序输出:

4.以下程序有问题:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int compare_int( void const *a, void const *b )
{
	return *( int * )a >= *( int * )b ? 1 : 0;
}

int compare_char( void const *a, void const *b )
{
	return *( char * )a >= *( char * )b ? 1 : 0;
}

int compare_string( void const *a, void const *b )
{
	return *( char ** )a >= *( char ** )b ? 1 : 0;
}

void my_sort( void *arr[], int len, int elementLen, int ( *compare )( void const *, void const * ) )
{
	int i = 0;
	int j = 0;
	
	for ( i = 0; i < len - 1; i++ ){
		for ( j = i + 1; j < len; j++ ){
			if ( compare( ( arr + i ), ( arr + j ) ) ){
				if ( 1 == elementLen ){
					char temp = arr[i];
					arr[i] = arr[j];
					arr[j] = temp;
				}
				else if ( 4 == elementLen ){
					int temp = arr[i];
					arr[i] = arr[j];
					arr[j] = temp;
				}else{
					char *temp = arr[i];
					arr[i] = arr[j];
					arr[j] = temp;
				}
			}
		}
	}
}

void print_intarr( int arr[], int len )
{
	int i = 0;
	for ( i = 0; i < len; i++ ){
		printf("%d ", arr[i] );
	}
	printf("\n");
}

void print_chararr( char arr[], int len )
{
	int i = 0; 
	for ( i = 0; i < len; i++ ){
		printf("%c ", arr[i] );
	}
	printf("\n");
}

void print_stringarr( char *arr[], int len )
{
	int i = 0;
	for ( i = 0; i < len; i++ ){
		printf("%s ", arr[i] );
	}
	printf("\n");
}

int main(void)
{
	int		numArr[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
	char	chArr[] = "helloworld";
	char	*strArr[] = {"hello", "world", "i", "love", "C"};


	my_sort( numArr, 10, 4, compare_int );
	print_intarr( numArr, 10 );

	my_sort( chArr, 11, 1, compare_char );
	print_chararr( chArr, 11 );

	my_sort( strArr, 5, 5, compare_string );
	print_stringarr( strArr, 5 );

	return 0;
}

但是不知道如何处理啊。想了一下,问题的根源就在于处理不同的位,于是上网搜答案去了:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int compare_int( void const *a, void const *b )
{
	return *( int * )a >= *( int * )b ? 1 : 0;
}

int compare_char( void const *a, void const *b )
{
	return *( char * )a >= *( char * )b ? 1 : 0;
}

int compare_string( void const *a, void const *b )
{
	return *( char ** )a >= *( char ** )b ? 1 : 0;
}

void swap( char *i, char *j, int recsize )    //每个字符进行交换
{
	char x;
	while ( recsize-- > 0 ){
		x = *i;
		*i++ = *j;
		*j++ = x;
	}
}

void sort( char *base, int nel, int recsize, int ( *comp )( char *, char * ) )
{
	char	*i;
	char	*j;
	char	*last;
	
	last = base + ( nel - 1 ) * recsize;

	for ( i = base; i < last; i += recsize ){
		for ( j = i + recsize; j <= last; j += recsize ){
			if ( comp( i, j ) > 0 ){
				swap( i, j, recsize );
			}
		}
	}
}

void print_intarr( int arr[], int len )
{
	int i = 0;
	for ( i = 0; i < len; i++ ){
		printf("%d ", arr[i] );
	}
	printf("\n");
}

void print_chararr( char arr[], int len )
{
	int i = 0; 
	for ( i = 0; i < len; i++ ){
		printf("%c ", arr[i] );
	}
	printf("\n");
}

void print_stringarr( char *arr[], int len )
{
	int i = 0;
	for ( i = 0; i < len; i++ ){
		printf("%s ", arr[i] );
	}
	printf("\n");
}

int main(void)
{
	int		numArr[10] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 0};
	char	chArr[] = "helloworld";
	char	*strArr[] = {"hello", "world", "i", "love", "C"};


	sort( numArr, 10, 4, compare_int );
	print_intarr( numArr, 10 );

	sort( chArr, 11, 1, compare_char );
	print_chararr( chArr, 11 );

	sort( strArr, 5, 4, compare_string );
	print_stringarr( strArr, 5 );

	return 0;
}
运行了一下,答案居然正确。我坦白以我现在的基础,写一天都写不出来这个答案。

真心佩服!!

程序运行如下:

5.

这是从答案中摘录出来的:

#define TRUE 1
#define FALSE 0
#define NULL 0
#define NUL ’\0’
enum { NONE, FLAG, ARG };
/*
** Determine whether the argument is a flag or one that requires a value.
*/
argtype( register int ch, register int control )
{
	while( *control != NUL )
		if( ch == *control++ )
			return *control == ’+’ ? ARG : FLAG;
	return NONE;
}
/*
** Process the arguments.
*/
char **
do_args( int ac, reg char **av, char *control,
		void (*do_arg)( int, char * ), void (*illegal_arg)( int ) )
{
	register char *argp;
	register int ch;
	register int skip_arg;
	while( (argp = *++av) != NULL && *argp == ’–’ ){
		skip_arg = FALSE;
		while( !skip_arg && ( ch = *++argp ) != NUL ){
			switch( argtype( ch, control ) ){
				case FLAG:
					(*do_arg)( ch, NULL );
					break;
				case ARG:
					if( *++argp != NUL || (argp = *++av) != NULL ){
						(*do_arg)( ch, argp );
						skip_arg = TRUE;
						break;
					}
					(*illegal_arg)( ch );
					return av;
				case NONE:
					(*illegal_arg)( ch );
					break;
			}
		}
	}
	return av;
}


转载于:https://my.oschina.net/voler/blog/162772

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值