C語言數(shù)組與指針一本道來

數(shù)組與指針.png

一本道來其他系列
C語言關(guān)鍵字
C語言注釋符號一本道來
C語言編譯預處理技術(shù)一本道來

指針的基礎(chǔ)

注意本節(jié)內(nèi)容可能在gcc下不能完成編譯,請切換到Windows平臺,使用dev-cpp或其他

  • 指針本質(zhì)上也是一個變量
  • 指針要占用一定的內(nèi)存空間(任何類型的指針的內(nèi)存大小是一樣的)
  • 指針用于保存內(nèi)存地址的值

*號的意義

  • 在指針聲明時,*號表示所聲明的變量為指針
  • *號表示取指針指向的內(nèi)存空間中的值

最佳實踐(指針占用的內(nèi)存空間)

#include <stdio.h>

int main()
{
    int i;
    int* pI;
    char* pC;
    float* pF;
    
    pI = &i;
    
    printf("%0X, %0X, %d\n", pI, &i, i);
    printf("%d, %d, %0X\n", sizeof(int*), sizeof(pI), &pI);
    printf("%d, %d, %0X\n", sizeof(char*), sizeof(pC), &pC);
    printf("%d, %d, %0X\n", sizeof(float*), sizeof(pF), &pF);
    
    return 0;
}

最佳示例(寫地址)需要在VS或其他windows系統(tǒng)下測試,

#include<stdio.h>
int main()
{
    int i;
    int * p;
    p=&i;
    *((int*)0x28FEB8)=100;          
    printf("%0X\n",p);
    printf("%d\n",i);
    return 0;
 } 

傳值調(diào)用與傳址調(diào)用

  • 當一個函數(shù)體內(nèi)部需要改變實參的值,則需要使用指針參數(shù)
  • 函數(shù)調(diào)用時實參將賦值到形參

數(shù)組的基礎(chǔ)

  • 數(shù)組在一片連續(xù)的內(nèi)存中存儲元素
  • 元素的個數(shù)可以是顯示的也可以是隱式的

例子

a[5]={1,2}

其中其他的都用0來填充,所以數(shù)組最后為

a[5]={1,2,0,0,0}

數(shù)組地址與數(shù)組名

  • 數(shù)組名代表數(shù)組首元素的地址
  • 數(shù)組的地址需要取地址才可以得到
  • 數(shù)組首元素的地址值與數(shù)組的地址值相同
  • 數(shù)組收元素的地址與數(shù)組的地址是兩個不同的概念

數(shù)組名的盲點

  • 數(shù)組名可以看作一個常量指針(指針所指向的內(nèi)容不能改變)
  • 數(shù)組“指向” 的是內(nèi)存中數(shù)組首元素的起始位置
  • 在表達式中數(shù)組名只能作為右值使用
  • 只有在下列場合中數(shù)組名不能看作常量指針
    • 數(shù)組名作為sizeof操作符的參數(shù)
    • 數(shù)組名作為& 運算符的參數(shù)

最佳錯誤

新建一個.c文件

char * p="hello world";

再建一個.c文件

#include<stdio.h>
extern char p[];
void main()
{
    printf("%s\n",p);
}

思路很簡單,就是輸出hello world,然而輸出的結(jié)果是錯誤的,原因就是上面列出來的幾點中的一點。
修改

Linux


#include<stdio.h>
extern char p[];
void main()
{
        printf("%s\n",(char *)*(unsigned int *)p);
}

Windows平臺


#include<stdio.h>
extern char p[];
void main()
{
        printf("%s\n",(char *)*(unsigned int *)p);
}

雖然會有warning但是程序運行是正確的

C語言中的字符串

  • 從概念上講,C語言中沒有字符串數(shù)據(jù)類型
  • 在C語言中使用字符數(shù)組來模擬字符串
  • C語言中的字符串是以'\0'結(jié)束的字符數(shù)組
  • C語言中的字符串可以分配于??臻g,堆空間或者只讀存儲區(qū)(不能被改變)
char* s1="Hello World1";(在只讀存儲區(qū))

字符串的長度

  • 字符串的長度就是字符串說包含字符的個數(shù)
  • C語言中的字符串的長度值得是第一個'\0'字符串出現(xiàn)的字符個數(shù)
  • C語言中通過'\0'結(jié)束來確定字符串的長度

標準庫,用來解決字符長度

strlen()

彩蛋(一行程序?qū)崿F(xiàn)strlen)

int strlen(const char * p)
{
    return (assert(s),(* p? strlen(p+1)+1:0));
 
}

不受限制的字符串函數(shù)

  • 不受限制的字符串函數(shù)是通過尋找字符串得結(jié)束符'\0'來判斷長度
    • 字符串復制函數(shù): char* strcpy(char* dst,const char* src)
    • 字符串連接:char* strcat(char* dst,const char* src)
    • 字符串比較函數(shù):int strcmp(const char* s1,const char* s2)

