欢迎光临
我们一直在努力

C语言基础学习Day6

一、函数的局部变量和全局变量

1.局部变量

(1)局部变量定义

        在函数内部定义的变量称为局部变量

        代码示例:

#include <stdio.h>

void test() {
int a = 10; // 局部变量
printf("a = %d\\n", a);
}

int main() {
test();
return 0;
}

(2)局部变量的特点

        作用域:只能在定义它的函数内部使用

        生命周期:函数调用时创建,函数结束时销毁

        示例:

void test() {
int a = 10;
}

每次调用test()
创建a
使用a
销毁a

        默认值:局部变量没有默认值,如果没有初始化,里面是随机值(垃圾值)

        代码示例

#include <stdio.h>

int main() {
int a;
printf("%d\\n", a);
return 0;
}

输出结果为:
12548
-3567
0

2.全局变量

(1)全局变量的定义

        在所有函数外部定义的变量称为全局变量

        代码示例

#include <stdio.h>

int num = 100; // 全局变量

void test() {
printf("num = %d\\n", num);
}

int main() {
printf("num = %d\\n", num);
test();

return 0;
}

(2)全局变量的特点

        作用域:从定义位置开始,到整个文件结束都可以使用

        生命周期:程序开始运行时创建,程序结束时销毁

        示例:

程序启动

创建全局变量

main执行

程序结束

销毁全局变量

        默认值:全局变量如果没有初始化,系统自动初始化为0;

        示例:

#include <stdio.h>

int num;

int main() {
printf("%d\\n", num);
return 0;
}

输出:
0

3.局部变量和全局变量同名

        (1)当局部变量和全局变量同名时,采取局部变量优先原则(就近原则)

        代码示例:

#include <stdio.h>

int num = 100; // 全局变量

int main() {
int num = 50; // 局部变量

printf("%d\\n", num);

return 0;
}

输出:
50

        (2)口诀:先找局部变量,找不到再找全局变量

4.局部变量和全局变量总结对比表

对比项局部变量全局变量
定义位置 函数内部 函数外部
作用域 当前函数内部 整个文件
生命周期 函数调用期间 程序运行期间
默认值 随机值(垃圾值) 0
存储位置 栈区(Stack) 全局区/静态区
是否推荐大量使用

二、指针变量的定义和使用

1.指针的定义

        指针本身也是一种变量,它是用来存放变量地址的变量(用大白话说就是指针变量指向谁,就把谁的地址赋值给指针变量)

2.指针的语法格式

类型 变量;
类型 * 指针变量 = &变量;

(1)在定义指针变量时:* 号表示所声明的变量为指针类型

                                         指针变量要保存某个变量的地址

(2)在使用指针变量时:* 叫解引用,可以操作(读取、修改)指针所指向的变量的值

(3)&:取地址,返回操作数的内存地址

·        例如:&a,那就是取a的地址

(4)指针的类型

指针类型指向的数据类型数据类型大小32位系统指针大小64位系统指针大小
char * char 1字节 4字节 8字节
short * short 2字节 4字节 8字节
int * int 4字节 4字节 8字节
long * long 4或8字节 4字节 8字节
float * float 4字节 4字节 8字节
double * double 8字节 4字节 8字节
long long * long long 8字节 4字节 8字节
void * 任意类型 无固定大小 4字节 8字节

(5)指针示例代码

#include <stdio.h>
#include <stdint.h>

int main() {
//定义一个整型变量
int a = 10;
printf("&a = %p\\n", &a);

//定义一个指针,指向a的地址
int* p = &a;
printf("p = %p,*p = %d\\n", p, *p);
//定义char* ,int* ,double*
char *p1;
int *p2;
double *p3;
//输出三个指针类型的长度
printf("%zu\\n", sizeof(p1));
printf("%zu\\n", sizeof(p2));
printf("%zu\\n", sizeof(p3));

return 0;
}

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

(1)间接修改的本质:通过指针访问改地址,然后改变它指向的值

(2)示例代码:

#include <stdio.h>

int main() {
int a = 10; // 原变量
int* p = &a; // 指针指向a

printf("修改前 a = %d\\n", a);

*p = 99; // 通过指针修改a的值

printf("修改后 a = %d\\n", a);

return 0;
}

