C语言学习之——数组与指针

最近看C相关的代码,总是很吃力,所以决定看看C Primer Plus这本书,这里对指针,字符串这些常用的部分做一个笔记

sizeof,指针,数组

  • 看代码:
//sum_arr1.c --数组元素之和
#include<stdio.h>
#define size 10
int sum(int ar[], int n);
int main(void)
{
    int marbles[size] = { 20, 10, 5, 1, 22, 18, 18,123, 123,12 };
    long answer;

    answer = sum(marbles,size);
    printf("the totole number of marbles is %ld.\n",answer);
    printf("the size of marbles is %ld.\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 %ld.\n", sizeof ar);
    return total;
}

  • 在这里插入图片描述
  • sizeof是以字节为单位,所以10个int的数组的size是40
  • ar长是8,说明指针地址长度是8个字节,即48位,这个计算机有关
  • sizeof 数组 和 sizeof指针的区别
  • 指针+1 ,指的是+1个存储单位,对于marbles数组来说是4个字节,即指针+1,地址大小+4
  • 对于c,ar[i]*(ar+i)这两个表达式等价。无论ar是数组名还是指针变量,这个两个表达式都没有问题;然而只有当ar是指针变量,才能用ar++这种表达式。

指针操作

  • demo
//ptr_ops.c -- 指针操作
#include<stdio.h>
int main(void)
{
    int urn[5] = { 100, 200, 300, 400, 500};
    int *ptr1, *ptr2, ptr3;

    ptr1 = urn;           //把一个地址赋给指针
    ptr2 = urn[2];

    printf("pointer value , dereference pointer, pointer address:\n");
    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);

    //指针加法
    ptr3 = ptr1 + 4;
    printf("\n adding an int to a pointer:\n");
    printf("ptr1 + 4 = %p, *(ptr1 + 4) = %d", ptr1 + 4, *(ptr1 + 4));
    //递增指针
    ptr1++;
    printf("\n value after ptr1++ \n");
    printf("ptr1 = %p, *ptr1 = %d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);
    //递减指针
    ptr2--;
    printf("\n value after ptr2-- \n");
    printf("ptr2 = %p, *ptr2 = %d, &ptr2 = %p\n", ptr2, *ptr2, &ptr2);
    --ptr1;//恢复指针
    --ptr2;
    printf("\n pointers reset to original values\n");
    printf("ptr1 = %p, ptr2 = %p\n", ptr1, ptr2 );
}

  • 在这里插入图片描述
  • 注意:
	double *pd;
	*pd = 2.4
  • 这样用错误,因为创建一个指针,未初始化时,系统只分配了储存指针本身的内存,并未分配储存数据的内存,即指针的值,因此,在使用指针之前,必须先用已分配的地址初始化它;而此处第二行的意思时把2.4存在pd指向的位置,但是pd未初始化,所以不知道存在哪里

关于const

对形参使用const:
int sum(const int ar[]);

const是为了告诉编译器,不能修改ar指向的数组中的内容,如果修改了,会编译器会捕获并抛出错误。这是为了让我们在给函数传参时保护数组数据不被改变。

void show_array(const double ar[], int n);
void mult_array(double ar[], int n)

如上面的声明,只是显示数组,那么不需要改变,传参加const;但是修改数组,需要改变,则不加;

const与指针赋值
  • 把const或非const数据的地址赋值给指向const的指针是合法的:
double rates[5] = {88.99 , 110.2, 123.1, 200.4, 210};
const double locked[4]={0.1, 0.2, 0.5, 0.9};
const double *pc = rates; //有效
pc = locked; //有效
pc = &rates[3];//有效
  • 然而,只能把非const数据的地址赋值给普通的指针,
double *pnc = locked; //无效

因为,如果可以的话,那么普通指针可以改变const数组的数据了。

  • 也不要把const数组当作实参传递给mult_array这样的函数
mult_array(rates,5);//有效
mult_array(locked,5);//不可以

因为使用形参修改const数据的后果未知

  • 注意 * const pc
double * const pc = rates; //指向数组开始
pc = &rates[3]; //不可,const指针不可改变指向
*pc = 12.1; //可以,可以更改const指针指向的值;

数组和多重指针

  • 看一个例子:
/*zippo1.c -- 指针和多维数组*/
#include<stdio.h>
int main(void)
{
    int zippo[4][2] = { { 2, 4 }, {6,8},{1,3},{2,0}};

    printf(" zippo = %p,    zippo + 1 = %p\n", zippo, zippo + 1);//zippo是两int组成的一维数组的地址,所以+1,相当于地址+8
    printf(" zippo[0] = %p,    zippo[0] + 1 = %p\n", zippo[0], zippo[0] + 1);//zippo[0] 是一个int的地址,所以+1,即地址+4, 你可以类比一维数组char ar[2];ar是一个char的地址,char[0]才是该地址的值,所以zippo[0]是两个Int组成的一维数
组的地址,那么zippo就是这个地址的地址
    printf(" *zippo = %p,    *zippo + 1 = %p\n", *zippo, *zippo + 1);//zippo的解引用应该是zippo[0]的地址,+1应该是地址+8
    printf("zippo[0][0]=%d\n", zippo[0][0]);
    printf("  *zippo[0]=%d\n", *zippo[0]);// *zippo[0]指的是zippo[0]指向的一int的解引用,那么还是首地址对应的一个int
    printf("  **zippo = %d\n", **zippo);// zippo是地址的地址,所以必须解引用
两次才能获得原始值,即zippo[0][0]
    printf("  zippo[2][1] = %d\n", zippo[2][1]);
    printf("*(*(zippo+2)+1) = %d\n",*(*(zippo+2)+1) );

    return 0;


}
  • 在这里插入图片描述
  • 二维数组zippo的地址和一维数组zippo[0]的地址相同,他们的地址都是各自数组首元素的地址,因而与&zippo[0][0]也相同
  • zippo[0]指向一个四字节的Int,所以+1,地址+4;zippo是一个内含2个int类型值的数组的地址,所以指向一个8字节的数据对象,所以+1,地址+8(相当于zippo是一个储存单位是8字节的一维数组)
  • zippo[0]与*zippo完全相同。对二维数组名解引用两次,得到储存在数组中的值,和数组使用两个[][]得到数组中的值对应
  • 所以zippo[2][1]与 *(*(zippo+2)+1) 是对应的,所以说用数组更直观
  • 考虑int (*pz) [2]:pz是一个指向内含两个int类型的数组。其实不用想太复杂,知道pz的储存单位是8个字节,*解引用与[]等价即可:
pz+1 即地址+8
pz[0] + 1 ,pz解一层引用即成为了指向int的地址,+1,即+4
*pz + 1同理
pz[0][0]*pz[0] **pz表示的都是首地址数据
pz[2][1]*(*(pz+2)+1)等价
  • 多维数组的函数声明

    int sum(int ar[][4]) //正确
    int sum(int ar[][])//不可
    int sum(int ar[3][4])//3会被忽略
    
    • 这样可以传入变长数组,第一个[]内可是1,2.。。
  • 复合字面量:

int sum(const int ar[])
...
int total3;
total3 = sum((int []){1,2,3,4,5,6});
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值