变量作用域及结构体的应用

星落化尘 2023-11-25 21:40:10
加精

经过上篇博客的痛苦折磨(学了半天看不懂,看懂了发现没有用),这次的博客难度是较低的,实用性极强

上一篇博客中讲到了全局变量,其实也有讲到局部变量,只是我没有特地说明,接下来我们来全面的学习变量的作用域

1. 变量的作用域

其实作用域主要分为两种,一种是全局变量,另一种就是局部变量

1.1 全局变量

学长式定义:作用于全局的变量就叫全局变量

具体使用方法如下:

#include<stdio.h>

int x = 0;    //全局变量

void f() {
    x++;
}

int main() {
    x++;
    f();
    printf("%d", x);    //2
}

写法比较简单,下面我们来说说全局变量的特点和应用

特点:很明显,定义在与main函数同级的地方能作用于所有的函数

应用:这里我们回顾一下以前我们写题的一个思路


#include<stdio.h>

int f(int x, int y) {
    y++
    return ++x; //x++返回值不对,再帮大伙回忆一下
}

int main() {
    int x = 0;
    int y = 0;
    x++;
    y++
    x = f(x);
    printf("%d\n", x);    //2
    printf("%d", y);    //1
}

我们如果要修改两个值,则做不到了

但是如果我们使用全局变量,这一切就都行得通

#include<stdio.h>

int x = 0;    //全局变量
int y = 0;

void f() {
    x++;
    y++
}

int main() {
    x++;
    y++;
    f();
    printf("%d %d", x, y);    //2 1
}

这是全部变量最重要的一个用途

还有一种用途就是去定义静态变量(不允许被修改的变量)

const double PI = 3.1415926

除此之外的优点是学长最喜欢的写法,老师最不允许的写法:所有变量都定义成全局变量(主要是偷懒懒得给函数写变量)

讲一讲为什么不推荐这么写:

1. 代码难以维护,可读性差

2. 编写递归函数的时候千万别这么写, 虽然是可以运行,但是定义的变量如果命名冲突会大大降低代码可读性

3. 定义在全局的变量会对内存有一定影响

1.2 局部变量

学长式定义:有明确作用域的变量

具体讲解如下:

#include<stdio.h>

int f(int x) {    //这个x就是f函数内的局部变量,一定离开这个函数体,这个变量就会销毁
    return ++x;
}

int main() {
    int x = 0;    //作用与main函数的局部变量,一样也是main函数结束被销毁
    x++;
    x = f(x);
    for (int i = 0; i <= x; i++) {   //这里的i就是for循环内的局部变量,如果for循环截止,i被销毁
        x++;
        if (x == i + 1) {
            int y = x;
            printf("%d\n", y);    //3
            //这个y则是if的局部变量,如果离开if的函数体,则y被销毁
        }
        x--;
    }
    int z = 0;    //这个是为下面讲解用的
    printf("%d", x);    //2
}

以上举得例子全都是大家平常会用到变量的例子,那么如何判断一个变量的作用域呢?有个很简单的办法!(其实用眼睛随便看都能看得出来,实在看不出来,编译器也会告诉你,你有没有写错)

方法:在这个变量的定义语句之前的第一个大括号,就是这个变量的作用域起点(定义在for循环内的变量不算,那个作用域是哪也很明显)

如:

int main(){    //x的起点
    int x
    //.....
}

if (x == i + 1) {    //y的起点
    int y = x;
    //....
}

//有的人会说,z的变量不是
//因为我少说了一个规则:
//如果之前有出现成对的括号就消掉

int main() {    //z的起点
    //..
    for (int i = 0; i <= x; i++) { 
        //....
        if (x == i + 1) {
            //....
        }    //抵消
        //....
    }    //抵消
    int z = 0;
    //....
}

下面我们来看点进阶的:

同名变量:(这种写法其实在大部分语言中是不允许的)

#include<stdio.h>

int x = 0;    //全局变量

void f(int x) {
    x++;
}

int main() {
    x++;
    f(x);
    printf("%d", x);    //1
}

按照我们刚刚讲全局变量来说,这个结果应该是 2,但是确是 1

那是因为变量的运算是就近原则(其实也可以说是谁的作用域最小,就是谁),在 f 函数中的 x 是其内部的局部变量,而不是全局变量中的 x

1.3 全局变量和局部变量的区别的应用方向

1.区别(系统上的)

  1.1. 全局变量保存在内存的全局存储区中,占用静态的存储单元;

  1.2. 局部变量保存在栈中,只有在所在函数被调用时才动态地为变量分配存储单元。

