《带你学C带你飞学习笔记》—— SE31局部变量和全局变量

知识点回顾

  1. 局部变量

在开始讲解函数之前,我们所理解的变量就是在内存中开辟一个存储数据的位置,并给它起个名字。因为那时候只有一个 main 函数,所以那时我们对它的作用范围一无所知,觉得定义了变量就随时随地可以使用它……直到我们学习函数才发现,不同函数之间定义的变量,它们是无法相互进行访问的。

  1. C99 新标准

C99 标准允许在 for 语句的第一个表达式部分声明变量,它的作用范围仅限于复合语句的内部。

值得一提的是,这里 for 语句因为定义了同名的i变量,所以它屏蔽了第一个定义的i变量。换句话说,在 for 语句的循环体中,无法直接访问到外边的i变量。

  1. C 语言允许在程序的任意位置声明变量

允许变量在需要时才声明是一件非常棒的事情,因为如果当你的函数体非常庞大的时候,你总不会愿意往前翻好几个屏幕去看某个变量 i、j、k 是什么意思吧。

有些朋友可能会有所担心:“要是到处定义变量,那一不小心重复定义了怎么办?”,其实这是编译器应该关心的问题(编译器会找出重复定义的变量并报错),你放心使用就可以了。

  1. 全局变量

在函数里边定义的,我们叫局部变量;在函数外边定义的,我们叫外部变量,也叫全局变量。有时候,我们可能需要在多个函数中使用共同的一个变量,那么就会用到全局变量。因为全局变量可以被本程序中其他函数所共用的。

  1. 全局变量初始化

与局部变量不同,如果不对全局变量进行初始化,那么它会自动初始化为 0。

  1. 当局部变量和全局变量同名时

如果在函数的内部存在一个与全局变量同名的局部变量,编译器并不会报错,而是再函数中屏蔽全局变量(也就是说在这个函数中,全局变量不起作用)。

  1. 如果一个全局变量在使用之后才被定义

那么你可以在对该全局变量进行访问之前,使用 extern 关键字对该变量名进行修饰。这样就相当于告诉编译器:这个变量我在后边定义了,你先别急着报错。

测试题

0. 为什么不建议大量使用全局变量?
答:因为大量使用全局变量会对程序结构产生不良的影响,而且可能导致程序中各个函数之间具有太多的数据联系(在模块化程序设计的指导下,我们应该尽量设计内聚性强,耦合性弱的模块。也就是要求你函数的功能要尽量单一,与其他函数的相互影响尽可能地少,而大量使用全局变量恰好背道而驰)。

1.请问在什么情况下变量 a 和变量 b 可以存放在同一个地址而里面的数据却允许不同?
答:当变量 a 和变量 b 分别为两个函数的局部变量时,就可能出现上述情况。

#include <stdio.h>

int func1(void);
int func2(void);

int func1(void)
{
        int a = 520;

        printf("a is %d, add of a is %p\n", a, &a);
}

int func2(void)
{
        int b = 880;

        printf("b is %d, add of b is %p\n", b, &b);
}

int main(void)
{
        func1();
        func2();

        return 0;
}

2.“C 语言是变量在需要的时候才进行定义的”。那如果一个全局变量在被访问后才定义,便会报错……请问如何阻止悲剧的发生?
第一,将所有的全局变量都写在文件的起始位置(这招仅适用于单源文件程序);
第二,用 extern 关键字对后边定义的全局变量进行修饰。这样就相当于告诉编译器:这个变量我在后边定义了,你先别急着报错。

3. 请问下面代码依次会打印什么内容?

#include <stdio.h>

int i = 520;

int func(void);

int func(void)
{
        printf("%d, ", i);

        int i = 880;

        for (int i = 1; i < 3; i++)
        {
                printf("%d, ", i);
        }

        printf("%d\n", i);
}

int main(void)
{
        func();

        return 0;
}

答:依次会输出 520, 1, 2, 880。
解析:不同地方声明的变量拥有不同的作用域,通常来说内层的变量作用域会覆盖外层的同名变量。

4. 请问下面代码有什么毛病?

#include <stdio.h>

int main(void)
{
        char *name;

        printf("What's your name:");
        scanf("%s", name);
        printf("Your name is %s\n", name);

        return 0;
}

