相关文章链接 : 1.【嵌入式开发】C语言 指针数组 多维数组 2.【嵌入式开发】C语言 命令行参数 函数指针 gdb调试 3.【嵌入式开发】C语言 结构体相关 的 函数 指针 数组 4.【嵌入式开发】gcc 学习笔记(一) - 编译C程序 及 编译过程 5.【C语言】 C 语言 关键字分析 ( 属性关键字 | 常量关键字 | 结构体关键字 | 联合体关键字 | 枚举关键字 | 命名关键字 | 杂项关键字) 6.【C 语言】编译过程 分析 ( 预处理 | 编译 | 汇编 | 链接 | 宏定义 | 条件编译 | 编译器指示字 ) 7.【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)
- 一. 指针
- 1. 指针 简介
- ( 1 ) 指针 概念 ( 本质 | 占用内存 ① 32位 4字节 ② 64 位 8 字节 | * ① 声明指针 ② 获取指向的值 )
- ( 2 ) 指针 简单示例 ( * 的读写内存作用 | 指针相关类型大小)
- 2. 传值 和 传址 调用
- ( 1 ) 相关概念 ( 传值调用 复制实际值 | 传址调用 复制地址值 )
- ( 2 ) 传址调用 ( 改变外部变量值 )
- 3. 常量 和 指针
- ( 1 ) 相关概念 ( 核心原则 左数右指 | 左数 ① const int* p ② int const* p 数据时常量 | 右指 int* const 指针是常量 )
- ( 2 ) 验证 常量 指针 相关概念 ( 左数右指 )
- 二. 数组
- 1. 数组 简介
- ( 1 ) 数组 概念 ( 数组地址 | 数组大小 显示 隐式 声明 | 数组初始化 [ 效率比后期赋值高 ] )
- ( 2 ) 数组 示例 ( 定义 | 大小 | 初始化 )
- 2. 数组地址与名称 概念
- ( 1 ) 数组 概地址 ( 数组名 [ 数组首元素地址 ] 和 &数组名 [ 数组地址 ] | 数组名 类似于 常量指针 | 数组拷贝 )
- ( 2 ) 数组 示例 ( 数组名 | 地址 | 数组拷贝禁止情况 )
- 3. 数组 与 指针 区别
- ( 1 ) 概念简介 ( ① 数组名就是首元素地址 不需要寻址 | ② 指针 中保存一个地址 指向首元素地址 需要寻址 | printf 打印 数组 或 指针 : 根据占位符自动判断打印地址还是打印内存中的具体内容 )
- ( 2 ) 代码示例 ( 数组 | 指针 编译器处理上的区别 )
- 三. 数组 指针 分析
- 1. 指针 加减 运算方式
- ( 1 ) 指针 加减法 运算 ( 指针指向的位置在同一个数组中改变才有意义 )
- (2) 数组大小计算示例
- ( 3 ) 指针 加法运算示例 ( 指针地址 + 4/8 * 被加数 )
- ( 4 ) 指针 减法 运算示例
- 2. 指针 比较 运算方式
- ( 1 ) 指针 比较 运算 ( 大于 小于 大于等于 小于等于 运算的前提是 必须指向同一数组 中的元素 | 任意两指针只能进行 等于 不等于 的比较 )
- ( 2 ) 指针 比较 运算代码示例 ( 用 指针 遍历数组 )
- 3. 数组访问方式
- ( 1 ) 下标 指针 访问 ( 推荐使用下标访问 )
- ( 2 ) 下标 指针 访问 数组 性能 代码示例
- 3. int array[]; array 和 &array 区别
- ( 1 ) int array[] 中 array 和 &array 意义 ( ① array 数组首元素地址 | ② &array 数组地址 )
- ( 2 ) array 和 &array 计算 代码示例
- 4. 数组参数
- ( 1 ) 数组参数 概念 ( 退化成指针 | 需要带上数组长度作为 附属参数 )
- ( 2 ) 数组参数 代码示例 ( 数组大小 | 数组参数大小 )
- 5. 数组 指针 对比 ( 内存分配 : ① 指针 分配 4 / 8 字节 ② 数组分配所有元素地址 | 作为参数 | 常量[ 数组 ] 变量[ 指针 ] 区别 )
- 四. 字符串
- 1. 字符串概念
- ( 1 ) 概念 ( 本质 是 char[] 数组 | '\0' 结尾 | 存储位置 栈 堆 常量区 )
- ( 2 ) 示例代码 ( 字符串概念 | 字符串 )
- 2. 字符串 长度
- ( 1 ) 字符串长度计算 ( 不包括 '\0' | 标准库中有该函数)
- ( 2 ) 代码示例 ( 字符串长度计算示例 )
- ( 3 ) 代码示例 ( 自己实现 strlen 方法 )
- 3. 字符串函数 长度不受限制 情况
- ( 1 ) 不受限制的字符串函数 ( 函数自动寻找 '\0' 确定字符串大小 | stpcpy | strcat | strcmp )
- ( 2 ) 代码示例 ( 自己实现字符串拷贝函数 )
- 4. 字符串函数 长度受限制 情况
- ( 1 ) 受限制的字符串函数 ( 推荐使用 降低错误率 )
- 五. 指针数组 与 数组指针
- 1. 数组指针
- ( 1 ) 数组类型介绍 ( 数组元素类型 | 数组大小 | 举例 int[8] )
- (2) 数组指针简介 ( 指向数组的 一个 指针 | 数组指针类型定义方式 : 数组元素类型 ( * 指针名称 ) [数组大小] )
- ( 3 ) 代码示例 ( 定义数组类型 | 数组指针用法 )
- 2. 指针数组
- ( 1 ) 指针数组简介 ( 数组中存储的元素是指针 | 数组指针 int (*array)[5] 本质是指针 | 指针数组 int* array[5] 本质是数组 )
- ( 2 ) 代码示例 ( 指针数组使用案例 )
- 3. main 函数参数 分析
- ( 1 ) main 函数简介
- (2) main 函数 代码示例
- 六. 多维数组 和 多维指针
- 1. 二维指针 ( 指向指针的指针 )
- ( 1 ) 二维指针简介 ( 指向指针的指针 )
- ( 2 ) 代码示例 ( 指针的传址调用 | 指向指针的指针 | 重置指针指向的空间 )
- 2. 二维数组
- ( 1 ) 二维数组 ( 存放方式 | 数组名 | 首元素类型 | 数组名 类似 常量指针 | )
- (2) 代码示例 ( 以一维数组方式遍历二维数组 | 体现二维数组的数据排列 )
- 3. 数组名
- ( 1 ) 数组名 简介 ( 数组首元素地址 | &数组名 是 数组地址 )
- ( 2 ) 代码示例 ( 数组名指针指向的内容 | 二维指针数组名对应的指针运算 )
- ( 3 ) 代码示例 ( 一维数组遍历 | 二维数组遍历 )
- ( 4 ) 代码示例 ( 为二维数组申请内存空间 )
- 五. 数组参数 与 指针参数
- 1. 数组参数退化为指针参数的意义
- ( 1 ) 数组参数退化的相关概念 ( 指针退化成数组 )
- ( 2 ) 代码示例 ( 二维数组参数 的指针退化 | 外层指针退化 | 内层数组指针没有退化 )
- 六. 函数指针
- 1. 函数类型 和 函数指针
- (1) 相关概念 ( 函数类型要素 ① 返回值, ② 参数类型, ③ 参数个数, ④ 隐含要素 : 参数顺序 | 函数指针类型 返回值类型 (*变量名) (参数列表) )
- ( 2 ) 代码示例 ( 定义函数指针 : ①typedef int(FUN)(int); FUN* p; 或者 ② void(*p1)(); | 给 函数指针 赋值 , 右值 可以直接使用 ① 函数名 或 ② &函数名 | 调用函数指针方法 : ① 函数指针变量名(参数) ② (*函数指针变量名)(参数) | 函数名 和 &函数名 是等价的 | 函数指针变量名(参数) 和 (*函数指针变量名)(参数) 也是等价的 )
- 2. 回调函数
- ( 1 ) 回调函数相关概念
- ( 2 ) 代码示例 ( 回调函数示例 )
- 3. 解读 复杂的 指针声明 ( 难点 重点 | ①找出中心标识符 ②先右 后左 看 确定类型 提取 ③ 继续分析 左右看 ... )
注意 : 博客中出现的关于指针的计算方式, 如果在 32 位电脑中, 指针的地址类型是 unsigned int 类型 , 占 4 字节 , 在 64 位电脑中 指针地址的类型是 unsigned long int , 占 8 个字节 ;
一. 指针 1. 指针 简介 ( 1 ) 指针 概念 ( 本质 | 占用内存 ① 32位 4字节 ② 64 位 8 字节 | * ① 声明指针 ② 获取指向的值 )指针简介 :
- 1.指针本质 : 指针本质也是一个变量 ;
- 2.占用内存 : 指针变量也要在内存中占用一定大小的空间, 不同 类型的指针占用的内存大小都是 相同的 ;
32位系统 指针 占用内存大小 4 字节, 64位系统 指针 占用内存大小 8 字节;
- 3.指针变量保存的值 : 指针变量中保存的是内存地址的值 ;
符号简介 :
- 1.声明指针 : 在 声明指针变量时, * 表示声明一个指定类型变量的指针 ;
- 2.使用指针 : 使用指针的时候, * 表示指针变量地址指向的内存中的值, 可以读取该地址的实际数据值 或者 向地址中写入实际数据值 ;
指针简单示例 :
- 1.代码示例 :
#include
int main()
{
//1. 指针简单使用, * 符号作用
int i = 666;
//声明 int 类型 指针, 使用 * 符号声明指针
int *p = &i;
//这里验证下 i 即存放在 p 地址的内容, * 用于读取 指针 p 地址中的数据
printf("%d, %x, %d\n", i, p, *p);
//等价于 i = 888, *p 代表指针指向的内容, p 是指针的地址, 之类 * 用于向 p 地址指向的内存中写入数据
*p = 888;
//改变一个变量的大小可以使用其地址来改变, 不一定必须使用变量名称
printf("%d, %x, %d\n", i, p, *p);
//2. 指针大小示例
//32位系统 指针 占用内存大小 4 字节, 64位系统 指针 占用内存大小 8 字节
int* p_int;
char* p_char;
//a. 打印 int* 类型指针 和 char* 类型指针的 指针变量本身大小
//b. 打印 指针指向的内容大小, int 指针指向 int 类型, 因此 sizeof(*p_int) 结果是 4, sizeof(*p_char) 结果是 1
printf("%ld, %ld, %ld, %ld\n", sizeof(p_int), sizeof(p_char), sizeof(*p_int), sizeof(*p_char));
//打印 int* 和 char* 类型大小, 打印 int 和 char 类型大小
printf("%ld, %ld, %ld, %ld\n", sizeof(int*), sizeof(char*), sizeof(int), sizeof(char));
return 0;
}
- 2.运行结果 :
传值调用 :
- 1.产生复制情况 : 传值调用时 会发生 实参数据值 复制到 形参中 ;
传址调用 :
- 1.实现方式 : 将指针当做函数的参数, 因为指针也是变量, 可以当做参数使用 ;
- 2.适用场景 : 如果需要在函数中修改实参的值, 并且执行函数完毕后保留下来, 这里就用到传址调用, 使用指针作为函数参数 ;
- 3.适用场景2 : 参数数据类型较复杂, 如果参数很大, 传值调用需要实参到形参的复制, 会浪费性能 ;
代码示例1 :
- 1.代码 :
#include
//传值调用案例, 任意改变参数的值, 不影响传入的变量值
int fun_1(int a, int b)
{
a = 444;
b = 444;
}
//传址调用案例, 如果在函数中修改了地址指向的内存的值, 那么最终的值改变了
int fun_2(int* a, int* b)
{
*a = 444;
*b = 444;
}
int main()
{
int x = 666, y = 888;
//传值调用
fun_1(x, y);
printf("x = %d, y = %d\n", x, y);
//传址调用
fun_2(&x, &y);
printf("x = %d, y = %d\n", x, y);
return 0;
}
- 2.执行结果 :
代码示例2 :
- 1.代码 :
#include
//传址调用, 替换传入的变量值
int swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main()
{
int x = 666, y = 888;
printf ("x = %d, y = %d\n", x , y);
swap(&x, &y);
printf ("x = %d, y = %d\n", x , y);
return 0;
}
- 2.执行结果 :
参考 : const 关键字 ;
const 修饰指针 : 需要符合下面的规则 :
声明特征const int* pp指针地址可变 p指针指向的内容不可变 (const 在 * 左边, 数据不可变)int const* pp指针地址可变 p指针指向的内容不可变 (const 在 * 左边, 数据不可变)int* const pp指针地址不可变 p指针指向的内容不可变 (const 在 * 右边, 地址不可变)const int* const pp指针地址不可变 p指针指向的内容不可变 (const 在 * 左边 和 右边, 数据和地址都不可变)const 修饰指针规则 : ***左数 右指 (左边数据是常量, 右边指针是常量)***; 左数 : const 出现在 * 左边时, 指针指向的数据为常量, 指向的数据不可改变; 右指 : const 出现在 * 右边时, 指针地址本身是常量, 指针地址不可改变;
( 2 ) 验证 常量 指针 相关概念 ( 左数右指 )参考 : const 关键字 ;
const 修饰指针规则 : 左数右指; 左数 : const 出现在 * 左边时, 指针指向的数据为常量, 指向的数据不可改变; 右指 : const 出现在 * 右边时, 指针地址本身是常量, 指针地址不可改变;
const 关键字 代码示例 : 修饰指针
- 1.代码示例1 : const 出现在 * 左边, const int* p = &i;
#include
int main()
{
//定义普通的变量, 用于取地址用
int i = 666;
//定义一个 const 在 * 左边的例子, 意义是 指针指向的内容是常量
//按照规则, 指针地址可改变, 指针指向的数据不可变
const int* p = &i;
//指针指向的数据不可改变, 这里会报错
*p = 444;
return 0;
}
- 2.代码示例2 : const 出现在 * 左边, int const* p = &i;
#include
int main()
{
//定义普通的变量, 用于取地址用
int i = 666;
//定义一个 const 在 * 左边的例子, 意义是 指针指向的内容是常量
//按照规则, 指针地址可改变, 指针指向的数据不可变
int const* p = &i;
//指针指向的数据不可改变, 这里会报错
*p = 444;
return 0;
}
- 3.代码示例3 : const 出现在 * 右边, int* const p = &i;
#include
int main()
{
//定义普通的变量, 用于取地址用
int i = 666;
//定义一个 const 在 * 右边的例子, 意思是 地址是常量
//按照规则, 指针地址不可改变, 指针指向的内容可变
int* const p = &i;
//指针指向的数据不可改变, 这里会报错
p = NULL;
return 0;
}
- 4.代码示例4 : const 同时出现在 * 左边 和 右边, const int* const p = &i;
#include
int main()
{
//定义普通的变量, 用于取地址用
int i = 666;
//定义 const 同时出现在 * 左边 和 右边, 则指针的地址 和 指向的数据都不可改变
const int* const p = &i;
//下面的两个操作, 一个是想修改指针地址, 一个是想修改指针值, 这两个都报错.
p = NULL;
*p = 444;
return 0;
}
数组 简介 :
- 1.概念 : 数组 是 相同类型 的 变量 的 有序集合 ;
- 2.数组示例 :
int array[6];
定义数组 int array[6]; 意义 : 数组中包含 6 个 int 类型的数据 , 数组中每个元素都是 int 类型的 ; 第一个元素地址 : array 是数组中第一个元素的起始地址; 下标 : 可以通过下标来获取数组中指定位置的元素, array[0] 是第一个元素的位置, array[5] 是第六个元素的位置 ;
数组大小 :
- 1.数组定义时必须声明大小 : 数组在定义时, 必须显示 或 隐式 的声明数组的大小 ;
- 2.显示声明数组大小 : 定义数组时, 在数组名称后的中括号中声明数组大小 ;
int array[5];
int array[5] = {1, 2, 3} ; //这个也是显示声明, 数组大小为 5, 但是只指定了 前三个元素的大小 ;
- 3.隐式声明数组大小 : 声明数组时, 不在中括号中声明数组大小, 只在初始化中初始化指定个数的元素, 那么元素的个数就是数组的大小 ;
//隐式初始化, 该数组个数为 4
int array[] = {0, 1, 2, 3};
数组初始化 :
- 1.完全初始化 : 数组大小为5, 将 5 个元素都在定义时指定位置 ;
- 2.部分初始化 : 数组大小为5, 如果初始化前 1 ~ 4 个元素, 剩余的元素默认初始化为 0 ;
- 3.初始化效率 : 初始化效率很高, 远远比依次赋值要高, 因此建议定义数组时最好初始化 ;
- 4.最佳实践 :
//这里只对数组的第一个元素进行初始化为0, 那么其余的元素默认也初始化为0, 初始化效率要远远高于依次赋值的效率
int array[5] = {0}
( 2 ) 数组 示例 ( 定义 | 大小 | 初始化 )
数组 大小 初始化 示例 :
- 1.代码 :
#include
//数组大小 和 初始化 示例
//数组大小 :
//初始化 : 如果不初始化, 那么数组中就是随机值; 全部初始化, 部分初始化 : 其余默认为 0
int main()
{
//1. 显示声明数组大小, 其实际大小以中括号为准, 大小为 5, 5个元素只有 前3个初始化为 0, 1, 2
//初始化说明 :
int array_1[5] = {0, 1, 2};
int array_3[5];
int array_4[5] = {0};
//2. 隐式声明数组大小, 其实际大小为 3, 三个元素全部初始化
int array_2[] = {0, 1, 2};
printf("array_1 大小 : %ld, array_1 数组个数 : %ld\n", sizeof(array_1), sizeof(array_1)/sizeof(*array_1));
printf("array_2 大小 : %ld, array_2 数组个数 : %ld\n", sizeof(array_2), sizeof(array_2)/sizeof(*array_2));
//打印 array_2 数组结果, 其中数组元素内容是 初始化值
printf("打印 int array_1[5] = {0, 1, 2}; 数组结果 : \n");
int i = 0;
for(i = 0; i
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?