当前位置:首页 » 编程软件 » 利用编译器原理获取结构体成员

利用编译器原理获取结构体成员

发布时间: 2022-12-30 10:41:01

1. 用c语言编写学生基本信息(最好用结构体)拜托啦,急求!

在C语言中,可以使用结构体(Struct)来存放一组不同类型的数据。结构体的定义形式为:
struct 结构体名{
结构体所包含的变量或数组
};
结构体是一种集合,它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member)。请看下面的一个例子:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
};
stu 为结构体名,它包含了 5 个成员,分别是 name、num、age、group、score。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。
注意大括号后面的分号;不能少,这是一条完整的语句。
结构体也是一种数据类型,它由程序员自己定义,可以包含多个其他类型的数据。

像 int、float、char 等是由C语言本身提供的数据类型,不能再进行分拆,我们称之为基本数据类型;而结构体可以包含多个基本类型的数据,也可以包含其他的结构体,我们将它称为复杂数据类型或构造数据类型。
结构体变量

既然结构体是一种数据类型,那么就可以用它来定义变量。例如:
struct stu stu1, stu2;
定义了两个变量 stu1 和 stu2,它们都是 stu 类型,都由 5 个成员组成。注意关键字struct不能少。

stu 就像一个“模板”,定义出来的变量都具有相同的性质。也可以将结构体比作“图纸”,将结构体变量比作“零件”,根据同一张图纸生产出来的零件的特性都是一样的。

你也可以在定义结构体的同时定义结构体变量:
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
将变量放在结构体定义的最后即可。

如果只需要 stu1、stu2 两个变量,后面不需要再使用结构体名定义其他变量,那么在定义时也可以不给出结构体名,如下所示:
struct{ //没有写 stu
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
这样做书写简单,但是因为没有结构体名,后面就没法用该结构体定义新的变量。

理论上讲结构体的各个成员在内存中是连续存储的,和数组非常类似,例如上面的结构体变量 stu1、stu2 的内存分布如下图所示,共占用 4+4+4+1+4 = 17 个字节。

但是在编译器的具体实现中,各个成员之间可能会存在缝隙,对于 stu1、stu2,成员变量 group 和 score 之间就存在 3 个字节的空白填充(见下图)。这样算来,stu1、stu2 其实占用了 17 + 3 = 20 个字节。

关于成员变量之间存在“裂缝”的原因,我们将在《C语言和内存》专题中的《C语言内存对齐,提高寻址效率》一节中详细讲解。
成员的获取和赋值

结构体和数组类似,也是一组数据的集合,整体使用没有太大的意义。数组使用下标[ ]获取单个元素,结构体使用点号.获取单个成员。获取结构体成员的一般格式为:
结构体变量名.成员名;
通过这种方式可以获取成员的值,也可以给成员赋值:
#include <stdio.h>
int main(){
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1;
//给结构体成员赋值
stu1.name = "Tom";
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
//读取结构体成员的值
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
return 0;
}
运行结果:
Tom的学号是12,年龄是18,在A组,今年的成绩是136.5!

除了可以对成员进行逐一赋值,也可以在定义时整体赋值,例如:
struct{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1, stu2 = { "Tom", 12, 18, 'A', 136.5 };
不过整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。

2. 编译器的工作原理

编译 是从源代码(通常为高级语言)到能直接被计算机或虚拟机执行的目标代码(通常为低级语言或机器语言)的翻译过程。然而,也存在从低级语言到高级语言的编译器,这类编译器中用来从由高级语言生成的低级语言代码重新生成高级语言代码的又被叫做反编译器。也有从一种高级语言生成另一种高级语言的编译器,或者生成一种需要进一步处理的的中间代码的编译器(又叫级联)。
典型的编译器输出是由包含入口点的名字和地址, 以及外部调用(到不在这个目标文件中的函数调用)的机器代码所组成的目标文件。一组目标文件,不必是同一编译器产生,但使用的编译器必需采用同样的输出格式,可以链接在一起并生成可以由用户直接执行的EXE,
所以我们电脑上的文件都是经过编译后的文件。

3. 结构体变量调用成员是是如何寻址的

呵呵,还在研究那。
我把VC6.0编译出的机器码给你看看:

22: Student stu;
004011BD lea ecx,[ebp-20h] //将stu的地址(ebp-20h)放入ecx寄存器
004011C0 call @ILT+55(Student::Student) (0040103c) //调用Student 的构造函数对ecx寄存器所保存地址上的对象进行构造
004011C5 mov dword ptr [ebp-4],0

23: stu.num = 01;
004011CC mov dword ptr [ebp-20h],1 //将1赋值给stu.num

ebp为栈基址指针,也就是函数栈帧底部的内存地址,很明显stu是放在栈帧底部20个字节的内存空间上的,num放在其中最上面的4个字节,也就是stu的首部,而name放在下面的16个字节中。
如果对程序的内部工作机制很感兴趣,就要看调试器列出的汇编代码,什么都能弄懂。

4. C语言结构体成员的引用问题

scanf的用法,格式后的参数都必须是地址,name是数组名,本身就是数据,age和sex都是整型数据,必须使用取地址符&。

p是struct student*类型,stu是stu[3]这个数组的第一个元素的地址,p指向第一个元素,因此ABC都是对的,D错在把(int*)变量值赋值给(struct student*)变量,有个强制转换则不会错,因为age是结构体第一个变量,其地址就是结构体变量的地址。

是指针间的赋值,必须保证左右操作数的指针类型是一致的。由于n是结构体sk的第一个变量,因此其地址与结构体变量地址相同,可以强制转换得到正确的地址。

c是一个二维数组,p是一个指向数组的指针,因此p的*运算都是得到一个数组,**运算才能取到数组中的值。只有D是**运算。

(4)利用编译器原理获取结构体成员扩展阅读:

C语言初学者比较喜欢的Turbo C2.0提供了400多个运行时函数,每个函数都完成特定的功能,用户可随意调用。这些函数总体分成输入输出函数、数学函数、字符串和内存函数、与BIOS和DOS有关的函数、 字符屏幕和图形功能函数、过程控制函数、目录函数等。

Windows系统所提供的Windows SDK中包含了数千个跟Windows应用程序开发相关的函数。其他操作系统,如linux,也同样提供了大量的函数让应用程序开发人员调用。

参考资料来源:网络-C语言

5. 编译原理词法语法语义错误题!!!!!求大神啊!!!!

错误如下

