文章更新,更加详细的介绍请看这篇: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语言指针的更多相关文章

  1. 用我所学去讲C语言指针

    文章更新,更加详细的介绍请看这篇:https://www.cnblogs.com/lulipro/p/7460206.html 很多人不敢讲C的指针,有些人讲不清,有些人怕讲错.初生牛犊不怕虎,就让我 ...

  2. 闭嘴,给你一个数!1分钟,学完C语言指针,不扎手只扎心的针!

    序言 指针是C语言学习者绕不过的一道坎,也是C语言学习者不得绕过的一道坎.辨别一个人C语言学的好赖就看他对指针的理解怎么样.指针内容也是工作面试经常问到的问题.本文将带你重新认识那个绊倒你的指针,以解 ...

  3. 逆向知识第十四讲,(C语言完结)结构体在汇编中的表现形式

    逆向知识第十四讲,(C语言完结)结构体在汇编中的表现形式 一丶了解什么是结构体,以及计算结构体成员的对其值以及总大小(类也是这样算) 结构体的特性 1.结构体(struct)是由一系列具有相同类型或不 ...

  4. 1164: 零起点学算法71——C语言合法标识符(存在问题)

    1164: 零起点学算法71——C语言合法标识符 Time Limit: 1 Sec  Memory Limit: 64 MB   64bit IO Format: %lldSubmitted: 10 ...

  5. - > 听学姐讲那过去的故事——打代码的小女孩

    童话故事 不知道大家有没有看过  天冷极了,下着雪,又快黑了.这是一年的最后一天——大年夜.在这又冷又黑的晚上,一个乖巧的小女孩在机房里调试程序.她从家里出来的时候还穿着一件外套,但是有什么用呢?那是 ...

  6. C语言指针专题——指针难学的4点原因

    前一篇跟大家聊了聊指针的概念,可是就算了解了指针是什么,为什么依然感觉难学?我试着从几个点切入,聊聊指针难学之处. 文末会给大家推荐几本书,有需要的朋友可以看看! 难点1. 讨厌的星号 定义指针变量p ...

  7. C语言_第一讲_C语言入门

    一.C语言的简介 1.C语言是一个标准,而执行标准的时候产生的自动化程序则是编译器2.了解:1983年美国国家标准化歇会(ANSI)制定了C语言标准.C语言的特点:3.代码的可移植性(理想状态是代码可 ...

  8. 零基础学Python--------第2章 Python语言基础

    第2章  Python语言基础 2.1 Python语法特点 2.11注释 在Python中,通常包括3种类型的注释,分别是单行注释.多行注释和中文编码声明注释. 1.单行注释 在Python中,使用 ...

  9. 陈正冲老师讲c语言之内存的申请malloc() 和释放free()

    1.如何使用 malloc 函数 不要莫名其妙,其实上面这段小小的对话,就是malloc的使用过程.malloc是一个函数,专门用来从堆上分配内存.使用malloc函数需要几个要求: 内存分配给谁?分 ...

随机推荐

  1. javascript优化--07模式(对象)02

    沙箱模式: 解决空间命名模式的几个缺点: 命名空间模式中无法同时使用一个应用程序或库的两个版本运行在同一个页面中,因为两者需要相同的全局符号: 以点分割,需要输入更长的字符,解析时间也更长: 全局构造 ...

  2. node连接--MongoDB

    简介: 传统关系类型(ORM:Object-Relational Mapper),MongoDB(ODM:Object Document Mapper); MongoDB是一个面向文档,schme无关 ...

  3. css 样式 图片平铺整个界面

    比如一个容器(body,div,span)中设定一个背景.这个背景的长宽值在css2.1之前是不能被修改的. 所以实际的结果是只能重复显示,所以出现了repeat,repeat-x,repeat-y, ...

  4. ural 1289. One Way Ticket

    1289. One Way Ticket Time limit: 1.0 secondMemory limit: 64 MB A crowed of volunteers dressed in the ...

  5. input:focus

    input:focus,select:focus,textarea:focus{outline:-webkit-focus-ring-color auto 5px;outline-offset:-2p ...

  6. 手把手教你 用 wpf 制作metro ProgressRing (Windows8 等待动画)

    效果图: 还在羡慕metro的ProgressRing吗? wpf 也可以拥有 首先说下思路, 一共6个点围绕一直圆转,所以需要使用rotation动画 并且一直转下去. 那么下面的问题就好解决了. ...

  7. CocoaPods 安装的第三方删除

    CocoaPods 第三方删除 我们使用CocoaPods非常高效地将一些第三方类库导入到我们的项目中,难免会出现一些错误,这时应怎么删除它呢?以下方法会帮你解决这个问题 打开Build Phases ...

  8. TYVJ P1080 N皇后 Label:dfs PS:以前做的一道题,贴出来防忘

    描述 检查一个如下的6 x 6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行.每列只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子. 上面的布局可以用序列2 4 6 1 3 5来描 ...

  9. HDU 4649 Professor Tian(DP)

    题目链接 暴力水过的,比赛的时候T了两次,优化一下初始化,终于水过了. #include <cstdio> #include <cstring> #include <st ...

  10. COJ0702 数学(三)

    数学(三) 难度级别:D: 运行时间限制:1800ms: 运行空间限制:262144KB: 代码长度限制:2000000B 试题描述 给出两个正整数a,b,求a*b. 输入 输入共两行,每行是一个正整 ...