结构体、共用体与C++基础

1、结构体

结构体是C编程中一种用户自定义的数据类型,类似于Java的JavaBean

//Student 相当于类名
//student和a 可以不定义,表示结构变量,也就Student类型的变量
struct Student
{
char name[50];
int age;
} student,a;
//使用typedef定义
typedef struct{
char name[50];
int age;
} Student;

当结构体需要内存过大,使用动态内存申请。结构体占用字节数和结构体内字段有关,指针占用内存就是4/8字节,因此传指针比传值效率更高。

struct Student *s = (Student*)malloc(sizeof(Student));
memset(s,0,sizeof(Student));
printf("%d\n", s->age);

字节对齐

内存空间按照字节划分,理论上可以从任何起始地址访问任意类型的变量。但实际中在访问特定类型变量时经常在特定的内存地址开始访问,这就需要各种类型数据按照一定的规则在空间上排列,而不是顺序一个接一个地存放,这就是对齐。

字节对齐的问题主要就是针对结构体。

struct MyStruct1
{
short a; //0x30 0x31 0x32 0x33
int b; //0x34 0x35 0x36 0x37
short c; //0x38 0x39 0x3a 0x3b
};
struct MyStruct2
{
short a; //0x2cb028 0x2cb029
short c; //0x2cb02a 0x2cb02b
int b; //0x2cb02c 0x2cb02d 0x2cb02e 0x2cb02f
};
//自然对齐
//1、某个变量存放的起始位置相对于结构的起始位置的偏移量是该变量字节数的整数倍;
//2、结构所占用的总字节数是结构中字节数最长的变量的字节数的整数倍。
// short = 2 补 2
// int = 4
// short = 2 补 2
sizeof(MyStruct1) = 12
// 2个short在一起组成一个 4
sizeof(MyStruct2) = 8
#pragma pack(2) //指定以2字节对齐
struct MyStruct1
{
short a;
int b;
short c;
};
#pragma pack() //取消对齐
//short = 2
//int = 4
//short = 2

合理的利用字节可以有效地节省存储空间

不合理的则会浪费空间、降低效率甚至还会引发错误。(对于部分系统从奇地址访问int、short等数据会导致错误)

自然对齐

  1. 某个变量存放的起始位置相对于结构的起始位置的偏移量是该变量字节数的整数倍;
  2. 结构所占用的总字节数是结构中字节数最长的变量的字节数的整数倍。

2、共用体

在相同的内存位置存储不同的数据类型

共用体占用的内存应足够存储共用体中最大的成员

//占用4字节
union Data
{
int i;
short j;
}
union Data data;
data.i = 1;
//i的数据损坏
data.j = 1.1f;

3、C++

输出

C使用printf向终端输出信息
C++提供了 标准输出流
#include <iostream>
using namespace std;
char *name = "Jay";
int time = 8;
cout << "Jay:" << time << "点," << "天台不见不散"<< endl;

函数符号兼容

第一节课中说到C的大部分代码可以在C++中直接使用,但是仍然有需要注意的地方。

//如果需要在C++中调用C实现的库中的方法
extern "C" //指示编译器这部分代码使用C的方式进行编译而不是C++

众所周知,C是面向过程的语言,没有函数重载。

void func(int x, int y);

对于 func 函数 被C的编译器编译后在函数库中的名字可能为func (无参数符号),而C++编译器则会产生类似funcii之类的名字。

//main.c / main.cpp
int func(int x,int y){}
int main(){return 0;}
gcc main.c -o mainc.o
gcc main.cpp -o maincpp.o nm -A mainc.o
nm -A maincpp.o

main.c

main.cpp

那么这样导致的问题就在于: c的.h头文件中定义了func函数,则.c源文件中实现这个函数符号都是func,然后拿到C++中使用,.h文件中的对应函数符号就被编译成另一种,和库中的符号不匹配,这样就无法正确调用到库中的实现。

因此,对于C库可以:

