C语言--第三章之函数,指针

一. 函数

1. 函数的概述

  函数是一种可复用的代码块, 用于执行特定的任务,来完成特定的功能

2. 函数的作用

函数对代码进行封装, 提高代码的编写效率, 提高代码的复用率

3. 函数参数的作用

增加函数的灵活性,可以根据需求在调用函数时, 通过参数传入不同的数据

4. 函数返回值的作用

函数外部想使用函数内部的数据, 在使用函数返回值时,需要注意,return是结束函数执行, return后面的代码将不再执行

5. 函数的形式

5.1 无参无返回值

其中void作为返回值类型,表示无返回值

5.2 有参无返回值

还是使用void

5.3 有参有返回值

返回值的类型就是函数的类型

代码示例
#include <stdio.h>
// 1. 无参,无返回值
// 定义函数
void a()
{
    printf("hello world\n");
}

// void 代表无返回值
// 2.有参,无返回值
void add(int a, int b)
{
    printf("%d\n", a + b);
}

// 3. 有参,有返回值
int add2(int a, int b)
{
    printf("%d\n", a + b);
    return a + b;
    printf("不执行\n");
}

int main()
{
    // 使用函数
    a();
    add(1, 2);
    int result = add2(4, 3);
    printf("result = %d\n", result);
    return 0;
}

6. 函数的声明

如果函数定义代码没有放在函数调用的前面, 这个时候需要先做函数的声明

所谓函数声明, 相当于告诉编译器, 函数是有定义的, 在别的地方定义,以便使编译能够正常进行

注意: 一个函数只能被定义一次, 但是可以声明多次

#include <stdio.h>

// 先声明  有sum 这个函数 
int sum(int a, int b);
sum(int a, int b);

int main() {


    sum(10, 20);
    return 0;
}
// 再去(实现)定义 
int sum(int a, int b) {
    return a + b;
}

// int sum(int a, int b) {
//     return a + b;
// }

7. 函数案例

需求:自定义一个函数,返回2个整数的最大值

#include <stdio.h>

// 求两个数最大值
int maxNum(int a, int b)
{
    return a > b ? a : b;
}

int main()
{
    int max = maxNum(10, 20);
    printf("%d\n", max);

    // 求三个数最大值
    int maxManyNum = maxNum(max, 99);
    printf("%d\n", maxManyNum);

    return 0;
}

8. 局部全局变量

8.1 局部变量

局部变量一般在函数中定义,也就是花括号中{ }

生命周期(生效的范围) : 函数中 ({ }内)

失效时间: 函数执行完成后失效(释放)

#include <stdio.h>

int add(int a, int b)
{
    // sum只在函数内生效
    int sum = a + b;
}

int main()
{

    printf("%d\n", add(10, 20));

    return 0;
}
8.2 全局变量

全局变量一般定义在函数外

声明周期(生效的范围) : 整个文件

失效时间: 整个文件代码运行完成才释放

#include <stdio.h>

int a = 10;

void prt()
{
    printf("%d\n", a);
}

int main()
{
    prt();
    printf("%d\n", a);

    return 0;
}

9. 多文件编程(重点)

1. 首先在add.h文件中声明方法add,

为了防止文件重复包含需要添加 #ifndef __文件名_H__#define __文件名_H__#endif

2. 在同名的add.c文件中实现add.h的方法

3. 在main.c主文件中使用add.h中的方法,这里需要引入#include "./add.h"

如果想要直接使用b.c文件中的变量和函数时

