关键字 - 第一讲

发布于:2025-08-05 ⋅ 阅读:(15) ⋅ 点赞:(0)

关键字深度剖析

  • 内容方面:这本书并不是一本好书,因为书中存在很多错误。

  • 编写思路:从编写思路上来看,它是一本好书。它用最少的篇幅,能够明确地涵盖C语言中的大部分重点内容。

  • 学习建议:要学完这本书,需要做到以下几点:

    1. 找到书中所有的错误。

    2. 补充大量的内容。

    3. 通过读书,系统地学习相关知识。


关键字 - 第一讲

目标

  • 初步了解关键字的分类。

  • 深刻理解变量的概念。

  • 深刻理解定义与声明的区别。

  • 理解auto关键字。

  • 从存储结构角度理解register关键字。


关键字分类

C语言一共有32个关键字(C90/C89标准),C99新增了5个关键字,但目前主流编译器对C99支持不好,因此默认使用C90标准,即32个关键字。

关键字 说明
auto 声明自动变量
short 声明短整型变量或函数
int 声明整型变量或函数
long 声明长整型变量或函数
float 声明浮点型变量或函数
double 声明双精度变量或函数
char 声明字符型变量或函数
struct 声明结构体变量或函数
union 声明共用数据类型
enum 声明枚举类型
typedef 用以给数据类型取别名
const 声明只读变量
unsigned 声明无符号类型变量或函数
signed 声明有符号类型变量或函数
extern 声明变量是在其他文件中声明的
register 声明寄存器变量
static 声明静态变量
volatile 说明变量在程序执行中可被隐含地改变
void 声明函数无返回值或无参数,声明无类型指针
if 条件语句
else 条件语句否定分支(与if连用)
switch 用于开关语句
case 开关语句分支
for 一种循环语句
do 循环语句的循环体
while 循环语句的循环条件
goto 无条件跳转语句
continue 结束当前循环,开始下一轮循环
break 跳出当前循环
default 开关语句中的“其他”分支
sizeof 计算数据类型长度
return 子程序返回语句(可以带参数,也可不带参数)

第一个C程序 - 补充内容

//1. vs2022中建立项目
//2. 编写第一个C程序"hello world"
#include <stdio.h>
#include <windows.h> //windows.h系统头文件,仅仅是为了停屏
int main()
{
printf("hello world!\n");
system("pause"); //pause停屏,暂时不讲,会用就行
return 0;
}
//运行程序的方式,当然可以用vs直接启动
//当然,也可以在vs项目中,找到代码生成的二进制可执行程序,双击即可。
//所以:我们的角色是写代码,编译器的角色是把文本代码变成二进制可执行程序。
//双击?不就是windows下启动程序的做法吗?
//那么启动程序的本质是什么呢?将程序数据,加载到内存中,让计算机运行!
//那么为什么要加载到内存中呢?这个在Linux专题有详细的说明

我们将文本代码转化成为可执行程序,可执行程序就是二进制文件,在Windows当中我们是可以通过双击来启动程序的!其本质就是将程序加载到内存当中!任何程序在运行之前都要从硬盘加载到内存当中!

加载到内存中是由冯诺依曼体结构过决定的! 

因为CPU访问内存超级快,所以程序/数据要加载到内存中!!!

  • 局部变量:包含在代码块中的变量,具有临时性。进入代码块时自动形成,退出代码块时自动释放。

  • 全局变量:在所有函数外定义的变量,具有全局性,整个程序运行期间都有效。

代码块:用{}括起来的区域称为代码块。

#include <stdio.h>
#include <windows.h>

int g_x = 100; // 全局变量

int main()
{
    int x = 10; // 局部变量
    printf("x:%d\n", x);
    system("pause");
    return 0;
}

定义与声明 - 补充内容

什么是变量(是什么)

在内存中开辟特定大小的空间,用来保存数据

关键字:内存

如何定义变量(怎么用)

int x = 10;
char c = 'a';
double d = 3.14;

类型 变量名 = 默认值

为什么要定义变量(为什么)

计算机是为了解决人计算能力不足的问题而诞生的。即,计算机是为了进行计算的。而计算,就需要数据。而要计算,任何一个时刻,不是所有的数据都要立即被计算。如同:要吃饭,不是所有的饭菜都要立即被你吃掉。饭要一口一口吃,那么你还没有吃到的饭菜,就需要暂时放在盘子里。这里的盘子,就如同变量,饭菜如同变量里面的数据。

换句话说,为何需要变量?因为有数据需要暂时被保存起来,等待后续处理。

那么,为什么吃饭要盘子?我想吃一口菜了,直接去锅里找不行吗?当然行,但是效率低。因为我们吃饭的地方,和做饭的地方,是比较"远"的。

变量定义的本质

我们现在已知:

  1. 程序运行,需要加载到内存中

  2. 程序计算,需要使用变量

那么,定义变量的本质:在内存中开辟一块空间,用来保存数据。(为何一定是内存:因为定义变量,也是程序逻辑的一部分,程序已经被加载到内存)

变量声明的本质

声明就是一种告知,告知这个变量是在某个地方已经定义好了,等后期编译链接就可以用到了。代码我们暂时不写,等到后面讲extern的时候,在细谈。

定义只能定义一次!

声名是可以多次的!


1.1 最宽宏大量的关键字 - auto

变量的分类 - 补充内容

局部变量:包含在代码块中的变量叫做局部变量。局部变量具有临时性。进入代码块,自动形成局部变量,退出代码块自动释放。[网上很多说函数中的变量是局部变量,不能说错,但说法是不准确的]

全局变量:在所有函数外定义的变量,叫做全局变量。全局变量具有全局性。

代码块:用{}括起来的区域,就叫做代码块

#include <stdio.h>
#include <windows.h>
int g_x = 100; //全局变量

int main()
{
    int x = 10; //局部变量,main函数也是函数,也有代码块{}
    printf("x:%d\n", x);

    system("pause");
    return 0;
}

变量的作用域 - 补充内容

作用域概念:指的是该变量的可以被正常访问的代码区域

#include <stdio.h>
#include <windows.h>

int main()
{
    int x = 10;
    if (x == 10){
        int y = 20;
        printf("局部: x: %d, y: %d\n", x, y);//只能在本代码块内有效
    }
    printf("局部: x: %d, y: %d\n", x, y);//报错,y不能被访问
    system("pause");
    return 0;
}

局部变量:只在本代码块内有效!!!

全局变量:整个程序运行期间,都有效!

#include <stdio.h>
#include <windows.h>

int g_x = 100; //全局变量

void show()
{
    printf("show: 全局: %d\n", g_x); //在任何代码块中都可以被访问
}
int main()
{
    show();
    printf("main: 全局: %d\n", g_x); //在任何代码块中都可以被访问,甚至被修改
    system("pause");
    return 0;
}
//

#include <stdio.h>
#include <windows.h>

int g_x = 100; //全局变量

int main()
{
    int g_x = 10; //局部变量,与全局同名
    printf("g_x:%d\n", g_x); //输出的是局部,也就是局部和全部同名的时候,优先局部。所以,强烈不建议这样干。
    system("pause");
    return 0;
}

变量的生命周期 - 补充内容

生命周期概念指的是该变量从定义到被释放的时间范围,所谓的释放,指的是曾经开辟的空间"被释放"

局部变量:进入代码块,形成局部变量[开辟空间],退出代码块,"释放"局部变量

全局变量:定义完成之后,程序运行的整个生命周期内,该变量一直都有效

通过上面的例子,我们能感知一二,不过真正说清楚,需要等函数讲完,在谈一下。尽管,上面理解也没问题。

作用域 vs 生命周期

深刻理解两者的区别:

作用域(Scope)指的是变量、函数或标识符在代码中的可见性和可访问性范围。

生命周期(Lifetime)指的是变量或对象从创建到销毁的整个存在时间。

