C++ day9 函数(字符串,结构,递归,函数指针) (二)

文章目录

  • 当函数遇到字符串
    • 三种表示字符串的方式的本质都是 char指针
    • 示例1 计算字符串中某个字符出现了几次
    • 示例2 建立字符串(用new分配内存,记得delete啊!!!)
  • 当函数遇到结构
    • 结构和数组的不同之处
    • 一:传递和返回结构本身(按值传递)
      • 示例1 两个结构的成员相加
      • 示例2 直角坐标和极坐标的转换
        • 展示cin一次输入两个参数
    • 二: 传递结构的地址(就不用返回什么了)
    • 三:传递和返回结构的引用
      • 示例 不要随便把值赋给引用accumulate(dup, five) = four;
  • 当函数遇到string对象
    • 示例1 字符串数组
  • array对象
    • 示例1
  • 递归 (divide-and-conquer strategy分而治之)
    • 示例1
    • 示例2 难死我了
  • 函数指针
    • 声明函数指针(先写原型,用(*pf)替代函数名即可)
      • pf和(*pf)虽矛盾,但等价
    • 示例1 使用函数指针调用函数
    • 复杂的函数指针,用auto(C++11 automatic type deduction)可以简化一点
      • 创建 指向函数指针数组 的 指针
      • 示例1 指向函数指针数组的指针(并不少见)
    • 除了auto以外,还可用typedef简化代码

当函数遇到字符串

三种表示字符串的方式的本质都是 char指针

一是char数组,用数组名表示字符串,实际char数组名就是char指针
二是char指针··
三是字符串常量,即双引号括起来的,实际上字符串常量就是指向他自己的指针,也是第一个字符的地址,说白了还是char指针

示例1 计算字符串中某个字符出现了几次

#include 
unsigned int c_in_str(const char *str, char ch);

int main()
{
    char mmm[10] = "minimum";
    char uuu[10] = "ululate";
    unsigned int m, u;

    m = c_in_str(mmm, 'm');
    u = c_in_str(uuu, 'u');
    std::cout << m << " m in " << mmm << std::endl;
    std::cout << u << " u in " << uuu << std::endl;
    return 0;
}

unsigned int c_in_str(const char *str, char ch)
{
    unsigned int ct = 0;
    while (*str)
    {
        if (*str == ch)
            ++ct;
        ++str;//我竟然忘了写更新表达式···
    }
    return ct;
}
3 m in minimum
2 u in ululate

其中函数原型和函数头也可以写为数组形式

unsigned int c_in_str(const char str[], char ch);

示例2 建立字符串(用new分配内存,记得delete啊!!!)

就是记不住delete怎么办

#include 
char * build_str(char ch, int n);

int main()
{
    char ch;
    int n;
    char *st;

    std::cout << "Enter a character: ";
    while (!(std::cin >> ch))
    {
        std::cin.clear();
        std::cout << "Bad input. Enter again: ";
    }
    std::cout << "Enter a positive integer: ";
    while (!(std::cin >> n))
    {
        std::cin.clear();
        std::cout << "Bad input. Enter again: ";
    }
    st = build_str(ch, n);
    delete [] st;//我忘了!!!
    std::cout << "The built string is: " << st << std::endl;
    return 0;
}

char * build_str(char ch, int n)
{
    char * st = new char[n + 1];
    st[n--] = '\0';
    while (n + 1)
    {
        st[n] = ch;
        --n;
    }

    return st;
}
Enter a character: r
Enter a positive integer: 4
The built string is: rrrr

函数这么写更好

char * build_str(char ch, int n)
{
    char * st = new char[n + 1];
    st[n] = '\0';
    while (n-- > 0)
    {
        st[n] = ch;
    }

    return st;
}

因为这样不会用到0 -1,毕竟0-1只有对int才等于-1,如果是unsigned int,那就死循环了,不是好设计

当函数遇到结构

结构和数组的不同之处

其实,函数处理结构比函数处理数组简单很多。因为处理结构和处理简单变量很像:

  • 函数按值传递结构,传递的是结构的副本,像普通变量一样;但是数组虽然也是按值传递,但是传的只是数组名,即一个常量地址,所以函数使用的不是数组的副本,而是原始数组。
  • 函数返回结构,而处理数组时,函数返回的还是数组的地址,不是数组副本。
  • 数组名是数组首元素的地址;但是结构名不是地址哦,他就是个普通标识符,要用&才可以获得地址。

