学习c++ Part02

学习c++ Part02

前言

时间:2023年6月29日 - 7月3日(这几天懈怠了检讨下,学习语言就需要速战速决!)

1.函数

函数的定义声明调用都与Java类似。
需要注意的是:如果调用在定义函数之前那么需要对函数进行提前声明,声明的格式与java中的抽象函数书写类似。
代码示例:

#include <iostream>
using namespace std;

//提前声明函数
void getIntArray(int arr[5], int n);
void sortIntArray(int arr[5], int n);
void printIntArray(int arr[5], int n);
int main() {
   //定义数组
   int arr[5] = {0};
   int n = sizeof(arr)/sizeof(arr[0]);
   //获取键盘输入的数组
   getIntArray(arr,n);
   //对数组进行排序
   sortIntArray(arr,n);
   //打印数组
   printIntArray(arr,n);
   return 0;
}
void printIntArray(int arr[5], int n) {
   for (int i = 0; i < n; ++i) {
       cout<<arr[i]<<" ";
   }
}

void sortIntArray(int arr[5], int n) {
   //冒泡排序
   for (int i = 0; i < n - 1; ++i) {
       for (int j = 0; j < n - i - 1; ++j) {
           if(arr[j]>arr[j+1]){
               int tmp = arr[j];
               arr[j] = arr[j+1];
               arr[j+1] = tmp;
           }
       }
   }
}

void getIntArray(int arr[5], int n) {
   cout<<"intput 5 numbers:";
   for (int i = 0; i < n; ++i) {
       cin>>arr[i];
   }
}

注意点:

  • 调用在定义之前,需要对方法进行提前声明
  • 普通变量,调用函数不能对其进行修改,引用变量(如数组、对象…)可以进行修改
void test(int num){
num++; 
}
int main(){
   int num = 10;
   test(num);
   cout<<num; //结果仍然为10,不是11
}

全局函数(默认函数)

  • 其他源文件都可以使用
  • 谁用谁extern声明

静态函数

  • 只能在当前源文件使用
    在这里插入图片描述

2.预处理

进程:可执行文件 从运行到结束 整个动态过程 叫作进程
32位平台。每个进程分配4G的虚拟空间,64 8G?
内存:

(只读)文字常量区:数值常量、字符常量、字符窜常量、符号常量
(只读)代码区:代码的二进制指令
(&可读可写)栈区:局部变量、函数的形参、返回值<48
(&可读可写)全局区:普通全局变量、静态局部变量、静态全局变量
(可读可写)堆区:使用malloc、calloc、realloc、free动态申请

