C++学习笔记(七)

一、预处理

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 = &num; // &取地址
    // 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 = &num;

(5)指针变量的类型

①. 指针变量自身的类型:将指针变量名拖黑,剩下的类型就是指针变量自身的类型

int *p // p自身的类型是 int *

指针变量自身的类型一般用于赋值语句的判断

int num = 10;
int *p = &num;
// 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;

num4B
0x040x030x020x01
低地址高地址

案例1:取出0x0102的值

#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    int num = 0x01020304;

    // 跨度为2个字节,且0x0102占两个字节,所以使用short类型
    short *p = (short *)&num;
    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 *)&num;
    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 *)&num;
    // 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 = &num;

short data = 20;
p = &data

万能指针一般用于函数的形参,达到算法操作多种数据类型的目的

注意:不能直接对void *p 的指针变量取 * 

int num = 10;
void *p = &num;

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 = &num;

    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 = &num;
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 = &num;
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列的列地址

arr1234
arr+15678
arr+29101112
*(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]1234
arr[1]5678
arr[2]9101112
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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

不懂编程的大学生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值