C/C++心得-结构体
先说句题外话,个人认为,基本上所有的高级语言被设计出来的最终目的是降低软件开发难度,提升软件开发人员素质和团队协作能力,降低软件维护的难度。在学习语言的时候,可以从这么方面来推测各种语言语法设计的原因,从来更好的掌握各种编程语言。
总结一下C语言中的数据类型结构:
1.常用基本数据类型(int, char, float, double, short, long等等)
2.数组
3.指针
4.枚举
5.结构体
6.公用体
...
这其中除了共用体不常用外,其他都是实际开发中经常用到的数据结构。其他的之前两篇都有说明,今天来说说枚举和结构体。
(初学者应该养成读代码从main开始读的习惯)
1.枚举
枚举作为一种数据类型,用于声明一组命名的常数,用来说明一种事物的不同类型或属性。实际应用作用的话,个人认为是提高代码的可读性,减小程序的维护难度。
举个例子,一个程序中需要使用变量表示颜色,那么写程序前我们需要先设定标准:0表示白色,1表示红色,2表示黄色,3表示蓝色...,那么程序代码如下:
#include <stdio.h>
#include <stdlib.h> void printColor(int color)
{
switch (color)
{
case :
printf("白色\n");
break;
case :
printf("红色\n");
break;
case :
printf("黄色\n");
break;
case :
printf("蓝色\n");
break;
default:
break;
}
} int main(int arg, char *args)
{
int color0 = ; // 白色
int color1 = ; // 红色
int color2 = ; // 黄色
int color3 = ; // 蓝色 printColor(color0);
printColor(color1);
printColor(color2);
printColor(color3); getchar();
return ;
}
(关于枚举的例子只是作为说明,这些作为例子有些不太好)如果程序代码量很大的情况下,0,1,2,3的这种数字意义很容易忘记,这时可以用到枚举
#include <stdio.h>
#include <stdlib.h> enum Color
{
White, // 白色
Red, // 红色
Yellow,// 黄色
Blue // 蓝色
}; void printColor(int color)
{
switch (color)
{
case White:
printf("白色\n");
break;
case Red:
printf("红色\n");
break;
case Yellow:
printf("黄色\n");
break;
case Blue:
printf("蓝色\n");
break;
default:
break;
}
}
int main(int arg, char *args)
{
int color0 = White; // 白色
int color1 = Red; // 红色
int color2 = Yellow; // 黄色
int color3 = Blue; // 蓝色 printColor(color0);
printColor(color1);
printColor(color2);
printColor(color3); getchar();
return ;
}
对枚举的理解应用暂时就如此,关于枚举的作为属性应用,以及其他方面的应用,有兴趣的可以查查其他资料。
2.结构体
结构体的意义之一同样是增加代码可读性。而且结构体配合指针使得C语言变得非常灵活。
结构体是由一系列具有相同或不同类型的数据组成。
下面用两种方法定义并输出一个学生的信息。
首先是不用结构体和枚举的方法
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> int getSex(short sex, char * sexVal)
{
if (sexVal == NULL)
{
return -;
}
switch (sex)
{
case :
strcpy(sexVal, "男");
break;
case :
strcpy(sexVal, "女");
break;
default:
return -;
break;
}
return ;
}
// 不用结构体和枚举的方法
int main(int arg, char * args[])
{
int num = ;
char name[] = "LiLei";
short sexN = ;
char sex[] = { '\0' };
if (getSex(sexN, sex) != )
{
return -;
}
printf("学号:%d,姓名:%s,性别:%s\n", num, name, sex); getchar();
return ;
}
下面使用结构体和枚举改造代码:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef enum _Sex
{
Male,
Female
}Sex; struct Student
{
int num;
char name[];
short sex;
}; int getSex(Sex sex, char * sexVal)
{
if (sexVal == NULL)
{
return -;
}
switch (sex)
{
case Male:
strcpy(sexVal, "男");
break;
case Female:
strcpy(sexVal, "女");
break;
default:
return -;
break;
}
return ;
} int main(int arg, char * args[])
{
struct Student stu = { , "LiLei", Male };
char sex[] = { };
if (getSex(stu.sex, sex) != )
{
return -;
}
printf("学号:%d,姓名:%s,性别:%s\n", stu.num, stu.name, sex);
getchar();
return ;
}
可以发现,在没有什么注释的情况下,从Main开始读代码,第二段代码是比较好理解。
使用结构体一般都会使用typedef给结构体起个别名,当然typedef在指针、数组等方面都有应用,使用typedef可以精简代码。下面举个例子
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> enum Sex
{
Male,
Female
}; typedef struct _Student
{
long num;
char name[];
short sex;
} Student; // 使用typedef给结构体起别名 int main(int arg, char * args[])
{
struct _Student stu1 = { , "hel", Male }; // 不使用typedef前的定义方式
Student stu2 = { , "yao", Male }; // 使用typedef的定义方式,主要是省去一个struct return ;
}
typedef的用法,先正常定义,然后用别名替代掉原变量(或函数名)名称的位置,如:
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> typedef int mI;
typedef char mCh;
typedef char mStr[];// 原来定义char数组是 char * str[32]; 这里将str替换为别名
typedef int (* name)(int a, int b); // int sum(int a, int b), 因为函数需要以指针形式表示,所以把sum替换为 * name int sum(int a, int b)
{
return a + b;
} int main(int arg, char *args[])
{
mI num = ;
mCh ch = 'x';
mStr str = "hello"; // 这里相当于定义 char str[32] = "hello";
name func = sum; // 函数指针,相当于 int func(int a, int b); func中的执行代码和sum函数相同
printf("num:%d,a=%c,str=%s,func(1, 2)=%d\n", num, ch, str, func(, ));
getchar();
return ;
}
再说说结构体的长度。结构体的长度要略大于或等于其内部成员的总长度。主要是为了程序速度,在一个结构体内有多个类型的成员的时候,会做一些“对齐”处理,该处理有可能导致结构体变量占用空间变大。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> struct Student1
{
int num;
char name[];
short sex;
};
struct Student2
{
long long num;
char name[];
short sex;
}; int main(int arg, char * args[])
{
struct Student1 stu1;
struct Student2 stu2;
printf("int:%d, char:%d, short:%d, longlong:%d\n", sizeof(int), sizeof(char), sizeof(short), sizeof(long long)); // 比较4种数据类型的长度
printf("long:%d,int:%d\n", sizeof(struct Student2), sizeof(struct Student1)); // 比较两种结构体的长度
printf("stu1:%x,stu2:%x\n", &stu1, &stu2); // 两个结构体变量的地址
getchar();
return ;
}
上面这段程序的输出结构为:
int:, char:, short:, longlong:
long:,int:
stu1:eafeb4,stu2:eafe7c
从第一行输出信息来看,我们可以算出结构体Student1声明的变量(int num, char name[32], short sex)的总长度应该为38,而第二行的实际输出结果为40.
同样可以算出结构体Student2声明的变量(long long num, char name[32], short)的总长度应该为42,而实际结果为48.
一个结构体的长度必定是其内部长度最大基本数据类型元素的整数倍,上面Student1和Student2都符合该项(数组不是基本数据类型)。
而且结构体变量的首地址必定能被其内部长度最大基本数据类型元素整除,如上面eafeb4可以被4整除,eafe7c可以被8整除。
3.结构体的一点高级用法
1.通过地址取结构体内成员的值(代码作为示例,真实开发中应该不会这样写)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h> enum Sex
{
Male,
Female
}; struct Student1
{
int num;
char name[];
short sex;
};
struct Student2
{
long long num;
char name[];
short sex;
}; int main(int arg, char * args[])
{
char * stu1Name = NULL;
char * stu2Name = NULL;
struct Student1 stu1 = { , "LiLei", Male };
struct Student2 stu2 = { , "WaLiu", Female }; printf("int:%d, char:%d, short:%d, longlong:%d\n", sizeof(int), sizeof(char), sizeof(short), sizeof(long long)); // 比较4种数据类型的长度
printf("long:%d,int:%d\n", sizeof(struct Student2), sizeof(struct Student1)); // 比较两种结构体的长度
printf("stu1:%x,stu2:%x\n", &stu1, &stu2); // 两个结构体变量的地址
printf("stu1:%x,stu2:%x\n", &stu1 + , &stu2 + ); stu1Name = (char *)(((char *)&stu1) + ); // 取stu1的地址,向后偏移4位,这个地址就是name的首地址,将其赋给stu1Name
stu2Name = (char *)(((char *)&stu2) + ); // 取stu2的地址,向后偏移8位,这个地址就是name的首地址,将其赋给stu2Name printf("stu1:%s,stu2:%s\n", stu1Name, stu2Name); // 打印出值
getchar();
return ;
}
以上代码执行结构为:
int:, char:, short:, longlong:
long:,int:
stu1:107fdb0,stu2:107fd70
stu1:107fe50,stu2:107ff30
stu1:LiLei,stu2:WaLiu
这说明可以通过地址偏移的方式取得结构体变量内部的值。
C/C++心得-结构体的更多相关文章
- CAN编写完分帧发送, 分帧接收,J1939位域型结构体心得
关于由多个不同的C文件构成的工程,我采用以下方法 以为400Hz数字电源程序为例 假设工程由以下文件组成 DC_Comm.c 主要完成串口通讯部分 DC_Config.c 主要完成时钟,外设 中断初始 ...
- c++ 数组 结构体
接下来的一点时间我将会记录下我看的c++的一些心得体会,人贵在坚持,希望我可以一直坚持下去!!Go Fighting! 一.c++复合数据类型: 数组类型的一些注意事项: sizeof的用法: 当 ...
- 漫谈C语言结构体
相信大家对于结构体都不陌生.在此,分享出本人对C语言结构体的学习心得.如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了.当然,水平有限,若发现不足之处恳请指出.代码文件test.c我放在 ...
- 漫谈C语言结构体【转】
相信大家对于结构体都不陌生.在此,分享出本人对C语言结构体的学习心得.如果你发现这个总结中有你以前所未掌握的,那本文也算是有点价值了.当然,水平有限,若发现不足之处恳请指出.代码文件test.c我放在 ...
- Go结构体实现类似成员函数机制
Go语言结构体成员能否是函数,从而实现类似类的成员函数的机制呢?答案是肯定的. package main import "fmt" type stru struct { testf ...
- C#基础回顾(二)—页面值传递、重载与重写、类与结构体、装箱与拆箱
一.前言 -孤独的路上有梦想作伴,乘风破浪- 二.页面值传递 (1)C#各页面之间可以进行数据的交换和传递,页面之间可根据获取的数据,进行各自的操作(跳转.计算等操作).为了实现多种方式的数据传递,C ...
- go语言结构体
定义: 是一种聚合的数据类型,是由零个或多个任意类型的值聚合成的实体. 成员: 每个值称为结构体的成员. 示例: 用结构体的经典案例处理公司的员工信息,每个员工信息包含一个唯一的员工编号.员工的名字. ...
- C语言中的结构体
用户自己建立自己的结构体类型 1. 定义和使用结构体变量 (1).结构体的定义 C语言允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为结构体. (2).声明一个结构体类型的一般形式为: ...
- C++_系列自学课程_第_12_课_结构体
#include <iostream> #include <string> using namespace std; struct CDAccount { double bal ...
随机推荐
- 深入理解JavaScript系列(40):设计模式之组合模式
介绍 组合模式(Composite)将对象组合成树形结构以表示“部分-整体”的层次结构,组合模式使得用户对单个对象和组合对象的使用具有一致性. 常见的场景有asp.net里的控件机制(即control ...
- c#中的程序集
程序集:一些相关类的包,比如三层中Model层都是一些数据库表的实体类.我们所用到的类都是位于各个程序集中,若需要调用某个类,就必须引用其所在的程序集. 访问级别:程序集中的类有四种访问级别,int ...
- C++测验代码
/* 返回字符串前n位和返回整数前n位 */ #include <iostream> unsigned long left(unsigned long num, int n); char ...
- [android] 切换界面的通用处理
实现不改变activity,只切换View 抽取View界面的基类 利用面向对象多态的思路,实现通用 TitleManager.java 管理标题 package com.tsh.lottery.vi ...
- SpringCloud实战之初级入门(二)— 服务注册与服务调用
目录 1.环境介绍 2.服务提供 2.1 创建工程 2.2 修改配置文件 2.3 修改启动文件 2.5 亲测注意事项 3.服务调用 3.1 创建工程 3.2 修改配置文件 3.3 修改启动文件 3.4 ...
- [Java反射基础四]通过反射了解集合泛型的本质
本文接上文"方法反射的基本操作",利用反射了解下java集合中泛型的本质 1.初始化两个集合,一个使用泛型,一个不使用 ArrayList list1 = new ArrayLis ...
- 工厂模式的C++、Java实现
1.工厂模式UML 图1. 工厂模式UML 2.C++实现 类视图如下: 图2. 工厂模式C++实现的类图 其中,Factory实现为: //工厂类. class Factory { public: ...
- 怎样关闭占用80端口的pid为4的进程
我也被这个问题给纠结了好几天.重装系统都三次了.终于找到原因了:我用的是sqlserver 2008;解决方法:window-sqlserver 2008-配置工具-sqlserver 配置管理器 找 ...
- 移动端适配(3)——rem适配
rem适配 <meta name="viewport" content="width=device-width,user-scalable=no"/&g ...
- 使用dojo.connect()添加事件的注意事项
使用dojo.connect()添加事件处理器是很方便的,不用再考虑跨浏览器的问题了.但要想正确地使用这个方法,仍然要注意几个问题: 1.用dojo.byId()获取的是dom元素,而用 ...