一:传递和返回结构本身(按值传递)

这样做当然和直接传递返回数组内容时,遇到的困境一样,结构越大,则时间和内存开销就越大,所以C早期根本不允许传递和返回结构本身。

但对于小型结构,也不失为一种方法。

这种方式编程特别简单

示例1 两个结构的成员相加

#include 
//travel_time就像是普通的类型名,可以作为函数中参数的类型和返回值的类型
struct travel_time
{
    unsigned int hours;
    unsigned int mins;
};
const int MINS_PER_HR = 60;
travel_time sum(travel_time day1, travel_time day2);
void show_time(travel_time);

int main()
{
    travel_time day1, day2, total;
    day1 = {5, 35};
    day2 = {4, 50};
    total = sum(day1, day2);
    show_time(total);
    travel_time day3;
    day3 = {7, 20};
    show_time(sum(total, day3));
    return 0;
}

travel_time sum(travel_time day1, travel_time day2)
{
    travel_time temp;

    temp.mins = (day1.mins + day2.mins) % MINS_PER_HR;
    temp.hours = (day1.hours + day2.hours) + \
    (day1.mins + day2.mins) / MINS_PER_HR;
    return temp;
}

void show_time(travel_time temp)
{
    std::cout << "Travel time: "
              << temp.hours << " hours, "
              << temp.mins << " minutes.\n";
}
Travel time: 10 hours, 25 minutes.
Travel time: 17 hours, 45 minutes.

示例2 直角坐标和极坐标的转换

#include 
#include 
struct rect
{
    double x;
    double y;
};
struct polar
{
    double distance;
    double angle;
};
const double RAD_TO_DEG = 57.29577951;
polar rect_to_polar(rect);
rect polar_to_rect(polar);
void showPolar(polar);
void showRect(rect);

int main()
{
    rect r = {1, 1};
    showRect(r);
    polar p = rect_to_polar(r);
    showPolar(p);
    rect r1 = polar_to_rect(p);
    showRect(r1);
    return 0;
}

polar rect_to_polar(rect r)
{
    polar p;

    p.distance = sqrt(r.x * r.x + r.y * r.y);
    p.angle = atan2(r.y, r.x);

    return p;
}

rect polar_to_rect(polar p)
{
    rect r;

    r.x = p.distance * cos(p.angle);
    r.y = p.distance * sin(p.angle);
    return r;
}

void showPolar(polar p)
{
    std::cout << "distance: " << p.distance
              << " angle: " << p.angle << std::endl;
}
void showRect(rect r)
{
    std::cout << "horizontal coordinate: " << r.x
              << " vertical coordinate: " << r.y << std::endl;
}
horizontal coordinate: 1 vertical coordinate: 1
distance: 1.41421 angle: 0.785398
horizontal coordinate: 1 vertical coordinate: 1

展示cin一次输入两个参数

因为类的运算符是用函数实现的,所以cin >> x会有返回值,返回的也是一个istream类对象。所以表达式std::cin >> r.x >> r.y的结果还是一个cin对象

int main()
{
    rect r;

    std::cout << "Enter x and y: ";
    while (std::cin >> r.x >> r.y)//聪明老套的使用cin
    {
        polar p = rect_to_polar(r);
        showPolar(p);
        std::cout << "Enter next x and y: ";
    }
    //输入错误则直接退出函数
    //如果需要输入错误了继续输入,那就要cin.clear(),还要读取掉非法输入哦
    return 0;
}
Enter x and y: 2 5
distance: 5.38516 angle: 1.19029
Enter next x and y: 1 1
distance: 1.41421 angle: 0.785398
Enter next x and y: 3 4
distance: 5 angle: 0.927295
Enter next x and y: 4
8
distance: 8.94427 angle: 1.10715
Enter next x and y: a

二: 传递结构的地址(就不用返回什么了)

把上面的程序改为传递指针,很意外的出错很多,掌握的不好

#include 
#include 
struct rect
{
    double x;
    double y;
};
struct polar
{
    double distance;
    double angle;
};
const double RAD_TO_DEG = 57.29577951;
void rect_to_polar(const rect *, polar *);
void polar_to_rect(const polar *, rect *);
void showPolar(const polar *);
void showRect(const rect *);

int main()
{
    rect r;
    polar p;

    std::cout << "Enter x and y: ";
    while (std::cin >> r.x >> r.y)//聪明老套的使用cin
    {
        rect_to_polar(&r, &p);
        showPolar(&p);
        std::cout << "Enter next x and y: ";
    }

    std::cout << "Enter distance and angle: ";
    //必须重置输入
    std::cin.clear();
    while (std::cin.get() != '\n')
        ;

    while (std::cin >> p.distance >> p.angle)//聪明老套的使用cin
    {
        polar_to_rect(&p, &r);
        showRect(&r);
        std::cout << "Enter next distance and angle: ";
    }
    return 0;
}

void rect_to_polar(const rect *r, polar *p)
{
    p->distance = sqrt(r->x * r->x + r->y * r->y);
    p->angle = atan2(r->y, r->x) * RAD_TO_DEG;
}

void polar_to_rect(const polar *p, rect * r)
{
    r->x = p->distance * cos(p->angle);
    r->y = p->distance * sin(p->angle);
}

void showPolar(const polar *p)
{
    std::cout << "distance: " << p->distance
              << " angle: " << p->angle << std::endl;
}
void showRect(const rect *r)
{
    std::cout << "horizontal coordinate: " << r->x
              << " vertical coordinate: " << r->y << std::endl;
}
Enter x and y: 1 1
distance: 1.41421 angle: 45
Enter next x and y: 3 4
distance: 5 angle: 53.1301
Enter next x and y: a
Enter distance and angle: 10 0.7
horizontal coordinate: 7.64842 vertical coordinate: 6.44218
Enter next distance and angle: a

我出错

  • 忘记传递指针时,不需要返回值,我还弄了指针返回值,写了程序才发现,那样还要分配内存,很麻烦。书上的办法就是直接把两个结构的指针都传进去,可以根据一个直接修改另一个,把不被修改的那个形参定义为const,然后不要返回值
  • 我在主程序中声明了两个指针变量,rect和polar类型的,试图把cin直接读取到指针指向的结构的成员里。这是完全错误的!!虽然编译器只是警告了,但是我声明了指针并没有让他们指向那个类型的某个结构,都没初始化,怎么给他指向的成员赋值啊,都不知道指向谁。这么低级的错误我还是在不断犯。
  • 我试图第一个while因为错误输入退出后,直接进入第二个while做反转换,我真是想的美。第一个while因为错误输入退出,那个导致错误输入的字符还在输入队列里,自然也进不去第二个while,程序就直接退出了。所以我在两个while中间加了一两步,第一步用cin.clear()清除错误标记。第二步用while读取输入缓冲队列里的字符。这样才能把第一次的错误的痕迹抹去,轻装进入第二个while

三:传递和返回结构的引用

示例 不要随便把值赋给引用accumulate(dup, five) = four;

#include 
#include 
struct free_throws
{
    std::string name;
    int made;
    int attempts;
    float percent;
};

void display(const free_throws & ft);
void set_pc(free_throws & ft);
free_throws & accumulate(free_throws & target, const free_throws & source);
void set_pc_p(free_throws * ft);//set_pc函数的指针版本

int main()
{
    //初始化多个结构对象
    //若指定的初始值比成员少,则余下成员初始化为0
    free_throws one = {"Ifelsa Branch", 13, 14};
    free_throws two = {"Andor Knott", 10, 16};
    free_throws three = {"Minnie Max", 7, 9};
    free_throws four = {"Whily Looper", 5, 9};
    free_throws five = {"Long Long", 6, 14};
    free_throws team = {"Throwgoods", 0, 0};

    free_throws dup;

    set_pc(one);//这个函数不能按值传递,否则不成功,可以使用指针和引用
    display(one);//此函数可以按值传递,但是相比于复制原始结构,按引用可节省时间和内存
    accumulate(team, one);//要修改第一个结构,所以第一个是引用,第二个是const引用
    display(team);

    display(accumulate(team, two));
    accumulate(accumulate(team, three), four);
    display(team);

    dup = accumulate(team, five);
    std::cout << "Displaying team:\n";
    display(team);
    std::cout << "Displaying dup after assignment:\n";
    display(dup);
    set_pc(four);
    
    //ill-advised assignment
    accumulate(dup, five) = four;//由于accumulate(dup, five)的返回值是结构体dup的引用
    //所以赋值使得结构体dup被修改
    //糟糕的是,这个程序必须把accumulate函数的返回值和第一个参数设置为非const,所以无法避免这种错误
    std::cout << "Displaying dup after ill-advised assignment:\n";
    display(dup);

    return 0;
}

