当前位置: 首页 > 文档资料 > C 语言程序设计 >

数组与指针

优质
小牛编辑
140浏览
2023-12-01

数组的基本概念

整个数组的地址和数组首元素的地址

void main()
{
    int a[] = {1, 2};
    int b[100] = {1, 3};
    int c[200] = {0}; //编译的时候就已经确定所有的值为零

    memset(c, 0, sizeof(c)); //显式的重置内存块
    //对一维数组 C 规定:
    //c 是数组首元素的地址 c+1 步长 4个字节
    //&c 是整个数组的地址 &c+1 步长 200*4 

    system("pause");
    return ;
}

数组数据类型

void main()
{
    typedef int (MyArrayType)[5]; //定义了一个数据类型,数组数据类型
    int i = 0;
    MyArrayType  myArray;  //int myArray[5];

    for (i=0; i<5; i++)
    {
        myArray[i] = i+1;
    }

    for (i=0; i<5; i++)
    {
        printf("%d ", myArray[i]);
    }

    printf("myArray代表数组首元素的地址 myArray:%d myArray+1:%d \n", myArray, myArray+1);
    printf("&myArray代表整个数组的地址 &myArray:%d &myArray+1:%d \n", &myArray, &myArray+1);

    system("pause");
    return ;
}

运行结果

1 2 3 4 5
myArray代表数组首元素的地址 myArray:6487600 myArray+1:6487604
&myArray代表整个数组的地址 &myArray:6487600 &myArray+1:6487620

数组指针

void main()
{
    char *Myarray[] = {"123", "456", "abc"}; //指针数组

    //数组指针:用一个指针来指向一个数组

    typedef int (MyArrayType)[5]; //定义了一个数据类型,数组数据类型
    int i = 0;
    MyArrayType  myArray;  //int myArray[5]; //用类型定义变量 
    MyArrayType *pArray; //定义一个指针变量,这个指针变量指向一个数组

    {
        int  a;
        int *p = NULL;
        p = &a;

    }
    {
        int myArray2[5]; //相当于一级指针
        pArray = &myArray2; //相当于2级指针
        for (i=0; i<5; i++)
        {
            (*pArray)[i] = i+1;
        }

        for (i=0; i<5; i++)
        {
            printf("%d ", (*pArray)[i]);
        }
    }

    system("pause");
    return ;
}

定义数组指针变量的第二种方法

void main()
{
    //定义声明一个数组指针类型
    typedef int (*PArrayType)[5];
    PArrayType pArray;  //告诉编译器给我分配一个指针变量

    int c[5];
    int i  = 0;
    pArray = &c;

    for (i=0; i<5; i++)
    {
        (*pArray)[i] = i + 1;
    }

    for (i=0; i<5; i++)
    {
        printf("%d ", (*pArray)[i]);
    }

    system("pause");
    return ;
}

定义数组指针变量的第三种方法

//前2种方法通过类型定义变量比较麻烦
void main()
{
    //int [][5]
    int (*pMyArray)[5]; //直接定义一个指向数组的数组指针变量
    int c[5];
    int i  = 0;
    pMyArray = &c;

    for (i=0; i<5; i++)
    {
        (*pMyArray)[i] = i + 1;
    }

    for (i=0; i<5; i++)
    {
        printf("%d ", (*pMyArray)[i]);
    }

    system("pause");
    return ;
}

多维数组的本质

多维数组名的本质是数组指针

数组首元素的地址和整个数组的地址是两个不同的概念,指针步长不一样

数组名代表数组首元素的地址,它是个常量。变量本质是内存空间的别名,一定义数组,就分配内存,内存就固定了。所以数组名起名以后就不能被修改了。

多维数组做函数参数退化过程

void printArray01(int a[3][5])
{
    int i, j, tmp = 0;
    for (i=0; i<3; i++)
    {
        for (j=0; j<5; j++)
        {
            printf("%d ", a[i][j]);
        }
    }
}

void printArray02(int a[][5])
{
    int i, j, tmp = 0;
    for (i=0; i<3; i++)
    {
        for (j=0; j<5; j++)
        {
            printf("%d ", a[i][j]);
        }
    }
}

void printArray03( int (*b)[5])
{
    int i, j, tmp = 0;
    for (i=0; i<3; i++)
    {
        for (j=0; j<5; j++)
        {
            printf("%d ", b[i][j]);
        }
    }
}

void main()
{
    int a[3][5], i=0, j=0;
    int tmp = 1;

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

    printf("----------------\n");
    printArray03(a);

    for (i=0; i<3; i++)
    {
        for (j=0; j<5; j++)
        {
            printf("%d ", a[i][j]);
        }
    }

    // a 多维数组名代表什么?
    printf("a %d , a+1:%d ", a, a+1);  //a+1的步长是20个字节 5*4
    printf("&a %d , &a+1:%d ", &a, &a+1);

    {
        //定义一个指向数组的指针变量 
        int (*pArray)[5] ;//告诉编译器分配4个字节的内存 32bit平台下
        pArray = a;

        printf("\n");
        for (i=0; i<3; i++)
        {
            for (j=0; j<5; j++)
            {
                printf("%d ", pArray[i][j]);
            }
        }
    }

    //多维数组名的本质是数组指针,步长:一维的长度

    // (a+i) 代表是整个第i行的地址  二级指针
    // *(a+i) 代表 1级指针  第i行首元素的地址
    // *(a+i) + j  ===> &  a[i][j]

    //*( *(a+i) + j) ===>a[i][j]元素的值

    //a[i][j] <=== >*( *(a+i) + j)
    //a[i] ===> a[0+i] ==> *(a+i);
    //a[i][j] === a[0+i][j]  ==>  *(a+i)[j]  ===>  *(a+i)[0 + j] ==>  *( *(a+i)  + j) 

    system("pause");
    return ;
}