  1. 在main函数中,调用了max函数取三个值中的最大值,而在max函数的声明和定义中都只有两个参数,属于语义错误,应该把参数中加一个“int z”(在max函数的定义中出现了名为c的变量,不能重名,故用z)

  2. 在max函数中,第二行的“c==a>b?a:b”从上下文来看没有任何意义,应该把条件表达式改为赋值表达式(“==”改为“=”),这个部分在语法分析中不会出问题,故属于语义错误(编译器不会报错)

  3. 在max函数的第三行中,额。。我也不知道这个想表达什么,而且点号一般用于获取结构体中的成员,属于后缀表达式,其yacc语法为

    postfix_expression '.' IDENTIFIER
    而在语法和词法分析中都不会出错,属于语义错误,应改为“c=c > z ? c : z;”


这样改动后,max中的局部变量d就没有了什么作用,建议删去

6. 如何用C语言读取txt文件中的数据到结构体数组中

1、在vscode里面添加了Python文件和用于读取的文本文件。

7. 结构体c语言是什么

在C语言中,结构体(struct)指的是一种数据结构,是C语言中复合数据类型(aggregate data type)的一类。

结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问

一、成员访问

结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问。直接访问应用于普通的结构体变量,间接访问应用于指向结构体变量的指针。

直接访问使用结构体变量名.成员名,间接访问使用(*结构体指针名).成员名或者使用结构体指针名->成员名。相同的成员名称依靠不同的变量前缀区分。

二、变量存储

在内存中,编译器按照成员列表顺序分别为每个结构体变量成员分配内存,当存储过程中需要满足边界对齐的要求时,编译器会在成员之间留下额外的内存空间。

如果想确认结构体占多少存储空间,则使用关键字sizeof,如果想得知结构体的某个特定成员在结构体的位置,则使用offsetof宏(定义于stddef.h)。

匿名struct

匿名struct、匿名union以及C++的匿名class,是指既没有类型名,也没有直接用这种类型定义了对象;如果紧随类型定义之后,又定义了该类型的对象,就不算是匿名类型,与普通情形的使用是一样的。

匿名类型作为嵌套定义,即在一个外部类(这里的类是指struct、union、class)的内部定义,则编译器就在匿名类型定义之后定义一个无名变量,并把该匿名类型的数据成员的名字提升到匿名类的外部类的作用域内。

如果匿名类型是连续嵌套,则最内部的匿名类型的成员名字被提升到最外部的可用变量名字访问的类的作用域内。

8. 编译原理的数据结构

编译原理一直是计算机学习的必修课.
当然,由编译器的阶段使用的算法与支持这些阶段的数据结构之间的交互是非常强大的。编译器的编写者尽可能有效实施这些方法且不引起复杂性。理想的情况是:与程序大小成线性比例的时间内编译器,换言之就是,在0 ( n )时间内,n是程序大小的度量(通常是字符数)。本节将讲述一些主要的数据结构,它们是其操作部分阶段所需要的,并用来在阶段中交流信息。 临时文件(temporary file):计算机过去一直未能在编译器时将整个程序保留在存储器中。这一问题已经通过使用临时文件来保存翻译时中间步骤的结果或通过“匆忙地”编译(也就是只保留源程序早期部分的足够信息用以处理翻译)解决了。存储器的限制现在也只是一个小问题了,现在可以将整个编译单元放在存储器之中,特别是在可以分别编译的语言中时。但是偶尔还是会发现需要在某些运行步骤中生成中间文件。其中典型的是代码生成时需要反填(backpatch)地址。例如,当翻译如下的条件语句时 if x = 0 then ... else ... 在知道else部分代码的位置之前必须由文本跳到else部分:
CMP X,0 JNE NEXT ;;
location of NEXT not yet known < code for then-part > NEXT : < code for else-part >
通常,必须为NEXT的值留出一个空格,一旦知道该值后就会将该空格填上,利用临时文件可以很容易地做到这一点。
如果想利用上面的编译原理开发一套属于自己的编程语言,或者想在一个产品中嵌入编程语言,可以参考zengl开源网开发的zengl编程语言,该编程语言为国人使用C语言开发,里面包含两个部分,一个是编译器,一个是解释执行中间代码的虚拟机。编译器包含了词法扫描,语法分析,中间代码输出等,虚拟机则类似JAVA一样解释执行中间代码。作者将所有的版本都公布出来,好让读者可以由浅入深的做研究,并且为了证明该编程语言的实用性,还结合SDL游戏开发库开发了一款图形界面和命令行界面的21点扑克小游戏 。
zengl编程语言目前适用平台为windows和linux (最开始在Linux下使用gcc开发,后来移植到windows平台)

9. C++ 结构体 成员函数

C++允许在结构中定义函数,该函数称为成员函数。其描述形式如下:
struct 结构名{
数据成员
成员函数
}
下面是一个使用成员函数的例子
#include <iostream>

/* run this program using the console pauser or add your own getch, system("pause") or input loop */
using namespace std;

struct Point
{
double m,n;
void Setab(double a,double b)
{
m=a;
n=b;
}

void display()
{
cout<<"display()------>>" <<m<<"\t"<<n<<endl;
}
};

int main(int argc, char** argv)
{
Point p;
p.Setab(2,3);
p.display();
cout <<"数据成员"<<p.m<<"\t" <<p.n <<endl;
return 0;
}

总结:
1、用到了结构块 结构块中的函数调用的方法,写完上面程序发现C++的结构块像Java中静态方法,直接用结构名调用数据成员和成员函数;还有一个就是“\t” 是制表符意思是横向跳到下一个制表符的位置,相当于键盘上的Tab键,“\n”换行相当于endl;
2、在结构中成员默认都是public如不不希望对象直接访问数据成员,可以将数据成员使用private来修饰,即私有的数据成员,必须通过共有的成员函数才能使用,成为数据的封装性,下个小例子看看数据的封装性。

10. C语言中结构体字节的计算方式