在main.c主文件中,先引入b.c文件(#include "./b.c")

使用extern + b.c中的变量和方法 (extern int d; extern void print_d();)

防止头文件重复包含

当一个项目比较大时, 往往都是分文件,这个时候有可能不小心把同一个头文件include多次, 或者头文件嵌套包含

为了避免同一个文件被include多次, C/C++中有两种方式

方法一:

#ifndef __文件名_H__
#define __文件名_H__

// 声明语句

#endif

方法二: (这种方式在嵌入式中不可用哦)

#pragma once  // 声明语句

二. 指针(重点)

1. 指针的定义

指针就是一种数据类型,用来存储地址,操作地址,指针的实质就是内存地址

指针变量指向谁, 就把谁的地址赋值给它

#include <stdio.h>

int main()
{

    // 定义一个变量
    int a = 20;
    // 打印a变量的值
    printf("%d\n", a);

    // 打印a变量的地址(%p)
    // &a中的&这里表示取地址符
    printf("%p\n", &a);

    // 将a的地址赋值给指针p
    int *p = &a;
    // 打印p变量的地址(%p)
    printf("%p\n", p);
    // 打印指针p指向的值(*指针变量)
    printf("%d\n", *p);

    return 0;
}

2. 通过指针间接修改变量的值

指针变量指向谁, 就把谁的地址赋值给指针变量

通过 * 指针变量,间接修改指针变量的值

#include <stdio.h>

int main()
{

    // 定义一个变量
    int a = 20;
    // 打印a变量的值
    printf("%d\n", a);  // 20
    int *p = &a;

    // 修改指针变量p对应的值
    *p = 80;
    printf("%d\n", a);  // 80

    return 0;
}

3. const修饰的指针变量

一句话总结: 自动忽略变量类型的前提下, const修饰谁,谁就不可以改变(const是常量,不可修改哦)

#include <stdio.h>

int main()
{
    int a = 1;
    int b = 2;

    // 1.此时const修饰的是*p1,所以p1可以修改,*p1不可以
    const int *p1 = &a;
    p1 = &b;
    //*p1 = 5;

    // 2.此时const修饰的是p2,所以p2不可以修改,*p2可以
    int *const p2 = &a;
    // p2 = &b;
    *p2 = 5;

    printf("%d\n", *p1);
    printf("%d\n", *p2);
    return 0;
}

4. 指针的大小(sizeof)

使用sizeof( )测量指针的大小, 得到的总是 : 4 或者8

sizeof( ) 测的是指针变量指向存储地址的大小

  • 在32位平台中, 所有的指针(地址) 都是32位(4字节)
  • 在64位平台中, 所有的指针(地址) 都是64位(8字节)

但是这里需要注意:

无论是定义几级的指针,对指针大小都没有影响(也就是***多少都关系)

并且,指针的大小和指针的类型也没有关系(也就是int, char都没关系)

#include <stdio.h>

int main() {

    char* p;
    int* p1;
    double* p2;
    int** p3;
    double**** p4;

    printf("sizeof p = %d\n", sizeof(p));
    printf("sizeof p1 = %d\n", sizeof(p1));
    printf("sizeof p2 = %d\n", sizeof(p2));
    printf("sizeof p3 = %d\n", sizeof(p3));
    printf("sizeof p4 = %d\n", sizeof(p4));

    return 0;
}

5. 指针的步长

指针步长指的是通过指针进行递增或者递减操作时, 指针所指向的内存地址相对于当前地址的偏移量

指针的步长取决于所指向的数据类型

  • 指针加n等于指针地址加上n个sizeof(type)的长度
  • 指针减n等于指针地址减去n个sizeof(type)的长度
#include <stdio.h>

int main() {

    char a = 10;
    char* p = &a;
    // 0x7ff7bbac63bb   p
    // 0x7ff7bbac63bc   p+1
    // char 类型 偏移   1字节
    printf("p = %p\n p+1 = %p\n", p, p + 1);
    printf("-------------\n");

    //     pp    = 000000000061FE00
    //      pp+1 = 000000000061FE04   // 4个字节
    // int 类型的指针偏移量是4个字节
    // 指针的步长取决于所指向的数据类型。
    int b = 10;
    int* pp = &b;

    printf("pp    = %p\n pp+1 = %p\n", pp, pp + 1);

    //     ppp    = 0x7ff7bfcff398
    //      ppp+1 = 0x7ff7bfcff3a0  // 8个字节
    double c = 20;
    double* ppp = &c;
    printf("ppp    = %p\n ppp+1 = %p\n", ppp, ppp + 1);

    return 0;
}

6. 野指针和空指针

  • 指针变量也是变量,是变量就可以任意赋值

  • 任意数值赋值给指针变量没有意义,因为这样的指针就成了野指针

    • 此指针指向的区域是未知(操作系统不允许操作此指针指向的内存区域)

  • 野指针不会直接引发错误,操作野指针指向的内存区域才会出问题

  • 为了标志某个指针变量没有任何指向,可赋值为NULL

    • NULL是一个值为0的宏常量

下面的代码中,出现了野指针的情况,请指出并修复错误

#include <stdio.h>

int main() {
    int *ptr;
    int num = 10;

    *ptr = num;

    printf("Value: %d\n", *ptr);

    return 0;
}

修改后

#include <stdio.h>

int main()
{
    // 设置为NULL,NULL是一个值为0的宏常量
    int *ptr = NULL;
    int num = 10;
    // 输出地址
    printf("%p\n", &num);  // 000000000061FE14
    printf("%p\n",ptr);  // 0000000000000000

    //*ptr = num;
    ptr = &num;

    printf("Value: %d\n", *ptr);

    return 0;
}

7. 多级指针

  • 简单来说, 一级就是 * ,二级就是 ** ,以此类推

  • C语言允许有多级指针存在,在实际的程序中一级指针最常用,其次是二级指针

  • 二级指针可以存储一级指针的地址,三级指针可以存储二级指针的地址,以此类推...

三. 指针和函数

1. 函数参数传值

  • 传值是指将参数的值拷贝一份传递给函数,函数内部对该参数的修改不会影响到原来的变量

2. 函数参数传址(地址) (重点)

  • 传址是指将参数的地址传递给函数,函数内部可以通过该地址来访问原变量,并对其进行修改

实例

编写一个程序,定义一个整型变量,初始值为100,通过某个函数修改改变量的内容为123

#include <stdio.h>

// 通过地址修改
int change2(int *p)
{
    printf("%d\n", *p);  // 100
    *p = 123;
    printf("%d\n", *p);  // 123
    return *p;
}

int main()
{
    int a = 100;

    printf("%p\n", &a);  // 地址

    // 通过地址修改
    printf("修改为: %d\n", change2(&a));

    return 0;
}
  • 10
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值