欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

《C primer plus》笔记:数组

程序员文章站 2023-12-22 22:14:46
...

20180605 qzd

1. 一维数组


  • 声明:和声明变量一样,但是数组不能直接赋值给它,可以初始化
int a[10];
  • 初始化:声明并初始化并非赋值,不允许先声明后直接赋值
int a[10] = {1,2,3,4,5,6,6,7,7,8}; //可读可写
const int b[10] = = {1,2,3,4,5,6,6,7,7,8};//只读数组
  • 未完全初始化:不初始化,数组里面同变量一样全是垃圾值,如果部分初始化,其余值为0
  • 指定初始化器:C99增加的特性,可以指定某个位置的值
int a[10] = {1, 2, 3, [4] = 10, 5, 6, [9] = 20};
// 1, 2, 3, 0, 0, 10, 5, 6, 0, 20
//直接到指定的值给其赋值,中间未赋值的设置为0
  • 赋值:通过循环依次给数组赋值,不允许作为单元给数组赋值
for(i=0; i<SIZE; i++)
  a[i] = i;
  • 边界:使用时要防止下标越界,gcc编译器允许编译,但是越界的值是垃圾值

    编译器是相信程序员的,程序员最好声明数组时,用符号常量来表示数组大小

  • 变长数组(VLA):c90不允许,c99允许,c11可选(不是必备)

int a = 5;
int a[a];   //a为变量,非常量

2. 多维数组


  • 声明:第一个是行,第二个是列
int a[3][4];
  • 初始化:2种方式
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int a[3][4] = {
                       {1, 2, 3, 4},
                       {5, 6, 7, 8},
                       {9, 10, 11, 12}
                     };
             // 3行4列
  • 赋值:使用双重循环进行赋值
for(a = 0; a < ROWS; a++)
    for( b = 0 ; b < COLS; b++)
        arr[a][b] = a + b;

3. 指针和数组


  • 指针与数组的联系:数组名是数组首元素的地址,首地址加1是下一个元素的地址
int a[3];    // a == &a[0];
#include <stdio.h>
#define SIZE 10
int main(){
    short a[SIZE] = {1,2,3,4,5,6,7,8,9,10};
    double b[SIZE] = {1,2,3,4,5,6,7,8,9,10};
    short i, *pa = a;
    double *pb = b;
    for (i = 0; i < 2; ++i)
    {
        printf("pa%d point %p\n", i, pa++);
    } 
    for (i = 0; i < 2; ++i)
    {
        printf("pb%d point %p\n", i, pb++);
    }
 }
// pa0 point 0060FF00
// pa1 point 0060FF02

// pb0 point 0060FEB0
// pb1 point 0060FEB8

系统中,地址是按字节编址,而在c语言中指针加1是指增加一个存储单元,而不是固定的字节

a[SIZE]; // a+5 == &a[0+5]  *(a+5) == a[5]
*a+1  ==> (*a)+1 ==> a[0]+1
*(a+1) ==> a[1]

4. 数组的传递


  • 函数原型与定义(数组):通过传指针的方式,传数组
int sum(int *, int);   //通过指针传递(数组首地址)
int sum(int [], int);   //等效1,提醒读者是数组
int sum(int * a, int n){}
int sum(int a[], int n){}
  • 调用数组:传递数组首地址
sum(a, SIZE);   //a为数组名, SZIE为数组大小
  • 指针形参:传2个指针表示数组的开始和结束
int sum(int * start, int * end){ 
    int sum = 0;
    while(start < end){
        sum += *start;
        start++;
    }
    return sum;
}

*是否可以 sum += start++ ? 答案是可以的

#include <stdio.h>
#define SIZE 4
int sum(int * start, int * end){
    int sum = 0;
    while(start < end){
        sum += *start++;
       //分析 * ++(后缀) 同优先级右结合
        //步骤分解 1\. start先使用用 sum = sum + *start;
        //                2\. start = start + 1;
    }
    return sum;
}
int main(void){
    int a[SIZE] = {1, 2 , 3, 4};
    printf("%d", sum(a, a+SIZE));
    return 0;
}
// 10  正确

另外两种形式又怎样呢?