答:虽然程序声明了一个字符指针变量(指望它来存放字符串),但是并没有对其(name)进行初始化,这样也就意味着 name 可能是指向任何地方的,那么它就是一个悬空指针。前面我们强调过,使用悬空指针是极其危险的,上面代码就是一个很好的例子:虽然程序可以运行,但当你输入名字之后,程序立即崩溃(段错误)。改成char *name[1024]即可。

5.请列举全局变量的优点
第一,全局变量的使用,使得多个函数之间的“通信”变得简单。比如课堂中小郭被“抱”的故事,如果不使用全局变量,那么要统计小郭被“抱”了多少次就要麻烦许多。
第二,全局变量一旦声明,内存地址就固定了,程序的读写效率高。如果使用局部变量,每次函数调用都要经历开辟栈空间,和释放栈空间的过程。

动手(不求甚解)

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

void shuffle(int array[], int length);
void deal(int array[], int player[3][14]);
void show(char names[3][40], int player[3][14]);

void shuffle(int array[], int length)
{
        int index, temp, i;

        srand(time(NULL));
        for (i = 0; i < length; i++)
        {
                index = rand() % (length - i) + i;
                if (index != i)
                {
                        temp = array[i];
                        array[i] = array[index];
                        array[index] = temp;
                }
        }
}

void deal(int array[], int player[3][14])
{
        int i, j, k = 0;

        // 模拟发牌:拿起一副无序的扑克每人轮流发一张牌
        for (i = 0; i < 14; i++)
        {
                for (j = 0; j < 3; j++)
                {
                        player[j][i] = array[k++];
                }
        }
}

void show(char names[3][40], int player[3][14])
{
        int i, j, poker;

        printf("\n方=方角,梅=梅花,红=红桃,黑=黑桃\n\n");

        for (i = 0; i < 3; i++)
        {
                printf("%s手上的牌是:", names[i]);
                for (j = 0; j < 14; j++)
                {
                       poker = player[i][j];

                       if (poker < 11 && 0 < poker)
                       {
                               printf("方%d ", poker);
                       }
                       else if (poker < 21 && 10 < poker)
                       {
                               printf("梅%d ", poker-10);
                       }
                       else if (poker < 31 && 20 < poker)
                       {
                               printf("红%d ", poker-20);
                       }
                       else if (poker < 41 && 30 < poker)
                       {
                               printf("黑%d ", poker-30);
                       }
                       else
                       {
                               switch (poker)
                               {
                                       case 41: printf("方J "); break;
                                       case 42: printf("方Q "); break;
                                       case 43: printf("方K "); break;
                                       case 44: printf("梅J "); break;
                                       case 45: printf("梅Q "); break;
                                       case 46: printf("梅K "); break;
                                       case 47: printf("红J "); break;
                                       case 48: printf("红Q "); break;
                                       case 49: printf("红K "); break;
                                       case 50: printf("黑J "); break;
                                       case 51: printf("黑Q "); break;
                                       case 52: printf("黑K "); break;
                                       case 53: printf("小王 "); break;
                                       case 54: printf("大王 "); break;
                               }
                       }

                }
                printf("\n\n");
        }

}

int main(void)
{
        int array[54];
        int player[3][14];
        int i, ch;
        char names[3][40];

        // 初始化扑克牌
        // 1~10代表方(角)1~10, 41、42、43代表方(角)J、Q、K 
        // 11~20代表梅(花)1~10, 44、45、46代表梅(花)J、Q、K 
        // 21~30代表红(桃)1~10, 47、48、49代表红(桃)J、Q、K 
        // 31~40代表黑(桃)1~10, 50、51、52代表黑(桃)J、Q、K 
        // 53、54当然就代表小王大王啦~
        for (i = 0; i < 54; i++)
        {
                array[i] = i + 1;
        }

        for (i = 0; i < 3; i++)
        {
                printf("\n请输入%d号玩家的名字:", i+1);
                scanf("%s", names[i]);
        }

        do
        {
                shuffle(array, 54);
                deal(array, player);
                show(names, player);

                printf("重新洗牌(Y/N)?");
                do
                {
                        ch = getchar(); // 过滤输入缓冲区的其他字符
                } while (ch != 'Y' && ch != 'N');
        } while (ch == 'Y');

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值