2.1 变量

  • 普通局部变量(栈区)
    • 作用范围和生命周期在{ }有效
    • 不初始化 内容随机 ;局部同名 就近原则
  • 普通全局变量(全局区)
    • 当前和其他源文件都有效 生命周期整个进程
    • 不初始化 内容为0;与局部变量同名优先选择局部变量
    • 其他文件使用全局变量时,需要extern声明(谁使用谁声明
  • 静态局部变量(全局区)
  • 作用范围:所在的{}复合语句之间有效 ;生命周期:整个进程有效
  • 不初始化内容为0;整个进程都存在(第一次定义有效)
void fun04()
{
   static int num=10;
   num++;
   cout<<"num = "<<num<<endl;
}
int main() {
   fun04();//num = 11
   fun04();//num = 12
   fun04();//num = 13
   fun04();//num = 14
}
  • 静态全局变量(全局区)
    • 作用范围:只能在当前源文件中进行使用 生命周期:整个进程
    • 不初始化内容为0

3.头文件

#include <head.h> //建议使用此形式 从系统目录中寻找
#include "head.h" //不建议使用此形式 优先从当前目录中寻找,找不到再从系统目录中寻找

4.宏函数

  • 编译四阶段:预处理、编译、汇编、链接
  • 不带参数的宏定义
#define PI 3.14  
#define MY_STR "hello world" 
#define N 100
  • 带参数的宏定义
#define MY_MUL(a, b)  a*b //宏不能有数据类型
cout<<MY_MUL(10,20);//10*20
  • 结束宏的作用域
    • 使用#undef
  • 注意事项:
    • 宏不能有参数类型
    • 宏不能保证参数的完整性,可以通过()来保证参数完整性
    • 宏的作用范围:从定义开始到当前文件结束都有效
    • 宏不能构成结构体、类的成员
  • 宏函数与普通函数的区别:
    • 宏函数不需要进栈出栈,调用多少次就会展开多少次,用空间换时间
    • 带参函数调用的时候需要进栈出栈,所以说节省了空间浪费了时间

5.指针

在32位平台,系统为每一个字节分配32的地址编号,编号称为地址,指针变量也是4字节大小

5.1 普通变量与指针变量建立关系:

int num = 10;
int *p = &num;  //&num 表示的num地址
cout<<*p; // 10(指向地址的内容)
cout<<p // 32位地址
// *p等价于num; p等价于&num

说法:

✅ 指针p指向了num
✅ 指针p保存了num地址
❎ 指针p指向了num地址

5.2 指针初始化

  • 如果不初始化 立即操作 会出现错误
    建议初始化为NULL
int *p = NULL;
  • 要学会判断指针变量的类型
//将指针名字去掉就是指针类型
//知道如何判断指针类型,可用于赋值判断语句
int num = 10 ;
int *p = &num;
//num 为 int ; &num 为 int * --> 对变量名取地址->整体加*
//p 为 int * ; *p 为 int --> 指针变量取* -> 整体减*
*&p == p
  • 指针变量指向类型
    将*和指针名去掉就是指向类型 int *p = &num 指向的就是int类型
  • 指针变量的指向类型决定了指针的指向宽度
  • 指针变量的指向类型决定了+1的跨度
int *p1 = &num; //宽度为4字节 +1的跨度为4字节
short *p2 = &num; //宽度为2字节 +1的跨度为2字节
char *p3 = &num; //宽度为1字节 +1的跨度为1字节

案例了练习:
在这里插入图片描述

int num = 0x01020304;
//案例1 :取出0x0102的值
short *p1 = (short *)&num;
*(p+1);
//案例2:取出0x02的值
char * p2 = (char *)&num;
*(p+2);
//案例3:取出0x0203的值
char * p3 = (char *)&num;
*(short *)(p+1);

5.3 指针变量的注意事项

5.3.1 void 不能定义普通变量,void * 可以定义指针变量
// void num: ❎
void *p ; //✅
  • p是万能的一级指针,如果用于函数形参可以达到操作多种数据类型的目的
  • 不要对void类型指针不能取※操作
int num = 10;
void *p = &num;
*p;//err p指向的类型为void 无法确定p的取值宽度 所以不能*p
5.3.2 指针变量未初始化不要取* ,初始化NULL不要取 * (段错误)
5.3.3 指针变量 不要越界
char ch = 'a';
int *p = &ch;
*p;//error 越界访问非法内存

5.4 数组元素指针

定义:指针只是指向数组第一个元素的地址
int arr[4] = {10,20,30,40}; 
int *p = &arr[0]
int *p = arr // arr = &arr[0]
// 也可以单独指向某个位置的元素
int *p = arr[2]; 
数组元素的指针变量和数组等价

*(p+1) = arr[1];

int arr[4] = {10,20,30,40};
int *p = arr;
*(p+1) = arr[1]; 
在使用中 【】就是 *()的缩写
int arr[5] = {10, 20, 30, 40, 50};
arr[1] = *(arr+1) = 1[arr] //20
  • 为啥arr = &arr[0]
&arr[0] == &*(arr+0) == arr+0 == arr
指向同一个数组元素的两个指针变量的关系

在这里插入图片描述

5.5 字符串指针

自我理解:字符串指针域数值指针的不同
字符串指针,指向的是字符串首元素的地址,而整个字符串放在了文字常量区(只读)可以通过首地址取到全部,这也就是为什么不需要加*也能取到整个字符串的原因
数值指针,则需要通过加* 来获取到指向地址的内容数值

图示:
在这里插入图片描述

“xxx” 可以表示字符窜 也可以表示"xxx"字符串的首元素地址
int *str = "hello world";
cout<<str; // hello world
cout<<*str; //h
因为字符串放在文字常量去不可写 不能赋值
int *str = "hello world";
str[6] = 'W' // 错误 权限冲突不可写

5.6 数值的指针数组

代码示例:

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<<*arr[i]<<" ";//10 20 30 40
}
cout<<endl;

5.7 字符指针数组

在这里插入图片描述
代码示例:

char *arr[4] = {"hahaha","hehehe","lalala","xixixi"};
int n = sizeof(arr)/sizeof(arr[0]);
for(int i = 0 ; i < n ; i++){
	cout<<arr[i]<<" ";
}

5.8 二维字符数组

char *arr1[4]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};
char  arr2[4][128]={"hehehehe", "xixixixixi", "lalalalala", "hahahahaha"};

arr1是在指针数组 存放的是每个字符串的首元素的地址
arr2是二维字符数组 存放的是每个字符串

5.9 指针的指针

n级指针变量可以保存n-1级指针变量的地址
int num = 10;
int *p = &num;
int **q = &p;
cout<<*p //10
cout<<**q // 10

5.9 指针数组和数组指针

指针数组与数组指针:

int *arr[5];//指针数组 本质是数组 每个元素为int *
int (*arr)[5];//数组指针 本质是指针变量 保存的是数组的首地址(概数组必须5个元素每个元素为 int)

数组指针的定义方式:

int arr[5] = {10,20,30,40,50};
int (*p)[5] = &arr;
  • &arr == p 代表的是数组首地址
  • *p == *&arr == arr 代表的是 数组的首元素地址(即第一行的首元素地址)
  • *p+1 == *&arr+1 == arr + 1 代表的是第一行1号位元素的地址
  • *(p+1) 代表第二行首元素的地址
  • *(* (p+1)) 代表第二行首元素的地址中的内容

5.10 数组与数组指针的关系

n维数组与n-1维数组指针是完全等价的
int arr[n];    		int *p;
int arr[n][m]; 		int (*p)[m]
int arr[n][m][k]; 	int (*p)[m][k]
n维数组 和n-1维的数组指针 等价
数组在作为函数参数时会被编译器自动优化成数组指针(64平台8B),n维数组优化成n-1维数组指针

5.11 多维数组在物理上 都是一维存储

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;
//输出结果:1-12

6.指针与函数

6.1 指针变量作为函数的参数

函数内部不能修改外部变量的值,但是当将外部变量的地址作为参数传入函数,函数则可以对该变量进行修改

void setNum02(int *p)//int *p=&num;
{
	//*p == num
	*p = 100; 
}
void test01()
{
	int num =0;
	setNum02(&num);//单向传递之 传地址
	cout<<"num = "<<num<<endl;//100 修改成功 
}

6.2 函数的返回值类型为指针

将函数内部参数的合法地址,作为返回值,给外部使用。
注意:函数返回的是static修饰的静态局部变量的地址
为什么:如果是普通局部变量,在{}函数调用完毕后会进行释放,那么返回的指针指向的是一个非法地址

int* getAddr(void)
{
	//int data = 100;//不要返回普通局部变量的地址 
	static int data = 100;
	return &data;
}
void test04()
{
    int *p = NULL;
    p = getAddr();
    cout<<"*p = "<<*p<<endl;//100
}

6.3 函数指针

函数指针,本质是指针变量,保存的是函数的入口地址,64位平台占用8B
定义&调用示例:

int myAdd(int x,int y){ return x+y;}
//方式1:
//int  (*p)(int,int) = NULL; //指针尽量初始化
//p = myadd;
//方式2:
int (*p)(int,int) = myadd;
//调用(传入实参)
cout<<p(10,20) <<endl;// 30
函数指针变量的注意事项
  • 函数指针变量 不要+1 无意义
  • 不要对函数指针变量取* 无意义(*p会被编译器优化成p)
  • 函数指针变量 判断大小 > < 无意义
  • 函数指针变量 可以赋值 p2=p1
  • 函数指针变量 可以判断相等 p2 ==p1
函数指针使用typedef定义
int myAdd(int x,int y){ return x+y;}
typedef int (*FUN_TYPE)(int,int);
FUN_TYPE p = myAdd;
函数指针的使用目的(函数指针作为参数,类似于“多态”实现)

目的:让函数功能多样化(相当于参入不同的函数指针,调用不同的方法)
案例:设计一个算法实现 加减乘除

int myAdd(int x,int y) {return x+y; }
int mySub(int x,int y) { return x-y;}
int myMul(int x,int y) {return x*y;}
int myDiv(int x,int y) {return x/y; }
//设计算法 操作上面的函数
int myCalc(int x,int y, int (*func)(int,int) )  
{return func(x,y);}
void test()
{
	//int (*func)(int,int) = myAdd;
    cout<<myCalc(10,20, myAdd)<<endl;//30
    cout<<myCalc(10,20, mySub)<<endl;//-10
    cout<<myCalc(10,20, myMul)<<endl;//200
    cout<<myCalc(10,20, myDiv)<<endl;//0
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值