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--

相關文章
相關標籤/搜索