-1.void func(para) 代表没返回值的函数
2.函数的返回数不可以是数组,可以是其他类型比如整型,浮点型,指针甚至是结构和对象
3.函数计算完成时将返回值放置在指定位置,有可能是在cpu寄存器有可能是在内存中,然后调用函数从这个位置获得返回值。
4.为什么要有函数原型:函数原型也叫函数声明,将函数的返回值和参数的类型和数量告诉编译器,即实现函数可以先被调用,后被实现。
5.在函数原型的参数列表中,只用包括类型就可以了,可以包括变量名,也可以不包括;原型中的变量名相当于占位符,因此不必与函数定义中的变量名相同。
6.假设cube的函数原型是 doube cube(dobule x),调用时double volume = cube(side);cube()使用的是side的副本,在cube函数修改形参不会对size进行改变;cube函数在执行时,side的值被传给cube函数,然后会创建一个 名为x的double变量,将传递的值赋给x。
7.函数中声明的变量是该函数私有的,函数被调用时分配内存,函数结束时内存被释放,这样的变量叫做局部变量。不同的函数的局部变量可以存在变量名一样的情况。
8.数组名被解释为数组的第一个元素的地址;对数组名使用sizeof得到是整个数组的长度,对元素sizeof得到的是元素的长度;将&运用于数组名时返回的是整个数组的地址。
将指针(包括数组名)+1,实际上加了一个与指针指向类型的长度相等的值
9.c++按值传递数据,使用普通参数时,函数使用的是数据的副本,而使用数组时,函数使用的是原始数据;可以用const保护数组不被修改,比如void show_array(const double ar[],int n);这种方式可以保护原始数组不被修改,但是并不意味着原始数组必须是常量。
10.c++中使用数组须获得数组的数据种类,数组的起始位置和数组元素数量,一般有两种方法:1.将数组起始处的指针作为一个参数,数组的长度作为另一个参数;2.指定元素区间:传递两个指针,一个标识数组的开头,另一个标识数组的尾部。
11.const 与指针:
int age = 39;
const int *pt = &age;
// pt的声明不意味着它指向的值实际上是一个常量,而只是意味着对于pt来说这个值时常量,即不能通过pt来更改这个值。
*pt = 20; //不被允许
age = 20;//被允许
---------------------------------------------------------------------------------
const float g_earth = 9.80;
const float *pe = &g_earth;//被允许
const float g_moon = 1.63;
float *pm = &g_moon;//不被允许
//对于第一种情况,既不能直接修改g_earth的值,也不能通过pe修改g_earth的值
//第二种情况c++是禁止的;c++禁止将const变量的地址赋给非const指针,如果非要这样做,可以用强制类型转换突破这个限制。
---------------------------------------------------------------------------------
//只有一级间接关系时,允许将非const指针赋给const指针
int age = 39;
int *pd = &age;
const int *pt = pd;
//有两级间接关系时,将不再允许将const和非const指针混合赋值的方式。
---------------------------------------------------------------------------------
// 可以将const数据和非const数据的地址赋给const指针,但是不能将const数据的地址赋给非const指针(因为这样违背了定义const指针的初衷)。
const int months[12] = {......}
int sum(int arr[],int n);
int j = sum(months,12)//不被允许,试图将const指针赋给非const指针。
----------------------------------------------------------------------------------
//常量指针可以防止通过指针来改变所指向的变量的值,但是不能防止改变指针的值。
int age = 39;
const int *pt = &age;
int sage = 80;
pt = &sage;//被允许
//可以声明指针常量使得无法修改指针的值。
int sloth = 3;
const int *ps = &sloth; // 不允许通过ps修改sloth的值但是可以改变ps的值
int * const finger = &sloth;//finger 只能指向sloth,但是允许通过finger改变sloth的值
//还可以声明指向const对象的const指针
double trouble = 2.0E30;
const trouble *const stck = &trouble;//stick只能指向trouble,而且不能通过stick改变trouble的值
12.函数和二维数组:
int array[m][n];
int total = sum(array,m);
sum的原型可以是 int sum(int (*arr)[n],int m):array是一个数组名,它的第一个元素本身是一个个数为n,类型为整型的数组,所以data的类型是指向一个四个int类型组成的数组的指针。
sum的原型也可以是 int sum(int arr[][n],int m)
二维数组元素的表示方法:arr[r][c] == *(*(arr+r)+c)
13.函数和c风格字符串
要将字符串作为参数传递给函数,表示字符串的方式有三种:char数组,字符串字面值,char类型指针。在函数原型内,上面三种选择的都是char型指针。
字符串中有内置的结束字符’\0’,所以不必将数组长度传递给函数。
函数返回一个字符串,返回的是char*
14.函数和结构
结构将其数据组合成单个实体或数据对象,该实体被视为一个整体。结构变量的行为接近基本类型的变量。如果将结构作为参数传递是按值传递的,如果结构非常大,则会增加内存要求,降低系统运行速度,所以许多程序员倾向于传递结构的地址。
15.函数和array对象
c++中,类对象是基于结构的,所以结构的编程方面考虑的因素也适用于类。类也可以按值传递给函数,函数处理的是原始对象的副本;类也可以传递指向对象的指针,让函数可以操作原始对象。
std::array<double,4> expenses // 使用一个array储存一年四季的开支
void show(std::array<double,4> da); // da an object
void fill(std::array<double,4>* pa); // pa a pointer
show(expenses); //按值传递
fill(&expenses);
16.函数指针
函数的地址是储存其机器语言代码的内存的开始地址。
要将函数作为参数进行传递,必须传递函数名。注意要区分传递的是函数的地址还是函数的返回值。
process(think); //传递函数地址
thought(think()); //传递函数的返回值
声明函数指针时应指定函数的返回类型以及函数的特征标(参数列表)
提供正确的运算符优先级,正确的声明函数指针(括号的优先级要比*高)。
double pam(int); //函数原型
double (*pf)(int); //pf是一个指向参数列表为一个int,返回类型为double的函数的指针。
double *pf1(int); // pf1是一个参数列表为一个int,返回类型为一个double指针的函数。
pf = pam; //可以将函数的地址赋给pf
函数指针在赋值时,需要和用来赋值的函数的特征标和返回类型相同,否则编译器将拒绝这种赋值。
double ned(dobule);
int ted(int);
double (*pf)(int);
pf = ned; //编译失败,特征标不同。
pf = ted; //编译失败,返回类型不同。
使用指针来调用函数,使用(*pf)时,将它看做函数名即可。
double pam(int);
double (*pf)(int);
pf = pam;
double x= pam(4); //通过函数名调用函数
double y = (*pf)(5); //通过指针调用函数
double z = pf(5); //通过指针调用函数
// 也就是说 (*pf)(5) 等价于 pf(5)
深入探讨函数指针
const double *f1(const double ar[],int n)//在函数原型中, const double ar[] 与 const double *ar意义相同
const double *f3(const double *,int n) // 参数列表里可以将形参参数名省略
const double *f2(const double [],int n)// 参数列表里可以将形参参数名省略
const double *(*p1)(const double *,int) = f1;//声明一个函数指针p1指向f1,const double * 指返回值类型
auto p2 = f2; //将f2的值赋给p2
cout <<(*p1)(av,3)<<":"<<*(*p1)(av,3)<<endl; //先输出一个double值得地址(函数返回值),后输出一个double值
cout <<p2(av,3)<<":"<<*p2(av,3)<<endl; //先输出一个double值得地址(函数返回值),后输出一个double值
// 运算符 [] 的优先级高于*
const double *(*pa[3])(const double *,int) = {f1,f2,f3} //声明数组pa,元素数量有三个,元素是指向返回值为const double*的函数的指针。
auto pb = pa; //声明一个和pa同类型的数组
const double *px = pa[0](av,3); //获取指针数组中的元素
const double *py = (*pb[1])(av,3); //获取指针数组中的元素
double x = *pa[0](av,3); //通过*获取指针指向的值
double y = *(*py)(av,3); //通过*获取指针指向的值
//可以创建一个指向数组pa的指针
auto pc =&pa;
const double *(*(*pd)[3])(const double,int) = &pa;
//pa(数组名)是数组第一个元素的地址,即&pa[0];&pa是整个数组的地址。pa和&pa的值相同,但类型不同,例如+1的意义不同。要获得第一个元素的值: *pa == pa[0] == **&pa
17.使用typedef对复杂的声明定义简单的别名
typedef const double *(*p_fun)(const double *,int); //将指向特征标为(const double *,int),返回值类型为 const double *的函数的指针的别名定义为 p_fun
p_fun p1 = f1; //将f1的地址赋给 p1
p_fun pa[3] = {f1,f2,f3}; // 定义一个指针数组,元素为3,指针指向p_fun类型的函数
p_fun (*pd)[3] = &pa; // 定义一个指向数组pa的指针