AutoLeaders控制组——C语言指针的学习笔记

指针学习笔记

9.1指针

9.1-1:&运算符

初次见到&是在scanf()中,在刚开始学习时&符号也是常常会被漏掉的一个符号。

1.&的性质

&实际上是跟加减乘除一样的运算符。

2.&的作用

获得变量的地址,把它拿出来告诉你。(之前的课中有提到过变量是可操作的存储区,占有一定内存,因而存在地址。)

3.地址的输出

地址的输出用16进制会比较方便,但是输出地址不应该用%x,而是用%p(p为小写)。此时地址的输出会自动转换为16进制。

地址的大小取决于编译器的架构。

在不同架构中int的大小都是4个字节。

但地址的大小会有所不同:

64位时地址为8个字节;32位时地址为4个字节。

因此在64位架构如果把地址强制转换成int类型,会丢失前面四个字节。(在地址对应的数比较大的情况下。)

综上:地址的输出应该用%p,而不是%x,因为地址和整数的大小并不永远相同。

4.&使用规范

&取的地址必须是一个确定的变量。不能是i+j,i++,++i等等。

5.地址的存放

(1)相邻的两个int变量在堆栈中从高到低分配位置,每个占据4个字节。

(2)数组的地址:通过以下代码

int a[10];
    printf("%p\n",&a);
    printf("%p\n",a);
    printf("%p\n",&a[0]);
    printf("%p\n",&a[1]);

得到0xbff8dd44;0xbff8dd44;0xbff8dd44;0xbff8dd48。

从而可以了解到&a==a==&a[0];&a[0]+4==&a[1]。(4==sizeof(int))

9.1-2:指针变量

1.指针的引入

前面说明了如何取出一个变量的地址,但单纯取出来没什么用。从而需要引入一个可以保存地址的变量——指针。

    int i;
    int* p=i;//"*p"表示一个指针,这个指针指向一个int 
    int* p,q;
    int *p,q;

后面两行表示的都是:星号指明p是一个指针,同时“*p”是一个int。

注意上面的格式并不能同时定义p,q两个为指针。

正确的应在q前也加上一个*。

2.作为参数的指针

指针可以作为一个函数的参数,通过取得变量的地址,可以实现在函数内对函数外变量的改变。同时需要用单目运算符*来访问地址上的变量。

void f(int *p);
 
int main()
{
    int i=6;
    f(&i);//f()函数调用。注意括号内的参数是变量的地址。 
    printf("i=%d",i);//输出函数调用后i的值 
    
    return 0; 
} 
​
void f(int *p)
{
    printf("*p=%d\n",*p);//*p表示一个int,对应i的值 
    *p=26;//改变在该地址上的变量(也就是外面的i)的值 
}

3.指针运算符&*互相反作用

&:取变量所对应的地址

*:取指针对应的地址所对应的变量的

9.1-3:指针的使用

1.指针的应用场景一:交换变量

在之前的课程中说到过无法定义一个交换变量的函数,但现在可以通过指针来实现。

void swap(int *pa,int *pb);
​
int main()
{
    int a=5;
    int b=6;
    swap(&a,&b);
    printf("a=%d,b=%d",a,b);
    
    return 0;
} 
void swap(int *pa,int *pb)
{
    int t=*pa;
    *pa=*pb;
    *pb=t;
}

运行结果a=6;b=5。两个变量的值成功被交换。

2.指针的应用场景二:返回多个值

函数只能返回一个值,但通过指针,我们可以实现多个值的返回。使得输入的参数运算出结果后返回。

void minmax(int a[],int len,int *max,int *min);
​
int main()
{
    int a[]={1,2,3,4,5,6,7,8,9,12,13,14,16,17,21,23,55,};
    int min,max;
    minmax(a,sizeof(a)/sizeof(a[0]),&max,&min);//返回min和max。
    printf("min=%d,max=%d",min,max);//此处不能用*min和*max。
    
    return 0;
}
void minmax(int a[],int len,int *max,int *min)
{
    int i;
    *max=*min=a[0];
    for(i=1;i<len;i++) {
        if(a[i]<*min) {
            *min=a[i];
        }
        if(a[i]>*max) {
            *max=a[i];
        }
    } 
}

3.指针的应用场景二b

函数返回状态,指针返回结果。通常我们是用0或-1来表示程序出错,但如果0和-1都在可能输出的结果中,这种方法就不能用了,所以我们要让结果从指针输出,同时出错的判断交由函数返回值来完成。

