C语言基础——指针(1)

要想了解指针,我们首先要对内存和地址有个概念:
在计算机的中央处理器(CPU)工作处理数据时,需要的数据在内存中读取,处理后的数据也会返回内存中,内存空间为了高效的管理,将内存化为一个一个的内存单元,每个内存单元是1字节,而一个字节又是8个比特位,于是,计算机的存储单位有了这些换算:
1Byte=8bit
1kb=1024byte
1mb=1024kb
1gb=1024mb
1tb=1024gb………
我们可以这样理解:内存空间就是一座大楼,每一个内存单元就是一个带有门牌号的房子,而每一个房子里又住着8个人,他们就是八个比特位,而CPU管理员可以通过每一个房子快速找到需要找的人并且进行操作,也就是说可以快速找到一个内存空间。
在计算机中,我们把每一个门牌号叫做地址,又给地址起了一个名字:指针
所以我们可以这样理解:地址=指针=“门牌号”,而每一个门牌号是不会变的,只有门里面住的人可能会变;于指针地址而言也是如此。


一.    指针的使用与理解
1.    取地址操作符:&
我们用以下代码来了解取地址操作符:
int a=10;
&a;
printf(“%p”,&a);
第一行的代码的本质是:向内存申请一块地址,将“10“这个值放在这个地址里,而于a而言,a对于计算机的存储内容并没有贡献,只是为了方便程序员在敲代码的时候更加便利,才设置了类似a这样的变量来储存;
第二行代码的意思是:取出存储10这个值的地址
第三行打印取出的地址:比如:0x006FFD70
但是我们不难发现,一个int类型的值应该是32个比特位,也就是说需要四个字节的地址,但是在打印的时候只会出现一个地址,原因是&a取出的是四个字节中地址较小的那个。


2.    指针变量
从上面的打印结果可以看出,我们通过取地址操作符得到的是一个数值,但这个数值有时也需要存储起来,以便之后使用,这时候,就需要用到指针变量了。
我们用以下代码来理解指针变量:
int a=10;
int * pa=&a;
这里的pa就是指针变量,a的地址将会被存储在pa这个指针变量中。int* pa的实质是这样的:*表示*之后的变量pa是指针变量,而int则表示pa这个指针变量指向的是整型的对象。


3.    解引用操作符:*
解引用操作符的出现,实际上是为了可以使用保存的地址:
我们通过下面的代码来解释:
int a=10;
int* pa=&a;
*pa=11;
在这里,*pa=10的意思就是通过pa这个指针变量中存放的地址,找到这个地址实际在电脑中的空间位置(也就是10这个数值所在的空间位置),并将其改为11。我们可以简单的理解为*pa=a;
注:在这里十分容易混淆的一点是:int* pa  和  *pa
前者中的*表示的是 pa这个变量是专门用来存放地址的指针变量,而后者中的*表示指向pa这个变量中所存放的内容的实际地址,二者有着根本的区别。


4.    指针变量的大小:
这里我们不详细解释,我们只需要知道,指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小都是相同的。


5.    指针变量的意义:
我们从两个点来感受一下指针变量的意义:


(1)    指针的解引用:
int a = 0x11223344;
int*  p = &a;
*p = 0;
当全部是int类型时,我们打开调试不难发现,a的四个字节全部被改为0,而倘若时char类型,我们会发现,只有第一个字节被改为0
因此,指针的类型决定了对指针解引用的时候有多大权限,比如char类型解引用就只能访问一个字节,而int类型就可以访问四个字节。



(2)    指针的加减法
用和上述一样的方法,将指针+1再调试,我们可以发现,char类型指针变量+1跳过一个字节,而int类型指针变量+1跳过了四个字节。
注:有一种指针是void类型,它通常用来接收不同数据类型的地址,但是不能直接进行指针运算。



二.    const修饰指针
const是可以修饰变量的,当用const修饰变量的时候,改变变量的值,变量就不能再被修改了,如:
const int a = 0;
a = 10;
这时候的编译器就会报错。



当然,我们也可以用const来修饰指针和指针变量,但是这之间有一些区别:
(1)    const放在*左边:
int a = 11;
int b = 10;
const int* p = &a;
*p = 20;
p = &b;
我们可以看到,在第四行报错了: 
这是因为const限制的是&a,这就意味着对p解引用后不能改原来地址的值


(2)const放在*右边:
int a = 11;
int b = 10;
int* const p = &a;
*p = 20;
p = &b;
这时候又看到第五行报错了:
这是因为这时的const限制的是p变量,但是没有限制*p所指向的地址。


(3)当两边都有*修饰的时候,无论是哪种方式,都不能够改变原来的值:
int a = 11;
int b = 10;
const int* const p = &a;
*p = 20;
p = &b;



三.    指针运算
指针一共有三种运算,它们分别是:指针加减整数:指针-指针;指针的关系运算。


1.指针加减整数
我们用数组来解释指针加减整数的运算:
int main()
{
    int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    int* p = &arr[0];
    int sz = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", *p);//使指针指向这个地址所存在的元素并且打印出来
        p++;//这里要用p++是因为一个int类型的指针+1会移动四个字节,而一个int元素恰好是四个字节
    }
    return 0;
}
因为数组在内存中是连续存放的,只要知道第一个元素,就可以递推得到其他元素。



2.指针-指针
前提:指针变量是同一种类型
结论:|指针-指针|=其之间元素的个数
在之前的学习中,我们知道,字符串后面都会有一个\0,我们可以自己写一个函数用来计算字符串的长度(其返回值是无符号整数):
size_t my_strlen(char* p)
{
    int count = 0;
    while (*p != '\0')
    {
        count++;//计数
        p++;这里要用p++是因为一个char类型的指针+1会移动一个字节,而一个字符恰好是一个字节
    }
    return count;
}
int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);//数组名其实是首元素的地址;arr=&arr[0]
    printf("%zd", len);
    return 0;
}



如果用指针-指针的方法,就可以这么写:
size_t my_strlen(char* p)
{
    char* start = p;
    char* end = p;
    while (*end != '\0')
    {
        end++;//这里要用end++是因为一个char类型的指针+1会移动一个字节,而一个字符恰好是一个字节
    }
    return end-start;
}
int main()
{
    char arr[] = "abcdef";
    size_t len = my_strlen(arr);//数组名其实是首元素的地址;arr=&arr[0]
    printf("%zd", len);
    return 0;
}



3.指针的关系运算:
在 C 和 C++ 中,指针的关系运算主要包括以下几种:
小于( < ) :比较两个指针所指向的内存地址,判断前者是否低于后者。
小于等于( <= ) :判断一个指针所指向的地址是否不高于另一个指针的地址。
大于( > ) :比较两个指针,确定前者所指向的地址是否高于后者。
大于等于( >= ) :判断一个指针所指向的地址是否不低于另一个指针的地址。
需要注意的是,指针的关系运算通常只在它们指向同一类型的数据,且处于同一内存区域(如同一数组)时才有明确且有意义的结果。否则,进行关系运算的结果可能是未定义的。

举例:
int main()
{
    int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
    int* p = &arr[0];
    int sz = sizeof(arr) / sizeof(arr[0]);
    while (p < arr + sz)
    {
        printf("%d", *p);
        p++;
    }
    return 0;
}



注:指针与指针之间之所以没有加法运算,就好比日期与日期之间不能进行加法运算一样,这里只要记住就可以了。

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值