### 指针入门
~~~
int i = 123;
//一般计算机中用16进制数来表示一个内存地址
printf("%#x\n",&i);
//int* int类型的指针变量 pointer指针 指针变量只能用来保存内存地址
//用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
//此时我们可以说 指针pointer指向了 i的地址
int* pointer = &i;
printf("pointer的值 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;
printf("i的值是%d\n",i);
system("pause");
~~~
#### 零起点学通C语言摘要 ####
> **指针是用来保存内存地址的变量,除了寄存器变量外,每个被定义的变量都有自己的内存地址,因此完全可以使用指针来存放除寄存器变量之外,任何已被定义的变量的地址,即使该变量没有被赋值**
```
#include<stdio.h>
int main()
{
int i=1;
printf("%p",&i);//通过取地址符&来获取变量i的地址,%p在print函数中,表示以十六进制输出地址,%i表示十进制输出
return 0;
}
```
输出结果:
~~~
0x7fffacb3476c
~~~
```
#include <stdio.h>
int main()
{
int i; //定义了一个变量i
int *p; //定义了一个变量p,*用来说明该变量是一个指针,要注意它的类型是int
p=&i; //将变量i的地址取出,赋给变量p,这样,变量p保存的就是变量i的地址
printf("&i:%p\n",&i); //输出i的地址,为0013FF7C
printf(" p:%p",p); //输出p的值,为0013FF7C。
return 0;
}
```
输出结果:
~~~
&i:0x7ffd987fd794
p:0x7ffd987fd794
~~~
```
#include <stdio.h>
int main()
{
int i=1;
int *p;
p=&i;
printf("&i:%p\n",&i);//利用&来得到i的地址,
printf("i:%d\n",i);//输出i的值1
printf("p:%p\n",p);//输出p的值,p保存的是i的地址
printf("*p:%d\n",*p);//通过指针p来找到i,并输出i的值,"*"是指针运算符,作用是读取他后面的指针所指向的变量的值
return 0;
}
```
输出结果
~~~
&i:0x7ffdbd4d3854
i:1
p:0x7ffdbd4d3854
*p:1
~~~
>[info] 注意:\*在不同的地方有不同的用途,当生命一个指针时,如 `int *p`,这里只是来说明p的类型是指针, 而`print(%d,* *p)`则是读取p所指向的变量的值
```
#include<stdio.h>
int main()
{
int i=10;
int *p;
p=&i;
printf("i=%d\n",i);
printf("*p=%d\n",*p);
printf("用指针来修改i的值...\n");
*p=0;
printf("i=%d\n",i);
printf("*p=%d\n",*p);
printf("将i的值赋为1...\n");
i=1;
printf("i=%d\n",i);
printf("*p=%d\n",*p);
return 0;
}
```
输出结果
~~~
i=10
*p=10
用指针来修改i的值...
i=0
*p=0
将i的值赋为1...
i=1
*p=1
~~~
> **指针就是用来保存内存地址的值,因此声明一个指针后,要用它来保存一个内存地址,如果不这样做,该指针就是一个野指针,它的默认值是随机的,会造成混乱,此时一定要将其值赋值为0**
```
#include <stdio.h>
int main()
{
//int *p;//野指针的话,默认值是随机的,这里值为0000003E
int *p=0;
printf("%p",p);
return 0;
}
```
输出结果
~~~
00000000
~~~
**指针也占用内存,因此他也有自己的地址,指针自身的地址和指针保存的地址是不同的**
```
#include <stdio.h>
int main()
{
int i=10;
int *p=&i;
printf("p指针的地址:%p\n",&p);
printf("p保存的地址:%p\n",p);
printf("p指向的变量的值:%d\n",*p);
return 0;
}
```
输出结果
~~~
p指针的地址:0x7ffffaca4f50
p保存的地址:0x7ffffaca4f5c
p指向的变量的值:10
~~~
#### 指针运算 ####
> 有加减运算、赋值运算、相减运算、比较运算
#### 加减运算 ####
~~~
#include <stdio.h>
int main()
{
int i=0;
int *p=&i;
printf("指针p保存的内存地址为:\t\t%p\n",p);//输出0060FF08
p++;
printf("自加后,指针p保存的内存地址为:\t%p\n",p);//输出0060FF0C,从0060FF08到0060FF0C(9,A,B,C)需要移动4个字节,这正好是int型变量所用的内存字节数
p--;
printf("自减后,指针p保存的内存地址为:\t%p\n",p);//输出为0060FF08
p=p-2;
printf("减2后,指针p保存的内存地址为:\t%p\n",p);//输出为0060FF00,减少了8个字节
return 0;
}
~~~
输出结果
~~~
指针p保存的内存地址为: 0x7ffcdd2603e4
自加后,指针p保存的内存地址为: 0x7ffcdd2603e8
自减后,指针p保存的内存地址为: 0x7ffcdd2603e4
减2后,指针p保存的内存地址为: 0x7ffcdd2603dc
~~~
#### 赋值运算 ####
**如将一个指针变量保存的地址赋值给另一个指针变量**
```
#include <stdio.h>
int main()
{
int i=0,j=1;
int *p1=&i;
int *p2=&j;
printf("p1:%p\n",p1);
printf("p2:%p\n",p2);
p1=p2;
printf("赋值后...\n");
printf("p1:%p\n",p1);
return 0;
}
```
输出结果
~~~
p1:0x7ffded01824c
p2:0x7ffded018248
赋值后...
p1:0x7ffded018248
~~~
#### 相减运算
```
#include <stdio.h>
int main()
{
int i=0,j=1;
int *p1=&i;
int *p2=&j;
printf("p1:%p\n",p1);
printf("p2:%p\n",p2);
*p1=p1-p2;
printf("两块内存的地址差:");
printf("%d\n",*p1);
return 0;
}
```
输出结果
~~~
p1:0x7fff5c24c99c
p2:0x7fff5c24c998
两块内存的地址差:1
~~~
> **地址差不是以字节为单元的,而是以操作数的类型为单元,如地址差为1,假如执行相减操作的指针类型为int,那么地址差是1个int单元,而1个int单元要占用4个字节的内存**
#### 比较运算 ####
```
#include <stdio.h>
int main()
{
int i=0,j=1;
int *p1=&i;
int *p2=&j;
printf("p1:%p\n",p1);
printf("p2:%p\n",p2);
if(p1>p2)
printf("p1的内存地址大于p2。\n");
else
printf("p1的内存地址小于p2。\n");
return 0;
}
```
输出结果
~~~
p1:0x7ffec9c409fc
p2:0x7ffec9c409f8
p1的内存地址大于p2。
~~~
#### 指针类型 ####
> **整型指针只能存储整型变量的地址,浮点型指针只能存储浮点型变量的地址,字符型指针只能存储字符型变量的地址,因为不同类型的指针,它们的运算方式不一样,指针类型决定了运算方式**
```
#include <stdio.h>
int main()
{
char ch;
int i;
char *p1=&ch;
int *p2=&i;
printf("字符型指针p1保存的内存地址为:\t\t%p\n",p1);//输出0060FF07
p1++;
printf("自加后,字符型指针p1保存的内存地址为:\t%p\n",p1);//输出0060FF08
printf("整型指针p2保存的内存地址为:\t\t%p\n",p2);//输出0060FF00
p2++;
printf("自加后,整型指针p2保存的内存地址为:\t%p\n",p2);//输出0060FF04
return 0;
}
```
输出结果
~~~
字符型指针p1保存的内存地址为: 0x7ffe400f328f
自加后,字符型指针p1保存的内存地址为: 0x7ffe400f3290
整型指针p2保存的内存地址为: 0x7ffe400f3288
自加后,整型指针p2保存的内存地址为: 0x7ffe400f328c
~~~
#### const与指针 ####
#### 1、常量指针 ####
可以将指针声明为常量指针,该指针不可改变,但是它指向的变量是可以改变的
```
#include <stdio.h>
int main()
{
int i=1;
int *const p=&i;
//p=p+1;
*p=3;
printf("%d",i);
return 0;
}
```
输出结果
~~~
3
~~~
#### 2、指向常量的指针 ####
定义一个常量的指针,则该指针指向的常量不可改变,但是该指针剋有被改变
```
#include <stdio.h>
int main()
{
int i=1;
const int *p=&i;
printf("%p\n",p);
p=p+1;
//*p=3; //指向的变量不可修改
printf("%p",p);
return 0;
}
```
输出结果
~~~
0x7ffff2ebd164
0x7ffff2ebd168
~~~
#### 3、指向常量的常指针
假如定义一个指向常量的常指针,则该指针指向的常量不可修改,同时该指针也不可以修改
```
#include <stdio.h>
int main()
{
int i=1;
const int *const p=&i;
//*p=3; //指向的变量不可修改
// p=p+1; //指针也不可修改
printf("%d",*p);
return 0;
}
```
输出结果
~~~
1
~~~
* 指针常见错误
* 声明了指针变量后 未初始化直接通过*p 进行赋值操作 运行时会报错
* * 未赋值的指针称为野指针
* 指针类型错误 如int* p 指向了double类型的地址, 通过指针进行读取操作时,读取值会出错
### 指针的练习
* 值传递和引用传递(交换两个数的值)
* 引用传递本质是把地址传递过去
* 所有传递其实本质都是值传递,引用传递其实也是传递一个值,但是这个值是一个内存地址
* 如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递
```
void swap(int* p, int* p2){
int temp = *p;
*p = *p2;
*p2 = temp;
}
main(){
int i = 123;
int j = 456;
//将i, j的地址传递过去
swap(&i,&j);
printf("i = %d, j = %d", i, j);
}
```
交换2个数的值
```
#include<stdio.h>
#include<stdlib.h>
/**
值传递 和引用传递 值传递和引用传递传递的实际上 都是数值 只不过引用传递传递的是地址值
如果想通过一个子函数来修改main函数中变量的值 一定要用引用传递
*/
//值传递
swap(int i, int j){
int temp = i;
i = j;
j = temp;
}
//引用传递
swap2(int* pointer, int* pointer2 ){
int temp = *pointer;
*pointer = *pointer2;
*pointer2 = temp;
}
main(){
int i = 123;
int j = 456;
//swap(i,j);
/**int temp = i;
i = j;
j = temp;
*/
swap2(&i,&j);//该函数的效果和上面注释中的代码效果一样
printf("i的值%d,j的值%d\n",i,j);
system("pause");
}
```
输出结果
~~~
i的值456,j的值123
~~~
* 返回多个值
* 把地址作为参数传入函数中,当函数执行完毕时,参数的值就已经被修改了
代码
```
#include<stdio.h>
#include<stdlib.h>
/**
"*" 的几个含义 int* 声明一个int类型的指针变量
x * y 乘法运算
int* pointer;
*pointer; 取出指针变量pointer 中保存的内存地址对应的内存中的值
*/
function(int* pointer, int* pointer2){
*pointer *= 2;
*pointer2 *=2;
}
main(){
int i =1;
int j = 2;
//char c;
function(&i,&j);
printf("i = %d,j = %d\n",i,j);
system("pause");
}
```
输出结果
~~~
i = 2,j = 4
~~~
#### 指针和数组之间的关系 ####
```
#include<stdio.h>
#include<stdlib.h>
/**
数组实际上就是一块连续的内存空间
*/
main(){
//char array[] = {'a','b','c','d','\0'};
int array[] = {1,2,3,4};
printf("array[0]的地址%#x\n",&array[0]);
printf("array[1]的地址%#x\n",&array[1]);
printf("array[2]的地址%#x\n",&array[2]);
printf("array[3]的地址%#x\n",&array[3]);
printf("array的地址%#x\n",&array);
//数组变量名的地址实际上是第一个元素的地址
//char* pointer = &array;
int* pointer = &array;
//char array2[] = "hello from c";
//char* pointer2="hello from c";//c语言中字符串写法
//printf("%s\n",pointer2);
/*
printf("*(pointer+0)=%c\n",*(pointer+0));
printf("*(pointer+0)=%c\n",*(pointer+1));
printf("*(pointer+0)=%c\n",*(pointer+2));
printf("*(pointer+0)=%c\n",*(pointer+3));
*/
printf("*(pointer+0)=%d\n",*(pointer+0));
printf("*(pointer+1)=%d\n",*(pointer+1));
printf("*(pointer+2)=%d\n",*(pointer+2));
printf("*(pointer+3)=%d\n",*(pointer+3));
system("pause");
}
```
输出结果
~~~
array[0]的地址0x52e90920
array[1]的地址0x52e90924
array[2]的地址0x52e90928
array[3]的地址0x52e9092c
array的地址0x52e90920
*(pointer+0)=1
*(pointer+1)=2
*(pointer+2)=3
*(pointer+3)=4
~~~
### 数组和指针的关系
* 数组占用的内存空间是连续的
* 数组变量保存的是第0个元素地址,也就是首地址
* *(p + 1):指针位移一个单位,一个单位是多少个字节,取决于指针的类型
### 多级指针
* int* p; int 类型的一级指针 int** p2; int 类型的二级指针
* 二级指针变量只能保存一级指针变量的地址
* 有几个* 就是几级指针 int*** 三级指针
* 通过int类型三级指针 操作int类型变量的值 ***p
```
int i = 123;
//int类型一级指针
int* p = &i;
//int 类型 二级指针 二级指针只能保存一级指针的地址
int** p2 = &p;
//int 类型 三级指针 三级指针只能保存二级指针的地址
int*** p3 = &p2;
//通过p3 取出 i的值
printf("***p3 = %d\n", ***p3);
```
* 多级指针案例 取出子函数中临时变量的地址
###指针的长度
* 不管变量的类型是什么,它的内存地址的长度一定是相同的
* 类型不同只决定变量占用的内存空间不同
* 32位环境下,内存地址长度都是4个字节,所以指针变量长度只需4个字节即可
* 区分指针类型是为了指针位移运算方便
代码
```
#include<stdio.h>
#include<stdlib.h>
/**
32位操作系统地址总线是32位 4个字节的变量来保存32位操作系统的内存地址 1byte 8位 4*8=32
32位操作系统 指针变量占4个字节和指针变量的类型无关
64位操作系统 指针变量占8个字节
*/
main(){
int* pointer;
double* pointerD;
printf("int类型的指针变量占%d个字节\n",sizeof(pointer));
printf("double类型的指针变量占%d个字节\n",sizeof(pointerD));
system("pause");
}
```
输出结果
~~~
int类型的指针变量占8个字节
double类型的指针变量占8个字节
~~~
#### main函数获取子函数中临时变量的地址 ####
```
#include<stdio.h>
#include<stdlib.h>
/**
main函数获取子函数中临时变量的地址
这其实还是值传递和引用传递的问题
*/
function(int** pointer){
int i = 123;
*pointer = &i;
printf("i的地址%#x\n",&i);
}
main(){
int* pointer1;
function(&pointer1);
printf("pointer1的值%#x\n",pointer1);
system("pause");
}
```
输出结果
~~~
i的地址0x1147410c
pointer1的值0x1147410c
~~~
- 前言
- JNI基础知识
- C语言知识点总结
- ①基本语法
- ②数据类型
- 枚举类型
- 自定义类型(类型定义)
- ③格式化输入输出
- printf函数
- scanf函数
- 编程规范
- ④变量和常量
- 局部变量和外部变量
- ⑤类型转换
- ⑥运算符
- ⑦结构语句
- 1、分支结构(选择语句)
- 2、循环结构
- 退出循环
- break语句
- continue语句
- goto语句
- ⑧函数
- 函数的定义和调用
- 参数
- 函数的返回值
- 递归函数
- 零起点学通C语言摘要
- 内部函数和外部函数
- 变量存储类别
- ⑨数组
- 指针
- 结构体
- 联合体(共用体)
- 预处理器
- 预处理器的工作原理
- 预处理指令
- 宏定义
- 简单的宏
- 带参数的宏
- 预定义宏
- 文件包含
- 条件编译
- 内存中的数据
- C语言读文件和写文件
- JNI知识点总结
- 前情回顾
- JNI规范
- jni开发
- jni开发中常见的错误
- JNI实战演练
- C++(CPP)在Android开发中的应用
- 掘金网友总结的音视频开发知识
- 音视频学习一、C 语言入门
- 1.程序结构
- 2. 基本语法
- 3. 数据类型
- 4. 变量
- 5. 常量
- 6. 存储类型关键字
- 7. 运算符
- 8. 判断
- 9. 循环
- 10. 函数
- 11. 作用域规则
- 12. 数组
- 13. 枚举
- 14. 指针
- 15. 函数指针与回调函数
- 16. 字符串
- 17. 结构体
- 18. 共用体
- 19. typedef
- 20. 输入 & 输出
- 21.文件读写
- 22. 预处理器
- 23.头文件
- 24. 强制类型转换
- 25. 错误处理
- 26. 递归
- 27. 可变参数
- 28. 内存管理
- 29. 命令行参数
- 总结
- 音视频学习二 、C++ 语言入门
- 1. 基本语法
- 2. C++ 关键字
- 3. 数据类型
- 4. 变量类型
- 5. 变量作用域
- 6. 常量
- 7. 修饰符类型
- 8. 存储类
- 9. 运算符
- 10. 循环
- 11. 判断
- 12. 函数
- 13. 数学运算
- 14. 数组
- 15. 字符串
- 16. 指针
- 17. 引用
- 18. 日期 & 时间
- 19. 输入输出
- 20. 数据结构
- 21. 类 & 对象
- 22. 继承
- 23. 重载运算符和重载函数
- 24. 多态
- 25. 数据封装
- 26. 接口(抽象类)
- 27. 文件和流
- 28. 异常处理
- 29. 动态内存
- 30. 命名空间
- 31. 预处理器
- 32. 多线程
- 总结
- 音视频学习 (三) JNI 从入门到掌握
- 音视频学习 (四) 交叉编译动态库、静态库的入门学习
- 音视频学习 (五) Shell 脚本入门
- 音视频学习 (六) 一键编译 32/64 位 FFmpeg 4.2.2
- 音视频学习 (七) 掌握音频基础知识并使用 AudioTrack、OpenSL ES 渲染 PCM 数据
- 音视频学习 (八) 掌握视频基础知识并使用 OpenGL ES 2.0 渲染 YUV 数据
- 音视频学习 (九) 从 0 ~ 1 开发一款 Android 端播放器(支持多协议网络拉流/本地文件)
- 音视频学习 (十) 基于 Nginx 搭建(rtmp、http)直播服务器
- 音视频学习 (十一) Android 端实现 rtmp 推流
- 音视频学习 (十二) 基于 FFmpeg + OpenSLES 实现音频万能播放器
- 音视频学习 (十三) Android 中通过 FFmpeg 命令对音视频编辑处理(已开源)