  在说计算方式之前先讲讲几个概念一个是 偏移量 还有一个是 内存对齐 。先说偏移量,网络对于它的定义是这样:把存储单元的实际地址与其所在段的段地址之间的距离称为段内偏移,也称为“有效地址或偏移量”。在结构体里面大概是指结构体变量中成员的地址和结构体变量地址的差。然后再说一下内存对齐这个概念:内存中存放基本类型数据时,计算机的系统会对其位置有限制,系统会要求这些数据的首地址的值是某个数的倍数,而这个数被称为该数据类型的对齐模数。虽然ANSI C标准中没有强制规定相邻声明的变量内存中要相邻,但是编译器会自动帮你处理这个问题,也就是相邻变量之间可能会填充一些字节。因此在这个问题上又有了编译器的区别。

  那我们先来讲讲结构体变量在微软的编译器的对齐吧

      1.结构成员的首地址要是其最宽的基本类型成员的整数倍。编译器在给结构体分配内存的时候先找到最宽的基本成员,然后再在内存中寻找地址,并将这个最宽的基本数据类型的大小作为对齐模数

       2.结构体每一个成员相对于首地址的偏移量是成员大小的整数倍,如果没有达到这个要求,编译器会自动填加字节。编译器在为结构体成员开辟内存的时候会先检查开辟内存的首地址与结构体变量的首地址之间的偏移量,如果是成员体的整数倍那么就存放这个变量,不然的话就在这个成员和上一个成员之间填充字节,以达到整数倍的目的

