美文网首页
第十章_数组和指针

第十章_数组和指针

作者: JAVA孙剑 | 来源:发表于2019-08-14 09:36 被阅读0次

1. 数组

  • 数组声明
int arr1[10];
float arr2[4];
char arr3[50] ;
  • 数组初始化
int arr[5] = {1,2,3,4,5};
  • 使用const声明数组,数组为只读,不能再修改。
const int arr[5] = {2,4,5,6,7};
  • 生命数组后如果未进行初始化,数组元素和未初始化的普通变量一样,存储的是垃圾值;如果只初始化了数组中的部分变量,则其余变量会被初始化为0。
  • 如果初始化数组时忽略方括号里的数字(即数组长度),编译器会根据初始化列表的项数来确定数组大小。
  • 使用for循环遍历数组时,可以通过sizeof arr / sizeof arr[0] 来获得数组元素的个数。
  • 也可以通过数组的下标来初始化数组元素
int arr[5] = {[2] = 10};    //将第三个元素初始化为10;
int arr[5] = {[1] = 3,6,8}    //将第2、3、4个元素初始化为3、6、8;
int arr[5] = {4,2,[0] = 3};    //注意,后面的初始化内容会覆盖掉前面的初始化内容
int arr[] = {[10] = 99};    //如果为指定数组长度,编译器会把数组的大小设置为足够装得下初始化的值,本数组长度为11;
  • 使用数组时要防止数组下标越界。例如长度为10的数组arr,arr[-2]或arr[10]这样的初始化会把值存在数组的前一个或后一个位置,该位置可能存储着其他变量,这些变量被覆盖掉可能导致程序运行混乱。
int a = 10;
int arr[5];
int b = 2;
//以下两条语句可能导致a和b的值被更改,编译器不会提示,所以程序员需要格外注意。
arr[-1] = 99;
arr[5] = 99;

2. 二维数组

  • 初始化二维数组,二维数组可以看做数组的数组
const int arr[3][4] = 
{
    {2,3,4,5},
    {4,5,6,7},
    {7,5,3,8}
};

3. 指针和数组

在C中,数组名就是数组首元素的地址。

int arr[5];
arr == arr[0];    //此语句成立
  • 在指针前面使用 * 运算符可以得到该指针所指向对象的值
  • 指针+1,指针的值递增他所指向类型的大小。例如arr[3]可以看做先找到内存的arr地址,然后向后移动3个元素的大小(取决于储存元素的类型),获得储存在该位置的值。
arr + 2 == &arr[2]    //两个相同的地址
*(arr + 2) == arr[2]    //两个相同的值

4. 函数、数组和指针

  • 定义一个函数时,如果该函数的参数是一个数组,形式参数应该是一个指针类型。
//函数原型可以省略参数名
int sum(int arr[]);    
int sum(int []);
int sun(int *);
int sum(int* arr);
//函数定义不能省略参数名
int sum(int arr[]){函数体}
int sum(int* arr){函数体}
  • 在函数中,使用参数列表中的数组时,要注意此时的参数并不是数组本身,它是一个指向数组首元素的指针。在使用sizeof时,得到的结果只是其中一个元素的size。
int main(){
    int arr[5] = {1,2,3,4,5};
    printf("%d",sizeof arr);    //打印的结果是20,因为arr数组有5个int值,每个int占4字节,所以整个数组的大小是20字节
    sum(arr);
}
int sum(int arr[]){
    printf("%d",sizeof arr);    //打印的结果是8,因为arr是一个指向arr首元素的指针,系统使用8字节存储地址,所以指针变量的大小是8字节(其他系统可能不是8字节)。
}
  • 函数处理数组必须知道何时开始,何时结束。可以使用一个整型参数告诉函数待处理的数组的元素个数,也可以传递两个指针,第一个指针指明数组的开始处,第二个指针指明数组的结束处。
