嵌入式開發之C基礎學習筆記06--數組和指針(很是重要,難點)

C語言進階學習

數組

1)數組的使用 java

2)字符串(String):數組(尤爲是一維數組)最經常使用的地方——————>C語言中字符串就是一維數組


數組:

定義:具備相同類型的數據的有序集合,並用惟一的名字來標識。
1)數組必須直接聲明,編譯器在編譯階段爲其分配內存空間
2)C89數組必須是定長的,數組的大小在編譯時是固定的;C99容許使用變長數組,數組的大小在運行時肯定
 void f(int longeur,int wide)
{
int matrix[longeur][wide];/*定義一個矩陣*/
/*數組的長度由兩個參數決定*/
}
3)數組的全部元素佔連續的內存空間,在內存中是線性(順序)存放的,保存數組所須要的內存空間直接與基本類型和數組長度有關。
  數組佔用的內存空間 = sizeof(基類型)*數組長度
4)C不檢查數組是否越界,程序能夠在兩邊越界。程序員應本身加入越界檢查。數組能夠越界使用,可是初始化時不容許!
5)向函數傳遞數組:
  定義數組形參的方法有三種:指針,定長數組,無尺寸數組
  void func1(int *a){...}
  void func2(int a[10]){...}
  void func3(int a[]){...}
  在函數的形參的聲明中,數組尺寸無所謂,由於C語言沒有邊界檢查
  實際上,第二種方法在編譯後,編譯器產生的代碼就是讓函數接受指針,並不生成10個元素的數組
  A.形參中的數據不能再理解爲數組,而必須理解爲指針:不能用sizeof求大小;但能夠再賦值,這與數組名的指針常量性質不同。傳值時有內容的賦值,但數組內的元素可能有不少,爲了不內容的大量賦值而佔用太多的內存,C規定數組傳參數就是傳指針
  B.int a[][]不能作性參,由於a是指向int[]這樣一種數據類型的數組指針,但小表大小沒有肯定。而int a[][8]能夠,而且能夠直接用二維數組名(無需顯示轉換)作其實參[必定要指定一維行參的個數]
6)在處理一個數組的元素時,使用指針自增(p++)的方式一般比直接使用數組下標更快,使用指針可以使程序得以優化
7)C容許定義多維數組,維數上限由編譯器定義。但多於三維的數組並不經常使用,由於多維數組所須要的內存空間對維數呈指數增加。而且計算各維下標會佔用CPU時間(存取多維數組元素的速度比存取一維數組元素的速度慢)
8)對數組初始化時注意,C89要求必須使用常量初始化字符,而C99容許使用很是量初始化字符來初始化本地數組


void main()
{
int i,j,min,temp;
int array[10];/*定義一個整型的一維數組*/
printf("please input ten integer:\n");
for(i=0;i<10;i++){
printf("array[%d]=",i);
scanf("%d",&array[i]);
}
printf("the array is");
for(i=0;i<10;i++){
printf("%d",array[i]);
}
printf("\n");
/*排序*/
for(i=0;i<9;i++)
{
min = i;
for(j=i;j<10;j++)
{
if(array[min]>array[j])min = j;
temp = array[i];
array[i] = array[min];
array[min] = temp;
}
}
printf("\nthe result:\n");
for(i=0;i<10;i++)
{
printf("%d",array[i]);
}
printf("\n");
}




字符串:

1)C沒有專門的字符串變量,對於它的操做所有由一維數組實現。字符串是字符數組的一種特殊形式,惟一的區別就在於她是做爲一個總體操做,而普通數組則不能。最終差異就在末尾的NULL(0)上
2)初始化操做:要使用字符串常量時則把它放到數據區中的CONST區(數據區,全局變量區和靜態變量區),用字符串常量初始化字符數組時有一個複製內容的操做,而不只僅是用一個指針指向他。實際上字符串常量是在常量區仍是在堆區,採用何種存儲結構,以及是否連續的問題,取決於不一樣的編譯器
3)字符串的輸入與輸出,下面的函數均由<stdio.h>定義
  A.printf("%s",str);
  B.puts(str);
  C.scanof("%s",str);
  D.gets(str)