void display(const free_throws & ft)
{
    using std::cout;
    cout << "Name: " << ft.name << '\n';
    cout << "Made: " << ft.made << '\t';
    cout << "Attempt: " << ft.attempts << '\t';
    cout << "Percent: " << ft.percent << '\n';
}

void set_pc(free_throws & ft)
{
    if (ft.attempts != 0)
        ft.percent = 100.0f * float(ft.made) / float(ft.attempts);
    else
        ft.percent = 0;
}

void set_pc_p(free_throws * ft)
{
    //set_pc函数的指针版本,调用:set_pc_p(&one);
    if (ft->attempts != 0)
        ft->percent = 100.0f * float(ft->made) / float(ft->attempts);
    else
        ft->percent = 0;
}

free_throws & accumulate (free_throws & target, const free_throws & source)
{
    //这个函数中target会被改变,所以有无返回值都行,要返回值是为了便于连续使用本函数
    target.attempts += source.attempts;
    target.made += source.made;
    set_pc(target);

    return target;//返回的是引用
}
Name: Ifelsa Branch
Made: 13        Attempt: 14     Percent: 92.8571
Name: Throwgoods
Made: 13        Attempt: 14     Percent: 92.8571
Name: Throwgoods
Made: 23        Attempt: 30     Percent: 76.6667
Name: Throwgoods
Made: 35        Attempt: 48     Percent: 72.9167
Displaying team:
Name: Throwgoods
Made: 41        Attempt: 62     Percent: 66.129
Displaying dup after assignment:
Name: Throwgoods
Made: 41        Attempt: 62     Percent: 66.129
Displaying dup after ill-advised assignment:
Name: Whily Looper
Made: 5 Attempt: 9      Percent: 55.5556

当函数遇到string对象

string对象虽然是字符串,但是比起数组,更像结构。

因为可以把一个结构赋给另一个结构,也可以把一个对象赋给另一个对象。
可以把一个结构作为一个实体传给函数。也可以把一个对象作为一个完整实体传给函数。

示例1 字符串数组

本来字符串数组是二维数组,但是用string数组看起来就像是一维数组一样,因为每一个字符串是一个string对象,字符串的数组本质被隐藏了。

string类使得字符串不再复杂,而像是int这些普通变量类型了。

#include 
#include 
const int SIZE = 5;
void display(const std::string sa[], int n);

int main()
{
    std::string list[SIZE];
    std::cout << "Enter your " << SIZE << " favorite astronomical sights:\n";
    std::cout << "#1: ";
    int i = 0;
    while (i < SIZE && getline(std::cin, list[i]))
    {
        ++i;
        if (i < SIZE)
            std::cout << "#" << (i + 1) << ": ";
    }
    display(list, i);
    return 0;
}

void display(const std::string sa[], int n)
{
    std::cout << "Your list:\n";
    int i;
    for (i = 0; i < n; ++i)
    {
        std::cout << "#" << (i + 1) << ": " << sa[i] << std::endl;//直接打印string对象
    }
}
Enter your 5 favorite astronomical sights:
#1: Orion Nebula
#2: M13
#3: SATURN
#4: Jupiter
#5: Moon
Your list:
#1: Orion Nebula
#2: M13
#3: SATURN
#4: Jupiter
#5: Moon

我的错:

  • string前面没用std::前缀
  • 打印#加数字的序号,容易在your list前面打印一个#6,我用if语句解决的问题,但不高明
  • 我不知道怎么写display的函数原型第一个参数,看了书直接写数组
  • 我竟然用cin输入字符串!!!这些字符串有可能由两个单词组成,即中间有空格,cin遇到空格就罢工了,必须用getline()读取一行,还会自动丢弃换行符。很方便

array对象

示例1

大家都是名称空间std的呀

用array模板类的类型名好长啊

array类型的对象是一个数组

#include 
#include 
#include 
const int Seasons = 4;
const std::array<std::string, Seasons> Snames =
{
    "Spring",
    "Summer",
    "Fall",
    "Winter"
};
void fill_array(std::array<double, Seasons> * p);
void display(std::array<double, Seasons> a);

int main()
{
    std::array<double, Seasons> expenses;//std::array相当于一个类型
    fill_array(&expenses);
    display(expenses);
    return 0;
}
void display(std::array<double, Seasons> a)
{
    int i, total = 0;
    std::cout << "Expenses:\n";
    for (i = 0; i < Seasons; ++i)
    {
        std::cout << Snames[i] << ": $" << a[i] << std::endl;
        total += a[i];
    }
    std::cout << "Total: $" << total << std::endl;
}
void fill_array(std::array<double, Seasons> * p)
{
    int i;
    for(i = 0; i < Seasons; ++i)
    {
        std::cout << "Enter " << Snames[i] << " expenses: ";
        std::cin >> (*p)[i];//p是指向array类型的对象的指针,*p就是一个array类型的对象
        //array类型的对象是一个数组
        //方括号优先级更高,所以圆括号是必需的
    }
}
Enter Spring expenses: 1000
Enter Summer expenses: 2000
Enter Fall expenses: 3000
Enter Winter expenses: 4000
Expenses:
Spring: $1000
Summer: $2000
Fall: $3000
Winter: $4000
Total: $10000

递归 (divide-and-conquer strategy分而治之)

C的递归就没学清楚,返回每一级反正觉得很对称,但是自己写程序就有点晕乎乎
C++ day9 函数(字符串,结构,递归,函数指针) (二)_第1张图片

示例1

#include 
void countdown(int n);

int main()
{
    countdown(5);
    return 0;
}

void countdown(int n)
{
    std::cout << "Counting down..." << n << std::endl;
    if (n > 0)
        countdown(n - 1);
    std::cout <<  n << ": Kaboom!\n";
}
Counting down...5
Counting down...4
Counting down...3
Counting down...2
Counting down...1
Counting down...0
0: Kaboom!
1: Kaboom!
2: Kaboom!
3: Kaboom!
4: Kaboom!
5: Kaboom!

看看每一级调用的n的地址

std::cout << "Counting down..." << n << " at " << &n << std::endl;
std::cout <<  n << " at " << &n << ": Kaboom!\n";

这说明每一级调用创建了新变量,其实很容易理解,你重新调用一次函数,肯定是要开辟一个新的栈内存块暂时存储函数内部的局部变量。

Counting down...5 at 0x6dfef0
Counting down...4 at 0x6dfed0
Counting down...3 at 0x6dfeb0
Counting down...2 at 0x6dfe90
Counting down...1 at 0x6dfe70
Counting down...0 at 0x6dfe50
0 at 0x6dfe50: Kaboom!
1 at 0x6dfe70: Kaboom!
2 at 0x6dfe90: Kaboom!
3 at 0x6dfeb0: Kaboom!
4 at 0x6dfed0: Kaboom!
5 at 0x6dfef0: Kaboom!

示例2 难死我了

#include 
const int Len = 66;
const int Divs = 6;
void subdivide(char ar[], int low, int high, int level);
int main()
{
    char ruler[Len];
    int i;
    for (i = 1; i < Len - 2; ++i)
    {
        ruler[i] = ' ';
    }
    ruler[Len - 1] =  '\0';
    int max = Len - 2;
    int min = 0;
    ruler[min] = ruler[max] = '|';
    std::cout << ruler << std::endl;
    for (i = 1; i <= Divs; ++i)
    {
        subdivide(ruler, min, max, i);
        std::cout << ruler << std::endl;
        for (int j = 1; j < Len - 2; ++j)
        {
            ruler[j] = ' ';
        }
    }
    return 0;
}

void subdivide(char ar[], int low, int high, int level)
{
    if (level == 0)
        return;
    int mid = (high + low) / 2;
    ar[mid] = '|';
    subdivide(ar, low, mid, level - 1);
    subdivide(ar, mid, high, level - 1);
}

结果真好看,可惜没没咋看懂

|                                                               |
|                               |                               |
|               |               |               |               |
|       |       |       |       |       |       |       |       |
|   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |   |
| | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | | |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||

函数指针

函数的地址是存储其机器代码的内存的开始地址

怎么获得呢?很简单:函数名就是函数的地址。

function1(think);//参数是think函数的地址
function2(think());//参数think函数的返回值

声明函数指针(先写原型,用(*pf)替代函数名即可)

double pam(int);
double (*pt)(int);//直接把pam替换为(*pt),pt是指向返回值是double类型,参数是int类型的函数的指针
//(*pt)和pam等价,也是函数,pt是函数指针
pt = pam;//让pt指向一个这种类型的具体的函数

double *pt(int);//不加圆括号,则pt是一个函数,参数是Int,返回值是double

pf和(*pf)虽矛盾,但等价

毕竟函数名本身就是个指针,那么pf也是函数指针,所以函数名和pf就等价

又因为刚才说的原型中函数名直接替换为(*pf),所以(*pf)和函数名也等价

但是pf和(*pf)肯定不等价

但是 C和C++采用的措施是:不管这个矛盾。程序员看哪个顺眼就用哪个。我看(*pf)更顺眼,因为一看就是函数指针。
C++ day9 函数(字符串,结构,递归,函数指针) (二)_第2张图片

示例1 使用函数指针调用函数

#include 
void estimate(int lines, double (*pf)(int));
double pam(int);
double besty(int);

int main()
{
    int lines;
    std::cout << "How many lines of code do you need?\n";
    std::cin >> lines;
    std::cout << "Here's pam's estimate: ";
    estimate(lines, pam);
    std::cout << "Here's besty's estimate: ";
    estimate(lines, besty);

    return 0;
}

void estimate(int lines, double (*pf)(int))
{
    std::cout << lines << " lines will take " << (*pf)(lines) << " hour(s).\n";
}
double pam(int lines)
{
    return 0.05 * lines;
}
double besty(int lines)
{
    return 0.02 * lines + 0.0004 * lines * lines;
}
How many lines of code do you need?
101
Here's pam's estimate: 101 lines will take 5.05 hour(s).
Here's besty's estimate: 101 lines will take 6.1004 hour(s).

复杂的函数指针,用auto(C++11 automatic type deduction)可以简化一点

假设有三个这样的函数原型,当然他们都是同函数类型的,只是写法不一样

const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);

那么指向他们这种函数类型的函数指针就是

const double * (*pf)(const double [], int);//方框也可以写为*

看上去是不是非常复杂,由于太过冗长 出错几率也就大了,所以这时候可以祭出C++的自动类型推断神器auto

比如声明上述函数指针时也初始化一下:

//方式1  老老实实写函数指针的类型
const double * (*pf)(const double [], int) = f1;
//方式2  auto
auto pf = f1;//auto会根据f1的类型自动推断pf的类型

但是auto神器的功能也不是特别多,auto自动类型推断只可以用于单值初始化,不可以用于初始化列表,比如数组的初始化

对最上面的三个同类型的函数f1, f2, f3,可以建立一个函数指针数组,即把指向他们三个的函数指针放在一个数组里,毕竟三者的函数指针是同类型的嘛,怎么声明这个数组呢,刚才说了不能用auto了,得老老实实自己写

const double * (*pf[3])(const double [], int);//只需把[3]放在函数指针pf后面,由于[]的优先级比*高,所以是数组,元素是指针
//pf是数组名,所以是第一个函数指针的地址,是指向函数指针的指针
auto pa = pf;//可以用auto初始化同类型的数组

从函数指针和指向函数数组的指针的声明和初始化上看,auto帮了大忙。我们应该感鞋C++的设计者提供了auto工具,因为这使得我们可以聚精于程序的设计上,而不是具体的细节。在这一点上,至少auto帮了一点忙。
当然,auto的自动类型推断背后都是编译器的功劳,因为auto声明的变量的类型和给他赋初始值的类型一致。

