learn_C_deep_10 extern在多文件下的理解、struct 关键字的理解与柔性数组、union 的内存级布局理解、enum 关键字的基本理解、typedef 的理解与分类、关键字总结

简介: learn_C_deep_10 extern在多文件下的理解、struct 关键字的理解与柔性数组、union 的内存级布局理解、enum 关键字的基本理解、typedef 的理解与分类、关键字总结

extern 在多文件下的理解与使用


       extern 是一个关键字,用于在程序中声明外部变量或函数。

变量的声明通常包含变量的类型和名称,但是如果变量定义在一个源文件中,而其它源文件也需要访问这个变量,则需要在其它源文件中声明该变量。此时可以使用 extern 关键字来告诉编译器,该变量已经在其它文件中定义好了,不需要再重新分配存储空间,只需要使用它即可。


例如:

在编译 file2.c 文件时,由于该文件中使用了 extern 关键字声明了变量 x,所以编译器不会分配新的存储空间,而是使用 file1.c 中已经定义好的变量 x。 函数的情况类似,如果一个函数定义在一个源文件中,而其它源文件也需要调用该函数,则需要使用 extern 关键字来声明该函数。


例如:

在编译 main.c 文件时,由于该文件中包含了 func.h 头文件,其中使用了 extern 关键字声明了函数 func,所以编译器知道该函数已经在其它源文件中定义好了,可以直接在链接时链接函数即可。


struct 关键字的理解与柔性数组


       在C语言中,struct是一个关键字,用于定义结构体。结构体是由多个不同类型的数据组成的集合,可以将它们组合成一个自定义的数据类型。结构体可以包含不同的数据类型,如整型、浮点型、字符型、数组以及其他结构体等,它们可以组合成复杂的数据结构。


例如学生:

 这段代码定义了一个包含姓名、年龄、性别和体重的学生结构体Stu,并在main函数中定义了一个Stu类型的变量s,并为其赋值。接着定义了一个指向Stu结构体的指针p,并将其指向变量s的内存地址。最后,通过指针p访问结构体中的成员变量,并将它们打印出来,分别使用了结构体指针的两种访问方式:(*p).name 和 p->name,它们的作用是一样的。


空结构体的大小



这段代码中定义了一个名为S的结构体,但是结构体里面没有任何成员。在代码中调用了sizeof操作符,对结构体S进行计算其大小的操作,但vs编译器在编译这段代码时会报错。根据C语言的规定,定义一个结构体或者联合体时,至少需要包含一个成员,否则这个结构体是无效的。


  这段代码中定义了一个名为S的结构体,但是结构体里面没有任何成员。在代码中调用了sizeof操作符,对结构体S进行计算其大小的操作,但gcc编译器在编译这段代码时不会报错,求得大小为0,并且可以定义变量(可以定义空间大小为0的变量),但是没有空间,不能进行初始化。


柔性数组


#include<stdio.h>
#include<stdlib.h>
struct S
{
  int num;
  int arr[];
};
int main()
{
  printf("%d\n", sizeof(struct S));//柔性数组不占用空间
  struct S* p = malloc(sizeof(struct S) + sizeof(int) * 10);
  int i = 0;
  p->num = 10;
  for (i = 0; i < p->num; i++)
  {
    p->arr[i] = i;
  }
  free(p);
  p = NULL;
  return 0;
}


union 的内存级布局理解


 在C语言中,union是一种特殊的数据类型,它允许您在同一内存位置存储不同的数据类型。它类似于结构体,但是它的所有成员共享同一内存空间,因此只能够保存其中一个成员的值。 定义一个union变量的语法与结构体相似,


例如:


这个union类型变量un可以保存整数int、字符char。但是,不能同时保存这两种类型,因为它们共享同一个内存空间。 Union的大小由其最大成员的大小确定,例如上面的例子中,un变量的大小是4字节,因为整形类型的成员是这个联合体中最大的。


基本认识与补充认识



union的内存布局



大小端对于union的影响



小练习

//联合体内部的成员起始地址都是一样的!!!
#include<stdio.h>
union un {
  int a;
  char c[4];
}*p,u;//全局变量*p、u
int main()
{
  p = &u;
  p->c[0] = 0x39;
  p->c[1] = 0x38;
  p->c[2] = 0x37;
  p->c[3] = 0x36;
  printf("0x%x\n", p->a);
  return 0;
}


enum 关键字的基本理解



enum 与 #define 的区别



typedef 的理解与分类



typedef 与 #define 的区别


1. 定义方式不同

       - typedef 是一种类型定义,用于给已有类型取一个新的名字,是在编译期间解析。