/*
    return ret 除法成功返回1,否则返回0 
*/
​
int divide(int a,int b,int *result); 
​
int main(void)
{
    int a=5;
    int b=2;
    int c;
    if(divide(a,b,&c)) {//只有返回ret=1,也就是除法成功时才进行输出 
        printf("%d/%d=%d",a,b,c); 
    }
    return 0;
}  
​
int divide(int a,int b,int *result)
{
    int ret=1;
    if(b==0) ret=0;
    else {
        *result=a/b;
    }
    return ret;
}

4.指针的常见错误

定义了指针变量,但还没有指向任何变量就开始使用指针

    int *p;
    *p=12;

可能导致在重要位置写入12;导致程序崩溃。

9.1-4:指针与数组

1.数组变量

函数参数表中的数组实际上是const指针。

数组变量本身表示地址,每个数组单元表达单个变量。

    int main()
    {
        int a[]={1,2,3,4,5,6,7,8,};
    }
    void f(int a[]){}
    int b[]-->int *const b;

函数参数中的数组a[]实际上跟main函数中的a[]是同一个。

数组为指针常量,数组变量间不能传递赋值。(指向的地址是一定的,但其内容可以修改)。

注:不是常量指针(指针可以指向其他地址,但是内容不可以改变)。

2.运算符

[]运算符可以对数组做,也可以对指针做:

p[0]==*p;

*运算符可以对指针做,也可以对数组做:

*a==a[0];

9.1-5:指针与const

1.指针常量与常量指针

    int *const q=&i;//q得到i的地址,指向i。不再指向其他变量
    const int *p=&i;//不能通过*p修改i(*p=…),但可以改变*p的指向和i的值

判断哪个被const的标志的const在*的前面还是后面

const int* p1==int const*p2;通过指针不可修改

int *const p3 指针不可修改

2.const数组

const int a[]={1,2,3,4,5,6};

a[]本身是一个指针常量,前面加const说明数组单元是const,因此不能改变且需要初始化。

3.const保护

在传递参数进函数时,为了保护函数外的变量或者数组。通过以下方式实现:

    void f(const int *p)或者void f(const int a[])

当传递的参数类型比地址大时,通过这个方式也能用较少字节数传值给参数。

参数小传参数,参数大传地址。

9.2指针运算

9.2-1指针运算

p为指针,p+1表示在地址上加上一个指针类型的大小(不是简单的加1)

如:char型加1;int型加4。

如果指针不是指向一片连续分配的空间,这种运算就没有意义。

1.指针计算

指针可以进行下面的操作:

1.指针加、减一个整数;

2.递增递减(++/--);

3.两个指针相减;(得到地址的差除以指针对应类型)

4.*p++:++的优先级比 *高,故不用加括号。该运算p位置上的地址会被取出来,然后p会加1,也就是位置后移一位。

2.指针比较

<,<=,==,>,>=,!=都可以对指针做。(比较在内存中的地址)

    char ac[]={0,1,2,3,4,5,6,7,8,9};
    char *p=ac;
    char *p1=&ac[5];
    printf("p=%p\n",p);
    printf("p+1=%p\n",p+1);
    printf("*(p+1)=%d\n",*(p+1));// *(p+n)<==>ac[n]
    printf("p1-p=%d\n",p1-p);//结果为5(5个int)
    
​
    int ai[]={0,1,2,3,4,5,6,7,8,9};
    int *q=ai;
    printf("p=%p\n",q);
    printf("p+1=%p\n",q+1);
    printf("*(q+1)=%d",*(q+1));

3. 0地址

内存中0地址是不能随便碰的,如果对其进行修改会导致程序崩溃。

但我们可以利用0地址表示返回的指针无效,程序无法运行。

NULL也可表示0地址。需要用0表示地址时可以直接用NULL。

4.指针的类型

任何类型的指针都是地址,大小都是一样的。但是指向不同类型的指针不能直接赋值。(避免覆盖赋值:改变int的一个单位时会改变char的四个单位)

5.用指针来做什么

1.需要传入较大的数据时用作参数;

2传入数组后对数组做操作;

3.函数返回不止一个结果/需要用函数修改不止一个变量

4.动态申请的内存

9.2-2动态内存分配

通过malloc函数可以进行内存的分配。

要用malloc需要先有 #include <stdlib.h>
int *a;//定义指针
a=(int *)malloc(n*sizeof(int));
//malloc要的参数是占据的空间(多少字节)
//(int*)是强制类型转换,因为malloc返回的是void*(指向类型不确定)

计算结束后需要用free(a);将内存还回去。而且应该还回原地址,不能是改变过的地址。

申请了没free对于大程序来说长时间运行会导致内存逐渐下降。

free过了再free会导致系统崩溃。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值