初学C语言系列:七、指针和八、请求头

七、指针

1:指针的作用是:实现内存的动态分配

变量:内存的位置。(Redis)数据库,也是保存在内存里面。

指针:内存的地址

比方说,有一栋房子,你住进去了,是不是说明你占用了这个房子的位置,你在你家的宅基地上,盖了一间房子。 ——变量(占用了内存的位置)

你房子盖好了,但是没有地址,所以我们要申请一个地址,然后,让你通过这个地址可以找到我盖的房子——  指针

所以说,我们实际做的任何事都是在操作内存空间。

而内存里面包含的是很多的操作指令

每个变量都有一个内存的位置,而每个内存的位置都定义了可使用&(地址符)运算符来访问地址

#include "stdio.h" 

int main()
{
    int num1 = 100; // 这是一个变量,占用内存位置
    int address1; // 声明了指针变量,只能赋值地址
    address1 = &num1; // 通过地址符把变量转换成了指针
    //返回的是一个16进制的地址符
    printf ("num1 当前地址为: % p \n",address1);
    int num2 = 200;
    int address2; 
    address2 = &num2; 
    printf ("num1 当前地址为: % p \n",address2);
    // 注意,分别改变 num1 num2 的值地址不会变化,因为 num1 num2 的位置已经定好了,改变的不是数
    // 还有 double float char 数据类型的指针
    int *ip;//整形指针
    double *dp;//double类型的指针
    float *fp;//float类型的指针
    char *ch;//char类型的指针
    //不同数据类型的指针之间唯一不同的是:指针所指向的变量或常量的数据类型不同!!!
    
    int var = 20;
    int *ip;
    ip = &var;//指针变量中存储了var的地址
    //通过指针赋值的变量的方式,去寻找到变量的地址
    printf("var打印出来的地址:%p \n",&var);
    printf("ip变量存储的地址:%p \n",ip);
    printf("ip变量的值:%d \n",*ip);
    return 0;
}

2:空指针

#include "stdio.h" 

int main()
{
    //通过变成的习惯,或是说,没有变量给我赋值的时候
    //最好也有一个指针。
    //空指针!!!不是0
    int *ip = NULL;
    //空指针也是有地址的
    printf("空指针的地址 %p \n",ip);//ip = 00000000
    //大多数操作系统的程序是不允许访问地址为0的内存,因为该内存是操作系统保留的。
    //内存地址为0有特别重要的意义:它表明该指针不指向可访问的内存位置。
    
    //当我们运行程序的时候,初始化一下数据(内存),释放内存。
    int *str;//声明
    free(str);//释放这个内存
    str = NULL;
    //进行内存的分配
    str = (int*)malloc(sizeof(int));//初始化一个内存
    if(str != NULL){
      *str = 100;
    }else{
      //str为NULL(空指针的情况)
    }
    return 0;
}

3:野指针:就是内存地址的错误信息

通俗理解:你拿到了门牌号,但是找不到房子

如果出现了野指针,就要进行内存释放

成因

1)未初始化:定义指针变量时未赋初值,其值随机,指向不确定内存地址,如int *ptr; ,ptr 未初始化,值不确定。

2)内存释放后未置空:用free或delete释放指针指向内存后,指针未设为NULL(C 语言)或nullptr(C++ 语言),仍指向已释放内存,像int *p = (int *)malloc(sizeof(int)); free(p); 执行后,p 成野指针。

3)返回局部变量指针:函数返回指向局部变量的指针,函数结束局部变量内存释放,指针指向无效地址,比如int* func() { int num = 0; return # } ,func 函数返回的指针指向已释放的局部变量num 的内存。

4)指针越界:指针操作超出所指向内存范围,指向不属于程序的内存区域。

#include "stdio.h" 

int main()
{
    char *str;
    gets(str);//标准的键盘输入
    puts(str);//输出一个字符串
    return 0;
}

八、请求头

1:单独引用 使用extern

//写它的原因:
//类比汽车驾驶的时候:1、启动  2、挂档   3、行驶
//程序运行的过程:1、预处理  2、编译  3、运行
//#include的作用就是程序的预处理部分
#include "stdio.h" //studio.h是系统封装好的请求头,处理了系统相关的操作方法
#include <windows.h>//操作windows电脑相关的方法

int main()
{
    printf("helloworld");
    return 0;
}

C语言 ——高级的编程语言;面向过程的语言,不具备封装、继承、多态——面向对象的特点

//home.c
#include "stdio.h" 
int father()
{
    printf("我的爸爸叫张帅\n");
    return 0;
}
int mother()
{
   printf("我的妈妈叫李美\n");
   return 0;
}
int me()
{
   printf("我叫张小美\n");
   return 0;
}
//header.c
#include "stdio.h" 
extern int father();
extern int mother();
extern int me();
int main()
{
    father();
    mother();
    me();
    return 0;
}

启动命令:

gcc header.c home.c -o header

./header

输出:我的爸爸叫张帅

         我的妈妈叫李美

         我叫张小美

2:复杂引用

//header.c
#include "stdio.h" 
#include "home.h"
// extern int father();
// extern int mother();
// extern int me();
int main()
{
    father();
    mother();
    me();
    yeye(80);
    return 0;
}
//home.c
#include <stdio.h>
//通过哪一个头文件进行了归类
#include "home.h"

int father()
{
    printf("我的爸爸叫张帅\n");
    return 0;
}
int mother()
{
   printf("我的妈妈叫李美\n");
   return 0;
}
int me()
{
   printf("我叫张小美\n");
   return 0;
}
int yeye(int age)
{
    printf("爷爷今年%d岁\n",age);
    return 0;
}
//home.h
#ifndef HOME_H
#define HOME_H

//把归类的函数放到这里面
int father();
int mother();
int me();
int yeye(int age);

#endif

启动命令:gcc header.c home.c -o header 

               ./header

而不是 gcc header.c -o header 

原因如下:gcc header.c -o header 只能编译 header.c 这一个文件,但程序需要同时编译并链接 header.c 和 home.c 两个文件,因为它们分别包含了主函数和函数实现。

1)编译与链接的区别

gcc header.c -o header 命令会编译 header.c 文件,但不会自动编译 home.c

虽然 header.c 中包含了 home.h 头文件(声明了函数原型),但头文件里只有函数声明,没有具体实现

函数的实际代码(实现)在 home.c 文件中,链接器需要这个文件来生成完整的可执行程序

2)链接错误的本质

当你执行 gcc header.c -o header 时,编译器只处理了 header.c

链接器在生成可执行文件时发现 father()、mother()、me() 这三个函数只有声明(在 home.h 中),但没有找到对应的实现代码

链接器无法从 header.c 中找到这些函数的实现,因为它们实际上在 home.c 中,所以报错 

可以理解为大鱼吃小鱼

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值