初步认识 C语言(下)

简介: 初步认识 C语言(下)

十五、关键字



auto  break  case  char  const  continue  default  do  double 
else  enum  extern  float  for  goto  if  int  long  register  
return  short  signed   sizeof  static  struct  switch  typedef
union  unsigned  void  volatile  while


1. typedef 关键字


typedef 关键字的作用: 可以将类型重命名。


#include <stdio.h>
// 将 unsigned int 类型重命名为 u_int
typedef unsigned int u_int;
int main()
{
  unsigned int a = 0;
  u_int b = 0;
  // a 和 b 的类型是等价的
  return 0;
}


2. register 关键字


register 关键字: register 关键字主要与寄存器有关。


下图是计算机存储数据常见的结构,其中寄存器就是直接集成在 CPU 上的,在过去的时代中,CPU 拿放数据都是与内存进行沟通;随着硬件的发展,CPU 的处理速度越来越快,而现在,由于寄存器的读写速度更快,CPU 可以直接对寄存器进行读写。如果 CPU 想要新的数据,那么内存再逐级往上替换,先替换缓存中的数据,再由缓存中的数据替换掉寄存器中的数据,这样一来,寄存器的数据可以一直保持最新的。


所以,寄存器的作用主要就是提高了 CPU 的处理速度。


500f715c62284ab498b65eb58f6ce5a6.png

鉴于此,C语言 就为我们提供了 register 关键字,可以让我们将变量直接放入寄存器中。但虽说提供了此关键字,但到最后是由编译器决定是否真正地放入寄存器中,因为寄存器的容量非常小,不可能随心所欲。


此外,寄存器变量不能使用 & 取地址,因为涉及到地址,只有内存中才会有。


register int a = 10;


3. static 关键字


(1) static 修饰局部变量


程序清单:


#include <stdio.h>
void test()
{
  int n = 1;
  printf("%d ", ++n);
}
int main()
{
  for (int i = 0; i < 10; i++)
  {
    test();
  }
  return 0;
}


输出结果:


baec9b91acc1405cac68a1624993c756.png


程序清单:


#include <stdio.h>
void test()
{
  static int n = 1; // 将局部变量设置成 static 
  printf("%d ", ++n);
}
int main()
{
  for (int i = 0; i < 10; i++)
  {
    test();
  }
  return 0;
}


输出结果:


7b4932961dbd49af937cbfc2d56a3775.png


总结:


对比上面的两个程序,得出总结:


当 static 关键字修饰一个局部变量的时候,就改变了局部变量的存储类型。原本一个局部变量是存储在栈区的,但被 static 修饰后,它就存储在了静态区。所以,被 static 修饰的局部变量,它的生命周期更长,可以被视为整个工程的生命周期。


94213f39de4c4a5f919bc4a6c6df91b7.png


也就是说,被 static 关键字修饰的局部变量,它出了局部作用域的范围不再被销毁,但它的作用域范围没有被改变。


分析代码: 局部变量 n 的作用域依然是 test 函数的内部,但 n 经初始化,并出了作用域后,不会被销毁。(下面的第三行初始化代码只用了一次)

void test()
{
  static int n = 1; 
  printf("%d ", ++n);
}


(2) static 修饰全局变量


程序清单:


06f5ac63c80f4723b3c0b2c2e85600cd.png


程序清单:


05bd42a1e46240ddb7ebb8672fc9220e.png


总结:


对比上面的两个程序,得出总结:


① 全局变量本身就具有外部链接属性,当一个 .c 文件用到了另一个 .c 文件的局部变量时,只需要在前者加上 extern 关键字声明即可。


② 而 static 修饰全局变量时,就将全局变量原本的外部链接属性变成了内部链接属性,所以后来被 static 修饰的全局变量只能在本 .c 文件中使用。


③ 综上所述,static 修饰全局变量的使用场景就是,在一个工程中,你防止一个 .c 文件篡改了另一个 .c 文件的全局变量数据。但实际上,全局变量用的并不多,因为使用它时,本身就会带来使用变量作用域混乱的问题。


d036cdaa53e141cfb741d17a31de90bf.png


(3) static 修饰函数


static 修饰函数和 static 修饰全局变量的思想差不多。当一个函数被 static 修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用,即使两个源文件属于同一个工程也不行。


