c语言指针学习
说到指针,先说说地址,看一段小程序
#include "stdio.h"
int main()
{
int a = 10;
int *p = &a;
printf("%pn", p);
return 0;
}
// output
0x7fff8b6a378c
每当我看到指针的输出 像这种"0x7fff8b6a378c"时候,头都大了,那时候老师说是地址,搞得糊里糊涂的。那什么是地址呢?当然我帮你百科一下。是系统 RAM 中的特定位置,通常以十六进制的数字表示,系统通过这个地址,就可以找到相应的内容。当使用80386时,我们必须区分以下三种不同的地址:逻辑地址、线性地址、物理地址;在进行C语言指针编程中,可以读取指针变量本身值(&操作),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干,比如上面那个"0x7fff8b6a378c" 就是逻辑地址。逻辑地址不是被直接送到内存总线,而是被送到内存管理单元(MMU)。MMU由一个或一组芯片组成,其功能是把逻辑地址映射为物理地址,即进行地址转换。下面是转换关系图。
指针
c语言相比汇编算应该算是高级了,却保留的了操作地址中高效的又抽象的形式。那么指针到底是什么呢? 在那本经典《c 程序设计语言》 是这样描述 : ”指针是一种保存变量地址的变量“,指针是一个特殊的变量,它里面存储的数值被解释成为内存里的一个地址,指针与地址不要混在一起,指针是存储地址一个变量,地址是内存分配。指针可以指向这个内存地址,也可以指向另一个内存地址,当指针指向一个内存地址,它们之间才发生联系,通过这个指针去操作这块内存,所以指针把我们带入到地址层面去操作数据,在php,java 这些高级语言没有这一层的操作。举个例子
//字符串翻转例子
#include "stdio.h"
#include "string.h"
void revstr(char *);
int main()
{
char str[] = "Zhen Shan Ren is good!";
revstr(str);
puts(str);
}
void revstr(char *str)
{
char *start, *end, temp;
start = str;
end = start + strlen(str) -1;
while (start++ < end--) {
temp = *start;
*start = *end;
*end = temp;
}
}
上面的例子是从指针的角度去处理字符串,我再revstr 函数中定义了两个指针,一个指针指向字符串的首地址,另一个指针指向字符串的末地址,把内容互换。 指针提供这样便利,可以通过加、减来访问这一块内存。然后再去改变内存的值。如果没有指针,只能去操作这样逻辑地址 “0x7fff8b6a378c”去计算下一个或上一个逻辑地址,会不会疯掉呢?所以指针把我们带入到地址层面去操作数据。指针难点是我们不是很清楚有些复杂的数据类型的在内存中存储。指来指去不知道指向那了。如果你能很清楚内存的分布,就不会指错地方!
指针的几个概念
1.指针的类型
基本数据类型比如 int、char ,还有 一些复杂的比如 int (*p)[], 指向数组的指针,像这种的判断就是指针名字去掉 , 指针的类型类型就是 int(*)[],其实就是指向数组的指针
2.指针所指向的类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当做什么来看待。 你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
例如:int*ptr:指针所指向的类型是int int(*ptr)[3]:指针所指向的的类型是int()[3]
3.指针的值
我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区域的首地址。
看一段代码:这段代码是问你p1 是否和p2 相等?
#include "stdio.h"
int main()
{
char *p1,*p2,*p3;
char ch[] = {'a', 'b', 'c'};
char **pp;
p1 = ch;
pp = &ch;
p2 = *pp;
if (p1 == p2) {
printf("p1 == p2n");
} else {
printf("p1 != p2n");
}
printf("p3 = %p", p3);
return 0;
}
结果是:
//p1 != p2
//p3 = 0x4005f0dxy
&ch 指针类型为 char (*)[3], 当运行到pp=&ch 时候,编译器会骂你 “warning: assignment from incompatible pointer type” 指针类型不匹配(在vc6下直接报错)。看一下p3 会有一个值,未初始化指针是有内存地址的,而且是一个垃圾地址。不知道这个内存地址指向的值是什么。这就是为什么不要对未初始化指针取值的原因。最好的情况是你取到的是垃圾地址接下来你需要对程序进行调试,最坏的情况则会导致程序崩溃。以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么?该指针指向了哪里?
还有一个题目可以试试
#include "stdio.h"
int main()
{
int a[5] = {1,2,3,4,5};
int *p = (int *)(&a+1);
printf("%d,%d", *(a+1), *(p-1));
}
指针与数组
“数组名就是指针”,“你就把当做指针理解”这是老师教的,却从不给个合理的解释,就像某组织教育无神论一样,你要信神就是迷信,我说这就是邪恶,缺乏对人最起码的尊重,当然在某组织的眼里我们都是奴才。好吧,假设数组名是指针
#include "stdio.h"
int main()
{
int a[] = {1,2,3,5};
int *p = a;
printf("a = %d, p =%d", sizeof(a), sizeof(p));
}
//output
//a= 16,p=4
从输出结果看两者根本就是两个事物,只能说数组名神似指针,数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组;那么数组名到底是什么:
符号表是编译原理中的一个概念,应用于编译器的词法分析和语义分析两个阶段。词法分析的目标是让编译器能知道这是个数组就好了,那么语义分析阶段就需要确定这个数组的具体空间了。所以我们定义了一个数组,编译器就会在符号表中加入数组的名字a,并且根据其指定的大小,开辟一段内存空间,把这段内存空间的首地址(也就是第一个元素的地址)存入符号表,这也就是为什么我们通过数组名就可以去访问数组的元素了。编译器这么做是为了使我们使用数组更加的方便,易懂。也有人说a是一个内存地址,也没有什么不妥的,因为编译器允许我们直接把a作为数组首地址来用。数组是一种线性的数据结构,数组名指向了那一片内存。
参考资料:
- java学习:数据增删改查、存储过程调用及事务处理
- 极客手工:自制51四驱无线遥控小车
- flash:二次贝塞尔曲线应用-生成飞机路径示意图
- 微信小程序重磅功能上线!一键连Wi-Fi/手机变门禁卡
- MySQL下载安装、基本配置、问题处理
- windows下命令行模式中cd命令无效的原因
- 分布式和集群区别?什么是云计算平台?分布式的应用场景?
- 中国移动也要搞自动驾驶,没了SIM卡怎么耍花样?
- python并发编程之多进程理论部分
- 使用concurrent.futures模块并发,实现进程池、线程池
- 人工智能与医疗
- 每周论文清单:知识图谱,文本匹配,图像翻译,视频对象分割
- 进程池、线程池、回调函数
- java学习:weblogic下JNDI及JDBC连接测试(weblogic环境)
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- PHP生成二维码与识别二维码的方法详解【附源码下载】
- 详解PHP多个进程配合redis的有序集合实现大文件去重
- 原生PHP实现导出csv格式Excel文件的方法示例【附源码下载】
- php ajax confirm 删除实例详解
- PHP模糊查询技术实例分析【附源码下载】
- 解决keras,val_categorical_accuracy:,0.0000e+00问题
- 导致python中import错误的原因是什么
- Python RabbitMQ实现简单的进程间通信示例
- PHP删除数组中特定元素的两种方法
- 如何写出让同事无法维护的代码?
- 浅谈php://filter的妙用
- PHP中“=>
- CI(CodeIgniter)框架中URL特殊字符处理与SQL注入隐患分析
- PHP实现获取毫秒时间戳的方法【使用microtime()函数】
- 利用PHP如何统计Nginx日志的User Agent数据