C-数组、数组与指针

1、数组

1.1 一维数组

1.1.1 定义
数组是内存中连续存储的有着相同类型的一组数据的集合。
1.1.2 定义形式
类型说明符 数组名[常量表达式] ; 如int a[5];
int型变量占4个字节的内存空间,那么5个int型变量就占4*5=20个字节的内存空间。
说明:
1)一个数组a[n],这个数组中元素最大的下标是n-1;而元素a[i]表示数组a中第i+1个元素。
2)[常量表达式],这个可以是“数字常量表达式”,或者是“符号常量表达式”,必须是常量,不能是变量,如果非要,那么用动态定义的方式。
1.1.3 初始化
1)完全初始化

int a[5] = {1, 2, 3, 4, 5};

初始化各元素使逗号隔开的,不是用分号。
当完全初始化时,[]里面就不用指定数组的长度,因为此时元素的个数已经确定了。

 int a[] = {1, 2, 3, 4, 5};

编程可以使用这种,方便。
2)部分初始化

int a[5] = {1, 2}

没有被初始化的元素补0。
1.1.4 数组元素的引用
只有在定义数组的同时才可以整体赋值,其他情况下都不能整体赋值,因为除此之外程序中任何地方看到“a[常量]”都不是数组,都只是数组的一个元素、一个变量
1.1.5 如何将数组a赋给数组b

b=a;

a、b都是数组名,数组名都是地址,一个常数,是不能相互赋值。
应该用for循环,逐个赋值。
1.1.6 获取数组长度
使用关键字sizeof,能获取某个变量或某种数据类型所占字节数,数组同理。
sizeof(a)/sizeof(a[0]) // 总的字节数除以一个元素所占的字节数就是数组的长度
1.1.6 宏定义
定义:用#define定义一个标识符来表示一个常量
定义形式: #define 标识符 常量 //注意,最后没有分号
特点:定义的标识符不占内存,只是一个临时的符号,预编译这个符号就不存在了
说明:
1)宏所表示的常量可以是数字、字符、字符串、表达式。其中最常用的是数字
2)#define的作用域为自#define那一行起到源程序结束。如果要终止其作用域可以使用**#undef**命令

1.2 二维数组

1.2.1 定义:
类型说明符 数组名[常量表达式][常量表达式];
如:int a[3][4];
说明:
1)与一维数组一样,行序号和列序号的下标都是从0开始的。
2)二维数组是不存在的,内存是不分行,不分列的,元素都按顺序一个个往后排。因此二维数组本质是一维数组。
3)a[0]、a[2]、a[3]分别是这三个一维数组的数组名
1.2.2 初始化
第一维的长度可以不指定,但第二维的长度不能省。(二维数组第二维不能省

int arr[3][3={123456789}int arr[3][3={{123}{456}{789}}

2. 数组和指针

2.1 一维数组和指针
定义:
1)数组中任意元素地址赋给指针

int a[10]; /*定义a为包含10个整型数据的数组*/  
int *p;   /*定义p为指向整型变量的指针*/
p=&a[i];

2)将数组首地址(第一个元素地址)赋给指针

p=&a[0];
p=a;//两个等价

说明:
p、a、&a[0]均代表同一存储单元的地址,它们是数组a的首地址,也是下标为0的数组元素的地址。应注意的是:p是变量,而a、&a[0]都是常量,因此不能赋值

int array[5],i;  //那么,下面的两条语句都**是错误的**:
array=&i;array++//左值要求是变量
int *p =array, *q;  q=array+3   //这是可以的,赋值不能,但是地址为一个常数,可以加一个整数,p++是可以的
//等价与下面两条
q=array[3];
q = p+3;

2.1.1 数组元素的引用
1)&array[i] 等价 p+i 等价array+i(i是个整数),表示第i个元素的地址,可得出

*(p+i)和*(array+i)就是array[i]。

