C专家编程(2)

理解声明(P64-66)

面对一些复杂的声明形式,可以通过以下两种方法来理解,分别是优先级法和图标法。下面以书上的
char * const *(*next)(); 为例,分别进行分析。

  • 优先级法(P64)

适用规则 解释
A 首先,看变量名next,并注意到它直接被括号所括住
B.1 所以先把括号里的东西作为一个整体,得出“next是一个指向...的指针”
B 然后考虑括号外面的东西,在星号前缀和括号后缀之间作出选择
B.2 规则告诉我们优先级较高的是右边的函数括号,所以得出“next是一个函数指针,指向一个返回...的函数"
B.3 然后,处理前缀*,得出指针所指的内容
C 最后,把char * const解释为指向字符的常量指针

这个声明表示“next是一个指针,它指向一个函数,该函数返回另一个指针,该指针指向一个类型为 char的常量指针”

  • 图表法(P65)

signal函数声明的解析

首先来看一下signal的声明形式:

void (*signal(int sig, void(*func)(int)))(int);

简化后为void(*signal(x,xx))(int),表明signal函数有两个参数(x和xx),并返回一个函数指针,指向的函数接受int类型的参数并返回void。而xx参数表示的函数与signal本身的形式一样。

因此可以使用typedef void(*pf)(int);来简化函数的声明,简化后为:pf signal(int, pf);

练习

char *(* c[10])(int **p);  // 答案在文章末尾

编程挑战

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

#define MAXTOKENLEN (256)
#define MAXTOKENS (16)
#define TOTAL_NUMS(array) (sizeof(array) / sizeof(array[0]))

char *STR_TYPE[] = {"char",   "short",    "int",  "long",   "float", "double",
                    "signed", "unsigned", "void", "struct", "union", "enum"};
char *STR_QUALIFIER[] = {"const", "volatile"};

typedef enum {
  TYPE,       /* 类型 */
  QUALIFIER,  /* 限定符 */
  INDENTIFIER /* 标识符 */
} type_t;

struct token {
  char type;
  char string[MAXTOKENLEN];
};

struct token stack[MAXTOKENS]; /* 保存第一个标识之前的所有标记 */
struct token this;             /* 保存刚读入的标记 */
char next_string[MAXTOKENLEN];

int top = -1; /* 栈顶 */
// 压栈
static void push(struct token t) { stack[++top] = t; }
// 出栈
static struct token pop(void) { return stack[top--]; }

// 字符串分类,获取当前标识的类型
static type_t classify_string(void) {
  int i = 0;
  char *s = this.string;

  for (i = 0; i < (int)TOTAL_NUMS(STR_TYPE); i++) {
    if (strcmp(s, STR_TYPE[i]) == 0) {
      return TYPE;
    }
  }
  for (i = 0; i < (int)TOTAL_NUMS(STR_QUALIFIER); i++) {
    if (strcmp(s, STR_QUALIFIER[i]) == 0) {
      return QUALIFIER;
    }
  }
  return INDENTIFIER;
}

// 获取标记
static void gettoken(void) {
  char *p = next_string; /* 取新的一段字符串 */

  while (*p == ' ') { /* 忽略空格 */
    p++;
  }
  strcpy(this.string, p); /* 字符串赋值 */
  p = this.string;

  if (isalnum(*p)) { /* 如果是字母数字组合 */
    while (isalnum(*++p))
      ;                                  /* 直到读取到其他字符 */
    strcpy(next_string, p);              /* 修改字符串 */
    *p = '';                           /* 加上字符串结束符 */
    this.type = (char)classify_string(); /* 判断类型 */
  } else if (*p != '') {               /* 单字符标记 */
    strcpy(next_string, p + 1);          /* 修改字符串 */
    this.type = *p;
    this.string[1] = '';
  }
}

// 读至第一个标识符
static void read_to_first_identifier(void) {
  gettoken();
  while (this.type != INDENTIFIER) /* 不是标识符,将标记入栈 */
  {
    push(this);
    gettoken(); /* 取下一个标记 */
  }

  printf("Identifier "%s" is ", this.string);

  gettoken();
}

/*************** 解析程序 ***************************************/
// 处理函数参数
static void deal_with_function_args(void) {
  while (this.type != ')') {
    gettoken();
  }
  gettoken();
  printf("function returning ");
}

// 处理函数数组
static void deal_with_arrays(void) {
  while (this.type == '[') /* 继续读取数字或']' */
  {
    printf("array ");
    gettoken();
    if (isdigit(this.string[0])) {             /* 如果是数字 */
      printf("0..%d ", atoi(this.string) - 1); /* 打印数组大小 */
      gettoken();                              /* 获取']' */
    }
    gettoken();
    printf("of ");
  }
}

// 处理任何指针
static void deal_with_any_pointers(void) {
  while (stack[top].type == '*') {
    pop();
    printf("pointer to ");
  }
}

// 处理声明器
static void deal_with_declarator(void) {
  if (this.type == '[') {
    deal_with_arrays();
  } else if (this.type == '(') {
    deal_with_function_args();
  }

  deal_with_any_pointers();

  while (top > -1) {
    if (stack[top].type == '(') {
      pop();
      gettoken();
      deal_with_declarator();
    } else {
      deal_with_any_pointers();
      printf("%s ", pop().string);
    }
  }
}

int main(void) {
  // 测试用例参考 https://blog.csdn.net/yyhustim/article/details/9612185
  char *str[] = {"char * const *(*next)()", "char *(* c[10])(int **p)",
                 "const int * grape",       "int const * grape",
                 "int * const grape",       "int sum(int a, int b)",
                 "char (*(*x())[])()",      "char (*(*x[3])())[5]"};
  for (int i = 0; i < (int)TOTAL_NUMS(str); i++) {
    printf("== %s
", str[i]);
    strcpy(next_string, str[i]);
    read_to_first_identifier();
    deal_with_declarator();
    printf("

");
    top = -1;
  }

  return 0;
}

答案

运行上面的程序,给出的结果为:

Identifier "c" is array 0..9 of pointer to function returning pointer to char

即c是一个大小为10的数组,其元素类型是函数指针,指向的函数的返回值是一个指向char的指针。

原文地址:https://www.cnblogs.com/maxiaowei0216/p/14247490.html