一、预处理
1. 内存分区
进程:可执行文件从运行到结果整个动态的过程就叫进程(占内存空间)
在32位平台每一个进程占4G空间(虚拟空间)
内存 | ||
可读可写 | 堆区 | 使用malloc,calloc,realloc,free动态申请 |
& 可读可写 | 栈区 | 局部变量,函数的形参,返回值>48 |
& 可读可写 | 全局区 | 普通全局变量,静态全局变量,静态局部变量 |
只读 | 文字常量 | 数值常量,字符常量,字符串常量,符号常量 |
只读 | 代码区 | 代码的二进制指令 |
2. 变量的存储
(1)普通局部变量
定义形式:在 { } 里面定义的普通变量叫普通局部变量
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 0; // num局部变量
{
int data = 0; // data局部变量
}
return 0;
}
作用范围:所在 { } 复合语句之间有效
生命周期:所在 { } 符合语句执行完释放
存储区域:栈区
注意事项:1. 普通局部变量不初始化,内容不确定
2. 普通局部变量同名遵循就近原则(尽量别同名)
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 0; // num局部变量
{
int num = 10; // 不同{}中的num局部变量
cout << num << endl; // 遵循就近原则: 10
}
cout << num << endl; // 0
return 0;
}
(2)普通全局变量
定义形式:在函数外定义的普通变量
#include <iostream>
using namespace std;
int num = 10;
int main(int argc, char *argv[])
{
cout << num << endl; // 10
{
cout << num << endl; // 10
}
return 0;
}
作用范围:当前源文件以及其他源文件都有效
生命周期:整个进程
存储区域:全局区
注意事项:1. 全局变量不初始化内容为0
2. 全局变量和局部变量同名优先选择局部变量
#include <iostream>
using namespace std;
int num = 10;
int main(int argc, char *argv[])
{
int num = 20;
cout << num << endl; // 20
{
cout << num << endl; // 20
}
return 0;
}
3. 其他源文件使用全局变量必须对全局变量进行extern声明(变量所在的源文件)extern声明外部可用。该变量或函数来自于其他源文件
code_1.cpp
// extern 声明data为int型,来自其他源文件
extern int data;
void add_data(void)
{
data += 100;
return;
}
code_2.cpp
// 声明 add_data 函数返回值为空,来自其他源文件
extern void add_data(void);
int data = 10; // 普通全局变量
int main()
{
add_data();
cout << "data = " << data << endl;
}
(3)静态局部变量
定义形式:在 { } 加static定义的局部变量就叫静态局部变量
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 20; // 普通局部变量
static int data = 40; // 静态局部变量
cout << num << endl; // 20
cout << data << endl; // 40
return 0;
}
作用范围:所在的 { } 复合语句之间有效
生命周期:整个进程有效
存储区域:全局区
注意事项:1. 静态局部变量不初始化内容为0
2. 静态局部变量整个进程都存在(第一次定义有效)
#include <iostream>
using namespace std;
void test();
int main(int argc, char *argv[])
{
test(); // num1 = 11, num2 = 11
test(); // num1 = 12, num2 = 11
test(); // num1 = 13, num2 = 11
test(); // num1 = 14, num2 = 11
return 0;
}
void test()
{
static int num1 = 10; // 静态局部变量
int num2 = 10; // 普通局部变量
num1++;
num2++;
cout << "num1 = " << num1 << endl;
cout << "num2 = " << num2 << endl;
}
(4)静态全局变量
定义形式:在函数外加static修饰定义的变量就是静态全局变量
int data1 = 10; // 普通全局变量
static int data2 = 20; // 静态全局变量
int main()
{
return 0;
}
作用范围:只能在当前源文件使用,不能在其他源文件使用
生命周期:整个进程
存储区域:全局区
注意事项:1. 静态全局变量不初始化内容为0
2. 静态全局变量只能在当前源文件使用
二、全局函数和静态函数
1. 全局函数(函数默认都为全局函数)
全局函数:在当前源文件以及其他源文件都可以使用
如果在其他源文件使用需要extern对全局函数进行声明
2. 静态函数(加static修饰的函数)
静态函数只能在当前源文件使用
static void func(void)
{
// 静态函数
}
三、头文件包含
头文件包含:在预处理阶段将头文件的内容原封不动的包含在目标文件中
#include <head.h> 建议 < > 包含系统头文件
< > 从系统指定目录寻找 head.h 的头文件
#include "head.h" 建议 " " 包含自定义头文件
" " 先从当前目录寻找 head.h 头文件,如果找不到再到系统指定的目录下寻找
四、宏
使用关键字 define 定义叫做 宏
#define PI 3.14 // 宏定义
在预处理阶段使用 3.14 替换所有出现 PI 的位置(宏展开)
注意:不要在宏后面加 ;(分号)
#include <iostream>
#include <math.h>
#define PI 3.14
using namespace std;
int main(int argc, char *argv[])
{
int r = 0;
cout << "请输入圆的半径:";
cin >> r;
cout << "圆的面积为:" << PI*pow(r,2) << endl;
return 0;
}
宏尽量大写与普通变量区分开
1. 不带参数的宏
#define PI 3.14
#define MY_STR "Hello World"
#define N 100
宏的作用范围:是从定义处开始到当前文件结束都有效
#undef 可以结束宏的作用域
宏没有作用域的限制,只在当前源文件有效
2. 带参数的宏(宏函数)
#include <iostream>
#define MY_MUL(a,b) a*b
using namespace std;
int main(int argc, char *argv[])
{
int a = 10;
int b = 20;
cout << a << '*' << b << '=' << MY_MUL(a,b) << endl;
return 0;
}
(1)宏的参数不能有类型
#define MY_MUL(a, b) a*b // erro
(2)宏不能保证参数的完整性
#include <iostream>
#define MY_MUL(a,b) a*b
using namespace std;
int main(int argc, char *argv[])
{
int a = 10;
int b = 20;
cout << MY_MUL(a,b) << endl; // 200
cout << MY_MUL(a+20,b+20) << endl; // 430
return 0;
}
a+20=30,b+20=40,乘积应该为1200,而不是430
可以使用()的形式让带参数的宏具有一定的完整性
#include <iostream>
#define MY_MUL(a,b) a*b
using namespace std;
int main(int argc, char *argv[])
{
int a = 10;
int b = 20;
cout << MY_MUL(a,b) << endl; // 200
cout << MY_MUL(a+20,b+20) << endl; // a+20*b+20=10+20*20+20=430
cout << MY_MUL((a+20),(b+20)) << endl; // 1200
return 0;
}
(3)宏不能作为结构体,类的成员
(4)案例
#include <iostream>
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) ((a)*(b))
using namespace std;
int main(int argc, char *argv[])
{
int a = 10;
int b = 20;
cout << MY_MUL1(MY_MUL2(10+10,20+20),MY_MUL1(10+10,20+20)) << endl; // 8220
// ((10+10)*(20+20))*10+10*20+20=(20*40)*10+200+20=800*10+220=8220
return 0;
}
3. 宏函数和普通函数的区别
带参宏被调用多少次就会展开多少次,执行代码的时候没有函数调用的过程,不需要压栈弹栈。所以带参宏,是浪费了空间,因为被展开多次,节省了时间
带参函数,代码只有一份,存在代码段,调用的时候去代码段取指令,调用的时候要压栈弹栈。有个调用的过程。所以说,带参函数是浪费了时间,节省了空间。
带参函数的形参是有类型的,带参宏的形参是没有类型的。
函数有作用域的限制,可以作为类的成员
宏函数没有作用域的限制,不能作为类的成员
五、指针
1. 指针变量
(1)内存概述
在32位平台,每一个进程都拥有4G的空间
系统为内从的每一个字节分配一个32位的地址编号(虚拟地址),这个编号称之为地址
无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节,即任何类型的指针变量都是4个字节的大小
(2)地址和指针变量的关系
地址就是内存的地址编号
指针变量:本质是变量,只是该变量保存的是内存的地址编号(不是普通的数值)
(3)指针变量的定义
①. 定义步骤:*修饰指针变量名称p,保存谁的地址就先定义谁,从上往下整体替换
案例:
// 定义一个指针变量p,保存int num的地址
*p // 修饰指针变量
int num // 保存谁的地址就定义谁
int *p // 从上往下整体替换
// 定义一个指针变量p,保存int arr[5]的首地址
*p
int arr[5]
int (*p)[5]
// 定义一个指针变量p,保存函数的入口地址 int func(int,int)
*p
int fun(int,int)
int (*p)(int,int)
// 定义一个指针变量p,保存结构体变量的地址 struct stu lucky
*p
struct stu lucky
struct stu *p
// 定义一个指针变量p,保存指针变量int *p的地址
*p
int *p
int **p
②. 在32位平台任何类型的指针变量都是4字节
#include <iostream>
#define MY_MUL1(a,b) a*b
#define MY_MUL2(a,b) ((a)*(b))
using namespace std;
int main(int argc, char *argv[])
{
cout << sizeof(int *) << endl;
cout << sizeof(long *) << endl;
cout << sizeof(short *) << endl;
cout << sizeof(float *) << endl;
cout << sizeof(double *) << endl;
cout << sizeof(char *) << endl;
cout << sizeof(int *****) << endl;
cout << sizeof(float *****) << endl;
cout << sizeof(char *****) << endl;
return 0;
}
③. 指针变量和普通变量建立关系
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 10;
int *p;
p = # // &取地址
// p的值就是num的地址,p保存了num的地址,p指向num
cout << *p << endl; // 10
return 0;
}
在定义指针时:* 仅仅修饰p为指针变量
在使用指针时:*p 表示取p所保存的地址编号对应空间的内容
(4)指针变量的初始化
指针变量在操作之前必须指向合法的地址空间
①. 指针变量如果不初始化,立即操作会出现段错误
②. 指针变量如果没有指向合法的空间,建议初始化为NULL
int *p = NULL; // NULL是赋值给了p int *p; p=NULL;
不要操作指向NULL的指针变量
③. 将指针变量初始化为合法的地址(变量的地址,动态申请的地址,函数入口地址)
int num = 10;
int *p = #
(5)指针变量的类型
①. 指针变量自身的类型:将指针变量名拖黑,剩下的类型就是指针变量自身的类型
int *p // p自身的类型是 int *
指针变量自身的类型一般用于赋值语句的判断
int num = 10;
int *p = #
// num 为 int &num 为 int * ------> 对变量名取地址,整体类型加一个*
// p 为 int * *p 为 int ------> 对指针变量取* 整体类型减一个*
*&p == p
// 在使用中,&与*相遇,从右往左依次抵消
②. 指针变量指向的类型(重要)
将指针变量名和离他最近的一个 * 一起拖黑,剩下的类型就是指针变量指向的类型
int *p; // p指向的类型为int
③. 指针变量的指向类型决定了取值宽度
int 类型数据 4个字节,所以当指针变量的指向类型为 int 时,*p会取出4个字节的值
其他类型分析如上
④. 指针变量的指向类型决定了+1的跨度
int 类型数据 4个字节,所以当指针变量的指向类型为 int 时,*(p+1) 会跨4个字节取值
其他类型分析如上
(6)综合案例
int num = 0x01020304;
num | 4B | ||
0x04 | 0x03 | 0x02 | 0x01 |
低地址 | 高地址 |
案例1:取出0x0102的值
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 0x01020304;
// 跨度为2个字节,且0x0102占两个字节,所以使用short类型
short *p = (short *)#
cout << hex << *(p+1) << endl; // 102,开头的0省略
return 0;
}
案例2:取出0x02的值
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 0x01020304;
// 跨度为2个字节,但0x02占一个字节,以小的为准,所以使用char类型
char *p = (char *)#
cout << hex << int(*(p+2)) << endl; // 2,开头的0省略
// *(p+2)取出的是对应的字符,需转成数字
return 0;
}
案例3:取出0x0203的值
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 0x01020304;
// 跨度为1个字节,但0x0203占两个字节,以小的为准,所以使用char类型
char *p = (char *)#
// 0x0203占两个字节,需要转换short类型
cout << hex << *(short *)(p+1) << endl; // 203,开头的0省略
return 0;
}
(7)指针变量的注意事项
①. void 不能定义普通变量
void num; // erro 不能给num开辟空间
②. void * 可以定义指针变量
void *p; // ok p自身类型为void *,在32位平台任意类型的指针位4B
// 系统会为p开辟4B空间,所以定义成功
p就是万能的一级指针变量,能保存任意一级指针的地址编号
int num = 10;
void *p = #
short data = 20;
p = &data
万能指针一般用于函数的形参,达到算法操作多种数据类型的目的
注意:不能直接对void *p 的指针变量取 *
int num = 10;
void *p = #
cout << *p << endl; // erro p指向的类型为void 无法确定p的取值宽度,所以不能*p
对p取 * 之前,对p先进行指针类型强转、
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num = 10;
void *p = #
cout << *(int *)p << endl; // 10
return 0;
}
③. 指针变量未初始化不能取 *
int *p;
*p; // erro
④. 指针变量初始化 NULL 不能取 *
int *p = NULL;
*P; // erro
⑤. 指针变量不能越界访问
char ch = 'a';
int *p = &ch;
*p; // erro 越界访问非法内存
// char类型1字节 int类型4字节
int num = 10;
int *p = #
p++;
*p; // erro 越界访问
2. 数组元素的指针
(1)数组元素指针概述
数组元素的指针变量是用来保存数组元素的地址
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
// 定义一个指针变量,保存数组元素的地址
int *p;
p = &arr[0];
cout << *p << endl; // 10
p = arr; // arr作为地址,第0个元素的地址 arr=&arr[0]
cout << *p << endl; // 10
p = &arr[3];
cout << *p << endl; // 40
return 0;
}
(2)在使用中 [ ] 就是 *( ) 的缩写
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);
cout << "arr[1] = " << arr[1] << endl; // 20
cout << "arr[1] = " << *(arr+1) << endl; // 20
cout << "arr[1] = " << *(1+arr) << endl; // 20
cout << "arr[1] = " << 1[arr] << endl; // 20
// []是*()的缩写 []左边的值放在+左边,[]里面的值放在+右边,整体取*
return 0;
}
为什么 arr = &arr[0] ?
&arr[0] = &*(arr+0) = arr+0 = arr
案例1:p[-1]的值是(30)
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int *p = arr+3;
cout << p[-1] << endl; // 30
return 0;
}
案例2:p[1]的值是(50)
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int *p = arr+3;
cout << p[1] << endl; // 50
return 0;
}
(3)指向同一数组元素的两个指针变量间的关系
两指针变量相减等于他们间的元素个数
两指针变量赋值= p2=p1 他们指向同一处
两指针变量判断相等== p2==p1 他们是否指向同一处
两指针变量判断大写 > < >= <= != p1>p2 p1!=p2 判断他们的位置关系
两指针变量不能相加(重要) p1+p2无意义
3. 字符串与指针
(1)字符数组
char str1[128] = "hello world";
str1 是数组,开辟了128字节,存放字符串 "hello world"
sizeof(str1) == 128字节
(2)字符串指针变量
char *str2 = "hello world";
str2 的本质是指针变量,只有4字节,但字符串 "hello world"占12字节,str2可以存放这个12字节的字符串吗?
存放不了,这个赋值的动作仅仅是将字符串首元素的地址赋给了 str2,双引号有两个作用:1. 描述里面是一个字符串 2. 取字符串首元素的地址,字符串存储在文字常量区,文字常量区开辟的空间大小为字符串的大小,开辟完空间后把首元素的地址赋值给str2
sizeof(str2) == 4字节(32位平台) or 8字节(64位平台)
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
char *str2 = (char *)"hello world";
// 输出字符串只需要输出首元素地址
cout << str2 << endl; // hello world
cout << str2[6] << endl; // w
// str2[6] = 'h'; erro,文字常量区只能读不能写
return 0;
}
4. 指针数组
指针数组:本质是数组,只是数组的每个元素为指针
32位平台:
char *arr1[4];
short *arr2[4];
int *arr3[4];
sizeof(arr1) == 168
sizeof(arr2) == 168
sizeof(arr3) == 168
(1)数值的指针数组
int num1 = 10;
int num2 = 20;
int num3 = 30;
int num4 = 40;
int *p1 = &num1;
int *p2 = &num2;
int *p3 = &num3;
int *p4 = &num4;
需要记录多个地址时会定义过多的指针变量,此时可以通过类似数组存放数字的方式,定义一个指针数组存放指针
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int num1 = 10;
int num2 = 20;
int num3 = 30;
int num4 = 40;
int *arr[4] = {&num1,&num2,&num3,&num4};
int n = sizeof(arr)/sizeof(arr[0]);
for(int i=0;i<n;i++)
{
cout << "num" << i+1 << " = " << *arr[i] << endl;
}
return 0;
}
(2)字符指针数组
char *arr[4] = {"hello world","C++","Python","I am Iron Man!"};
每个字符串大于4字节,仅仅把每个字符串的首元素地址放在字符指针数组里,字符串存在文字常量区
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
char *arr[4] = {"hello world","C++","Python","I am Iron Man!"};
int n = sizeof(arr)/sizeof(arr[0]);
for(int i=0;i<n;i++)
{
cout << "第" << i+1 << "个字符串为:" << arr[i] << endl;
}
// 取每个字符串的第三个字符
for(int i=0;i<n;i++)
{
cout << "第" << i+1 << "个字符串的第3字符为个为:" << *(arr[i]+2) << endl;
}
return 0;
}
(3)二维字符数组
char *arr1[4] = {"hello world","C++","Python","I am Iron Man!"};
char arr2[4][128] = {"hello world","C++","Python","I am Iron Man!"};
arr1是在指针数组存放的是每个字符的首元素的地址
arr2是二维字符数组存放的是每个字符串
六、指针的指针
int num = 10;
int *p = #
int **q = &p;
n级指针变量可以保存n-1级指针变量的地址
七、数组指针
1. 数组首元素地址和数组首地址
数组首元素地址:&arr[0] == arr arr+1跳过一个元素
数组的首地址:&arr &arr+1跳过整个数组
2. 数组指针,本质是指针变量,保存的是数组的首地址
int arr[5] = {10,20,30,40,50};
int (*p)[5] = NULL; // 数组指针
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int (*p)[5] = NULL;
// int (*p)[5] = &arr 保存数组首地址
cout << "sizeof(p) = " << sizeof(p) << endl; // 4B
cout << "p = " << p << endl; // 4
cout << "p+1 = " << p+1 << endl; // 0x14 = 20, 1个元素4字节,5个元素20字节,跳过了整个数组
return 0;
}
p = &arr *p = *&arr = arr = 数组首元素地址
通过数组指针取出数组内的某个元素
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int (*p)[5] = &arr;
// 取出第三个元素
cout << "数组第三个元素为:" << *(*p+2) << endl; // 30
cout << "数组第三个元素为:" << p[0][2] << endl; // 30
// *(*p+2) = *(*(p+0)+2) = *(p[0]+2) = p[0][2]
return 0;
}
案例:
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int (*p)[5] = &arr;
cout << *((int *)(p+1)-2) << endl; // 40
// p为数组指针,+1跳到数组结束,int强转指针类型,跨度为4字节,-2回退8个字节,到元素40的地址
return 0;
}
总结:
int *arr[5]; // 指针数组 本质是数组 每个元素为int *
int (*arr)[5]; // 数组指针 本质是指针变量 保存的是数组的首地址
3. 二维数组和数组指针的关系
深入了解二维数组
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
二维数组名 代表的是第0行的行地址 +1跳过一个行
对行地址取 * 代表当前第0列的列地址
arr | 1 | 2 | 3 | 4 |
arr+1 | 5 | 6 | 7 | 8 |
arr+2 | 9 | 10 | 11 | 12 |
*(arr+2) | *(arr+2)+1 | *(arr+2)+2 | *(arr+2)+3 |
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
cout << arr+1 << endl; // 第二行的行地址
cout << *(arr+1)+1 << endl; // 第二行第二列的列地址
// *(arr+1)+1 = arr[1]+1
cout << *(*(arr+1)+1) << endl; // 第二行第一列的元素
// *(*(arr+1)+1) = *(arr[1]+1) = arr[1][1]
return 0;
}
(2)二维数组和一维数组指针的关系
一维数组指针和二维数组名是完全等价的
如果是为了操作二维数组,仅仅使用二维数组就行,一维数组指针是为了优化数组作为函数参数
int arr[n] int *p;
int arr[n][m] int(*p)[m];
int arr[n][m][k]; int *(p)[m][k]
n维数组和n-1维的数组指针等价
4. 多维数组的物理存储
不管几维数组在物理上都是一维存储,在逻辑上是多维的
arr[0] | 1 | 2 | 3 | 4 |
arr[1] | 5 | 6 | 7 | 8 |
arr[2] | 9 | 10 | 11 | 12 |
arr[2][0] | arr[2][1] | arr[2][2] | arr[2][3] |
用一维的方式访问二维数组
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof(arr[0][0]);
int *p = &arr[0][0];
int i = 0;
for(i=0;i<row*col;i++)
{
cout << p[i] << ' ';
}
cout << endl;
return 0;
}
七、指针与函数
1. 指针变量作为函数的参数
如果想在函数内部修改外部变量的值,需要将外部变量的地址传递给函数(重要)
案例1:单向传递之传值
函数内部不能修改外部变量的值
#include <iostream>
using namespace std;
void setNum(int data)
{
data = 100;
}
void test()
{
int num = 0;
setNum(num); // 单向传递之传值
cout << "num = " << num << endl; // 0 修改不成功,形参开辟了新的空间,修改的是形参的值
}
int main(int argc, char *argv[])
{
test();
return 0;
}
案例2:单向传递之传地址
函数内部就可以修改函数外部的值
#include <iostream>
using namespace std;
void setNum1(int *p) // int *p = &num
{
*p = 100;
}
void test1()
{
int num = 0;
setNum1(&num); // 单向传递之传地址
cout << "num = " << num << endl; // 100 修改成功
}
int main(int argc, char *argv[])
{
test1();
return 0;
}
2. 一维数组作为函数的参数
函数内部想操作(读或写)外部数组元素,将数组名传递给函数
一维数组作为函数的形参会被优化维指针变量
#include <iostream>
using namespace std;
// 一维数组作为函数的参数,会被编译器优化为指针变量
// void outArray(int arr[5], int n)
void outArray(int *arr, int n)
{
cout << "内部sizeof(arr) = " << sizeof(arr) << endl; // 4
int i = 0;
for(i=0;i<n;i++)
{
// cout << *(arr+i) << ' ';
cout << arr[i] << ' '; // 推荐
}
cout << endl;
}
int main(int argc, char *argv[])
{
int arr[5] = {10,20,30,40,50};
int n = sizeof(arr)/sizeof(arr[0]);
cout << "外部sizeof(arr) = " << sizeof(arr) << endl; // 20
// 遍历数组
outArray(arr,n);
return 0;
}
3. 二维数组作为函数的参数
函数内部想操作函数外部的二维数组,需要将二维数组名传递给函数
二维数组作为函数的形参会被有华为一维的数组指针
#include <iostream>
using namespace std;
// 二维数组作为函数的参数,会被编译器优化为一维指针变量
// void outArray(int arr[3][4], int row, int col)
void outArray(int (*arr)[4], int row, int col)
{
cout << "内部sizeof(arr) = " << sizeof(arr) << endl; // 4
int i = 0;
int j = 0;
for(i=0;i<row;i++)
{
for(j=0;j<col;j++)
{
cout << arr[i][j] << ' '; // 推荐
}
}
cout << endl;
}
int main(int argc, char *argv[])
{
int arr[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int row = sizeof(arr)/sizeof(arr[0]);
int col = sizeof(arr[0])/sizeof(arr[0][0]);
cout << "外部sizeof(arr) = " << sizeof(arr) << endl; // 48
// 遍历数组
outArray(arr,row,col);
return 0;
}
4. 函数的返回值类型为指针类型
将函数内部的合法地址通过返回值返回给函数外部使用
注意:函数不要返回普通局部变量的地址
错误示范:
#include <iostream>
using namespace std;
int *getddr(void)
{
int data = 100;
return &data; // 不要返回普通局部变量的地址
}
int main(int argc, char *argv[])
{
int *p = NULL;
p = getddr(); // 没有输出,函数调用完成后被释放,指针访问非法空间
cout << "*p = " << *p << endl;
return 0;
}
解决办法:
#include <iostream>
using namespace std;
int *getddr(void)
{
static int data = 100;
return &data;
}
int main(int argc, char *argv[])
{
int *p = NULL;
p = getddr();
cout << "*p = " << *p << endl;
return 0;
}
八、函数指针
1. 函数指针的定义
函数名代表函数的入口地址
函数指针:本质是一个指针变量,只是该变量保存的是函数的入口地址
// 函数指针 p只能保存有两个int类型形参以及返回值为int类型的函数的入口地址
int (*p)(int, int) = NULL;
#include <iostream>
using namespace std;
int myAdd(int x, int y)
{
return x+y;
}
int main(int argc, char *argv[])
{
int (*p)(int x, int y) = NULL;
cout << "sizeof(p) = " << sizeof(p) << endl; // 4
// 函数指针 与 函数入口地址建立关系
p = myAdd;
// 通过p调用函数
cout << "结果为:" << p(10,20) << endl;
return 0;
}
2. 函数指针变量注意事项
函数指针变量不要 +1 无意义
不要对函数指针变量取 * 无意义
int (*p)(int, int) = my_add;
// *p 会被编译器优化为p
函数指针变量判断大小 无意义
函数指针变量可以赋值 p1 = p2
函数指针可以判断相等 p2 == p1
3. 函数指针变量使用typedef定义
#include <iostream>
using namespace std;
int myAdd(int x, int y)
{
return x+y;
}
int main(int argc, char *argv[])
{
// 给函数指针类型取别名
typedef int (*FUN_TYPE)(int x, int y);
// 函数指针 与 函数入口地址建立关系
FUN_TYPE p = myAdd;
cout << "sizeof(p) = " << sizeof(p) << endl; // 4
// 通过p调用函数
cout << "结果为:" << p(10,20) << endl;
return 0;
}
4. 函数指针作为函数的参数
目的:让算法功能多样化
案例:设计一个算法,完成加减乘除
#include <iostream>
using namespace std;
float myAdd(float x, float y)
{
return x+y;
}
float mySub(float x, float y)
{
return x-y;
}
float myMul(float x, float y)
{
return x*y;
}
float myDiv(float x, float y)
{
return x/y;
}
// 设计算法 操作上面的函数
float myCal(float x, float y, float (*p)(float, float))
{
return p(x,y);
}
int main(int argc, char *argv[])
{
float x = 10;
float y = 20;
cout << x << " + " << y << " = " << myCal(x,y,myAdd) << endl;
cout << x << " - " << y << " = " << myCal(x,y,mySub) << endl;
cout << x << " * " << y << " = " << myCal(x,y,myMul) << endl;
cout << x << " / " << y << " = " << myCal(x,y,myDiv) << endl;
return 0;
}