2)p[i]与*(p+i)是等价的
因此有四种表达方式::①array[i];②*(array+i);③*(p+i);④p[i]。 ①④形式一样,②③形式一样
说明:对应前面指针章节讲的,p++,如p刚开始指向a[3],p++实际就是a[4],移动了一个int,一个步长,四个字节。
2.2 二维数组和指针
2.2.1 二维数组的组成
int a[3][4]={{0,1,2,3},{4,5,6,7},{8,9,10,11}};
从下图第一行来看,3行4列的二维数组a,是由3个一维数组按顺序组成的。
在这里插入图片描述
3个一维数组名分别是a[0]、a[1]、a[2],也都是地址常量。这3个数组又都是包含4个整型元素的一维数组。
如数组a[0]包含了a[0][0]、a[0] [1]、a[0][2]和a[0][3]
2.2.2 二维数组的地址
二维一般取元素地址是&a[i] [j]
1)从一维角度取地址
通过一维数组名a[i]加上整数j来实现,即a[i]+j就是a[i][j]的地址
得出第一个等价关系:

①&a[i][j]
②a[i]+j

2)再推导
在一维中,a[i]是与*(a+i)等价的,因此a[i][j]等价于*(a+i)+j

3)从元素个数角度,再再推导
a[i][j]与a[0][0]相差4*i+j个元素(图看出的),利用“移动指针”(还没定义,所以引号,但是可以当做)表示:

&a[0][0]+4*i+j;
a[0]+4*i+j

2.2.3 取值方式

*(&a[i][j])
② *(a[i]+j)
③ **(a+i)+j)
④ *(&a[0][0+4*i+j)
⑤ *(a[0+4*i+j)

2.2.4 指向二维数组的指针变量
定义:数组指针指向具有n个元素的一维数组(数组指针(指向数组的指针)指向二维数组)
类型标识符 (*指针变量名)[长度]
如:

int a[3][4], (*p)[4],p=a; //指针第二维也不能少

说明:
1)p指向a[0],那么p+1,则指向一维数组a[1];p+2则指向一维数组a[2]。
2)定义p为指向二维数组的指针变量。它表示p是指针变量,又称为行指针,它指向具有4个整型元素的一维数组(指向由四个元素构成的一维数组)。
3)装的是一维数组,且是4个int型的

① p[i][j] 
② **(p+i)+j) 
③ *(p[i]+j) 
④ (*(p+i))[j]

3、指针数组
定义:一个数组的元素值为指针则是指针数组。(装的是地址
要求: 指针数组的所有元素都必须是具有相同存储类型(自动的(auto)、静态的(static)、寄存器(register)、外部的(exteren))和指向相同数据类型的指针变量。
1)定义形式:

数据类型 *指针数组名[元素个数]  //与上面二维数组的指针少了个括号
int *pa[2];表示定义了一个指针数组pa,它由指向int型数据的pa[0]和pa[1]两个指针元素组成。

2)应用:二维数组和指针数组
二维数组a[2][3]可分解为a[0]和a[1]这两个一维数组,它们各有3个元素。指针数组pa由两个指针pa[0]和pa[1]组成。可以把一维数组a[0]和a[1]的首地址分别赋予指针pa[0]和pa[1]

int a[2][3] ,*pa[2];
a[0=a[0];
或pa[0=&a[0][0];
pa[1=a[1];
或pa[1=&a[1][0];

在这里插入图片描述
在程序中a[i][j]、(a[i]+j)、(pa[i]+j) 、pa[i][j]
3)指针数组和数组指针变量的区别
联系:都可用来表示二维数组,但是意义和方法不同。
区别:

int*p)[3];  //数组指针

表示一个指向二维数组的指针变量。该二维数组的列数为3或分解后一维数组长度为3。

int *p[3];  //指针数组,由指针构成的一个数组

表示p是一个指针数组,有三个数组元素p[0]、p[1]、p[2],而且均为指针变量。
4、数组常见的算法
4.1 倒置算法
首尾交换

#include "stdio.h"
//数组首尾交换
main()
{  
int a[10],i,j,t;  
for(i=0;i<10;i++)       /*用for循环给数组输入10个数*/     
       scanf("%d"&a[i]);  
for(i=0,j=9;i<j;i++,j--)               /*循环实现逆置*/  
{     
    t=a[i];     
    a[i]=a[j];     
    a[j]=t;  
  }     
for(i=0;i<10;i++)       /*用for循环输出排序后的10个数组元素*/     
       printf("%d",a[i]);  
putchar('n');
}

4.2 折半查找
要求:已经排好序
特点:相比链表,一般数组效率更高。

  # include <stdio.h>

    int main(void)
    {
        int a[] = {13, 45, 78, 90, 127, 189, 243, 355};
        int key;  //存放要查找的数

        int low = 0;
        int high = sizeof(a)/sizeof(a[0]) -1;
        int mid;

        int flag = 0;   //标志位,用于判断是否存在要找的数

        printf("请输入您想查找的数:");
        scanf("%d", &key);

        while ((low <= high))
        {
                mid = (low + high) / 2;

                if (key < a[mid])
                {
                        high = mid -1;
                }
                else if (a[mid] < key)
                {
                        low = mid +1;
                }
                else
                {
                        printf("下标 = %d\n", mid);
                        flag = 1;
                        break;
                }
        }
        if (0 == flag)
        {
                printf("sorry, data is not found\n");
        }
        return 0;
    }
    

4.3 数组插入、删除
数组不擅长插入和删除。数组的优点在于它是连续的,所以查找数据速度很快。但这也是它的一个缺点。正因为它是连续的,所以当插入一个元素时,插入点后所有的元素全部都要向后移;而删除一个元素时,删除点后所有的元素全部都要向前移
1)插入

  # include <stdio.h>
    int main(void)
    {
        int a[23] = {1, 5, 66, 8, 55, 9, 1, 32, 5, 65, 4, 8, 5, 15, 64, 156, 1564,15, 1, 8, 9, 7, 215};
        int b[24];  //用来存放插入数字后的新数组,因为又插入了一个值,所以长度为24
        int Index;  //插入值的下标,Index是“下标”的英文单词
        int num;  //插入的值
        int i;  //循环变量

        printf("请输入插入值的下标:");
        scanf("%d", &Index);
    printf("请输入插入的数值:");
    scanf("%d", &num);

    for (i=0; i<24; ++i)
    {
            if (i < Index)
            {
                    b[i] = a[i];  /*循环变量i小于插入值位置Index时,每一个元素所放的位置
不变*/
            }
            else if (i == Index)
            {
                    b[i] = num;  //i等于Index时,将插入值赋给数组b
            }
            else
            {
                    b[i] = a[i-1];   /*因为插入了一个新的元素,所以插入位置后的每一个元素所
存放的位置都要向后移一位*/
            }
    }

    for (i=0; i<24; ++i)
    {
            printf("%d\x20", b[i]);
    }
    printf("\n");

    return 0;
}

2)删除

   #include <stdio.h>
    int main(void)
    {
        int a[23] = {1, 5, 66, 8, 55, 9, 1, 32, 5, 65, 4, 8, 5, 15, 64, 156, 1564,
    15, 1, 8, 9, 7, 215};
            int b[22];  /*用来存放删除数字后的新数组,因为删除了一个值,所以长度为22/
            int Index;  //要删除的值的下标
            int i;  //循环变量

            printf("请输入要删除的值的下标:");
            scanf("%d", &Index);

            for (i=0; i<23; ++i)
            {
                    if (i < Index)
                    {
                            b[i] = a[i];  /*循环变量i小于插入值位置Index时,每一个元素所存放的位
    置不变*/
                    }
                    else
                    {
                            b[i] = a[i+1];  /*删除值后面的元素都往前移一位,要删除的值直接被覆盖*/
                    }
            }

            for (i=0; i<22; ++i)
            {
                    printf("%d\x20", b[i]);  // \x20表示空格
            }
            printf("\n");

            return 0;
        }

4.4 排序
1)冒泡排序
2)插入排序
3)选择排序
4)快速排序
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值