#ifdef __cplusplus
extern "C"{
#endif
void func(int x,int y);
#ifdef __cplusplus
}
#endif //__cplusplus 是由c++编译器定义的宏,用于表示当前处于c++环境

extern 关键字 可用于变量或者函数之前,表示真实定义在其他文件,编译器遇到此关键字就会去其他模块查找

引用

引用是C++定义的一种新类型

//声明形参为引用
void change(int& i) {
i = 10;
}
int i = 1;
change(i);
printf("%d\n",i); //i == 10

引用和指针是两个东西

引用 :变量名是附加在内存位置中的一个标签,可以设置第二个标签

简单来说 引用变量是一个别名,表示一个变量的另一个名字

注意点

  • 引用相当于是变量的别名(基本数据类型、枚举、结构体、类、指针、数组等,都可以有引用)
  • 对引用做计算,就是对引用所指向的变量做计算
  • 在定义的时候就必须初始化,一旦指向了某个变量,就不可以再改变,“从一而终”
  • 可以利用引用初始化另一个引用,相当于某个变量的多个别名
  • 不存在【引用的引用、指向引用的指针、引用数组】

引用存在的价值之一:比指针更安全、函数返回值可以被赋值

引用的本质

  • 引用的本质就是指针,只是编译器削弱了它的功能,所以引用就是弱化了的指针
  • 一个引用占用一个指针的大小

字符串

C字符串

字符串实际上是使用 NULL字符 '\0' 终止的一维字符数组。

//字符数组 = 字符串
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
//自动加入\0
char str2[] = "Hello";

字符串操作

函数 描述
strcpy(s1, s2); 复制字符串 s2 到字符串 s1。
strcat(s1, s2); 连接字符串 s2 到字符串 s1 的末尾。
strlen(s1); 返回字符串 s1 的长度。
strcmp(s1, s2); 如果 s1 和 s2 相同,则返回 0;如果 s1 < s2 则返回小于0;如果 s1>s2 则返回大于0
strchr(s1, ch); 返回指向字符串 s1 中字符 ch 的第一次出现的位置的指针。
strstr(s1, s2); 返回指向字符串 s1 中字符串 s2 的第一次出现的位置的指针。

说明:strcmp:两个字符串自左向右逐个字符相比(按ASCII值大小相比较)

C++ string类

C++ 标准库提供了 string 类类型,支持上述所有的操作,另外还增加了其他更多的功能。

#include <string>
//string 定义在 std命令空间中
usning namespace std;
string str1 = "Hello";
string str2 = "World";
string str3("你好世界");
string str4(str3); // str1拼接str2 组合新的string
string str5 = str1 + str2;
// 在str1后拼接str2 str1改变
str1.append(str2);
//获得c 风格字符串
const char *s1 = str1.c_str();
//字符串长度
str1.size();
//长度是否为0
str1.empty();
......等等

命名空间

namespace 命名空间 相当于package

namespace A{
void a(){}
} 错误 : a();
// :: 域操作符
正确: A::a(); //当然也能够嵌套
namespace A {
namespace B{
void a() {};
}
}
A::B::a(); //还能够使用using 关键字
using namespace A;
using namespace A::B;

当全局变量在局部函数中与其中某个变量重名,那么就可以用::来区分

int i;
int main(){
int i = 10;
printf("i : %d\n",i);
//操作全局变量
::i = 11;
printf("i : %d\n",::i);
}

explicit用法

简介

explicit 只对构造函数有效,用来避免隐式类型转换。且只对仅含有一个参数的类构造函数有效,因为多于两个的时候是不会发生隐式转换的(除非只有一个参数需要赋值,其他的参数有默认值)。

用法

首先定义一个类:

class String{
public:
String (int n); // 分配n个字节空间给字符串
String (const char* p); // 用字符串p的值初始化字符串
}; // 正常初始化的方法:
String s1(10); // 10个字节长度的字符串
String s2("Hello world!"); // s2的初始值为 Hello world // 隐式转换的写法:
String s3 = 10; // 编译通过,分配10个字节长度的字符串
String s4 = 'a'; // 编译通过,分配int('a')个字节长度的字符串
String s5 = "a"; // 编译通过,调用的是String (const char* p)

使用explicit关键字:

class String{
public:
explicit String (int n); // 分配n个字节空间给字符串
String (const char* p); // 用字符串p的值初始化字符串
}; // 此时:
String s3 = 10; // 编译不通过,不允许隐式转换类型
String s4 = 'a'; // 编译不通过,不允许隐式转换类型

使用explicit的好处

当出现下面的场景时,explicit关键字能够在编译阶段给出错误:

class A {
A(int a);
}; int function(A a);

此时,若要调用function(2),则会隐式转换2为A类型,显然不是我们想要的,从而可以使用explicit关键字修饰A 的构造函数避免隐式转换的问题。并且也可以避免String s4 = 'a'; 这种奇怪的赋值语句出现。

内联函数

使用inline修饰函数的声明或者实现,可以使其变成内联函数。

特点

  • 编译器会将函数调用直接展开为函数体代码
  • 可以减少函数调用的开销
  • 会增大代码体积

注意

  • 尽量不要内联超过10行代码的函数
  • 有些函数即使声明为inline,也不一定会被编译器内联,比如递归函数。

什么时候使用内联函数?

  1. 函数代码体积不大。
  2. 频繁调用的函数。

内联函数与宏

对比宏,内联函数多了语法检测和函数特性

  1. 宏容易出错;
  2. 宏不可调试;
  3. 宏无法操作类的私有对象;
  4. 内联函数可以更加深入的优化;



    例如:
#define MUTI(x,y) x*y

inline int muti(int x,int y){
return x * y;
} cout<< MUTI(1+1,2) <<endl;// 3
cout<< muti(1+1,2) <<endl;// 4

C++ 内联函数

C++内联函数的使用