注意pf是函数指针数组名,所以

*pf == pf[0] == **&pf;//&pf是整个数组的地址,不要理解为是存储pf的地址哈,记住这是个数组:pf和&pf的值相同,
//但是pf + 1表示数组中下一个元素的地址
//而&pf + 1表示数组后方一个12字节(3个指针,一个指针要4个字节存储,因为是32位的)内存块的地址

创建 指向函数指针数组 的 指针

重点是他指向的是整个数组哈,pf只是指向数组首元素,这是不一样的

auto pc = &pf;//幸好有auto,不然我真不会写了,这样pc + 1就是指向数组后面下一个12字节内存块的指针了

//书上当然还是教了该怎么不用auto写
const double * (*(*pd)[3])(const double *, int);//直接把函数指针的pf换为(*pd)就好啦,这样就又多了一层间接
//写着不难,但不懂得人一看····绝对晕乎

那怎么写函数调用呢

double av[3] = {1.2, 5.3, 5.8}; 

const double * x = (*pf[0])(av, 3); 
const double * y = pf[0](av, 3);

*x = *(*pf[0])(av, 3);
*y = *pf[0](av, 3);

示例1 指向函数指针数组的指针(并不少见)

类的虚方法的实现基本都要用到指向函数指针数组的指针的哦吗,但是幸运的是,具体细节都是编译器处理

#include 
const double * f1(const double ar[], int n);
const double * f2(const double *ar, int n);
const double * f3(const double *, int);

int main()
{
    double x[3] = {1.22, 3.45, 6.67};

    const double * (*pf1)(const double *, int) = f1;
    auto pf2 = f2;
    std::cout << "Using pointers to functions:\n";
    std::cout << "Address    Value\n";
    std::cout << (*pf1)(x, 3) << ": " << *(*pf1)(x, 3) << std::endl;
    std::cout << pf2(x, 3) << ": " << *pf2(x, 3) << std::endl;

    const double * (*pa[3])(const double [], int) = {f1, f2, f3};
    auto pb = pa;
    std::cout << "\nUsing an array of pointers to functions:\n";
    std::cout << "Address    Value:\n";
    for (int i = 0; i < 3; ++i)
        std::cout << pa[i](x, 3) << ": " << *pa[i](x, 3) << std::endl;
    std::cout << "\nUsing a pointer to a pointer to a function:\n";
    std::cout << "Address    Value:\n";
    for (int i = 0; i < 3; ++i)
        std::cout << pb[i](x, 3) << ": " << *pb[i](x, 3) << std::endl;

    std::cout << "\nUsing pointers to an array of pointers:\n";
    std::cout << "Address    Value:\n";
    auto pc = &pa;//easy way to declare pc
    const double * (*(*pd)[3])(const double [], int) = &pa;//hard way to declare pd
    std::cout << (*pc)[0](x, 3) << ": " << *(*pc)[0](x, 3) << std::endl;
    std::cout << (*pd)[0](x, 3) << ": " << *(*pd)[0](x, 3) << std::endl;
    return 0;
}
//为了测试函数指针,所以定义了这么无聊的函数
const double * f1(const double ar[], int n)
{
    return ar;
}
const double * f2(const double *ar, int n)
{
    return ar + 1;
}
const double * f3(const double *ar, int n)
{
    return ar + 2;
}
Using pointers to functions:
Address    Value
0x6dfeb8: 1.22
0x6dfec0: 3.45

Using an array of pointers to functions:
Address    Value:
0x6dfeb8: 1.22
0x6dfec0: 3.45
0x6dfec8: 6.67

Using a pointer to a pointer to a function:
Address    Value:
0x6dfeb8: 1.22
0x6dfec0: 3.45
0x6dfec8: 6.67

Using pointers to an array of pointers:
Address    Value:
0x6dfeb8: 1.22
0x6dfeb8: 1.22

除了auto以外,还可用typedef简化代码

typedef const double * (*pf)(const double *, int);//pf成了一个类型名,可以直接用它声明变量
pf pf1 = f1;//pf1是一个pf类型的指针变量,指向函数f1

你可能感兴趣的:(C++)