结构体、共用体与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. 在K8s中,提供的DNS组件是什么?有什么特性?

    在Kubernetes (K8s)集群中,用于内部DNS服务的组件已经从早期的kube-dns过渡到了coredns. kube-dns(已弃用): 在Kubernetes 1.10版本之前,kube ...

  2. Linux输出转换命令 xargs

    一.基本用法 xargs命令的作用,是将标准输入转为命令行参数. 原因:大多数命令都不接受标准输入作为参数,只能直接在命令行输入参数,这导致无法用管道命令传递参数 如下面 echo 不接受标准输出做参 ...

  3. Qt processEvents - 解决线程中事件阻塞(如槽函数被阻塞)

    百度了一会,发现没太有文字讲这件事情,因此整理成文字记录一下. processEvents介绍 长时间运行的操作可以调用processEvents() 保持应用程序响应能力. void QCoreAp ...

  4. Vue3学习(16) - 左侧显示分类菜单

    写在前面 和大家不太一样,我觉得今年的自己更加relax,没有亲戚要走,没有朋友相聚,也没有很好的哥们要去叙旧,更没有无知的相亲,甚至可以这么说没有那些闲得慌的邻居. 也可以说是从今天开始,算是可以进 ...

  5. 西门子SIMATIC LPMLV30 库的模式和状态管理器

    从基于S7-1200 / S7-1500的OMAC PackML V3.0获取到的文章内容,用于记录查看 基本信息 根据PackML_V3.0,该库包含了用于机械模式和状态管理器的功能模块. • 机械 ...

  6. linux 测试网络速率

    1. ethtool ethtool是很强大的查询网卡(嵌入式称为phy芯片)配置的工具,几乎phy芯片芯片手册寄存器能配置的选项,ethtool都能查询到:嵌入式调试phy芯片的时候经常用到该命令: ...

  7. Moon.Orm版本维护及下载

    MoonOrm最新版及代码生成器 (2020-8-29)

  8. Annotation-specified bean name conflicts with existing

    问题说明 Annotation-specified bean name conflicts with existing,non-compatible bean definition of same n ...

  9. Go 项目的文件布局

    转自 kcq 的 https://github.com/golang-standards/project-layout https://github.com/golang-standards/proj ...

  10. .NET周刊【2月第3期 2024-02-25】

    国内文章 4.1k Star!全面的C#/.NET/.NET Core学习.工作.面试指南 https://www.cnblogs.com/Can-daydayup/p/18027117 DotNet ...