c语言指针学习

  前言

  近期俄罗斯的陨石、四月的血月、五月北京的飞雪以及天朝各类血腥和混乱,给人一种不详的预感。佛祖说的末法时期,五浊恶世 ,十恶之世,人再无意法约束,道德沦丧,和如今正好吻合。尤为是在天朝,空气,水,食品,你能告诉还有没有问题的吗?不知大难至,世人依旧忙。祸福相依,危中有机。那些高级生命,出于慈悲,会救渡咱们,可是你要去思考,去发现机缘。 最近较闲,没事就学点基础知识,成天在上层晃,感受晕的厉害,接地气。关于指针我上学的时候学过一点,个人老师说“指针很难呢“,当时觉得这老师挺谦虚的。后来才知道其实他想说"我也搞不懂",不懂就别乱比喻的了,把指针比喻成门牌号,信封邮寄地址,如今我看到指针就想起门牌号,信封地址。想一想都是泪。php

  地址

   说到指针,先说说地址,看一段小程序html

#include "stdio.h"

int main()
{
    int a = 10;
    int *p = &a;
    printf("%p\n", p);
    return 0;  
}

// output
0x7fff8b6a378c

  每当我看到指针的输出 像这种"0x7fff8b6a378c"时候,头都大了,那时候老师说是地址,搞得糊里糊涂的。那什么是地址呢?固然我帮你百科一下。是系统 RAM 中的特定位置,一般以十六进制的数字表示,系统经过这个地址,就能够找到相应的内容。当使用80386时,咱们必须区分如下三种不一样的地址:逻辑地址、线性地址、物理地址;在进行C语言指针编程中,能够读取指针变量自己值(&操做),实际上这个值就是逻辑地址,它是相对于你当前进程数据段的地址(偏移地址),不和绝对物理地址相干,好比上面那个"0x7fff8b6a378c" 就是逻辑地址。逻辑地址不是被直接送到内存总线,而是被送到内存管理单元(MMU)。MMU由一个或一组芯片组成,其功能是把逻辑地址映射为物理地址,即进行地址转换。下面是转换关系图。java

 

  关于内存地址怎么转换能够参考一下的博文。《再论逻辑地址、线性地址》、 《我理解的逻辑地址、线性地址、物理地址和虚拟地址编程

  指针

  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.指针的类型segmentfault

      基本数据类型好比 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  == p2\n");
    } else {
      printf("p1 != p2\n");
    }

    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做为数组首地址来用。数组是一种线性的数据结构,数组名指向了那一片内存。

 --EOF--

相关文章
相关标签/搜索