注意事項

  • 不受限制的字符串函數(shù)都是以'\0'作為結(jié)束標記來進行的,因此輸入?yún)?shù)必須包含'\0'
  • strcat和strcpy必須保證目標字符數(shù)組的剩余空間足以保存整個源字符串
  • strcmp以0值表示兩個字符串相同
    • 第一個字符串大于第二個字符串的時候返回值大于0
    • 第一個字符串小于第二個字符串的時候返回值小于0
    • strcmp不會修改參數(shù)值,但依然以'\0'作為結(jié)束符號

彩蛋(實現(xiàn)strcpy)

char* strcpy(char* dst,const char* src)
{
    char* ret=dst;
    assert(dst && src);
    while((*dst++=*src++)!='\0');//指針循環(huán)效率高
    return ret;
}

長度受限制的字符串函數(shù)

  • 長度受限的字符串函數(shù)接收一個顯示的長度參數(shù)用于限定操作的字符串
    • 字符串復制:char* strncpy
    • 字符串連接
    • 字符串比較

指針數(shù)組和數(shù)組指針分析

數(shù)組類型

  • C語言中的數(shù)組有自己特定的類型
  • 數(shù)組的類型由元素類型數(shù)組大小共同決定
    • int array[5]的類型為int[5]

定義數(shù)組類型

  • C語言中通過typedef為數(shù)組類型重命名
    • typedef type(name)[size];
  • 數(shù)組類型(重命名了一種一個數(shù)組類型):
    • typedef int(AINT5)[5];
    • typedef float(AFLOAT10)[10];
  • 數(shù)組定義:
    • AINT5 i
    • AFLOAT10 f

數(shù)組指針

  • 數(shù)組指針用于指向一個數(shù)組
  • 數(shù)組名是數(shù)組首元素的起始地址,但并不是數(shù)組的起始地址
  • 通過將取地址符號&作用于數(shù)組名可以得到數(shù)組的起始地址
  • 可通過數(shù)組類型1定義數(shù)組指針:ArrayType* pointer;
  • 也可以直接定義:type (*pointer)[n];(用于定義數(shù)組指針)
    • pointer為數(shù)組指針的變量名
    • type為指向的數(shù)組的類型
    • n為指向的數(shù)組的大小

最佳示例

#include <stdio.h>

typedef int(AINT5)[5];
typedef float(AFLOAT10)[10];
typedef char(ACHAR9)[9];

int main()
{
    AINT5 a1;
    float fArray[10];
    AFLOAT10* pf = &fArray;
    ACHAR9 cArray;
    char(*pc)[9] = &cArray;
    char(*pcw)[4] = cArray;
    
    int i = 0;
    
    printf("%d, %d\n", sizeof(AINT5), sizeof(a1));
    
    for(i=0; i<10; i++)
    {
        (*pf)[i] = i;
    }
    
    for(i=0; i<10; i++)
    {
        printf("%f\n", fArray[i]);
    }
    
    printf("%0X, %0X, %0X\n", &cArray, pc+1, pcw+1);
    
    //pc+1指向整個數(shù)組的后一個數(shù)組
}

指針數(shù)組

  • 指針數(shù)組是一個很普通的數(shù)組
  • 指針數(shù)組中每個元素為一個指針
  • 數(shù)組指針的定義:type* pArray[n];
    • type*為數(shù)組中每個元素的類型
    • pArray為數(shù)組名
    • n為數(shù)組大小
#include <stdio.h>
#include <string.h>

int lookup_keyword(const char* key, const char* table[], const int size)//第二個是指針數(shù)組
{
    int ret = -1;
    
    int i = 0;
    
    for(i=0; i<size; i++)
    {
        if( strcmp(key, table[i]) == 0 )
        {
            ret = i;
            break;
        }
    }
    
    return ret;
}

#define DIM(a) (sizeof(a)/sizeof(*a))

int main()
{//指針數(shù)組
    const char* keyword[] = {
            "do",
            "for",
            "if",
            "register",
            "return",
            "switch",
            "while",
            "case",
            "static"
    };
    
    printf("%d\n", lookup_keyword("return", keyword, DIM(keyword)));
    printf("%d\n", lookup_keyword("main", keyword, DIM(keyword)));
}

main函數(shù)的參數(shù)

  • main函數(shù)可以理解為操作系統(tǒng)調(diào)用的函數(shù)
  • 在執(zhí)行程序的時候可以向main函數(shù)傳遞參數(shù)

main函數(shù)的四種參數(shù)類型

int main()
int main(int argc)
int main(int argc,char *argv[])
int main(int argc,char *argv[],char *env[])