       3.结构体所占的总内存大小要是最大成员体大小的整数倍,如果不是,那么编译器会在末尾补充字节。结构体的最后一个成员,不仅要满足前两条原则,最后一条准则也要满足。

      接下来来看看几个例子。

这个结构体在VS 2017下的sizeof的运算结果是12。那么根据上面的对其规则我们来对其进行计算。

首先是char a。char大小是1,相对于首地址的偏移量是0,然后是int i。int i的大小是4,相对于首地址的偏移量是1,但是1不是4的整数倍,所以编译器会自动在char a和int i之间填充字节字节。所以in i的偏移量是4。而之后的float b大小4偏移量就是int i的偏移量加上int i的大小故float b的偏移量大小就是8,而8正好是4的倍数那么就不会有字节填充。而结构体的大小也就自然是最后8+4=12了

从这里也可以看出结构体大小等于最后一个成员体的大小加上它的偏移量。

那么我们再来看一个例子

那么我们再利用之前的算法来对其进行运算,int i的大小是4偏移量是0,int c 的大小是4偏移量是4,double b的大小是8,偏移量是8。char a的大小是1 ,偏移量是16。那么这个结构体的变量的大小就是16+1=17吗?答案肯定不是这样的。在VS的sizeof的运算下这个的结果是24,为什是24呢,那么这之前说的最后一条原则就要用上了。结构体的大小确实是等于最后一个成员的偏移量加上最后一个成员的大小,但是如果这个结构不满足是结构体中最大成员大小的整数倍这个条件那么,编译器会自动在最后填充字节使其满足,也就是说,虽然我们计算出的结果是17但是17并不是8的倍数,所以编译器自动在最后填充字节使其成为8的倍数,即自动扩充成24.、

那我们再来说一下GCC编译器下的模式。GCC编译器在Windows环境下用的会比较少,主要在Linux平台下使用GCC编译器就不遵守微软的编译器下的一些准则了,比如之前 说过的对齐模数。微软的编译器下的对齐模数是结构体成员中最大的大小而在GCC编译器下对齐模数最大只能是4。这就意味着对齐模数只能是1,2,4中的一个。因此之前讲过的在微软编译器下的的一些原则会有些不同。之前讲过的成员的首地址的偏移量要是成员大小的整数倍在这里就有点区别了。在GCC中如果成员大小小于等数4那么继续按照之前的标准就好了,但如果大于4,则结构体每个成员相对于结构体首地址的偏移量只能按照是4的整数倍来进行判断是否添加填充。来看一个简单的例子。

在这个例子中,如果是按照微软的编译器的话计算的结构应该是16,但是在GCC编译器下是12。道理就是之前讲的。

  虽然理论上如此但是我自己在试的时候发现上面的运算结果是16,没错是16而不是12!为什么呢?难道是这个理论错了么,当然不是。我们可以尝试在GCC下计算一下sizeof(int *)(是int *而不是int)。你会发现结果是8(如果是sizeof(int)那么结果结果就是4),输出结果是8那就解决了我们的疑惑。 64位系统的对其长度默认是8而32位的才是4 。这就合理解释了为什么计算结果是16而不是12。

对齐模数的选择只能是基于基本数据类型,所以对于结构体嵌套结构体就不能这么,至于其的计算方式之后再补充

热点内容
什么是广告脚本设计 发布:2025-09-18 05:52:09 浏览:651
移动版我的世界服务器 发布:2025-09-18 05:38:49 浏览:958
使用jsp脚本输出九九乘法表 发布:2025-09-18 05:22:11 浏览:663
出行解压 发布:2025-09-18 05:20:54 浏览:574
安卓手机画线怎么用 发布:2025-09-18 05:16:43 浏览:697
解压吃蔬菜 发布:2025-09-18 05:10:04 浏览:818
php判断数组个数 发布:2025-09-18 04:54:02 浏览:665
linuxmd5c 发布:2025-09-18 04:47:04 浏览:345
数据结构编译器哪个好 发布:2025-09-18 04:33:52 浏览:437
ad转换c语言 发布:2025-09-18 04:21:21 浏览:755