4800056e5aed4668a6c5f22ffc47ebb3.png


十六、指针



1. 指针与内存


指针就是地址,有了地址,就能帮助我们快速地找到一块内存空间。


#include <stdio.h>
int main() 
{
  int a = 10;
  int* pa = &a; // 取出 a 的地址赋值给指针变量 pa
  *pa = 20; // *pa == a
  printf("%d\n", a);
  return 0;
}


输出结果:


252472ffdf4548459c8a2bcc7cd32e6c.png


在上面的程序中,


&a 表示取出 int变量 a 的地址 (取出的是 变量a 的第一个字节地址);

*pa 表示解引用 指针变量 pa,*pa 就等价于 a.

如下图所示:(假设虚拟地址空间为 32位)


ebb139b4d4e041689ea92ca7e4a8531a.png


注意事项:


内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。为了能够有效访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。在 C语言中,每创建一个变量就会在底层开辟地址。


① 内存会被划分为小的内存单元,一个内存单元的大小是1个字节。


② 每个内存单元都有编号,这个编号也被称为:地址 / 指针。


③ 地址 / 指针可以存放在一个变量中, 这个变量称为指针变量,指针变量也是一个变量,它也有自己的地址。


④ 通过指针变量中存储的地址,就能找到指针指向的空间。


2. 指针变量的大小


程序清单:


#include <stdio.h>
int main() 
{
  int a = 10;
  char ch = 'a';
  double d = 3.14;
  int* pa = &a;
  char* pc = &ch;
  double* pd = &d;
  printf("%d\n", sizeof(pa));
  printf("%d\n", sizeof(pc));
  printf("%d\n", sizeof(pd));
  return 0;
}


输出结果:


25af19de90914e12a7cddab3c5617d53.png


结论:


指针变量是用来存放地址的。所以,地址的存放需要多大空间,指针变量的大小就应该是多大。


① 32位 机器,支持 32位 虚拟地址空间,其产生的地址就是 32位,所以此时指针变量就需要 32位 的空间存储,即 4字节。

② 64位 机器,支持 64位 虚拟地址空间,其产生的地址就是 64位,所以此时指针变量就需要 64位 的空间存储,即 8字节。


十七、结构体



结构体是 C语言 中的自定义类型,它可以用来描述一个包含多种类型的信息。比方说:一名学生的信息 (名字、年龄、学号)


C语言 的结构体和 Java 中的类差不多,我们刚开始创建的结构体的时候,它就相当于是一块空白的模板,我们可以通过这个模板再引申同种类别的东西。例如,下面的 Student 就相当于一个类型,它的地位与 int、char 差不多,只不过 Student 类型是由我们自己自定义的类型。


程序清单1


#include <stdio.h>
struct Student
{
  char name[20];  // 名字
  int age;    // 年龄
  int studentID;  // 学号
};
int main() 
{
  struct Student student1 = {"Jack", 18, 32};  
  struct Student student2 = {"Bruce", 20, 05};
  printf("%s %d %d\n", student1.name, student1.age, student1.studentID);
  struct Student* ps1 = &student1;
  printf("%s %d %d\n", (*ps1).name, (*ps1).age, (*ps1).studentID);
  printf("%s %d %d\n", ps1->name, ps1->age, ps1->studentID);
  return 0;
}


输出结果:


fff0ec4bdc664028a818f640555af473.png


注意事项:


在访问结构体成员时,可以采用 . 或者 -> ,前者对应结构体变量,后者对应结构体指针变量。


程序清单2


#include <stdio.h>
#include <string.h>
struct Student
{
  char name[20];  // 名字
  int age;    // 年龄
  int studentID;  // 学号
};
int main()
{
  struct Student student1 = { "Jack", 18, 32 };
  printf("%s %d %d\n", student1.name, student1.age, student1.studentID);
  //student1.name = "小红"; // error
  student1.age = 17;
  student1.studentID = 25;
  strcpy(student1.name, "小红");
  printf("%s %d %d\n", student1.name, student1.age, student1.studentID);
  return 0;
}


输出结果:


54c7a0c72fc749e7a3721f0803101649.png

注意事项:


上面的程序中,有一处代码是错误的,因为在 C语言 中,没有字符串类型,它并不像 Java 直接就能修改。由于 name 变量本身是一个字符数组,所以下面的代码使用的其实是数组名,即数组首元素地址。所以从类型的角度来看,字符串 " 小红 " 不能直接赋值给一个指针变量,所以编译器就会直接报错。


student1.name = "小红"; // error


十八、C语言 的内存布局



在我们平时写程序时,创建的变量都会在底层开辟内存。C语言 的内存布局分为三大块:栈区、堆区、静态区。


94213f39de4c4a5f919bc4a6c6df91b7.png


备注:栈区是我们平时使用最多的区域。栈区的使用习惯:先使用高地址处的空间,再使用低地址处的空间。

目录
相关文章
|
18天前
|
人工智能 安全 API
近期 AI 领域的新发布所带来的启示
2024 年以来,AI 基础设施的快速发展过程中,PaaS 层的 AI 网关是变化最明显的基建之一。从传统网关的静态规则和简单路由开始,网关的作用被不断拉伸。用户通过使用网关来实现多模型的流量调度、智能路由、Agent 和 MCP 服务管理、AI 治理等,试图让系统更灵活、更可控、更可用。国庆期间 AI 界发布/升级了一些产品,我们在此做一个简报,从中窥探下对 AI 网关演进新方向的启示。
231 27
|
2月前
|
存储 人工智能 自然语言处理
RAG:让AI聊天不再"张口就来"
想让你的AI助手不再一本正经地胡说八道?RAG技术就是那个神奇的'外挂'!通过一个智能客服的真实场景,轻松学会如何让AI既博学又靠谱,告别AI幻觉,拥抱真实世界的知识!
|
2天前
|
人工智能 自然语言处理 前端开发
构建AI智能体:六、体验Trae指定Qwen-Turbo模型自动生成问答系统
本文介绍如何使用字节跳动的AI编程工具Trae与阿里通义千问Qwen-Turbo模型,快速生成一个智能问答系统。通过图文结合方式,演示从环境搭建、指令生成到界面优化的全过程,涵盖前后端代码自动生成、模型调用封装及交互优化技巧,展现AI辅助开发的高效与趣味,助力开发者提升生产力。
85 12
|
1月前
|
机器学习/深度学习 算法 物联网
面向能效和低延迟的语音控制智能家居:离线语音识别与物联网集成方案——论文阅读
本文提出一种面向能效与低延迟的离线语音控制智能家居方案,通过将关键词识别(KWS)集成至终端设备,结合去中心化Mesh网络与CoAP协议,实现本地化语音处理。相较云端方案,系统能耗降低98%,延迟减少75%以上,显著提升响应速度与能源效率,为绿色智能家居提供可行路径。(236字)
178 17
面向能效和低延迟的语音控制智能家居:离线语音识别与物联网集成方案——论文阅读
|
1月前
|
SQL 数据采集 安全
Waf的分类和绕过Waf的方法有哪些?
WAF(Web应用防火墙)是保护Web应用的安全防线,常见类型包括软件型、硬件型、云WAF和自定义WAF。各类WAF各有优劣,云WAF因成本低、易部署广受欢迎。然而,WAF并非绝对安全,攻击者可通过增加负载、构造异常数据包、绕过白名单等方式尝试绕过防护。了解WAF原理与绕过技巧,有助于提升Web安全防护能力。
|
2月前
|
存储 监控 安全
告别版本混乱!同步盘让团队协作不再成为噩梦
同步盘是一种支持多成员、跨设备实时文件同步的企业云存储产品。用户只需在电脑、手机等设备上安装同步盘客户端,所有指定资料便会自动上传至云端,且随时随地同步到任意终端,实现企业高效协同办公,减少了数据的传递和管理时间。
|
5月前
|
运维 Devops 测试技术
云效DevOps:加速企业数字化转型的利器
云效DevOps是阿里巴巴推出的企业级一站式平台,覆盖需求管理到应用运维全生命周期。其核心价值包括全流程自动化、高效协同、质量保障与可视化度量。功能涵盖代码管理、持续集成、持续交付及应用运维,支持微服务架构、大规模团队协作等最佳实践。成功案例显示,云效显著提升企业软件交付效率与质量,助力数字化转型,未来将融合AI技术实现更智能的交付体验。
344 11
|
11月前
|
Prometheus 监控 Cloud Native
系统监控负载
【10月更文挑战第19天】