• C Primer Plus(6) 中文版 第10章 数组和指针 10.4 函数、数组和指针


    10.4 函数、数组和指针
    处理数组的函数,该函数返回数组中所有元素之和。
    记住,数组名是该数组首元素的地址。所以函数的参数应该是一个指针。
    函数从该参数获得了该数组元素首元素的地址,知道要在该位置上找出一个整数。注意,该参数并未包含数组元素个数的信息。我们有两种方法让函数获得这一信息。第一种方法是,在函数代码中写上固定的数组大小: 
    int sum( int *ar ){ //相应的函数定义 
        int i;
        int total = 0;
        
        for( i = 0; i < 10; i++ ){ //假设数组有10个元素 
            total += ar[i];     //ar[i]与*(ar + i)相同 
        } 

    既然能使用指针表示数组名,也可以用数组名表示指针。
    该函数有限制,只能计算10个int类型的元素。另一个比较灵活的方法是把数组大小作为第2个参数:
    int sum( int *ar, int n ){ //更通用的方法
        int i;
        int total = 0;
        
        for( i = 0; i < n; i++ ){
            total += ar[i];
        } 
        return total;

    这里,第1个形参告诉函数该数组的地址和数据类型,第2个形参告诉函数该数组中元素的个数。
    关于函数的形参,还有一点要注意。只有在函数原型或函数定义中,才可以用int ar[]代替int *ar:
    int sum( int ar[], int n );
    int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形式参数。第2种形式(int ar[])提醒读者指针ar指向的不仅仅是一个int类型值,还是一个int类型数组的元素。
    注意 声明数组形参
    因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。只有在这种情况下,C才会把int arr[]和int *ar解释成一样。也就是说,ar是指向int的指针。由于函数原型可以省略参数名,所以下面4种原型都是等价的:
    int sum( int *ar, int n );
    int sum( int *, int );
    int sum( int ar[], int n );
    int sum( int [], int );
    但是,在函数定义中不能省略参数名。下面两种形式的函数定义等价:
    int sum( int *ar, int n ){
        //其他代码已省略 

    int sum( int ar[], int n ){
        //其他代码已省略 

    可以使用以上提高的任意一种函数原型和函数定义。 
    程序打印原始数组的大小和表示数组的函数形参的大小(如果你的编译器不支持用转换说明%zd打印sizeof返回值,可以用%u或%lu来代替)。
    // sum_arr1.c -- sums the elements of an array
    // use %u or %lu if %zd doesn't work
    #include
    #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)     // how big an array?
    {
        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;

    /* 输出:

    */

    sizeof ar的值是8。这是因为ar并不是数组本身,它是一个指向marbles数组首元素的指针。我们系统中用8字节存储地址,所以指针变量的大小是8字节(其他系统中地址的大小可能不是8字节)。简而言之,marbles是一个数组,ar是一个指向marbles数组首元素的指针,利用数组和指针的特殊关系,可以用数组表示法来表示指针ar。
    10.4.1 使用指针形参
    函数要处理数组必须知道何时开始,何时结束。sum()函数使用一个指针标识数组的开始,用一个整型形参表明待处理数组的元素个数(指针形参也表明了数组中的数据类型)。但是这并不是给函数传递必备信息的唯一方法,还有一种方法是传递两个指针,第1个指针指明数组的开始处(与前面用法相同),第2个指针指明数组的结束处。
    指针作为函数的形参
    /* sum_arr2.c -- sums the elements of an array */
    #include
    #define SIZE 10
    int sump(int * start, int * end);
    int main(void)
    {
        int marbles[SIZE] = {20,10,5,39,4,16,19,26,31,20};
        long answer;
        
        answer = sump(marbles, marbles + SIZE);
        printf("The total number of marbles is %ld.\n", answer);
        
        return 0;
    }

    /* use pointer arithmetic   */
    int sump(int * start, int * end)
    {
        int total = 0;
        
        while (start < end)
        {
            total += *start; // add value to total
            start++;         // advance pointer to next element
        }
        
        return total;

    /* 输出:

    */ 

    因为while循环的测试调教是一个不相等的关系,所以循环最后处理的一个元素是end所指向位置的前一个元素。这意味着end指向的位置实际上在数组最后一个元素的后面。C保证在给数组分配空间时,指向数组后面第一个位置的指针仍是有效的指针。这使得while循环的测试条件时有效的,因为start在循环中最后的值是end。注意,使用这种‘越界’指针的函数更为简洁:
    answer = sump( marbles, marbles + SIZE );
    如果end指向数组的最后一个元素而不是数组末尾的下一个位置,则必须使用下面的代码:
    answer = sump( marbles, marbles + SIZE - 1 );
    这种写法既不简洁也不好记,很容易导致编程错误。顺带一提,虽然C保证了marbles + SIZE有效,但是对marbles[SIZE](即存储在该位置上的值)未做任何保证,所以程序不能访问该位置。
    还可以把循环体压缩成一行代码:
    total += *start++;
    一元运算符*和++的优先级相同,但结合律是从右往左,所以start++先求值,然后才是*start。也就是说,指针start先递增后指向。如果使用*++start,顺序则反过来,先递增指针,再使用指针指向位置上的值。如果(*start)++,则先使用start指向的值。再递增该值,而不是递增指针。这样,指针将一直指向同一个位置,但是该位置上的值发生了变化。虽然*start++的写法比较常用,但是
    *(start++)这样写更清楚。
    /* order.c -- precedence in pointer operations */
    #include
    int data[2] = {100, 200};
    int moredata[2] = {300, 400};
    int main(void)
    {
        int * p1, * p2, * p3;
        
        p1 = p2 = data;
        p3 = moredata;
        printf("  *p1 = %d,   *p2 = %d,     *p3 = %d\n",
               *p1     ,   *p2     ,     *p3);
        printf("*p1++ = %d, *++p2 = %d, (*p3)++ = %d\n",
               *p1++     , *++p2     , (*p3)++);
        printf("  *p1 = %d,   *p2 = %d,     *p3 = %d\n",
               *p1     ,   *p2     ,     *p3);
        
        return 0;

    /* 输出:

    */

    只有(*p3)++改变了数组元素的值,其他两个操作分别把p1和p2指向数组的下一个元素。
    10.4.2 指针表示法和数组表示法
    处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择是使用数组表示法还是指针表示法。使用数组表示法,让函数是处理数组的这一意图更加明显。另外,许多其他语言的程序员对数组表示法更加熟悉。其他程序员可能更习惯使用指针表示法,觉得使用指针更自然。
    至于C语言,ar[i]和*(ar + i)这两个表达式都是等价的。无论ar是数组名还是指针变量,这两个表达式都没问题。但是,只有当ar是指针变量时,才能使用ar++这样的表达式。
    指针表示法(尤其与递增运算符一起使用时)更接近机器语句,因此一些编译器在编译时能生成效率更高的代码。然而,许多程序员认为他们的主要任务是确保代码正确、逻辑清晰、而代码优化应该留给编译器去做。 

  • 相关阅读:
    Unity优化(1)——合并Mesh
    13.Redis系列之布隆过滤器
    JavaScript和Node.js的关系
    Mapbox 与 Babylon.js 可视化 添加人物动画
    Java内部类解析
    MySQL数据库
    初识深度学习
    Win11如何更新BIOS?
    YOLO改进系列之注意力机制(CoTAttention模型介绍)
    [React] react-redux基本使用
  • 原文地址:https://blog.csdn.net/weixin_40186813/article/details/126246729