#include <stdio.h>
int a[2] = {1, 2};
int main(){
    int *p1, *p2, *p3;
    p1 = p2 = p3 = a;
    printf("%d,%d,%d\n", *p1, *p1++, *p1); 
    //分析: 函数中参数的传递的顺序是从右往左传递
    //     1.*p1 = 1  ==> no3 = 1
    //     2.*p1++ ==> *p1 = 1 , p1 += 1 ==> no2 = 1
    //     3.*p1 ==> a[1] ==> no3 = 2
    printf("%d,%d,%d\n", *p2, (*p2)++, *p2);
    //     1.*p2 = 1  ==> no3 = 1
    //     2.(*p2)++ ==> *p2 = a[0], no2 = 1, a[0] = 2 
    //     3.*p2 = a[0] = 2 ==> no1 = 2;
    printf("%d,%d,%d\n", *p3, *++p3, *p3);
    //     1.*p3 = a[0]  ==> no3 = 2
    //     2.*++p3 ==> p3= p3+1 = &a[1] =>   no2 = *&a[1] = 2
    //     3.*p3 = a[1] = 2 ==> no1 = 2
    return 0;
}
// 2 1 1
// 2 1 1
// 2 2 2

函数中参数的传递的顺序是从右往左传递

5. 指针操作

  • 赋值
  • 解引用
  • 取地址
  • 与整数相加
  • 递增
  • 与整数相减
  • 递减
  • 求差
  • 比较

6. 保护数组数据

  • 原因:通过地址传的数组都是原始数据,可以改变
    但有些时候不需要改变数组的原始数据
  • 对形参使用const:不需要改变数组内容时,最好加上const
int sum(coust int *, int);    //不能完全保护
int sum(const int [], int);   //完全保护
  • const其他内容:const比#define更加灵活,不应该把const数组作实参传递
const double PI = 3.14;       //PI值不能被改变
const int a[SIZE] = {1,3,4};  //数组值不能被改变
const int *p = a;             //将int类型声明为const,不能用p来改变a的值
//可以用数组改变其值,p也可以指向其他变量
int * const p = a;            //常量指针不能指向其他地方
//可以修改指向的值
const int * const p = a;      //不能指向其他地方,也不能通过p来改变变量的值

7. 指针和多维数组


  • 首地址之间的关系:a == a[0] == &a[0][0] 值虽然相同,但代表的含义不同
int a[3][4] = {
                    {1,2,3,4}
                    {5,6,7,8}
                    {7,8,9,1}
                    };
// a == &a[0][0];     二维数组的首地址
// a[0] == &a[0][0];   作为第一个数组名a[0]的首地址也为二维数组的首地址
// 即有 a == a[0]  值虽然相同,但代表的含义不同
  • 解引用:

a &a[0][0] 二维数组首地址
a+2 &a[2] 二维数组第3个元素地址
*(a+2) &a[2][0] 二维数组第3个元素的首地址
*(a+2)+1 &a[2][1] 二维数组第3个元素的第二个元素的地址
((a+2)+1) a[2][1] 二维数组第3个元素的第二个元素的值

  • 地址步长:a + 1 跨一个数组元素, *a + 1 跨一个数元素

  • 指向多维数组的指针:[]优先级高于*,所以一定要加()

int (*p) [2];  //声明含有两个int的数组
int a[2][3];
p = a;         //指向二维数组a
int * p[2];    //声明一个含有两个指针的数组
  • 指针兼容性:指针之间类型没有隐式转化,类型不匹配会编译出错
  • 多维数组与函数:形参3种写法,指针式无法省略参数会引发歧义
int sum(int (*p)[COLS], int ROWS);  //指针式
int sum(int a[][COLS], int ROWS);   //数组式
int sum(int [][COLS], int ROWS);    //数组略参式
  • 变长数组:变长数组并不能改变大小,只是在初始化的时候可以用变量来初始化大小
int sum(int a[rows][cols], int rows, int cols); //无效顺序
int sum(int rows, int cols, int a[rows][cols]); //有效
int sum(int, int, int a[*][*]); //省略形参名

const int SIZE = 80;
int sum[SIZE]; c11允许 ,c90好像不允许
  • 复合字面量:C99加入的 { 2,3,4,5,9 }
