C语言 数组和指针

数组

  • 数组部分初始化,剩余元素都会被初始化为0
    int days[12]={31,28,[4]=31,30,31,[1]=29};
    输出为{31,29,0,0,31,30,31,0,0,0,0,0}
    int stuff[]={1,[6]=4,9,10};
    输出为{1,0,0,0,0,0,4,9,10};
  • C不允许数组作为一个单元赋给另一个数组
int oxen[4]={5,3,2,8};
int yaks[4];
yaks=oxen;//不允许
yaks[4]=oxen[4];//数组下标越界
yaks[4]={5,3,2,8};//不起作用
  • 编译器不会检查数组下标是否使用得当

声明数组之前,使用符号常量表示数组大小,确保整个程序中的数组大小一致。

  • 变长数组

多维数组

  • 在计算机内部,二维数组是按顺序储存的
  • 初始化时,某列表中的数值数超过了数组每行的元素,则会出错,但不会影响其他行的初始化

初始化时,可省略内部的花括号,只保留最外面的花括号。只要保证初始化的数值个数正确,初始化的效果与上面相同。如果初始化数值不够,则按照先后顺序逐行初始化,用完所有值后,后面的值会统一初始化为0。

指针和数组

指针提供一种以符号形式使用地址的方法。

  • 数组名是数组首元素的地址。假设num是一个数组,则有num=&num[0]
    在C中,指针加1指的是增加一个存储单元。对数组而言,这意味着加1后的地址是下一个元素的地址,而不是下一个字节的地址
  • 在指针前面加*运算符可以得到该指针所指向对象的值
    数组和指针的关系十分密切。
    例如:dates + 2 == &dates[2]*(dates+2)=dates[2]

*dates+2指的是dates的第一个元素加2
*(dates+2)指的是dates的第三个元素

函数、数组和指针

假设编写一个处理数组的函数,该函数应该返回数组中所有元素之和,待处理的是名为marbles的int类型数组。如何调用函数?
可能的函数调用为:total=sum(marbles);
则该函数的原型是什么?

  • 数组名是该数组的首元素的地址,所以实际参数marbles是一个存储int类型的地址,应把它赋值给指针形式的参数,即该形参应该是一个指向int类型的指针
    对应的函数原型为int sum(int * ar);
    sum()函数从参数中获得了该数组首元素的地址,知道要在该位置找出一个整数。
    注意:该参数并未包含数组元素个数的信息。
    一共有两种解决方法。
    1.在函数代码中写上固定的数组大小
    2.把数组大小作为第二个参数传入
  • int *ar形式和int ar[]都表示ar是一个指向int的指针。
//由于函数原型可以省略参数名,所以下列四种原型都是正确的
int sum(int *ar,int n);
int sum(int *,int);
int sum(int ar[],int n);
int sum(int [],int);
#include <stdio.h>
#define SIZE 10

int sum(int[], int);
int main(void) {
    int marbles[SIZE] = { 20,10,5,39,4,16,19,26,31,20 };
    long answer;

    answer = sum(marbles, SIZE);

    printf("The total number of marbles is %ld.\n", answer);
    printf("The size of marbles is %zd bytes.\n",
        sizeof marbles);

    return 0;
}
int sum(int ar[], int n) {
    int i;
    int total = 0;
    for (i = 0; i < n; i++)
        total += ar[i];
    printf("The size of ar is %zd bytes.\n", sizeof ar);
    return total;
}

输出为

The size of ar is 8 bytes.
The total number of marbles is 190.
The size of marbles is 40 bytes.

由于传入函数中的是指向数组首元素的指针,并不是数组本身,字符长度为8。
数组是int类型,一个元素占四个字节,共10个元素,40个字节。

使用指针形参

  • 函数处理数组需要知道何时开始、合适结束。函数使用一个指针来指向数组的首地址,用整数表明待处理形参的元素个数。同时也可以再使用一个指针来表示数组的末地址
    声明函数:int sump(int *,int *);
    调用函数:answer = sump(marbles,marbles+SIZE);

C在给数组分配空间时,指向数组后一位的位置的指针仍然是有效的指针,但不可以访问。

  • *start++,(*start)++,*(start++),ar[i],*(ar+i)
    *start++*(start++)等效,指针+1,(*start)++是数组start的首元素+1,ar[i]*(ar+i)等效

指针操作

  • 赋值:可以把地址赋给指针。例如:用数组名、待地址运算符&的变量名、另一个指针进行赋值。
    地址应该和指针类型兼容,不能把double类型的地址赋给指向int类型的指针
  • 解应用:*运算符给出指针指向地址上存储的值
    不要解应用未初始化的指针,例如:int *pt;*pt = 5;,pt未被初始化,所以不知道5被存储到何处
  • 取址:指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址

减法有两种,一种是指针减去整数得到另外一个指针,一种是指针减去指针,得到一个整数。

保护数组中的数据

  • 对参数使用const。例如:int sum(const int ar[],int n);
    确保在函数使用数组时,将其视为常量,不可更改
    const int days[12]={31,28,31,30,31,30,31,31,30,31,30,31};
    如果程序稍后尝试改变数组元素的值,编译器将生成一个编译器错误消息。
    double rates[5]={88.99,100.12,59.45,183.11,340.5};
    const double * pd=rates;这句话表明不能使用pd来改变它所指向的值
    *pd=28.99;pd[2]=222.22,不允许
    rates[0]=99.99;,允许,因为rates未被const限定
    pd++;,允许
    只能把非const数据的地址传输给普通指针,否则通过指针就能改变const数据
    不要把const数组作为实参传入函数中
  • const可以声明并初始化一个不能指向别处的指针
    double * const pc = rates;
    可以更改指针指向参数的值,但指针只能指向retes[0]

多维数组

int zippo[4][2];
zippo是该数组首元素的地址,zippo的首元素是一个内含两个int值的数组。
在数值上zippo==zippo[0]==zippo[0][0]
zippo是一个占用两个int大小对象的地址,zippo[0]是一个占用一个int大小对象的地址。
故zippo+1与zippo[0]+1不相等。
zippo[0][0]==**zippo==*zippo[0]
zippo[2][1]==*(*(zippo+2)+1)

指向多维数组的指针

  • 声明:int (* pz)[2];表示pz指向一个内含两个int类型值的数组
    int * pz[2];表示一个指针数组,每个元素都指向int

指针的兼容性

函数和多维数组

*junk是一个3行4列的数组,则声明函数形参:void somefuction(int (* pt)[4]);
同时可以这样声明:void somefunction(int pt[][4]);
注意,第一个方括号是空的,这表明pt是一个指针。
在声明或定义函数时,只能省略最左边括号中的数值,因为最左边的括号只用于表明这是一个指针,其他括号中的数值则用于描述指针所指向数据对象的类型。
int sum(int ar[][12][10][30],int n)int sum(int (*ar)[12][10][30])等效。

变长数组

使用变量表示数组的维度。

  • 变长数组必须是自动存储类别
  • 不能在函数声明中初始化它们
  • 变长数组不改变大小,创建后的变长数组大小保持不变。‘变’指的是:在创建数组中,可以使用变量指定数组的维度。
  • 函数声明:int sum(int rows,int cols,int ar[rows][cols]);
    因为ar的声明要使用rows和cols,所以在形参列表中必须在声明ar之前先声明这两个形参。
  • 声明时可以省略形参名,但必须用星号来代替省略的维度
    int sum(int,int,int ar[*][*]);
    新声明的sum()可以处理任意大小的int二维数组

复合字面量

(int [2]){10,20}

  • int [2]即是复合字面量的类型名
    复合字面量也可以省略大小,编译器会自动计算数组当前的元素个数。
  • 不能先创建再使用,必须在创建的同时使用它。
int * pt1;
pt1 = (int [2]){10,20};
  • 可以作为实际参数传递给带有匹配形参的函数

复合字面量好处:把信息传入前不必先创建数组

int (* pt2)[4];
pt2 = (int [2][4]){{1,2,3,-9},{4,5,6,-8}};

复合字面量是提供只临时需要的值的一种手段

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • 1.const 关键字声明后数据不能修改 2.sizeof 数组名指的是整个数组的大小(以字节为单位) size...
    KnighTovi阅读 608评论 0 0
  • 数组名是数组首元素的地址。很多情况下数组名字等价于一个指针,比如 ar[i]和*(ar+1)这两个表达式都是等价的...
    dap2erp阅读 322评论 0 0
  • 一本道来其他系列C语言关键字C语言注释符号一本道来C语言编译预处理技术一本道来 指针的基础 注意本节内容可能在gc...
    PcDack阅读 1,297评论 0 2
  • 概述 C 语言的数组是一种将标量数据聚集成更大数据类型的方式。其实现的方式非常简单,很容易翻译为机器代码。C 语言...
    seraphzxz阅读 685评论 0 51
  • 总:如果编写的函数需要修改数组,在声明数组形参的不使用const;如果编写的函数不用修改数组,那么在声明数组形参时...
    先给自己定一个小目标阅读 259评论 0 0