一是char数组,用数组名表示字符串,实际char数组名就是char指针
二是char指针··
三是字符串常量,即双引号括起来的,实际上字符串常量就是指向他自己的指针,也是第一个字符的地址,说白了还是char指针
#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);
就是记不住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早期根本不允许传递和返回结构本身。
但对于小型结构,也不失为一种方法。
这种方式编程特别简单
#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.
#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 >> 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
我出错
#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数组看起来就像是一维数组一样,因为每一个字符串是一个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
我的错:
大家都是名称空间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
C的递归就没学清楚,返回每一级反正觉得很对称,但是自己写程序就有点晕乎乎
#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!
#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函数的返回值
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)肯定不等价
但是 C和C++采用的措施是:不管这个矛盾。程序员看哪个顺眼就用哪个。我看(*pf)更顺眼,因为一看就是函数指针。
#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).
假设有三个这样的函数原型,当然他们都是同函数类型的,只是写法不一样
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);
类的虚方法的实现基本都要用到指向函数指针数组的指针的哦吗,但是幸运的是,具体细节都是编译器处理
#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
typedef const double * (*pf)(const double *, int);//pf成了一个类型名,可以直接用它声明变量
pf pf1 = f1;//pf1是一个pf类型的指针变量,指向函数f1