写这篇文章时由于没有参考过什么权威资料教材,所以有些观点可能是错误的, 不过我本人都是经过大量调试后才写出来的啦..

1. 内存由多个单位为8bit = 1byte的单元组成,每1个单元都配有1个编号,这个就是内存地址。
           计算机的主存储器是内存,而不是硬盘,因为cpu只能直接访问内存...  而cpu访问内存是根据内存的编号来访问的,这个编号就是内存地址,cpu获得内存地址就能直接寻址到对应的内存单位。

2.指针就是内存地址..它们是同一概念
           很多人都讲指针就是指向内存地址的东西, 但是我认为指针就是内存地址..

3.指针变量的概念
           
首先,指针变量是1个变量, 也就是它的值是可以变的。  其次,指针变量是1个特殊的变量, 它存放的值是1个指针(也就是内存地址啦,所以我认为它们是同一样野..

4.指针变量所占字节
            
在32位的操作系统中, 系统会使用32个2进制位来表示内存地址, 就是从0000...(32个0)....00 到1111...(32个1)..111, 在这里面的每1个2进制数的都可以表示1个地址, 那么这里面有多少个地址呢? 就是2的32次方 = 4G 个啊。

而每1个地址对于1个大小为1byte=8bit(1字节)的内存单元, 所以32位系统最多只支持4GB内存的原因就是这个了,因为地址不够用啊.

而每1个地址都要用32个bit(位来表示), 那么如果要存放这个地址在内存中, 就必须要有32个bit的位置的内存,前面讲到了,每个内存单位的容量是8bit, 所以需要4个内存单元来存放1个内存地址(指针), 也就是说1个指针变量需要 4个地址的内存来存放. 占4个字节。
             即sizeof(p) = 4 了 //p是1个指针
             即对于32位系统来讲, 1个指针变量的长度是4字节
            
通常我们会讲2进制的地址换算成十六进制来表示,
             就是从0x 00000000  到0x FFFFFFFF    
             上面的0x 只不过是十六进制的标志.

             在64位的操作系统中,相应地系统会用64个2进制位来表示1个地址, 理论上会有17亿多G个地址, 支持17亿GB内存...当然只是理论上了啊。
               所以64位操作系统中, 1个地址需要64bit内存来存放, 也就是1个指针变量长度是8byte(字节)啦

由于本屌的系统是64位的, 所以下面的内容和例子都是基于64位系统来说明的。

下面是一张内存的大概结构图

          上面都是一些基本概念, 下面开始才是真正想要说的内容.

5.为什么定义指针变量时要同时指定它的类型。
         
通常我们定义1个指针时要同时指定指针的类型:
          例如

char * p;  //定义1个char类型的指针
int * p;     //定义1个整性指针

其实,指针的类型决定了指针获取对应地址内存单元的后续单元个数, 怎么理解呢?
         
         参考上图,下面的指针就是1个char 类型的指针,指向了上面的char类型的变量, 地址是00000000FFFF0001,  而char类型只占1个字节, 所以cpu找到这个地址后就直接获得char类型的值了( 01100001 的值是97 ascii换算后就是字符'a')

5.1 int类型变量占4字节, 占用4个地址的内存
         但是如果下面的指针要指向上图蓝色的int类型变量呢?
         可以由图中得知,int 类型占4个字节啊, 也就是占住4个地址, 分别是00000000FFFF0002, 00000000FFFF0003,
00000000FFFF0004, 00000000FFFF0005,    但是下面char类型指针只能存放1个地址啊?

如果我们强制把其中1个int类型的赋给char 类型指针, 就很能报错了, 因为char 类型指针只能访问1个地址内存啊...

例如我们写个代码如下:

  int i = 123;char * p;p = &i; // error   char类型指针不能指向int类型的变量

       5.2 要指向int类型的变量或常量, 则必须要用int 类型的指针
        
    如果要用指针指向那个Int 类型的变量, 就必须定义1个int 类型的指针

  int i = 123;int * p;p = &i; //正确

那是不是指 int 类型指针的长度是char 类型指针的4倍, 能存放4个地址呢?
               当然不是, 其实所有类型的指针长度都一样的, 都只能放1个地址!

所有当执行 p =&i; 时,  系统实际上是把变量i的头1个字节的地址 赋值给指针p.
                而p去访问他的存放地址时, 首先就会指向int类型i的头1个字节, 而因为它是1个int类型的指针, 所以它会头1个字节开始, 共访问 int类型的长度个数的字节.

而int 类型的长度是4嘛. 所以p指需要知道 int类型的变量的头1个地址, 然后从头1个字节逐个访问4个字节的内存, 就能完全取得int类型的值了.
                
                如下图:


                    
 6.指针可以和整数执行加减运算
          我们经常在代码看到 p++;  p--; 这种语句. 其中p是1个指针啦.
          那么p+1 到底代表什么呢?
          其实假如 n 是 1个整数

那么 p+ n 其实就是另1个指针,  它 指向P的地址再加上 p指向的数据类型(也就是p的指针类型)的长度 乘于 n.

也就是假如p 是1个char 类型的指针, 它指向的地址是 0000FFFF00000001 , 那么p + 1 就指向 0000FFFF00000002
           假如p是1个 Int 类型的指针 它的指向的地址是FFFFFFFF00000001, 那么p+1 就指向 FFFFFFFF00000005
       
           也就是
            p + n指向的地址 = p指向的地址  + sizeof(p的类型) * n

画个图:

假如上面的内存存放的是1个int 类型数组的数据P.   那么P指向的就是头1个数组元素的地址了.
        而数组的变量名字本身是1个指向第1个数组元素的指针
        也就是 *p = p[0]
        而p + 1 指向的是下1个元素的地址 即 *(p+1) =  p[1]

所以就得出经典公式  *(p+n) = p[n]


8.
不同的类型的指针可以指向同1个地址.
      
大家留意上图, 貌似int 类型 p 和 char 类型的指针q 都指向同1个地址啊..
         写几行代码做个例子.

         int i = 97;int * p = &i; // 定义1个int 类型指针 指向i的地址int * p2 = &i; //在定义1个指向i的地址的int类型指针 p2, 完全无问题的,只不过浪费8字节去存放这个指针变量了.现在我想定义1个指向i地址的char 类型指针?char * q = &i;   //定义1个指向i的地址的 char  类型指针, // 在linux gcc里是可以通过编译的, 但是有warning 信息, 不推荐

那么正确的写法是什么呢?
         因为i 是1个 int类型变量,   &i就是 这个变量的首个字节地址.  如果要将这个地址传给1个 char类型的变量.
         就要对其进行格式化

正确写法:

     char * q = (char *)&i;      //将int类型变量i的头部地址格式花成 char类型的地址.

这个就是指针的格式化了( 内存地址格式化)  // 这句话只是我自己的理解, 有可能是错的!

那么这样做有什么效果呢?
         char类型指针 q指向 int类型 i后,  *q 是什么?

见下图:

因为q指向的地址内容是 01100001 ,就是 97的二进制表示嘛, 而q是1个char 类型的指针. 所以 *q 就是 97的 ascii对应字符 就是小写字母'a'啊.

下面写个例子程序:

执行结果:

其实 *q 的真实地址内容是 01100001 这个8个bit 的二进制数.  作为char类型的值就是'a'了,上面程序第个printf 函数只是强制输出为十进制数.

附上gdb的调试信息, 可以看到本例子 p 和 q的指向地址都是 0x7fffffffdebc 啦

9.内存插入数据的规律.
     
我们来研究下数据是怎样插入内存的.
           我们知道1个int 类型是占4字节啦 (讲过好多次了)
           而当定义1个i 变量时. 系统就会给它分配 4字节内存.
           而当给i 赋值时, 系统就忘对应内存写数据了
           例如 当执行  i = 97; 时,         系统会将97 换成二进制 1100001, 但这只占7个位. 还有前面32 -7 = 25个位就是0了. 所以
           97 = 00000000 00000000 00000000 01100001

问题是这些数据怎么插入内存呢,  见上图debug信息: 内存里是
           01100001
           00000000
           00000000
           00000000

点解貌似反了啊. 不是应该如下吗:
           00000000
           00000000
           00000000
           01100001

内存每1个位(bit) 都有顺序. 分高低位.
           
到底怎么分呢,
               首先,地址小是地位. 地址大的是高位.
               相同地址的8个bit中, 按照常识, 左边是高位.
              
                而插入数据时,  是从低位开始写入地位的内存, 一直到高位.
               例如97 这个整数, 
                00000000  00000000   00000000 01100001
                从左到右是高到低,  插入内存时就下图所示了:

假如我定义1个int类型变量

  int i = 24930;

接下来定义1个char 类型指针指向它.

              char *q = &iprintf("*q is %c\n", *q)        //*q是多少呢?printf("*(q+1) is %c\n", *(q+1))    //*(q+1) 又是多少呢?

步骤还是先把24930 这个化为二进制数 110000101100010, 然后补齐到32bit (4字节)
              就是 00000000  00000000 0110000101100010
              那么p 就是指向最低位(头部地址)的字节   01100010  ,化成10进制数是98 啊, ascii码就是小写字母'b'了
              而p+1 指向p的地址加上 1乘于 char类型的长度.  就是下1个地址啊. 就是指向 01100001 ascii码就是小写字母'a'了
             
            下面就是这个例子程序

结果:

10.内存如何存入int类型的负数
    
继续用上面那个例子, 假如我定义1个int 类型的负数
       int i = -24930;
       那么内存里的数值是什么呢?

我们可以分析 24930的二进制是

00000000  00000000 0110000101100010
    

    本文第1张插图已经提过int 类型最高位是用来代表正负的. 所以加上正负位:
    
10000000  00000000 0110000101100010

那么内存里的数据就是上面那个二进制数吗?

不是的因为实际上负数是按补码形式来存储的,  而负数的补码是取反再+1
      
10000000  00000000 01100001 01100010      
11111111    11111111  1001111010011101        先取反(符号位忽略)
11111111    11111111  10011110 10011110       再+1

上面第3行的就是-24930 在内存中的数据了

gdb的信息

C语言 关于指针的格式化相关推荐

  1. c语言字符型输入格式化,c语言之字符串和格式化输入输出

    字符串和格式化输入输出 #include #include #define DENSITY 62.4 int main(void) { float weight, volume; int size, ...

  2. Go语言fmt.Sprintf(格式化输出)

    Go语言fmt.Sprintf(格式化输出) 格式化在逻辑中非常常用.使用格式化函数,要注意写法: fmt.Sprintf(格式化样式, 参数列表-) 格式化样式:字符串形式,格式化动词以%开头. 参 ...

  3. C语言重点——指针篇(一篇让你完全搞懂指针)

    C语言重点--指针篇(一篇让你完全搞懂指针) 一. 前言 C语言是比较偏底层的语言,为什么他比较偏底层,就是因为他的很多操作都是直接针对内存操作的. 这篇我们就来讲解C语言的一大特点,也是难点,指针和 ...

  4. c语言中void指针,C 语言 void指针

    C 语言 void指针 到目前为止,我们已经研究了分配给指针的地址应该与指针声明中指定的类型相同. 例如,如果我们声明了int指针,则此int指针不能指向float变量或某种其他类型的变量,即它只能指 ...

  5. 理解C语言中指针的声明以及复杂声明的语法

    昨天刚把<C程序设计语言>中"指针与数组"章节读完,终于把心中的疑惑彻底解开了.现在记录下我对指针声明的理解,顺便说下如何在C语言中创建复杂声明以及读懂复杂声明. 本文 ...

  6. C语言函数指针 和 OC-Block

    C语言函数指针 和 OC-Block 一. C语言函数指针 关于函数指针的知识详细可参考: http://www.cnblogs.com/mjios/archive/2013/03/19/296703 ...

  7. 【C 语言】指针 与 数组 ( 指针 | 数组 | 指针运算 | 数组访问方式 | 字符串 | 指针数组 | 数组指针 | 多维数组 | 多维指针 | 数组参数 | 函数指针 | 复杂指针解读)

    相关文章链接 : 1.[嵌入式开发]C语言 指针数组 多维数组 2.[嵌入式开发]C语言 命令行参数 函数指针 gdb调试 3.[嵌入式开发]C语言 结构体相关 的 函数 指针 数组 4.[嵌入式开发 ...

  8. c语言中指针中 - 和 。的区别?

    c语言中指针中 -> 和 .的区别? 例子1:比如有如下结构 typedef strut node{ int data;   strut node * next; } ListNode; Lis ...

  9. 清华大学c语言指针ppt,清华大学出版社-C语言10指针.ppt

    清华大学出版社-C语言10指针 void print(char *name[ ],int n) {int i: for(i=0:i<n:i++) printf(″%s\n″,name[i]): ...

最新文章

  1. FinanceJson
  2. C语言API编写窗体界面和按钮
  3. cookie和session原理
  4. (九)Spring 事务开发、事务属性详解
  5. php5.5 反序列化利用工具_Yii框架反序列化RCE利用链2
  6. 计算机报名锁定后可以修改吗,网上报名正式提交后 报名信息即被锁定 无法修改...
  7. SilverLight小游戏
  8. linux jira mysql_JIRA配置连接MySQL数据库
  9. golang之正则校验(验证某字符串是否符合正则表达式)
  10. CODE[VS] 1098 均分纸牌 ( 2002年NOIP全国联赛提高组)
  11. Java基础总结04-数组
  12. C++在windows下获得运行主机的硬件信息:CPU序列号、MAC地址、硬盘序列号、主板序列号
  13. 新计算机 安装win2000,虚拟机安装Windows 2000超详细教程
  14. PLC指令系统的介绍
  15. 倾斜摄影原理与关键技术介绍
  16. IDEA关联MySQL数据库库
  17. 如何用VB语言实现四连环游戏(重力四子棋)?
  18. AutoCAD Civil 3D-用Civil 3D建立路面
  19. 小程序快速生成朋友圈海报分享图
  20. ssh-keygen处理gitee

热门文章

  1. [How TO]-ubuntu20.10上安装Pulse Secure客户端
  2. 2022-03-31 一些后续
  3. MoeCTF 2021Re部分------大佬请喝咖啡,A_game
  4. IOS(objective-c)实现md5加密(32位小写)的工具类
  5. 【Linux】服务器常用命令
  6. 【Laravel】Fatal error: Declaration of Illuminate\Container\Container::get($id) must be compatible
  7. Composer update 问题: Could not authenticate against github.com
  8. 13、设置默认字符集和校对规则
  9. Linux系统运行级别
  10. Spring boot显示登录用户