结构体、共用体与C++基础的更多相关文章

  1. C++结构、共用体、枚举

    一.结构 结构是C++OOP的基石.学习有关结构的知识僵尸我们离C++的核心OOP更近. 结构是用户定义的类型,同一个结构可以存储多种类型数据,这使得将一个事物的不同属性构成一个对象成为了可能.另外C ...

  2. 5、数组&字符串&结构体&共用体&枚举

    程序中内存从哪里来 三种内存来源:栈(stack).堆(heap).数据区(.date): 栈(stack) 运行自动分配.自动回收,不需要程序员手工干预: 栈内存可以反复使用: 栈反复使用后,程序不 ...

  3. C语言高级-结构,共用体,文件,链表

    C语言结构 标准声明方式 struct student{        int age;        char sex;    }; 这个可以在main函数中定义:  struct student ...

  4. C语言基础 (11) 结构体 ,共用体 枚举 typedef

    1 课堂回顾 作用域与生命周期 2 static 局部变量 2 打字游戏 3 内存分区代码分析 4 结构体基本操作 (复合类型[自定义类型 #include <stdio.h> #incl ...

  5. 瘋子C语言笔记(结构体/共用体/枚举篇)

    (一)结构体类型 1.简介: 例: struct date { int month; int day; int year; }; struct student { int num; char name ...

  6. C++复合类型(结构,共用体,枚举)

    •结构是用户定义的类型,而结构的声明定义了这种类型的数据属性. 一.关键字struct声明:   定义了一种新类型 struct inflatable{ char name[20];//结构成员 fl ...

  7. C基础知识(8):结构体、共用体、位域

    结构体 数组允许定义可存储相同类型数据项的变量,而结构体是C编程中另一种用户自定义的可用的数据类型,它允许用户可以存储不同类型的数据项. struct 语句的格式如下: struct [structu ...

  8. 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符

    [源码下载] 不可或缺 Windows Native (8) - C 语言: 结构体,共用体,枚举,类型定义符 作者:webabcd 介绍不可或缺 Windows Native 之 C 语言 结构体 ...

  9. C语言------结构体和共用体

    仅供借鉴.仅供借鉴.仅供借鉴(整理了一下大一C语言每个章节的练习题.没得题目.只有程序了) 文章目录 1 .实训名称 2 .实训目的及要求 3.源代码及运行截图 4 .小结 1 .实训名称 实训8:结 ...

  10. 【C语言入门教程】7.4 共用体

    7.4 共用体 共用体又称为联合体,是由不同的数据类型组成的一个整体.与结构体不同的是,共用体每次只能使用其中一个成员.结构体的总长度是结构体所有成员长度之和,共用体的总长度是其中最长一个数据类型的长 ...

随机推荐

  1. CH58x/CH57x硬件SPI操作外部flash学习记录

    官方提供的58x的spi例程,spi主机模式下的发送方式有三种单字节发送,FIFO连续发送,DMA连续发送.本文分别对SPI0主机模式下三种发送模式进行使用. 本次使用的是CH582m做为主机,W25 ...

  2. ASP.NET Core分布式项目实战(Consent 代码重构)--学习笔记

    任务23:Consent 代码重构 新建一个 Sercices 文件夹,在文件夹下新建一个 ConsentService,专门用于处理 Consent 的逻辑,我们会把 controller 中不是 ...

  3. Linux-expect(以交互形式输入命令,实现交互通信)

    1.expect简介 expect是一种脚本语言,它能够代替人工实现与终端的交互,主要应用于执行命令和程序时,系统以交互形式要求输入指定字符串,实现交互通信. 安装命令: yum install ex ...

  4. JS leetcode 买卖股票的最佳时机 题解分析,我离职了。

    壹 ❀ 引 昨天下班后,还是找经理提出了辞职,没有犹豫的裸辞,今天与人事的对话不小心被后台的同事听到,一下在公司传开了,下午我与同事们多人对线,被他们的消息轰炸....没错,我真的要走了. 因为什么原 ...

  5. NC17871 CSL分苹果

    题目链接 题目 题目描述 CSL手上有n个苹果,第i个苹果的质量是wi,现在他想把这些苹果分给他的好朋友wavator和tokitsukaze.但是CSL为了不让他们打架,根据质量决定尽量地均分成两堆 ...

  6. 立创EDA使用笔记

    立创EDA 在立创EDA上制作的PCB可以直接导入到嘉立创下单, 每个注册用户每个月可以下两个免费订单, 限制PCB尺寸10CMx10CM, 每单五片, 连制作到快递全部免费, 非常适合做样板. 立创 ...

  7. 【Android】Message、Handler、MessageQueue、Looper 详解

    1 前言 ​ Handler 即处理器,常用于跨线程通讯:线程A 和线程 B 拥有同一个 handler 对象,在线程 A 中使用 handler 的 sendMessage() 方法发送消息,在线程 ...

  8. MySQL写入SQL整个执行流程

    innodb存储引擎中一条sql写入的详细流程     第0步:会先去看缓冲区有没有这条数据,如果有就不进行缓存,直接进入第三步.   第1步:会将要修改的那一行数据所在的一整页加载到缓冲池Buffe ...

  9. 【Android逆向】破解看雪9月算法破解第三题

    这题的目标是算法还原,并写出注册机 1. 9月份算法第一题.apk 安装到手机 2. 随意输入账号密码,提示错误 3. apk拖入到jadx中 public native boolean regist ...

  10. 项目实战:Qt西门子PLC通讯调试和模拟工具(包含PLC上位机通讯,PLC服务器)

      前言   西门西PLC.台达触摸屏.法兰克机床等等多年以前玩得比较多,改造机床.维修机床.给机床编程等等,没事还能车个零件啥的,对于多年以前的研发改造,有时间就重新整理下.  先上点有历史年代感的 ...