小結(jié)

  • 數(shù)組指針本質(zhì)上是一個指針
  • 數(shù)組指針指向的值是數(shù)組的1地址
  • 指針數(shù)組本質(zhì)上是一個數(shù)組
  • 指針數(shù)組中的每個元素的類型是指針

多維數(shù)組和多維指針

  • 指針變量在內(nèi)存中會占用一定的空間
  • 可以定義指針來保存指針變量得地址值
int main()
{
    int a=0;
    int* p=NULL;
    int** pp=NULL;
    pp=&p;
    *pp=&a;
    return 0;
}

指向指針的指針

  • 為什么需要指向指針的指針?
    • 指針在本質(zhì)上也是一個變量
    • 對于指針來講也有傳值調(diào)用與傳址調(diào)用

最佳示例(動態(tài)內(nèi)存大小更變)

#include<stdio.h>
#include<malloc.h>
int rest(char** p,int size,int new_size)
{
    int ret=1;
    int len=0;
    int i=0;
    char* mid=NULL;
    char* pt=NULL;
    char* pp=*p;
    if((p!=NULL)&&(new_size>0))
    {
        mid=(char*)malloc(3);
        pt=mid;
        len=(size<new_size)?size:new_size;
        for(i=0;i<len;i++)
        {
            *pt++=*pp++;
        }
        free(*p);
        *p=pt;
        
    }else{
        ret=0;
    }
    return ret;
}
void main()
{
    char *p=(char*)malloc(5);
    printf("%0X\n",p);
    if(rest(&p,5,3))
    {
        printf("%0X\n",p);
    }
}

二維數(shù)組與數(shù)組指針

  • 二維數(shù)組在內(nèi)存中以一維的方式排布
  • 二維數(shù)組中的第一維是一維數(shù)組
  • 二維數(shù)組中的第二維才是具體的值
  • 二維數(shù)組的數(shù)組名可以看作常量指針
二維數(shù)組.png
#include<stdio.h>
#include<malloc.h>
 void printArray(int a[],int size)
 {
    int i=0;
    printf("printfArray:%d\n",sizeof(a));
    for(i=0;i<size;i++)
    {
        printf("%d\n",a[i]);
     }
 }
 int main()
 {
    int a[3][3]={{0,1,2},{3,4,5},{6,7,8}};
    int *p=&a[0][0];
    printArray(p,9);
    return 0;
    
 }

數(shù)組名

  • 一維數(shù)組名代表數(shù)組收元素的地址
  • 二維數(shù)組同樣代表數(shù)組首元素的地址
#include<stdio.h>
int main()
{
    int a[5][5];
    int (*p) [4];
    p=a;
    printf("%d\n",&p[4][2]-&a[4][2]);
}

答案為-4,因為(*p)一次跨越4個,而a一次跨越5個

數(shù)組的遍歷

int a[3][3]={{}};
for(i=0;i<3;i++)
for(j=0;j<3;j++)
*(*(a+i)+j)

動態(tài)分配二維數(shù)組

原理大家很聰明就不解釋
二維數(shù)組動態(tài)分配.png
#include<stdio.h>
#include<malloc.h>
int** malloc2d(int row,int col)
{
    int** ret=(int**)malloc(sizeof(int*)*row);   
    int* p=(int*)malloc(sizeof(int)*row*col);
    int i=0;
    if(ret&&p)
    {
        for(i=0;i<row;i++)
        {
            ret[i]=p+col*i;
        }
    }else{
        free(ret);
        free(p);
        ret=NULL;
        p=NULL;

    }
    return ret;
}
void del_Array(int** a)
{
    free(a);
}
void main()
{
    int i=0,j=0;
    int row=0,col=0;
    printf("please input the row\n");
    scanf("%d",&row);
    printf("please input the col\n");
    scanf("%d",&col);   
    int** p=malloc2d(row,col);
    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
        {
            p[i][j]=i+j;
        }
    }
    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
        {
            printf("%d ",p[i][j]);
        }
        printf("\n");     
    }

}

數(shù)組參數(shù)和指針參數(shù)分析

注意C語言的編譯器會讓(不論是一維數(shù)組還是二維數(shù)組)數(shù)組參數(shù)退化為指針