4)字符串運算:下面的函數均由<string.h>定義
 strcpy(s1,s2),strcat(s1,s2),strlen(str),strcmp(s1,s2),strchr(s1,ch),strstr(s1,s2)
注意:字符數組,字符指針之間的==比較是地址的比較,結果不多是true.但字符常量的==比較則不必定。爲了不二義性,應該儘量用strcmp()來比較
jellonwu @jintao :~/Desktop$ vim test13.c
#include <stdio.h>
#include <string.h>
main()
{
        char a[20] = "hello world";
        //字符串最後一個字符默認會多加一個字符'\0'
        //等價於a[0]='h';a[1]='e'....a[11]='\0'
        char b[20];
        int len;
        len = strlen(a);//字符串長度不包含最後一個字符中止符'\0'
        printf("The string length is %d.\n",len);
        len = sizeof(a);
        printf("The string sizeof vlaue is %d.\n",len);
        strcpy(b,a);
        printf("%s\n",b);
}
jellonwu @jintao :~/Desktop$ gcc test13.c -o test13
jellonwu @jintao :~/Desktop$ ./test13
The string length is 11.
The string sizeof vlaue is 20.
hello world


======================================


指針(Pointer)-----尤爲重要

指針是C語言的精華
1)使用指針的好處:
  -->可以內調用函數靈活地修改實參變量的值
  -->支持動態內存分配,可以方便地實現動態的數據結構(如二*數和鏈表)
  -->能夠提升某些程序的效率
  -->實現緩衝方式的文件存取
2)指針是地址。技術上,任何類型的指針均可以指向內存的任何位置。可是指針的操做都是基於類型的。
  指針操做是相對於指針的基類型而執行的,儘管在技術上指針能夠指向對象的其餘類型,但指針始終認爲它指向的是其基類型的對象。指針操做受指針類型而不是她所指向的對象類型的支配


指針表達式:
1)printf("%p",....)/*以宿主計算機使用的格式現實地址*/
2) 指針轉換:
 -->void * 型:爲Generic Pointer,經常使用來講明基類型未知的指針
 它容許函數指定參數,此參數能夠接受任何類型的指針變量而沒必要報告類型失配
 當內存語義不清時,也經常使用於語指原始內存
 例子:一個函數能夠返回多個類型(相似於malloc())
 void * f1(void *p)
 {
return p;
 }

 void main(void)
 {
int a = 100;
int * pp;
pp = &a;
printf("%d\n",*(int*)f1(pp));
 }


地址自己就是個整數
指針自己也是個變量


 -->其餘類型的指針轉換必須使用明確的強制類型轉換。但要注意,一種類型的指針向另外一種類型轉換時可能會產生不明確的行爲。
 -->容許將int型轉換爲指針或將指針轉換爲int型,但必須使用強制類型轉換,且轉換結果是已經定義的實現,可能致使非定義的行爲。(轉換0時不須要強制轉換,由於它是NULL指針)
 -->爲了與C++更好的兼容,不少C程序員捨棄了指針轉換,由於在C++中,必須使用強制類型轉換,包括void * 型
3)指針算術:能夠用於指針的算術操做只有加法和減法
  -->指針與整數的加,減法
  -->從一個指針中減去另外一個指針(主要目的是爲了指針偏移量)
4)指針比較:主要用語兩個或多個指針指向共同對象的狀況


初始化指針:
1)非靜態局部指針已聲明但未賦值前,其值不肯定
2)全局和靜態局部指針自動初始化爲NULL
3)賦值前使用指針,不只可能致使程序癱瘓,還有可能使OS崩潰,錯誤可謂嚴重之至!
4)【習慣用法】對於當前沒有指向合法的內存空間的指針,爲其賦值NULL
  由於C保證空地址不存在對象,因此任何空指針都意味着它不指向任何對象,不該該使用它
  用空指針來講明不用的指針基本上就是程序員遵照的協定(但並非C的強制規則)
  例如:int * p = NULL;
  * p = 1;/*ERROR!*/ 
    /*能經過編譯,但對0賦值一般會使程序崩潰*/