4.指针步长

(1)定义:指针步长指的是指针每移动一次,实际跳过的字节数(步长大小由指针指向的数据类型决定)

(2)指针加n等于指针地址加上 n 个 sizeof(type) 的长度

         指针减n等于指针地址减去 n 个 sizeof(type) 的长度

(3)指针步长的数据类型

指针类型指向数据类型大小指针步长(p++移动距离)备注
char * 1 字节 1 字节 最小单位,单字节
short * 2 字节 2 字节 2字节类型指针步长为2
int * 4 字节(PC)/2字节(STC8) 4 或 2 字节 与平台相关
long * 4 或 8 字节 4 或 8 字节 根据系统位数
float * 4 字节 4 字节 单精度浮点
double * 8 字节 8 字节 双精度浮点
long long * 8 字节 8 字节 64位整数
void * 不确定 通常等于平台指针大小 不能直接解引用,需要类型转换

(4)代码示例

#include <stdio.h>

int main() {
char ch;
char *p1 = &ch;
printf("p1:%p, p1+1: %p\\n", p1, p1 + 1); // 步长为1字节

int a;
int *p2 = &a;
printf("p2:%p, p2+1: %p\\n", p2, p2 + 1); // 步长为4字节

double d;
double *p3 = &d;
printf("p3:%p, p3+1: %p\\n", p3, p3 + 1); // 步长为8字节

return 0;
}

5.空指针

(1)定义:空指针指向“没有任何有效内存地址”的指针,它是安全的,不能用来访问数据

(2)特点:指针有明确的值NULL(一般是0);

                    不能解引用,否则会出现运行错误

                   用途:初始化指针、判断指针是否指向有效地址

(3)代码示例;

#include <stdio.h>

int main() {
int *p = NULL;

if (p == NULL) {
printf("p 没有指向有效内存\\n");
}

return 0;
}

输出:
p 没有指向有效内存

6.野指针

(1)定义:野指针指向未知或非法内存地址的指针,它可能随机指向某块内存,访问会导致不可预知的错误

(2)野指针的产生原因:未初始化指针(最常见)

                                          指针指向的内存被释放

                                          指针指向局部变量的地址,但局部变量已超出作用域

(3)代码示例:(代码可以运行,但输出随机值)

#include <stdio.h>

int main() {
int *p; // 指针未初始化
printf("%d\\n", *p); // 解引用未初始化指针,输出垃圾值(随机值)
return 0;
}

7.多级指针

(1)多级指针的定义:普通指针保存的是变量的地址,而多级指针保存的是指针变量的地址

(2)代码示例:

#include <stdio.h>

int main() {

int a = 100;

// 一级指针
int* p1 = &a;
printf("&a=%p\\n", &a);
printf("p1=%p\\n", p1);
printf("&p1=%p\\n", &p1);

// 二级指针,可以存储一级指针变量的地址
int** p2 = &p1;
printf("p2=%p\\n", p2);
printf("&p2=%p\\n", &p2);

// 三级指针,可以存储二级指针变量的地址
int*** p3 = &p2;
printf("p3=%p\\n", p3);
printf("&p3=%p\\n", &p3);

printf("———————\\n");

// 通过一级指针访问100,打印出来
printf("*p1=%d\\n", *p1);

// 通过二级指针访问100,打印出来
printf("**p2=%d\\n", **p2);

// 通过三级指针访问100,打印出来
printf("***p3=%d\\n", ***p3);

return 0;
}

8.指针与常量

(1)常量指针(指向常量的指针)

        1.语法格式:两种方法相同

const int *p;

int const *p;

        2.含义:p可以改变指向,但是*p不能修改(即指针可变,指向的数据不可变)

        3.代码示例:

#include <stdio.h>

int main()
{
int a = 10;
int b = 20;

const int *p = &a;

p = &b; // √ 可以

//*p = 100; // × 不允许

printf("%d\\n", *p);

return 0;
}

(2)指针常量

        1.语法格式;

int * const p = &a;

        2.含义:p不能改变指向,*p可以修改(即指针不可变,指向的数据可变)

        3.示例代码:

#include <stdio.h>

int main()
{
int a = 10;

int * const p = &a;

*p = 100;

printf("%d\\n", a);

return 0;
}

(3)常量指针常量

        1.语法格式:

const int * const p = &a;

        2.含义:p不能改,*p不能改(即指针不可变,数据不可变)

        3.示例代码:

#include <stdio.h>

int main() {
int a = 10;
int b = 20;

// 定义常量指针常量
const int * const p = &a;

printf("p 指向的数据: %d\\n", *p);

// *p = 100; // ❌ 错误:数据不可改
// p = &b; // ❌ 错误:指针不可改

return 0;
}

(4)总结表格

写法指针能改方向数据能改
int *p
const int *p ×
int * const p ×
const int * const p × ×

三、指针和函数

1.函数参数传值

(1)在C语言中,函数调用时采用的是值传递(意思是:调用函数时,会把实参的值复制一份给形参,函数内部修改的是副本,不会影响原来的变量)

(2)代码示例:

// 函数参数传值,函数内部交换2个变量的值,验证函数外部的变量有没有改变
#include <stdio.h>

// 函数定义
void func(int m, int n) {
// 函数内部交换2个变量的值
int temp = m;
m = n;
n = temp;
printf("函数内部 m = %d, n = %d\\n", m, n);
}

int main() {
int a = 10;
int b = 20;
// 调用函数,值传递
func(a, b);
printf("函数外部 a = %d, b = %d\\n", a, b);

return 0;
}

(3)总结版:定义函数时的是形参,调用函数时的是实参。实参把值复制给形参,形参改变一般                          不影响实参。

2.函数参数传址(指针传参)

(1)参数传址实际上就是指针传参

(2)代码示例:

#include <stdio.h>
//定义了一个指针类型的函数
void change(int *p)
{
//指针的值为100
*p = 100;
}

int main()
{
//定义了一个整型a
int a = 10;
//调用函数change,把a的地址赋值给p,p的地址和a的地址现在是相同的
change(&a);
//所以a = *p ,a = 100
printf("%d\\n", a);

return 0;
}

(3)总结口诀:传值传的是数据,形参实参两块地;

                           传址传的是地址,指针找到原数据;

                           *p就是访问目标,修改之后立即生效。

3.函数指针

(1)定义:函数指针就是指向函数的指针变量

(2)语法格式:

返回值 (*函数指针变量)(形参列表);

(3)函数指针变量的定义,其中返回值、形参列表需要和指向的函数匹配(类型必须相同)

(4)代码示例:

#include <stdio.h>

int add(int a, int b)
{
return a + b;
}

int main()
{
// 定义函数指针
int (*fp)(int, int);

// 指向add函数
fp = add;

// 通过函数指针调用函数
int result = fp(10, 20);

printf("result = %d\\n", result);

return 0;
}

4.函数指针用作函数参数

(1)函数指针可以作为另一个函数的参数,这样就可以动态决定调用那个函数

(2)优点:可以实现回调机制

                    可以动态切换代码

                    避免写重复代码

(3)代码示例:

#include <stdio.h>

// 普通函数
int add(int a, int b)
{
return a + b;
}

int sub(int a, int b)
{
return a – b;
}

// 函数指针作为参数
int calculate(int x, int y, int (*fp)(int, int))
{
return fp(x, y); // 通过函数指针调用函数
}

int main()
{
int a = 10, b = 5;

// 调用calculate,并传入add
printf("a+b = %d\\n", calculate(a, b, add));

// 调用calculate,并传入sub
printf("a-b = %d\\n", calculate(a, b, sub));

return 0;
}

5.回调函数

(1)回调函数 = 通过函数指针传递给另一个函数,然后在合适的时候被调用的函数

(2)回调函数本质上还是普通的函数,它的特殊之处在于它不是自己调用,而是作为参数传给别的函数,由别的函数调用

(3)经典的回调结构:

void execute(void (*fp)())
{
fp();
}

int main()
{
execute(test);
}

(4)更容易理解的图片

赞(0)
未经允许不得转载:171主机测评 » C语言基础学习Day6
分享到: 更多 (0)

评论 抢沙发

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址