int a[2] = {10 ,20}; //初始化数组
(int [2]) {10, 20};  //初始化匿名数组,必须在创建的同时使用它
(int []) {10, 20};   //省略大小,编译器自动计算
int *p = (int []) {10, 20}; //首地址给指针p
sum( (int []){10, 20}, SIZE );  //用作实参
  • 小训练:一维数组3种拷贝函数
#include <stdio.h>
#define N 5
void cp_arr(double [], const double [], int);
void cp_ptr(double [], const double *, int);
void cp_ptrs(double *, const double *, const double *);
void show_arr(double [], int);
int main(void){
    double source[N] = {1, 2, 3, 4, 5};
    double target1[N], target2[N], target3[N];
    cp_arr(target1, source, N);
    cp_ptr(target2, source, N);
    cp_ptrs(target3, source, source+N);
    show_arr(target1, N);
    putchar('\n');
    show_arr(target2, N);
    putchar('\n');
    show_arr(target3, N);
    putchar('\n');
    return 0;
}
void cp_arr(double target[], const double source[], int n){
    int i;
    for (i = 0; i < n; ++i)
    {
        target[i] = source[i];
    }
}
void cp_ptr(double target[], const double *source, int n){
    int i;
    for (i = 0; i < n; ++i)
    {
        target[i] = *(source+i);
    }
 }
void cp_ptrs(double target[], const double *source_start, const double *source_end){
    while(source_start < source_end){
        *target = *source_start++;
        target++;
    }
}
void show_arr(double a[], int n){
    int i;
    for (i = 0; i < n; ++i)
    {
        printf("%.2lf ", a[i]);
    }
}
  • 小训练:二维数组处理函数
#include <stdio.h>
#define ROWS 3 //行数
#define COLS 5 //列数
void getArr(double [][COLS] ,int);
void averageGroup(double [], const double [][COLS], int);
double average(const double [][COLS], int);
double max(double [][COLS], int);
void swap(double *, double *);
void show(double [], int, double, double);

int main(void){
   double arr[ROWS][COLS] = {0};
   double aveGroup[ROWS] = {0}; //注意初始化
   double ave = 0;
   double m = 0;
   getArr(arr, ROWS);
   averageGroup(aveGroup, arr, ROWS);
   ave = average(arr, ROWS);
   m = max(arr, ROWS);
   show(aveGroup, ROWS, ave, m);
   return 0;
}
//给二维数组赋值
void getArr(double a[][COLS], int rows){
   int i, j;
   for (i = 0; i < rows; ++i)
   for (j = 0; j < COLS; ++j)
   scanf("%lf", &a[i][j]);
}
//求每行数据的平均值
void averageGroup(double b[], const double a[][COLS], int rows){
   int i, j;
   double sum = 0;
   for (i = 0; i < rows; ++i){
       for (j = 0; j < COLS; ++j)
           sum += a[i][j]; 
       b[i] = sum/rows;
   }
}
//求所有数据的平均值
double average(const double a[][COLS], int rows){
   int i, j;
   double sum = 0;
   for (i = 0; i < rows; ++i)
       for (j = 0; j < COLS; ++j)
           sum += a[i][j]; 
    return sum/(COLS*rows);
}
//求二维数组的最大值
double max(double a[][COLS], int rows){
     int i, j;
     for (i = 0; i < rows; ++i)
     {
         for (j = 0; j < COLS; ++j)
             if(a[i][j] > a[i][j+1])
                 swap(&a[i][j], &a[i][j+1]);
          if(a[i][j] > a[i+1][j])
             swap(&a[i][j], &a[i+1][j]);
         }
     return a[i][j];
}
//打印结果
void show(double a[], int size, double av, double m){
     int i;
     for(i = 0; i < size; i++){
         printf("%.2lf ", a[i]);
     }
     printf("\naverage = %.2lf\n", av);
     printf("max = %.2lf\n", m);
 }
//交换两个变量值
void swap(double *p, double *q){
     double temp = *p;
     *p = *q;
     *q = temp;
}

参考:https://suoyuesmile.github.io/2015/07/03/c2/

上一篇:

下一篇: