【计算机408学习笔记】C语言(初级) 06指针

06指针

一、上节作业(重点)
1. 输入N个数(N小于等于100),输出数字2的出现次数。
要求:输入的格式是两行,第一行输入要输的元素个数,比如5;第二行输入  1 2 2 3 2,那么输出结果为3,因为2出现了3次。
#include <stdio.h>


int main() {
    int len;
    int a[100];
    int i,res;


    //获取第一行:元素个数
    scanf("%d",&len);
    //获取第二行:数组元素。循环读取
    for(i=0;i<len;i++)
    {
        scanf("%d",&a[i]);
    }
    //统计数字2出现的次数
    for(i=0,res=0;i<len;i++)
    {
        if(a[i]==2)
        {
            res++;
        }
    }
    printf("%d",res);
    return 0;
}

2. 读取一个字符串,字符串可能含有空格,将字符串逆转,原来的字符串与逆转后字符串相同,输出0,原字符串小于逆转后字符串输出-1,大于逆转后字符串输出1。例如输入 hello,逆转后的字符串为 olleh,因为hello 小于 olleh,所以输出-1。

#include <stdio.h>
#include <string.h>


int main() {
    char c[50],d[50];
    int len,i,j,res;
    //获取字符串
    gets(c);
    len=strlen(c);


    //逆转字符串
    for(i=0;i<len;i++)
    {
        j=len-i-1;
        d[j]=c[i];
    }
    d[j]='\0';


    //比较字符串
    res=strcmp(c,d);
    if(res<0)
    {
        printf("%d\n",-1);
    }
    else if(res==0)
    {
        printf("%d\n",0);
    }
    else if(res>0)
    {
        printf("%d\n",1);
    }
    return 0;
}
  • 注意:windows下使用VS集成开发环境,不能使用gets。fgets(c,sizeof(c),stdin);等价于gets(c);
二、 指针的本质(间接访问原理)
1.指针的定义
  • 编址:内存区域中的每字节都对应一个编号(该编号未进行存储)。
  • 内存单元:如果在程序中定了一个变量,那么在对程序进行编译时,系统就会给这个变量分配内存单元。
  • 直接访问:按变量地址存取变量值的方式。如printf("%d",i);
  • 间接访问:将变量i的地址存放到另一变量中。(先访问起始地址,再通过起始地址访问变量i)
  • 指针变量:一种特殊的变量,用来存放变量地址。(存起始地址)。格式:基类型 * 指针变量名; 如int * i_pointer;
  • 指针:一个变量的地址成为该变量的指针。指针=地址。
  • 指针变量i_pointer本身占多大空间?如果是64位应用程序,则为8字节;如果为32位应用程序,则为4字节。sizeof(i_pointer);
2.取地址操作符与取值操作符
  • 取地址操作符:也称为引用&,可以获取一个变量的地址值。
  • 取值操作符:也称为解引用*,可以得到一个地址对应的数据。
  • 指针变量的初始化是某个变量取地址来赋值,不能随机写个数。
  • 例子
#include <stdio.h>


int main() {
    int i=5;
    int *i_pointer=&i;


    printf("i=%d\n",i);//直接访问
    printf("*i_pointer=%d\n",*i_pointer);//间接访问
    return 0;
}
  • 结果
i=5
*i_pointer=5
  • 注意:
(1)指针变量前面的*表示该变量为指针变量,指针变量名不含*。如float *i_pointer;变量名为 i_pointer。
(2)在定义指针变量时必须指定其类型,只有整型变量的地址才能放到指向整型变量的指针变量中。
float a;
int *pointer_1;
pointer_1=&a;//错误,类型不同
(3)&*pointer_1与*&a的含义
&*pointer_1与&a相同,都表示变量a的地址,也就是pointer_1;
*&a与*pointer_1相同,都表示变量a的值,也就是a;
&与*两个运算符的优先级相同,但是按自右向左的方向结合。
(4)int *a,b,c;不等于int *a,*b,*c;
int *a,b,c;中,*a是整型指针变量,b,c是普通的整型变量。
三、指针的传递使用场景
  • 指针的使用场景通常有两个:传递与偏移。
  • 例子
#include <stdio.h>


void change(int j)//j是形参
{
    j=5;
}


int main() {
    int i=10;
    printf("before change i=%d\n",i);
    change(i);//i是实参
    printf("after change i=%d\n",i);
    return 0;
}
  • 结果
before change i=10
after change i=10
  • C语言的函数调用是值传递,实参赋值给形参,j=i;
  • 进程:程序启动起来就是进程。
  • 例子
#include <stdio.h>


void change(int *j)
{
    *j=5;//*j等价于i,间接访问
}


int main() {
    int i=10;
    printf("before change i=%d\n",i);
    change(&i);
    printf("after change i=%d\n",i);
    return 0;
}
  • 结果
before change i=10
after change i=5
四、 指针的偏移使用场景
1.指针的偏移
  • 对指针的加减称为指针的偏移,加就是向后偏移,减就是向前偏移。
  • 数组名中存储了数组的起始地址。
  • 例子
#include <stdio.h>


#define N 5


int main() {
    int a[N]={1,2,3,4,5};//数组名中存储了数组的起始地址。a中存储的就是一个地址值
    int *p;
    p=a;


    int i;
    for(i=0;i<N;i++)
    {
        printf("%d\n",a[i]);
    }


    return 0;
}
  • 结果
1
2
3
4
5
  • 指针偏移的长度是其基类型的长度,也就是sizeof(int)。如*p可以得到元素a[0],*(p+1)就可以得到元素a[1]。
  • 例子
#include <stdio.h>


#define N 5


int main() {
    int a[N]={1,2,3,4,5};//数组名中存储了数组的起始地址。a中存储的就是一个地址值
    int *p;
    p=a;


    int i;
    for(i=0;i<N;i++)
    {
        printf("%d\n",*(p+i));//与上例中的a[i]等价
    }


    return 0;
}
  • 结果
1
2
3
4
5
  • 例子
#include <stdio.h>


#define N 5


int main() {
    int a[N]={1,2,3,4,5};//数组名中存储了数组的起始地址。a中存储的就是一个地址值
    int *p;
    int i;
    p=&a[4];//将数组a中的最后一个元素地址赋值给p;
    for(i=0;i<N;i++)
    {
        printf("%d\n",*(p-i));
    }


    return 0;
}
  • 结果
5
4
3
2
1
  • int、float数组偏移1位都是4个字节。
2.指针与一维数组
  • 数组名作为实参传递给子函数时,是弱化为指针的。
  • 例子
#include <stdio.h>


void change(char *d)
{
    *d='H';
    d[1]='E';//等价于*(d+1)
    *(d+2)='L';
}


int main() {


    char c[10]="hello";
    change(c);
    puts(c);
    return 0;
}
  • 结果
HELlo
五、指针与malloc动态内存申请,栈与堆的差异(重要)
1.指针与动态内存申请
  • 栈空间:大小在编译时是确定的。C语言的数组长度固定是因为其定义的整型、浮点型、字符型变量、数组变量都在栈空间中。栈是计算机系统提供的数据结构,计算机会在底层的对栈提供支持:分配专门的寄存器存放栈的地址,压栈操作、出栈操作都有专门的指令进行,这就决定了栈的效率比较高。
  • 堆空间:大小不确定。堆是C/C++函数库提供的数据结构,机制很复杂,如为了分配一块内存,库函数会按照一定的算法在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间,有可能调用系统功能去增加程序数据段的内存空间。堆的效率比栈低得多。
  • malloc函数,在执行#include<stdlib.h> void *malloc(size_t size;时,需要给malloc传递的参数是一个整型变量;返回值为void*类型的指针(无类型指针),只能用来存储一个地址而不能进行偏移(因为malloc并不知道我们申请的空间用来存放什么类型的数据,所以确定要用来存储什么类型的数据后,都会将void*强制转换为对应的类型)。
  • 栈空间由系统自动管理,堆空间的申请和释放需自行管理。
  • free函数的头文件及格式:#include <stdlib.h>  void free(void *ptr);P的地址值必须是malloc当时返回的地址值,不能进行偏移(因为申请一段堆内存空间时,内核记录的是起始地址和大小,所以释放时内核用对应的首地址进行匹配)。如果要偏移进而存储数据,可以定义两个指针变量来结果。
  • 例子
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


int main() {


    int size;//size代表我们要申请多大的空间
    char *p;
    scanf("%d",&size);//输入要申请的空间大小


    p=(char*)malloc(size);//malloc返回的是void*代表无类型指针,需要强制转换


    strcpy(p,"malloc success\n");
    puts(p);


    free(p);//free时必须使用malloc申请时返回的指针值,不能进行任何偏移
    printf("free success\n");
    return 0;
}
  • 结果
5
malloc success
free success
  • 注意:需要注意指针本身大小,和其指向的空间大小,是两码事。
2.栈(stack)空间和堆(heap)空间的差异
  • 例子
#include <stdio.h>




char *print_stack()
{
    char c[100]="I am print_stack function";
    char *p;
    p=c;
    puts(p);
    return p;
}


int main() {
    char *p;
    p=print_stack();//调用print_stack后,print_stack栈空间释放
    puts(p);//p不能获取栈空间的数据,打印就会出现错误
    return 0;
}
  • 结果
I am print_stack function
I am print_stack
  • 例子
#include <stdio.h>
#include <string.h>
#include <stdlib.h>


char *print_stack()
{
    char c[100]="I am print_stack function";
    char *p;
    p=c;
    puts(p);
    return p;
}


char *print_malloc()
{
    char *p=(char*)malloc(100);//堆空间在整个进程中一直有效,不因为函数结束而消亡
    strcpy(p,"I am print_malloc function");
    puts(p);
    return p;
}


int main() {
    char *p;
    p=print_stack();
    puts(p);


    p=print_malloc();
    puts(p);
       free(p);//堆空间只有在free时才释放
    return 0;
}
  • 结果
I am print_stack function
I am print_stack
I am print_malloc function
I am print_malloc function
  • 8
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

废柴老妈

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值