13.1 进一步探讨指向指针的指针
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i = 5;
int *pi = &i;
int **ppi;
ppi = π
//如果没有ppi = π则这是错误的赋值方法,因为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;
}