2. 应用方向

  1.1 全局变量主要作用于多函数返回值无法实现的函数

  1.2 局部变量则适用于其余所有的地方(全局变量不是不行,是不规范,但是如果你专注于算法,那么全写全局变量是一个比较好的写法)

2. 结构体

因为是比较偏应用而非概念的博客,所以我们就跳过定义什么乱七八糟的,直接讲其应用(绝对不是因为学长根本不会)

结构体原本应该算是初步接触 面向对象(不是你们想的那个对象)的思想,但是这个思想在C语言中应用很少

代码书写还是要讲以下的:

#include<stdio.h>

struct point {
    int x;    //结构体变量
    int y;
    int sum() {    //结构体函数
        return x + y;
    }
};

//这些变量与函数必须依赖于一个结构体实体存在

int main() {
    struct point p;
    p.x = 1;
    p.y = 2;
    printf("%d", p.sum());    //2
}


应用:

1. 以上的这类书写主要是为了减少代码的书写,因为如果要记录一堆点的横坐标和纵坐标需要定义两个数组,但是用结构体只需要一个,且使用起来方便简单,成双成对

#include<stdio.h>

struct point {
    int x;
    int y;
    int sum() {
        return x + y;
    }
};

int main() {
    struct point p[10];
    for (int i = 0; i < 10; i++) {
        p[i].x = i+1;
        p[i].y = i-1;
    }
    for (int i = 0; i < 10; i++) {
        printf("%d %d\n", p[i].x, p[i].y);
    }
}

目前你们只需要掌握如上的应用即可

在这期内容结束之前,我们再一起看一题课后题吧(好久没帮你们写作业了)

Chap 9 结构 课后作业

L2-2 通讯录排序

输入n个朋友的信息,包括姓名、生日、电话号码,本题要求编写程序,按照年龄从大到小的顺序依次输出通讯录。题目保证所有人的生日均不相同。

输入格式:

输入第一行给出正整数n(<10)。随后n行,每行按照“姓名 生日 电话号码”的格式给出一位朋友的信息,其中“姓名”是长度不超过10的英文字母组成的字符串,“生日”是yyyymmdd格式的日期,“电话号码”是不超过17位的数字及+-组成的字符串。

输出格式:

按照年龄从大到小输出朋友的信息,格式同输出。

输入样例:

3
zhang 19850403 13912345678
wang 19821020 +86-0571-88018448
qian 19840619 13609876543

输出样例:

wang 19821020 +86-0571-88018448
qian 19840619 13609876543
zhang 19850403 13912345678

在看代码之前,强调一下:这题用普通的数组是没办法写的,因为排序会打乱原本多个数组的结构

除非你再存一个数组,存下标,这里就不掩饰这种写法了,太麻烦 (学有余力的同学可以写一下,想了解的也可以私信我)

 代码如下:

#include <stdio.h>
#include <stdlib.h>     //qsort的头文件

struct person {
    char name[100];    //原本可以设置小一点,但是懒得看题目就可以设置大一点
    char bir[100];
    char phone[100];
};

int n;

// 对比函数
int cmp(const void *a, const void *b) {    //因为需要更换数组内的数据,所以要写指针
    const struct person *p = (const struct person *)a;
    const struct person *q = (const struct person *)b;
    return strcmp(p->bir, q->bir);    //顺便复习一下上上一篇博客的字符串比较
}

int main() {
    scanf("%d", &n);
    struct person p[100];
    for (int i = 0; i < n; i++) {
        scanf("%s %s %s", p[i].name, p[i].bir, p[i].phone);
    }
    qsort(p, n, sizeof(struct person), cmp);
    //qsort()的用法, 我用中文给你们写一下
    //qsort(要排序的数组, 数组的长度, 数组内部变量的大小, 排序函数)
    for (int i = 0; i < n; i++) {
        printf("%s %s %s\n", p[i].name, p[i].bir, p[i].phone);
    }
    return 0;
}

2. 其主要与指针一起应用于这个学期期末你们要学习的链表及下个学期你们要学习的二叉树(难度很高)

链表没准就是下一篇博客的内容啦,那我们下周再见

Bye~

...全文
107 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

212

社区成员

发帖
与我相关
我的任务
社区描述
程序设计基础课程教学群
c语言c++ 高校 福建省·厦门市
社区管理员
  • xmzq001
  • 鹿饮涧鸣
  • jiangxiaoju
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

请加入学习社区的软件23级同学修改社区昵称为学号+姓名,以便登记作业提交情况。

试试用AI创作助手写篇文章吧