运行结果

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
a 6487536 , a+1:6487556
&a 6487536 , &a+1:6487596
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

多维数组在内存中是线性存储的

1499597321060

1499597705463

多维数组

void printfArray(int *array, int size)
{
    int  i = 0;
    for (i=0; i<size; i++)
    {
        printf("%d ", array[i]);
    }
}
void main()
{
    int a[3][5];
    int i, j, tmp = 1;

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

    //把二维数组当成一维数组来打印证明线性存储
    printfArray((int *)a, 15);

    system("pause");
    return ;
}

运行结果

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15

数组作函数参数的退化问题

数组作函数参数,传参传递的只是数组首元素的首字节地址。

无法向函数传递一个数组,C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。

这么做是有原因的。在C 语言中,所有非数组形式的数据实参均以传值形式(对实参做一份拷贝并传递给被调用的函数,函数不能修改作为实参的实际变量的值,而只能修改传递给它的那份拷贝)调用。然而,如果要拷贝整个数组,无论在空间上还是在时间上,其开销都是非常大的。更重要的是,在绝大部分情况下,你其实并不需要整个数组的拷贝,你只想告诉函数在那一刻对哪个特定的数组感兴趣。这样的话,为了节省时间和空间,提高程序运行的效率,于是就有了上述的规则。同样的,函数的返回值也不能是一个数组,而只能是指针。这里要明确的一个概念就是:函数本身是没有类型的,只有函数的返回值才有类型。很多书都把这点弄错了,甚至出现“XXX 类型的函数”这种说法。简直是荒唐至极!

void fun(int buf[100]){ // 等价于 int buf[] 和 int *buf
      printf("%d\n", sizeof(buf)); // 4
}

int main(void){
      int buf[100];
      printf("%d\n", sizeof(buf)); // 400
      fun(buf);
      return 0;
}

void fun(int n, int buf[n]){ // [n]中的n只是起到说明的作用
      printf("%d\n", sizeof(buf));
}

等价关系

数组参数等效的指针参数
一维数组 char a[30]指针 char*
指针数组 char *a[30]指针的指针 char **a
二维数组 char a[10][30]数组的指针 char(*a)[30]

指针数组

#include <stdio.h>
void main()
{
    // 定义并初始化3个整型变量
    int a = 1;
    int b = 2;
    int c = 3;
    int * arr[3] = { &a, &b, &c }; // 定义长度为3的指针数组并将数组元素指向整型变量
    printf("arr[0]=%p, &a=%p, *arr[0]=%d, a=%d\n", arr[0], &a, *arr[0], a);
    printf("arr[1]=%p, &b=%p, *arr[1]=%d, b=%d\n", arr[1], &b, *arr[1], b);
    printf("arr[2]=%p, &c=%p, *arr[2]=%d, c=%d\n", arr[2], &c, *arr[2], c);
}

运行结果

arr[0]=0062FE4C, &a=0062FE4C, *arr[0]=1, a=1
arr[1]=0062FE48, &b=0062FE48, *arr[1]=2, b=2
arr[2]=0062FE44, &c=0062FE44, *arr[2]=3, c=3

数组作函数参数,数组没有副本机制,传递是地址

void  run(char *p[5]) //数组没有副本机制,传递是地址  
{  
    printf("\nrun=%d", sizeof(p));//4个字节  
    for (char **pp = p; pp < p + 5; pp++)  
    {  
        system(*pp);  
    }  
}  

void  run1(char **px) //数组没有副本机制,传递是地址  
{  
    printf("\nrun=%d", sizeof(px));//4个字节  
    for (char **pp = px; pp < px + 5; pp++)  
    {  
        system(*pp);  
    }  
}  

void main()  
{  
    char *p[5] = { "calc", "notepad", "mspaint", "tasklist &pause", "write" };  
    printf("\nmain=%d", sizeof(p)); // 20个字节

      //轮询一个数组,需要一个指针,轮询一个指针数组,需要一个二级指针  
    for (char **pp = p; pp < p + 5; pp++)  
    {  
        system(*pp);  
    }  

      // run(p); 
    run1(p);  
    getchar();  
}

指针数组的应用场景

求关键字在表中的位置