為什么退化

  • C語言中只會以值拷貝的方式傳遞參數(shù)
  • 當向函數(shù)傳遞數(shù)組時
    • 將整個函數(shù)拷貝一份傳入函數(shù)(不可?。?/li>
    • 將數(shù)組名看作常量指針傳數(shù)組首元素地址

二維數(shù)組參數(shù)

  • 二維數(shù)組參數(shù)同樣存在退化問題
    • 二維數(shù)組可以看作是一維數(shù)組
    • 二維數(shù)組中的每一個元素是一維數(shù)組
  • 二維數(shù)組參數(shù)中第一維的參數(shù)可以省略(退化過程)
void f(int a[10])->void f(int a[])->void f(int *a)
void g(int a[3][3])->void g(int a[][3])->void g(int (*a)[3])

注意事項

  • C語言中無法向一個函數(shù)傳遞任意的多維數(shù)組(針對二維以上)
  • 為了提供正確的指針運算,必須提供除一維之外的所有維的長度
  • 限制
    • 一維數(shù)組-必須提供結(jié)束的標志
    • 二維數(shù)組-不能直接傳遞給函數(shù)
    • 多維-無法使用
#include<stdio.h>
#include<malloc.h>
void access(int a[][3],int row)
{
    int col=sizeof(*a)/sizeof(int);//去推導出列的數(shù)量
    int i=0,j=0;
    for(i=0;i<row;i++)
    {
        for(j=0;j<col;j++)
        {
            printf("%d ",a[i][j]);
        }
        printf("\n");
    }
    
}
void main()
{
    int a[3][3]={
    {1,2,3},
    {4,5,6},
    {7,8,9}
    };
    access(a,3);
}

函數(shù)與指針分析

函數(shù)

函數(shù)類型

  • C語言中的函數(shù)有自己特定的類型
  • 函數(shù)的類型由返回值,參數(shù)類型和參數(shù)個數(shù)共同決定的
  • C語言中通過typedef為函數(shù)類型重命名
    • typedef type name(parameter list)
    • typedef int f(int,int);

函數(shù)指針

  • 函數(shù)指針用于指向一個函數(shù)
  • 函數(shù)名是執(zhí)行函數(shù)體的入口地址(類似于數(shù)組名字)
  • 可通過函數(shù)類型定義函數(shù)指針:FuncType* pointer;
  • 也可以直接定義:type (*pointer)(parameter list);
    • pointer為函數(shù)指針變量名
    • type為指向函數(shù)的返回值
    • 參數(shù)類型的列表

函數(shù)指針的本質(zhì)與使用

回調(diào)函數(shù)

  • 回調(diào)函數(shù)是利用函數(shù)指針實現(xiàn)的一種調(diào)用機制
  • 回調(diào)機制的原理
    • 調(diào)用者不知道具體事件發(fā)生的時候需要調(diào)用的具體函數(shù)
    • 被調(diào)用函數(shù)不知道何時被調(diào)用,只知道被調(diào)用后需要完成的任務
    • 當具體事件發(fā)生時,調(diào)用者通過函數(shù)指針調(diào)用具體函數(shù)
  • 回調(diào)機制的將調(diào)用者和被調(diào)用的函數(shù)分開,兩者互相不依賴

最佳示例C語言回調(diào)函數(shù)

#include <stdio.h>

typedef int(FUNC)(int);

int test(int i)
{
    return i * i;
}

void f()
{
    printf("Call f()...\n");
}

int main()
{
    FUNC* pt = test;
    
    //void(*pf)() = &f;//老方法
    
    //pf();
    //(*pf)();//老方法
    
    printf("Function pointer call: %d\n", pt(2));
}

進階回調(diào)

#include <stdio.h>

typedef int(*FUNCTION)(int);

int g(int n, FUNCTION f)
{
    int i = 0;
    int ret = 0;
    
    for(i=1; i<=n; i++)
    {
        ret += i*f(i);
    }
    
    return ret;
}

int f1(int x)
{
    return x + 1;
}

int f2(int x)
{
    return 2*x - 1;
}

int f3(int x)
{
    return -x;
}

int main()
{
    printf("x * f1(x): %d\n", g(3, f1));//注冊
    printf("x * f2(x): %d\n", g(3, f2));
    printf("x * f3(x): %d\n", g(3, f3));
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

  • 指針是C語言中廣泛使用的一種數(shù)據(jù)類型。 運用指針編程是C語言最主要的風格之一。利用指針變量可以表示各種數(shù)據(jù)結(jié)構(gòu); ...
    朱森閱讀 3,624評論 3 44
  • 前言 把《C++ Primer》[https://book.douban.com/subject/25708312...
    尤汐Yogy閱讀 9,698評論 1 51
  • 版權(quán)聲明:本文為 gfson 原創(chuàng)文章,轉(zhuǎn)載請注明出處。注:作者水平有限,文中如有不恰當之處,請予以指正,萬分感謝...
    gfson閱讀 3,504評論 0 6
  • 平時為了提高開發(fā)效率,會使用一些Xcode插件,例如VVDocuments等,每次隨著Xcode更新,可能就用不了...
    ShenYj閱讀 449評論 0 2
  • 看到情侶之間洋溢的微笑偶爾會有羨慕,感覺他們是那么的幸福,看到情侶之間的不和,又會感覺慶幸。同時也感覺有趣的靈魂在...
    珂求不可及閱讀 352評論 0 3

友情鏈接更多精彩內(nèi)容