//第一种,指明元素的个数
int sum(int arr[],int size);
int sum(int* arr,int size);
//第二种,指明数组的结束处
int sum(int arr[],int* end);
int sum(int * arr,int* end);
//下面是传入结束指针的两种用法,sum函数将参数数组的元素累加。
int sum(int arr[], int* end) {
    int sum = 0;
    //用法一
    for (size_t i = 0; i < end - arr; i++) {
        sum += arr[i];
    }
    //用法二
    while (end>arr){
        sum += *arr; //*arr表示当前数组元素的值
        arr++;  //以上两行代码也可以压缩为 sum += *arr++;
    }
  • 形式参数的指针表示法和数组表示法对于C语言来说是等价的,两个表达式都没问题。但是对于程序员来说,使用数组表示法让代码更清晰。

5. 指针操作

  • 指针赋值:注意两个指针的类型应该一致,不能把一个double类型的地址赋给指向int类型的指针。
int arr[5] = {1,2,3,4,5};
int * ptr;
ptr = &arr[4]; //把一个地址赋给指针变量
  • 解引用:使用*符号解引用指针,获得该指针指向的值。
  • 取址:使用&运算符获得指针本身的地址。
  • 指针与整数相加:整数会与指针指向类型的大小相乘,然后与指针地址相加,因此arr+4与&arr[4]等价。如果结果超出数组范围,计算结果则是未定义的(垃圾值)。除非正好超过数组末尾第一个位置,C保证该指针有效
  • 递增指针:递增指向数组的指针可以让该指针指向数组的下一个元素
  • 指针减去一个整数:与指针与整数相加相反。&arr[3]-2与arr[1]等价。
  • 递减指针:与递增相反,让指针指向前一个元素
  • 指针求差:两个指针指向同一个数组的不同元素,通过求差得出两个元素的距离。例如int数组中,pointer1 - pointer2得4,指的是两个指针相隔4个int。
    注意,编译器不会检查指针是否仍指向数组元素

创建一个指针时,系统只分配了储存指针本身的内存,没有分配存储数据的内存。因此,在使用指针之前,必须先用已分配内存的地址初始化它。

double * pointer; //未初始化的指针
*pointer = 2.5;   //严重的错误

double d = 6;
pointer = &d;    //正确的初始化

6. 保护数组中的数据

在函数中,对参数列表的数组进行操作会修改原数组的值,因为函数通过指针直接使用了原始数据。如果函数的意图不是修改原数组的数据内容,在函数原型和函数定义中应该使用const关键字,避免误操作修改数组内容。这样如果遇到修改数组内容的表达式,编译器会报出错误。

int sum(const int arr[]);
int sum(const int arr[]){函数体}

只能把非const数据的地址赋给普通指针。使用非const标识符修改const数据,结果是未定义的。

double rates[5] = {1.1,2.3,442.1,44.2,54.1};
double * const pointer = rates;
pointer = &rates[2] ;    //不允许,该指针不能指向别处
*pointer = 45.55;    //允许,可以改变rates[0]处的值
const double * const ptr = rates; //既不允许该指针指向别处,也不允许改变地址上的值。
ptr = &rates[4];  //不允许
*ptr = 45.545;    //不允许

7. 指针和多维数组

    int arr[4][2] = {{5, 6}, {8, 4}, {3, 2}, {9, 7}};
    printf("arr[0] = %d",**arr);   //取得的是5,等价于*arr[0]
    printf("*arr[2] = %d\n",*arr[2]);  //取得的是3

要特别注意,与 zippo[2][1]等价的指针表示法是((zippo+2) + 1)。看上去比较复杂,应最好能理解。下面列出了理解该表达式的思路:

int n = 5;
double d = 10.1;
int * ptr1 = &n;
double * ptr2 = &d;
d = n;    //可以,隐式类型转换
ptr2 = ptr1;    //不允许两个不同类型的指针赋值

//假设有如下声明
int * pt;
int (*pa)[3];  //一个指向包含三个int元素数组的指针
int ar1[2][3];
int ar2[3][2];
int **p2; // 一个指向指针的指针
有如下的语句:
pt = &ar1[0][0]; // 都是指向int的指针
pt = ar1[0]; // 都是指向int的指针
pt = ar1; // 无效
pa = ar1; // 都是指向内含3个int类型元素数组的指针pa = ar2; // 无效
p2 = &pt; // both pointer-to-int *
*p2 = ar2[0]; // 都是指向int的指针
p2 = ar2; // 无效

在函数原型中声明多维数组时,第一个方括号中的值省略(即使填写,编译器也会忽略该值)。后面的方括号的数值不能省略。

int sum(int arr [] [10] [5],int rows );  //正确,等价于下面的语句
int sum(int (*arr)[10][5],int rows);

变长数组:变长数组不能改变大小,这里的变指的是在创建数组时,可以使用变量制定数组的维度。

int sum2d(int rows,int cols,int arr[rows][cols]); //ar是一个变长数组,注意不能改变参数顺序
//或者使用下面的格式
int sum2d(int ,int ,int arr[*][*]); 
//函数定义如下,该函数可以处理任意大小的二维int数组
int sum2d(int rows, int cols, int ar[rows][cols]) {
    int r;
    int c;
    int tot = 0;
    for (r = 0; r < rows; r++) {
        for (c = 0; c < cols; c++) {
            tot += ar[r][c];
        }
    }
    return tot;
}

8. 复合字面量

复合字面量是提供临时需要的值的一种手段。一旦离开定义该字面量的块作用域,程序无法保证该字面量是否存在。

//普通创建数组
int arr[2] = {1,2};
//使用复合字面量创建匿名数组
(int [2]){1,2};
(int []){1,2,3};  //也可以省略大小,编译器会自动计算数组元素个数
//因为复合字面量是匿名的,所以不能先创建再使用它,必须在创建的同时使用。
int *ptr;
ptr = (int [ ]){1,2,3};
//也可以使用复合字面量作为实际参数传给函数;
int sum(int * p,int size);    //函数原型
sum((int []){2,3,4},3);    //这样的好处是使用函数时不用先创建数组
int (*ptr)[4];    //声明一个指针
ptr = (int [2][4]){{12,3,44,5},{3,4,5,6}};

相关文章

  • C语言指针相关

    一、指针数组 和 数组指针的区别:1:指针数组,还是数组,数组每个元素是指针。2:数组指针,是指针,指向的是数组。...

  • JNI基础 -- C++基础知识(指针数组)

    数组和指针,数组指针,指针数组 数组 声明一个数组 这个比较简单,不做过多介绍 数组指针 数组指针是一个指针,指向...

  • 数组指针和指针数组

    1.数组名 数组首元素的地址和数组地址是两个不同的概念 数组名代表数组首元素的地址,它是个常量. 变量本质是内存空...

  • 数组指针和指针数组

    1.数组指针(又称行指针) 二维数组赋给一指针时: 2.指针数组 二维数组赋给一指针数组: 小结:数组指针只是个指...

  • 数组指针和指针数组

    指针*p不仅仅是地址(数据访问的位置),还包括所指向类型,即p+1移动的步长(如何访问)。数组指针,char (*...

  • 数组指针和指针数组

    输出结果:Example 数组指针:我我是是大大好人好人Example 指针数组: 我是大好人

  • 数组指针和指针数组

    https://www.cnblogs.com/mq0036/p/3382732.html 一维数组名和二维数组名...

  • 关于二维数组及数组指针和指针数组的深度思考(涉及指针)

    转载请注明在纠结指针数组和数组指针时无意发现的小细节。总算搞清数组指针和指针数组。 数组指针定义 int (*p)...

  • 指针

    指针 数组指针和指针数组 函数指针和指针函数 指针作为参数 指针多用于处理值传递,减少值复制耗费的内存

  • C语言特性(指针数组和指向指针变量的指针)

    指针数组和指向指针变量的指针 指针数组与main()函数形参 声明指针数组与声明普通数组的语法类似,其语法格式如下...

网友评论

      本文标题:第十章_数组和指针

      本文链接:https://www.haomeiwen.com/subject/mqlvjctx.html