- #define 是一种预处理编译指令,用于替换代码中的文本宏,是在预处理阶段解析,即在编译器处理代码之前。


2. 定义的作用域不同

       - typedef 定义的作用域是局部的,只在当前定义的作用域内有效。

       - #define 定义的作用域是全局的,从定义位置开始到文件末尾都有效。


3. 可读性和维护性不同

       - typedef 的定义会产生一个新的类型名,使得代码更容易理解和维护。

       - #define 定义的宏不会改变原有的类型或者结构体,容易引起混淆和错误。


4. 可以对已有类型(包括自定义类型)进行重定义

       - 使用 typedef 语句,可以为 C 语言中已有的类型定义一个新的名字。

       - #define 指令无法直接对 C 语言中已有的类型进行重定义。


总的来说,typedef 适用于定义新的数据类型或别名,而 `#define` 适用于定义常量和函数宏等。

//问题1:
typedef int * ptr_t;
ptr_t p1,p2;
问:p1,p2分别是什么类型
//问题2:
#define PTR_T int*
PTR_T p1, p2;
问:p1,p2分别是什么类型


问题1:p1和p2分别是指向int类型的指针变量。由于typedef int * ptr_t; 定义了一个名为ptr_t的类型,它是指向int类型的指针类型,因此ptr_t p1,p2; 相当于 int* p1, p2; 即p1和p2都是指向int类型的指针变量。

问题2中,#define PTR_T int* 定义了一个名为PTR_T的宏,将int*替换为PTR_T。因此 PTR_T p1, p2; 相当于 int* p1, p2; 这里,p1是指向int类型的指针变量,而p2是int类型的普通变量。但是这种定义方式可能会带来理解上的混淆,因为它让人很难区分p1和p2的类型,不建议使用。正确的做法是使用typedef类型定义来明确变量的数据类型。

#include <stdio.h>
#define INT32 int
typedef int int32;
int main()
{
  //unsigned int32 a = 10; C中typedef不支持这种类型的扩展,不能当成简单的宏替换
  unsigned INT32 a = 20; //宏简单替换,可以
  return 0;
}

   在typedef中,INT32类型仅被视为一种新的数据类型,C中typedef不支持这种类型的扩展,不能当成简单的宏替换,而在宏定义中,INT32被视为简单的字符替换。


关键字总结


数据类型关键字(12个)



控制语句关键字(12个)



存储类型关键字(5个)



注意:存储关键字,不可以同时出现,也就是说,在一个变量定义的时候,只能有一个。


其他关键字(3个)



现在我们来看看这个有没有问题

typedef static int s_int;


error C2159: 指定了一个以上的存储类,因为static和typedef都是存储类型关键字,而存储类型关键字不可以同时出现,所以不能这样写代码。

相关文章
|
10天前
|
存储 安全 编译器
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
12 0
【C语言】动态内存管理 -- -- 深入了解malloc、calloc、realloc、free、柔性数组(万字深入了解)
|
13天前
|
测试技术 定位技术 开发工具
内存泄漏分类
内存泄漏分类
23 2
|
14天前
|
程序员 编译器 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
23 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(下)
|
14天前
|
C语言 C++
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(中)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
20 0
|
14天前
|
编译器 数据库 C语言
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free
23 0
C语言进阶⑰(动态内存管理)四个动态内存函数+动态通讯录+柔性数组_malloc+free(上)
|
19天前
|
存储 缓存 Java
释放C盘空间:释放Windows休眠文件和关闭虚拟内存
在 Windows 11 专业版中,可以通过以下步骤来释放休眠文件(Hibernate File),以释放磁盘空间。休眠文件是系统休眠(Hibernate)功能所需要的文件,它保存了系统的当前状态,以便在休眠状态下恢复。如果你不使用休眠功能,如果因为C盘空间不足,可以考虑释放这个文件来腾出磁盘空间。
6382 0
|
19天前
|
缓存 Java 编译器
JMM内存模型 volatile关键字解析
JMM内存模型 volatile关键字解析
17 0
|
19天前
|
存储 机器学习/深度学习 Java
【Java探索之旅】数组使用 初探JVM内存布局
【Java探索之旅】数组使用 初探JVM内存布局
31 0
|
19天前
|
Windows
虚拟机内存越用越少,即使文件都永久删除了!!!
虚拟机内存越用越少,即使文件都永久删除了!!!
|
19天前
|
人工智能 算法 BI
【经典问题】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?
【1月更文挑战第26天】【经典问题】给两个文件,分别有100亿个整数,我们只有1G内存,如何找到两个文件交集?