函数、数组和函数(C Primer Plus第六版)

99 阅读2分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

学前服用

动画版的这个听完再去看书上的实例真的很不错! www.bilibili.com/video/BV1MJ…

一、指针和数组

指针提供一种以符号形式使用地址的方法。因为计算机硬件非常依赖于地址,指针在某种程度上把程序员想要传达的指令以更尽皆机器的方式表达。

使用指针能让程序更有效率

flizny == &flizny[ 0]; //数组名是该数组首元素的地址 flizny和sflizny[0] 都表示数组首元素的内存地址(&是地址运算符)。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值

#include <stdio.h>
#define SIZE 4

int main(void)
{
    short dates[SIZE];
    short * pti;
    short index;
    double bills[SIZE];
    double * ptf;
    pti = dates;//把数组地址赋值给指针
    ptf =  bills;

    for (index = 0;index < SIZE; index++)
    {
      printf("pointers+%d : %p  %p\n",index,pti + index,ptf+index);
    }
    return 0;
}


pointers+0 : 000000000061FE00 000000000061FDE0

pointers+1 : 000000000061FE02 000000000061FDE8

pointers+2 : 000000000061FE04 000000000061FDF0

pointers+3 : 000000000061FE06 000000000061FDF8

我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节。在C中,指针加1指的是增加一个存储单元。

对数组而言,**这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址,这是为什么必须声明指针所指向对象类型的原因之一。**只知道地址不够,因为计算机要知道储存对象需要多少字节(即使指针指向的是标量变量,也要知道变量的类型,否则*pt就无法正确地取回地址上的值。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-N1lGRJaX-1634202380884)(E:\Typora\Image\image-20211012153924351.png)]

对于上图的理解:

在C语言中,指针加1是增加一个存储单元,因为pti指针的类型是short类型,所以指针+1,其值每次递增两个字节

  • 指针的值是它所指向对象的地址。地址的表示方式依赖于计算机内部的硬件。许多计算机(包括PC和 Macintosh)都是按字节编址,意思是内存中的每个字节都按顺序编号。这里,一个较大对象的地址(如double类型的变量)通常是该对象第一个字节的地址。

  • 在指针前面使用*运算符可以得到该指针所指向对象的值。

  • 指针加1,指针的值递增它所指向类型的大小(以字节为单位)。

    data + 2 == &data[2]; //相同地址

    *(data + 2) == data[2]; //相同的值

容易混淆的概念

*(dates + 2) //dates第3个元素的值

*dates + 2 //dates的第一个元素的值加2

实例

#include <stdio.h>
#define MONTH 12

int main(void)
{
    int days[MONTH] = {31,28,31,30,31,30,31,31,30,31,30,31};
    int index;

    for (index = 0; index < MONTH; index++)
    {
        printf("Month %2d has %d days.\n",index+1,*(days + index));
    }
    

    return 0;
}

二、函数、数组和指针

假设要编写一个处理数组的函数,该函数返回数组中所有元素之和,待处理的是名为marbles的int类型数组。应该如何调用该函数?也许是下面这样:

total = sum(marbles);//可能的函数调用

那么,该函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数marbles是一个存储int类型值的地址,应把它赋给一个指针形式参数,即该形参是一个指向int的指针

int sum(int * ar); //对应的原型

sum()从该参数获得了什么信息?它获得了该数组首元素的地址,知道要在该位置上找出一个整数,注意,该参数并未包含数组元素个数的信息。第一种方法是,在函数代码中写上固定的数组大小;

实例

#include <stdio.h>
#define SIZE 10
int sum(int ar[],int n);
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);//ar是一个指向
    return total;//sum函数会返回一个数组所有元素之和
    
}




输出结果为:

The size of ar is 8 bytes.

The total number of marbles is 190.

The size of marbles is 40 bytes.

注意,marbles的大小是40字节。这没问题,因为marbles内含10个int类型的值,每个值占 4字节,所以整个marbles的大小是40字节。

但是,ar才8字节。这是因为ar并不是数组本身,它是一个指向marbles数组首元素的指针。

我们的系统中用8字节储存地址,所以指针变量的大小是8字节(其他系统中地址的大小可能不是8字节)。

ar是一个指向marbles数组首元素的指针,利用C中数组和指针的特殊关系,可以用数组表示法来表示指针ar。