//一个入口多个出口
int searcheKeyTable(const char* table[], const int size, const char* key, int *pos)
{
    int rv = 0;
    int i = 0;
    int inum = 0;
    if (table==NULL || key==NULL || pos==NULL)
    {
        rv = -1;
        printf("func searcheKeyTable:%d", rv);
        return rv;
    }

    //间接的证明数组做函数参数的退化
    inum = (sizeof(table)/sizeof(*table)); // inum = 1

    for(i=0; i<size; i++)
    {
        if( strcmp(key, table[i]) == 0 )
        {
            *pos = i;
            //break;
            return rv;    
        }
    }

    //没有找到返回-1
    if (i == size)
    {
        *pos = -1;
    }
    return rv;
}

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

int main()
{
    int inum = 0;
    int pos = 0;
    int a[10];
    int i = 0;
    //指针数组
    char*   c_keyword[] = {
        "while", 
        "case",
        "static",
        "do"
    };

    searcheKeyTable( c_keyword, DIM(c_keyword),"do", &pos);
    // ===> 带参数的宏
    //searcheKeyTable( c_keyword, (sizeof(c_keyword)/sizeof(*c_keyword)),"do", &pos);
    //searcheKeyTable( c_keyword, inum,"do", &pos);
    printf("pos:%d\n", pos);
    //searcheKeyTable(NULL, DIM(c_keyword),"do", &pos);
    //printf("pos:%d\n", pos);
    searcheKeyTable( c_keyword, DIM(c_keyword), "static", &pos);
    printf("pos:%d\n", pos);

    system("pause");
    return ;
}

命令行参数

//main函数是操作系统调用的函数
//在程序执行的时候可以向main函数传递参数

/*
argc 命令行参数
argv 命令行参数数组
env  环境变量数组

int main();
int main(int argc);
int main(int argc, char *argv[])
*/

int main(int argc, char* argv[], char**env)
{
    int i = 0;

    printf("---------- Begin argv ----------\n");
    for(i=0; i<argc; i++)
    {
        printf("%s\n", argv[i]);
    }

    //     for(i=0; argv[i]!=NULL; i++)
    //     {
    //         printf("%s\n", argv[i]);
    //     }
    printf("---------- End argv ----------\n");

    printf("\n");

    printf("---------- Begin env ----------\n");

    for(i=0; env[i]!=NULL; i++)
    {
        printf("%s\n", env[i]);
    }

    printf("---------- End env ----------\n");

    getchar();
}

指针数组的自我结束能力

void main()
{
    int inum = 0;
    int pos = 0;
    int a[10];
    int i = 0;
    //指针数组的自我结束能力
    char*   c_keyword[] = {
        "while", 
        "case",
        "static",
        "do",
        '\0'
    };  

    char*   c_keyword2[] = {
        "while", 
        "case",
        "static",
        "do",
        0
    }; 

    char*   c_keyword3[] = {
        "while", 
        "case",
        "static",
        "do",
        NULL
    }; 

    for (i=0; c_keyword[i] != NULL; i++)
    {
        printf("%s\n", c_keyword[i]);
    }
    printf("\n....\n");
    for (i=0; c_keyword2[i] != NULL; i++)
    {
        printf("%s\n", c_keyword2[i]);
    }
    printf("\n....\n");
    for (i=0; c_keyword3[i] != NULL; i++)
    {
        printf("%s\n", c_keyword3[i]);
    }

    system("pause");
}

函数指针数组

斐波那契数列

#include <math.h>
int main()
{
    int arr[20] = { 1, 1 };
    int i;
    for (i =2; i < 20; i++) //i从2开始
    {
        arr[i] = arr[i - 1] + arr[i - 2];
    }
    for (i = 0; i < 20; i++)
    {
        if (i % 5 == 0)  //第5个元素就换行
            printf("\n");  
        printf("%-5d", arr[i]);
    }
    system("pause");
}

运行结果

1    1    2    3    5
8    13   21   34   55
89   144  233  377  610
987  1597 2584 4181 6765

打印杨辉三角

#include<stdio.h>
#include<stdlib.h>
void main()
{
    int i, j, n = 0;
    int arr[10][10] = { 1 }; //定义一个10行10列的二维数组,初始化为1

    while (n < 1 || n > 10)  //此while循环控制用户只能输入n = 10
    {
        printf("请输入n的值:\n");
        scanf("%d", &n);
    }
    for (i = 1; i < n; i++)  //外层循环控制杨辉三角的行数
    {
        arr[i][0] = 1;         //每一行第1个元素都赋值为1,即第1列都为1
        for (j = 1; j <= i; j++)    //内层控制杨辉三角的列数
              //每个元素等于其上一行左边和上边两个元素之和
            arr[i][j] = arr[i - 1][j - 1] + arr[i - 1][j];
    }
    for (i = 0; i < n; i++)  //双重for循环打印这个二维数组中的元素
    {
        for (j = 0; j <= i; j++)
            printf("%-5d", arr[i][j]);
        printf("\n");
    }
    system("pause");
}

运行结果(n = 10)

1
1    1
1    2    1
1    3    3    1
1    4    6    4    1
1    5    10   10   5    1
1    6    15   20   15   6    1
1    7    21   35   35   21   7    1
1    8    28   56   70   56   28   8    1
1    9    36   84   126  126  84   36   9    1