用我所学去讲C语言指针
文章更新,更加详细的介绍请看这篇:https://www.cnblogs.com/lulipro/p/7460206.html
很多人不敢讲C的指针,有些人讲不清,有些人怕讲错。初生牛犊不怕虎,就让我讲讲。
下面开始。
一、指针的定义
指针是内存单元的编号。内存单元是以字节为单位的。所以指针就是字节的编号。
比如我们的个人电脑,内存一般4GB吧,那么一共就有 : 4*1024*1024*1024 = 4294967296 字节,也就是4294967296个编号。一个字节拥有一个编号,
范围从 0 ~ 4294967296-1 。
画个图表示:(注意字节由8位bit组成,为了直观我没画出来)
但是呢,一般我们是用16进制来表示的这些编号,但效果都一样。
二、变量与内存的关系
现在我们来看C 变量在内存中的样子。我们使用自动变量(局部变量)来讲解。
int a = 5; //假设a存储在编号为0x02开始的位置
说明:
内存可以存储数据,所以我们把每个字节当做是一个“箱子”。数据存入内存就好比在箱子里面放数据
但是C语言的不同数据类型占用的字节数是不都一样的,所以,每种数据类型占的”箱子”的个数不都一样。
比如char型,只要一个字节就够了,所以一个字符只需一个“箱子”。
而int型需要4(一般是4个字节)个“箱子”才放得下。
double型则需要8个“箱子”。
来分析上图中用橙色框起来的4个字节的内存块,这里就存储了a这个变量。
我们从4个方面去讨论这个内存块:
1、内存的数据
我们的变量赋值为5,所以内存的数据就是 0000 0000 0000 0000 0000 0000 0000 0101 (大端模式)
每个字节地址: 0x02 0x03 0x04 0x05
2、*内存的名字 (对于我们的程序使用的内存来说,并不是每一个内存块都有名字)
名字就是变量名a
3、内存的地址
变量a占用了4个字节,那么,哪一个字节的地址,才是变量a的地址呢?答:第一个低地址字节的地址,也就是0x02
4、内存的宽度
这里的a变量占用了4个字节,这就是他的宽度。
这里提一下,为后面讲指针的宽度做铺垫 :)
三、定义和使用指针变量
什么是指针变量。我们知道int类型变量用来存储整形值,12、1003、9823......
那么,同样的道理,指针变量就是用来保存地址的变量。
定义指针变量:在指向的变量的类型上加个* ,如下:
int* p_int; //指向int类型变量的指针 double* p_double; //指向idouble类型变量的指针 int(*p_func)(int,int); //指向返回类型为int,有2个int形参的函数的指针 int(*p_arr)[]; //指向含有3个int元素的数组的指针 struct Student *p_struct; //结构体类型的指针 int** p_pointer; //指向 一个整形变量指针的指针
既然我们已经定义指针变量了,那么接下来就用指针变量来存储其它变量的地址。
取地址符号 &
#include<stdio.h> int Add(int a,int b); struct Student
{
int age;
double score;
char name[]; }; int main(void)
{ int val_int = ;
double val_double = 12.00; int arr[]={,,}; struct Student stu={
,
98.00,
"Jack"
}; int*p = NULL; /**********************************************************/ int* p_int = &val_int; double* p_double = &val_double; int(*p_func)(int,int) = &Add; //or =Add int(*p_arr)[] = &arr; struct Student *p_struct = &stu; int** p_pointer = &p; return ;
} int Add(int a,int b)
{ return a+b; }
四、指针的属性和使用
我认为指针有2个属性: ①指针的值 ②指针的宽度
指针的值很好理解,比如第一个例子 int a = 5; int*p = &a; 那么p的值就是a的地址0x02
指针的宽度:由指针的类型决定。
如果说指针的值标记了某个数据在内存的起始位置,那么,指针的宽度就决定了从起始地址
对应的字节开始,往后还有多少个字节,也是属于这个内存数据的。
生活情景:“老王,去帮我去仓库拿个货,我的货从78号箱开始,并且有2个。” 于是老王取出 78和79号箱子的货物递给你
假如你对老王说:“老王,帮我去仓库取我的货,我的从78号箱开始,一共
有几个箱子,我也记不住了” “那我怎么取,取少了你就有损失了,取多了别人也不干了,你不要难为我啊”
这点我的另一篇随笔也讲到了 .
#include<stdio.h>
int main(void)
{ int a = ; int* p=&a; printf("%d\n", *((unsigned char*)p) ); //读取内存数据时,取的宽度的比存时的宽度小,数据缺失 return ;
}
#include<stdio.h>
int main(void)
{
int arr[] = {,}; arr[] = ; //ops! 访问越界
return ;
}
前面我们讲了内存块的4点要素,第4点就是内存块的宽度,他是和指针的宽度是一一对应的。一个指针变量指向了这个内存块,
那么指针的宽度就是这个内存块的宽度。这也表明,我们在使用指针的时候,不要越界,也不要取少,取少了取出的数据会不完整,
越界就更严重了,程序会挂掉的,因为你访问了不属于你的内存。
扩展:有木有 没有宽度的指针呢? 答:这个可以有 void* p;
void* 类型的指针对应与C# or java中的Object类型变量,它可以指向任何类型变量。 不过他只保存了内存的起点,没有保存宽度信息,如果你想
取出原来的数据,你必须做出正确的强制转换。
五:一对相反操作的运算符 * 和 &
& 取地址符 &a 就获取了a的指针,然后我们就可以用int*p变量出承接这个地址 p = &a; 我们就说p指向了a
* 解地址符 *操作符有一个很形象的动词 :解 。p保存了a的起始地址和延续宽度,那么,*p则是确定起始地址,量出宽度,
获取这个内存块。 因此我们可以通过a 的地址p去操作(读/写)这个内存块了。
p也是一个变量,他自己也有地址,这就长产生了指针的指针。
注意:我所说的指针的宽度并不是说指针变量占用的内存大小,而是通过这个指针指向的内存块的宽度。指针的占用的字节数是一定的,一般情况下,指针变量都占用4个字节。上面图中我也画了4个字节。
六、为什么要使用指针
你可能会问:我有变量名,为什么还要绕一转,用指针去使用内存数据?
但并不是这样,下面是2个例子。
①通过动态申请的内存是匿名的,没有名字,只能通过指向这块内存的指针去使用。
malloc() realloc() calloc() 这里不扩展了。
②一个经典的题目:用函数交换2个变量的值。
#include<stdio.h>
void swap_2b(int a,int b);
void swap_hack(int *pa,int *pb);
void swap_normal(int*pa,int*pb); int main()
{
int a = ;
int b = ;
swap_2b(a,b); //Can`t swap;
swap_normal(&a,&b); //OK
swap_hack(&a,&b); //OK return ;
} //2b青年写的函数
void swap_2b(int a,int b)
{
int t;
t=a;
a=b;
b=t; } //程序员写的函数
void swap_normal(int*pa,int*pb)
{
int t;
t=*pa;
*pa=*pb;
*pb=t; } //黑客写的函数
void swap_hack(int *pa,int *pb)
{
*pa = *pa^*pb;
*pb = *pa^*pb;
*pa = *pa^*pb; }
我们发现只有2b青年没用指针哈。
原因很简单,C语言函数是按值传递的,2b青年写的代码之所以达不到效果是因为它操作的内存块错了。
无论他在函数里面怎么换a和b的值,main函数中的a和b都不会变。因为swap函数中的a和b是随函数调用新生成的变量,是副本,而不是源对象。
但是传递指针就不一样了,因为内存数据的指针是独一无二的。一个人有唯一的身份证一样。
七、野指针和NULL指针
指针变量在使用前或者使用完,好的习惯是赋值为NULL ,NULL 是编号为0的字节的地址。指向NULL表示不指向任何变量。
NULL就像剑鞘,野指针就像暗箭,如果你不像被暗箭所伤,就让他归鞘。
最后:由于内容比较多,可能有许多地方表达不当,或有疏漏,错误,希望大家及时指出,谢谢 : )
用我所学去讲C语言指针的更多相关文章
- 【C】用我所学去讲C语言指针
很多人不敢讲C的指针,有些人讲不清,有些人怕讲错.初生牛犊不怕虎,就让我讲讲. 下面开始. 一.指针的定义 指针是内存单元的编号.内存单元是以字节为单位的.所以指针就是字节的编号. 比如我们的个人电 ...
- 闭嘴,给你一个数!1分钟,学完C语言指针,不扎手只扎心的针!
序言 指针是C语言学习者绕不过的一道坎,也是C语言学习者不得绕过的一道坎.辨别一个人C语言学的好赖就看他对指针的理解怎么样.指针内容也是工作面试经常问到的问题.本文将带你重新认识那个绊倒你的指针,以解 ...
- 逆向知识第十四讲,(C语言完结)结构体在汇编中的表现形式
逆向知识第十四讲,(C语言完结)结构体在汇编中的表现形式 一丶了解什么是结构体,以及计算结构体成员的对其值以及总大小(类也是这样算) 结构体的特性 1.结构体(struct)是由一系列具有相同类型或不 ...
- 1164: 零起点学算法71——C语言合法标识符(存在问题)
1164: 零起点学算法71——C语言合法标识符 Time Limit: 1 Sec Memory Limit: 64 MB 64bit IO Format: %lldSubmitted: 10 ...
- C语言指针的陷阱
C语言指针的陷阱 分类: C/Cpp 转自:http://blog.csdn.net/porscheyin/article/details/3461670 “C语言诡异离奇,陷阱重重,却获得了巨大 ...
- (转载)c语言指针学习
前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...
- 2-Linux C语言指针与内存-学习笔记
Linux C语言指针与内存 前面我们对于: c语言的基本用法 makeFile文件的使用 main函数的详解 标准输入输出流以及错误流管道 工具与原理 指针与内存都是c语言中的要点与难点 指针 数组 ...
- c语言指针学习【转】
前言 近期俄罗斯的陨石.四月的血月.五月北京的飞雪以及天朝各种血腥和混乱,给人一种不详的预感.佛祖说的末法时期,五浊恶世 ,十恶之世,人再无心法约束,道德沦丧,和现在正好吻合.尤其是在天朝,空气,水, ...
- 不可或缺 Windows Native (7) - C 语言: 指针
[源码下载] 不可或缺 Windows Native (7) - C 语言: 指针 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 指针 示例cPointer.h #i ...
随机推荐
- org.springframework.beans.TypeMismatchException: Failed to convert property value of type 'null' to required type 'double' for property 'band'; nested exception is org.springframework.core.convert.Con
本文为博主原创,未经允许不得转载: 先将异常粘贴出来: 20:37:26,909 ERROR [com.suning.fucdn.controller.ProductDataStaticsContro ...
- Linux配置中文输入法(搜狗输入法)
一.基础知识 在原生ubuntu14.04英文环境系统中只有IBus拼音,真的好难用.由于搜狗输入法确实比Linux系统下其它的中文输入法都要好用得多,所以我决定在我的Ubuntu 14.04系统中安 ...
- Ajax+Struts2用户注册功能实现
详细请参考源码(Github):https://github.com/QQ3330447288/ajaxRegister 1.目录结构 2.截图 3.核心代码: register.jsp <sc ...
- ranch 源码分析(二)
接上ranch 源码分析(一) 上次讲到了ranch.erl的start_listener函数,下面我们详细分析下这个函数 -module(ranch). %...... 省略若干行 -spec st ...
- java对redis的基本操作,ZZ
java对redis的基本操作 http://www.cnblogs.com/edisonfeng/p/3571870.html
- 『Python CoolBook』使用ctypes访问C代码_上_用法讲解
一.动态库文件生成 源文件hello.c #include "hello.h" #include <stdio.h> void hello(const char *na ...
- 『TensorFlow』降噪自编码器设计
背景简介 TensorFlow实现讲解 设计新思路: 1.使用类来记录整个网络: 使用_init_()属性来记录 网络超参数 & 网络框架 & 训练过程 使用一个隐式方法初始化网络参数 ...
- django1
* <pre>━━━━━━神兽出没━━━━━━ * ┏┓ ┏┓ * ┏┛┻━━━┛┻┓ * ┃ 王 ┃ * ┃ ┃ * ┃ ┳┛ ┗┳ ┃ * ┃ ┃ * ┃ ┻ ┃ * ┃ ┃ * ...
- Android流媒体开发之路一:Camera2采集摄像头原始数据并手动预览
Android Camera2采集摄像头原始数据并手动预览 最近研究了一下android摄像头开发相关的技术,也看了Google提供的Camera2Basic调用示例,以及网上一部分代码,但都是在Te ...
- erlang大法好
可惜haxe不能生成erlang.不过没关系,s6k输入法的实际执行方案,现在由typescript改用haxe.cdt3的ts地位不变. 以后这个博客大部分内容都是跟haxe/typescript相 ...