特性 作用域(Scope) 生命周期(Lifetime)
关注点 空间维度:代码的哪些部分可以访问 时间维度:变量存在的时间段
决定因素 代码结构(如函数、块) 存储类别(如自动、静态、动态)
主要问题 "在哪里可以访问这个变量?" "这个变量何时创建和销毁?"

auto 相关

如何使用:一般在代码块中定义的变量,即局部变量,默认都是auto修饰的,不过一般省略

默认的所有变量都是auto吗?不是,一般用来修饰局部变量

中断一下:后面我们听到的,局部变量,自动变量,临时变量,都是一回事。我们称局部变量

#include <stdio.h>
#include <windows.h>

int main()
{
    for (int i = 0; i < 10; i++) {
        printf("i=%d\n", i);
        if(1)
        {
            auto int j = 0; //自动变量
            printf("before: j=%d\n", j);
            j += 1;
            printf("after : j=%d\n", j);
        }
    }
    system("pause");
    return 0;
}

思考:用auto修饰可以吗?去掉j的auto可以吗?

结论:完全可以,已经很多基本永不使用。

C语言中的auto关键字

含义

  • 表示"自动存储期"的变量(默认的存储类别)

  • 是C语言四种存储类别说明符之一(autoregisterstaticextern

特点

  • 默认情况下,函数内定义的变量都是auto

  • 极少显式使用,因为不加存储类别说明符的局部变量默认就是auto

  • 变量生命周期限于其所在的作用域(函数/块执行期间)

void func() {
    auto int x = 5;  // 显式使用auto(完全等同于不加auto)
    int y = 10;      // 隐式auto
}

这个和我们的C++11的auto不同哦:C++11的auto

  • 类型推导:让编译器根据初始化表达式自动推导变量类型

  • 完全改变了原有auto关键字的含义


1.2 最快的关键字 - register

其实,CPU主要是负责进行计算的硬件单元,但是为了方便运算,一般第一步需要先把数据从内存读取到CPU内,那么也就需要CPU具有一定的数据临时存储能力。注意:CPU并不是当前要计算了,才把特定数据读到CPU里面,那样太慢了。

所以现代CPU内,都集成了一组叫做寄存器的硬件,用来做临时数据的保存。

存储金字塔

全部集成在现代CPU内部

距离CPU越近的存储硬件,速度越快。

寄存器的认识

当前,各位同志可以不关系硬件细节,只要知道CPU内集成了一组存储硬件即可,这组硬件叫做寄存器。

寄存器存在的本质

在硬件层面上,提高计算机的运算效率。因为不需要从内存里读取数据啦。

register 修饰变量

尽量将所修饰变量,放入CPU寄存区中,从而达到提高效率的目的

那么什么样的变量,可以采用register呢?

  1. 局部的(全局会导致CPU寄存器被长时间占用)

  2. 不会被写入的(写入就需要写回内存,后续还要读取检测的话,register的意义在哪呢?)

  3. 高频被读取的(提高效率所在)

  4. 如果要使用,请不要大量使用,因为寄存器数量有限

这里除了上面的,再有一点,就是register修饰的变量,不能取地址(因为已经放在寄存区中了嘛,地址是内存相关的概念)

#include <stdio.h>
#include <windows.h>

int main()
{
    register int a = 0;
    //a = 200;//可以被写入,但是register的意义就没了!
    printf("%a = %p\n", &a);
    //编译器报错:错误 1 error C2103:寄存器变量上的"&"
    //注意,这里不是所有的编译器都报错,目前我们的vs2013是报错的。
    //鉴于有些同志刚开始学,不给大家增加负担,我们后面再慢慢引入另一个编译器。
    system("pause");
    return 0;
}

我的意见:该关键字,不用管,因为现在的编译器,已经很智能了,能够进行比人更好的代码优化。早期编译器需要人为指定register,来进行手动优化,现在不需要了。


网站公告

今日签到

点亮在社区的每一天
去签到