jellonwu @jintao :~/Desktop$ vim test14.c
#include <stdio.h>
void main()
{
        int i ;
        char a[] = "I am a student";
        char b[20];
        char * p1, * p2;
        p1 = a; p2 = b;
        //a和b是數組名是一個指針常量,在內存中固定分配,是一個地址,因此沒用&符號
        for(;*p1!='\0';* p1++,* p2++)
        {
                *p2 = *p1;
        }
        //p1其實就是a,a是一個維數組,默認最後加了一個'\0'
        //上面的for循環過程爲:其實就是遍歷數組a中的每個字符,p1++,其實就是a++就是內存地址+1個字節,也就是下標+1
        //p2[0] = p1[0]對應就是b[0]=a[0],p1++,p2++後再來一次循環,p2[1]=p1[1]對應b[1]=a[1]....直到末尾'\0'結束
        //這一過程實際上就等因而將p1整個數組指針賦給了p2指針
        *p2 = '\0';//加上一個'\0'表示數組結束,若是不加'\0',由於b是20個長度,打印出來剩下的可能都是亂碼
        printf("string a is:%s\n",a);
        printf("string b is:");
        for(i = 0;b[i]!='\0';i++)
        {
                printf("%c",b[i]);
        }
        printf("\n");


}
jellonwu @jintao :~/Desktop$ gcc test14.c  -o test14
jellonwu @jintao :~/Desktop$ ./test14 string a is:I am a student string b is:I am a student 函數指針:   void process(char *a,char *b,int(*apple)(const char *,const char *))   /*函數是經過指針調用的,apple是函數指針,傳入函數在內存中的首地址*/   在工做中常須要向過程傳入任意函數,有時須要使用函數指針構成的數組。如在解釋程序運行時,常須要根據語句調用各類函數。此時,用函數指針構成的數組取代大型switch語句是很是方便的,由數組下標實施調用。   函數不能直接看成類型做爲參數,由於函數自己不是類型,函數指針纔是類型 int * f(int * a);-->表示函數返回值是指針類型 int (* f){int * a};--->表示函數指針 例子 #include <stdio.h> #include <string.h> void check(char * a,char * b,int (*cmp)(const char *,const char *)); void main() { char s1[80],s2[80]; int(* p)(const char*,const char *); /*函數指針*/ p = strcmp;/*將函數strcmp的地址賦給函數指針p*/ printf("輸入兩個字符串:\n"); gets(s1);/*輸入字符串1*/ get(s1);/*輸入字符串2*/ check(s1,s2,p);/*經過指針變量p傳遞函數strcmp的地址*/ } void check(char * a,char * b,int(* cmp)(const char *,const char *) ) { printf("測試是否相等:\n"); if(!(* cmp)(a,b)) { printf("結果:相等\n") } else { printf("結果:不相等\n") } } 指針動態分配(Dynamic Allocation)內存空間: 指程序在運行中取得內存空間。   全局變量在編譯時分配內存空間,非靜態局部變量使用桟區,二者在運行時使用固定長度的內存空間   1)爲了實現動態的數據結構,C動態分配函數在堆區分配內存空間。堆是系統的自由內存區,空間通常都很大   2) 核心函數malloc()分配函數和free()釋放函數,使用malloc必須使用free,這個是必須的.   3) 堆區是有限的,分配內存空間後必須檢查malloc()的返回值,確保指針使用前它是非空的。使用malloc()必定要判斷malloc返回值進行判斷,若是返回值是NULL表示申請內存失敗   4)絕對不要使用無效的指針調用free(),不然將破壞自由表 指針相關問題:   1)在某些狀況下,用const來限制指針對提供程序的安全性有重要意義。你們能夠仔細看一下微軟編寫的系統函數便會有所體會   好比拷貝字符串這個操做,被拷貝的字符串由於不能被修改,應該加上const   2)指針的錯誤難以定位,由於指針自己並無問題。問題在於,經過錯誤的指針操做時,可能引入最難排除的錯誤:程序對未知內存區進行讀或寫操做   --> 讀:最壞的狀況是取得無用數據   --> 寫:可能沖掉其餘代碼或數據  這種錯誤可能要到程序執行了至關一段時間後纔出現,所以把排錯工做引入歧途 使用指針必定要肯定指針指向內存的什麼位置 常見錯誤例子  例子:未初始化的指針  int * p;  scanf("%d",p);/*ERROR*/  運行小程序時,p中隨機地址指向安全區域(不指向程序的代碼和數據,或OS)的可能性比較大。但隨着程序的增大,p指向重要區域的機率增長,最終使程序癱瘓。 指針與數組: 一維數組名可被不嚴格地認爲是指針常量:可執行指針的操做;可按地址單元賦值和引用;可用來初始化同類型的指針變量;但不能對一維數組名複製。 char p[] = "Hello,World"; char *p = "Hello,world";//指針變量 /*兩條語句徹底等價*/ 1)指針數組:經常使用於放置指向串的變量,數組中的每個元素都是指針   在一些特殊項目中,可能須要把若干個串做爲參數傳遞給函數,但串的長度不能肯定,這時採用頂長數組顯然不合適   例子一:給頂錯誤編號後,輸出錯誤信息   void print_error(int n)   {       static char *error[] = {"Syntax Error\n","Variable Error\n","Disk Error\n"};       printf("%s",error[n])   }   例子二:打印命令行參數,相似於echo命令   void main(int argc,char **argv, char **env)   { while(*++argv) printf("%s",*argv);   }   argc --->附加參數的個數,默認包含函數名,什麼參數沒有argc是1   argv --->參數具體的字符串數組   例子三:訪問命令行參數中的字符(對argv施加第二下標)    void main(int argc,char *argv[],char **env)    { int i,j; for(i = 0;i<argc;++i) { j = 0; while(argv[j]) { putchar(argv[j]); j++; }   printf("\n"); }    }  注意:  --->不要像數組那樣按下標單獨複製,也不要使用*(p+n)這樣的間接引用來修改某個單元的值,這樣作均可能引發運行錯誤,由於字符串常量是在常量區,是不容許被修改的。嚴格來講,用一個普通指針指向一個常量是不對的。會產生一個cannot overt from const type * to type *的編譯錯誤。字符指針的初始化是一個特列,可用一個字符指針指向一個字符串常量,但仍然不能修改其內容,將const char *強制轉換成char *能得到正確的地址,可引用,但仍然不能修改其內容。(將const int *轉換成int *則沒法得到正確的地孩子,這是字符串常量的特殊性)  --->按下標或*(p+n)這樣的簡介引用來讀取某單元的內容有時是可行的,這取決於不一樣的編譯器對字符串常量的存放方式:在常量區仍是堆區;採用何種存儲結構;是否連續(塊鏈就不連續)。而字符數組這樣引用和賦值老是可行的,由於它開闢了一塊連續的空間並賦值了內容。 2)對於數組a[],輸出a,*a,&a的值都是同樣,但咱們並不能認爲含義同樣。a有雙重含義,只不過經過printf()表現出來的是首元素的地址;*a中a去了其數組指針含義,值爲第一個元素的地址;&a中a去了其自定義函數類型含義,值爲第一個元素地址。若把a賦給數組指針變量p,則&p是p的地址,由於p並不具有數組這一層自定義數據類型含義。用數組名初始化指針或數組指針時作了自動類型轉化,取代了其指針含義。   也就是說:不要把賦值就理解爲同樣了,這個等之後學C++後就會有一個比較深刻的認識了,C++的運算符重載和自動類型轉化隱含了不少東西 3)純指針數組:即java中定義數組的方法。這樣的數組是真正的多級指針,不能用sizeof()求數組大小,但能夠實現多維動態,並且存取效率較高(無需作乘法尋址)---這個不經常使用 4)指向指針的指針:也就是二級指針,也不經常使用 char * * p 5)動態分配的數組(Dynamically ) 例子一:char *p = (char *)malloc(80); /*必須對p進行下面的測試,以免使用空指針*/  if(!p)  {   printf("ERROR!\n"); exit(1);  }  get(p);  for(int i = strlen(p)-1;i>=0;i--) putchar(p);  free(p); 例子二:  int (*p)[10] = (int(*)[10])malloc(4*sizeof(int));  /*爲了與C++兼容,必須捨棄全部的指針轉換*/  if(!p)  { printf("ERROR\n"); exit(1);  }    桟自己就是內存,只不過是特殊的內存
相關文章
相關標籤/搜索