嵌入式C/C++学习

嵌入式C/C++学习

  • 嵌入式C/C++学习
    • 基本数据类型
    • 引用数据类型
    • struct 用法
      • 自然对界
      • 指定对界
    • static 关键字
    • const 关键字
    • union 关键字
    • 浅拷贝
    • 深拷贝
      • 深浅拷贝的区别
    • memset()函数
    • extern 关键字

嵌入式C/C++学习

基本数据类型

包括七类:number、string、boolean、null、undefined、symbol、以及ES10中新增的BigInt(任意精度整数)
直接存储在中。

引用数据类型

有常规键值对的无序对象{a:1},数组[1 ,2 , 3]及函数等。
Object类:存储该对象在栈中引用,真实数据存放在内存中。即栈中存储了指针,指针指向堆中该实体的起始地址

栈内存和堆内存比较:

栈内存
name:a
val:堆地址

堆内存:无法直接访问,必须访问该对象在堆内存的地址,由地址再去获取值。
val:[0, 1, 2, 3] //实体

struct 用法

结构体自引用,不是包含同类型的结构体变量,而是包含同类型的结构体指针
示例:

struct Book
{
	int id;
	struct Book book;
};
# 不可取,为限制结构体大小,死递归
# 正确做法
struct Book
{
	int id;
	struct Book* book;
};
# 指针变量的大小是固定可算的

自然对界

自然对界:编译器会自动进行成员变量的对齐,以提高运算效率。
struct 是一种复合数据类型,其构成元素既可以是基本数据类型(如 int、long、float 等)的变量,也可以是一些复合数据类型(如 array、struct、union 等)的数据单元。

自然对界(natural alignment)即默认对齐方式,是指按结构体的成员中 size 最大的成员对齐

指定对界

一般地,可以通过下面的方法来改变缺省的对界条件:

  • 使用伪指令**#pragma pack (n)**,编译器将按照 n 个字节对齐;
  • 使用伪指令**#pragma pack (),取消自定义字节对齐方式。
    注意:如果#pragma pack (n)中指定的 n 大于结构体中
    最大成员的 size**,则其不起作用,结构体仍然按照 size 最大的成员进行对界。
    示例:
#pragma pack (n) 
struct naturalalign 
{ 
 char a; 
 int b; 
 char c; 
}; 
#pragma pack ()

n 为 4、8、16 时,其对齐方式均一样,sizeof(naturalalign)的结果都等于 12。而当n为2时,其发挥了作用,使得 sizeof(naturalalign)的结果为 6

题目:已知 WAV 文件格式如下表,打开一个 WAV 文件,以适当的数据结构组织 WAV 文件头并解析 WAV格式的各项信息。
WAVE 文件格式说明表
嵌入式C/C++学习_第1张图片
解答:
将 WAV 文件格式定义为结构体 WAVEFORMAT:

typedef struct tagWaveFormat 
{ 
 char cRiffFlag[4]; 
 UIN32 nFileLen; 
 char cWaveFlag[4]; 
 char cFmtFlag[4]; 
 char cTransition[4]; 
 UIN16 nFormatTag ; 
 UIN16 nChannels; 
 UIN16 nSamplesPerSec; 
 UIN32 nAvgBytesperSec; 
 UIN16 nBlockAlign; 
 UIN16 nBitNumPerSample; 
 char cDataFlag[4];
  UIN16 nAudioLength; 
} WAVEFORMAT;

假设 WAV 文件内容读出后存放在指针 buffer 开始的内存单元内,则分析文件格式的代码很简单,为:

WAVEFORMAT waveFormat; 
memcpy( &waveFormat, buffer,sizeof( WAVEFORMAT ) );

直接通过访问 waveFormat 的成员,就可以获得特定 WAV 文件的各项格式信息。
考查面试者组织数据结构的能力,有经验的程序设计者将属于一个整体的数据成员组织为一个结构体,利用指针类型转换,可以将 memcpy、memset 等函数直接用于结构体地址,进行结构体的整体操作。

static 关键字

(1)函数体内 static 变量的作用范围为该函数体,不同于 auto 变量,该变量的内存只被分配一次,因此其值在下次调用时仍维持上次的值;
(2)在模块内的 static 全局变量可以被模块内所用函数访问,但不能被模块外其它函数访问
(3)在模块内的 static 函数只可被这一模块内的其它函数调用,这个函数的使用范围被限制在声明它的模块内;
(4)在类中的 static 成员变量属于整个类所拥有对类的所有对象只有一份拷贝
(5)在类中的 static 成员函数属于整个类所拥有,这个函数不接收 this 指针,因而只能访问类的static 成员变量。

const 关键字

(1)欲阻止一个变量被改变,可以使用 const 关键字。在定义该 const 变量时,通常需要对它进行初始化,因为以后就没有机会再去改变它了;
(2)对指针来说,可以指定指针本身为 const,也可以指定指针所指的数据为 const,或二者同时指定为 const;
(3)在一个函数声明中,const 可以修饰形参,表明它是一个输入参数,在函数内部不能改变其值;
(4)对于类的成员函数,若指定其为 const 类型,则表明其是一个常函数,不能修改类的成员变量;
(5)对于类的成员函数,有时候必须指定其返回值为 const 类型,以使得其返回值不为“左值”。
示例:

const classA operator*(const classA& a1,const classA& a2);
//operator*的返回结果必须是一个 const 对象。

union 关键字

(1)所有成员共用一个空间(共享);
(2)同一时间只有一个成员是有效的;
(3)union的大小是最大的成员size(修改一个成员会影响其他成员)。

浅拷贝

浅拷贝是指拷贝对象时,只是简单地复制对象的成员变量的值,包括指针类型的成员变量。这样,在原对象和拷贝对象中的指针成员变量将指向同一块内存地址。如果其中一个对象修改了指针指向的内存,另一个对象也会受到影响。

按位拷贝对象,它会创建一个新对象,该对象有着对原始对象的一份精确拷贝。其中原始对象可分为基本类型和引用类型。

基本类型:所拷贝的为基本类型的

引用类型:所拷贝的为内存地址。引用类型需注意的是:一个对象改变了实体,则会影响到另一个对象,即只复制对象空间而不复制资源。

注意:当结构体中存在指针类型成员(引用类型)时,需要重写struct的拷贝构造函数并进行“=”操作符重载

深拷贝

深拷贝是指拷贝对象时,不仅复制对象的成员变量的值,还会为指针类型的成员变量分配新的内存空间并将原对象指针指向的内容复制到新的内存空间中。这样,在原对象和拷贝对象中的指针成员变量将指向不同的内存地址,彼此之间不会相互影响。

实现:
用JSON.stringify()将对象转换成JSON字符串,再用JSON.parse()将字符串解析成对象,一去一来,新的对象产生了,而且对象会开辟新的栈。(可以实现对象和数组的深拷贝,不能处理函数)。

JSON.stringify()方法是将一个JavaScript值(对象/数组)转换为一个JSON字符串,不能接受函数。

深浅拷贝的区别

示例:

#include 

class MyClass {
public:
    int* data;
    
    MyClass(int value) {
        data = new int(value);
    }
    
    // 拷贝构造函数
    MyClass(const MyClass& other) {
        data = new int(*other.data);
    }
    
    ~MyClass() {
        delete data;
    }
};

int main() {
    MyClass obj1(5);
    MyClass obj2 = obj1;  // 调用拷贝构造函数进行拷贝
    
    // 修改obj1的data值
    *obj1.data = 10;
    
    std::cout << *obj1.data << std::endl;  // 输出:10
    std::cout << *obj2.data << std::endl;  // 输出:5,obj2的data值没有改变
    
    return 0;
}


//MyClass类包含一个指针类型的成员变量data。
//在拷贝构造函数中,我们进行了深拷贝,即为data成员变量分配新的内存空间,并将原对象的data值复制到新的内存空间中。
//因此,obj1和obj2的data指针指向不同的内存地址,修改一个对象的data值不会影响另一个对象。

总结:

  • 浅拷贝只是简单地复制对象的成员变量的值,包括指针类型的成员变量,指向同一块内存地址。
  • 深拷贝不仅复制对象的成员变量的值,还会为指针类型的成员变量分配新的内存空间,并将原对象指针指向的内容复制到新的内存空间中,指向不同的内存地址

memset()函数

memset()函数用于将指定内存块的内容设置为指定的值。它是C/C++标准库中的函数,声明在头文件中。

内存赋值,用于给一块内存空间进行赋值。可以用于对一片内存空间逐字节进行初始化。逐字节拷贝

在给char以外的数组赋值时,只能初始化为0/-1

//memset()函数的原型
void* memset(void* ptr, int value, size_t num);
//参数说明
/*
ptr:指向要填充的内存块的指针。
value:要设置的值,通常是一个无符号字符(unsigned char)。
num:要填充的字节数。*/

注意memset()函数将ptr指向的内存块的前num个字节都设置为value的值。通常情况下,value的取值范围是0-255,但在C标准中,memset()的参数value是int类型,可以取任意整数值,但其取值范围是unsigned char的取值范围。

使用时应注意:(1)内存块的大小应该是字节数的整数倍。(2)memset()函数返回指向ptr的指针

#include 
#include 

int main() {
    char str[10];
    
    // 将str数组的前5个字节都设置为字符'a'
    memset(str, 'a', 5);
    
    // 输出结果:aaaaa
    std::cout << str << std::endl;
    
    return 0;
}

//使用memset()函数将字符数组str的前5个字节都设置为字符'a',最终输出结果为"aaaaa"。

extern 关键字

extern int a;
//仅仅是一个变量的声明,并不是定义变量a,并未给a分配内存空间。
//变量a在所有模块中作为一种全局变量只能被定义一次。

extern关键字用于声明一个全局变量或函数,表示该变量或函数是在其他文件中定义的。使用extern关键字可以在当前文件中引用其他文件中定义的全局变量或函数。

具体来说,extern关键字有以下用法:

(1)声明全局变量:可以在一个文件中使用extern关键字声明一个在其他文件中定义的全局变量,以便在当前文件中引用该变量。
(2)声明全局函数:可以在一个文件中使用extern关键字声明一个在其他文件中定义的全局函数,以便在当前文件中调用该函数。

示例:
假设有两个文件,file1.cpp和file2.cpp,其中file1.cpp中定义了一个全局变量和一个全局函数,而file2.cpp需要引用这些变量和函数。可以在file2.cpp中使用extern关键字来声明这些变量和函数,以便在file2.cpp中使用它们。

// file1.cpp
int globalVar = 10;

void globalFunc() {
    // 函数实现
}

// file2.cpp
extern int globalVar;  // 声明全局变量
extern void globalFunc();  // 声明全局函数

int main() {
    // 使用全局变量和函数
    globalVar = 20;
    globalFunc();

    return 0;
}

你可能感兴趣的:(C++基础知识,c语言,c++,学习,物联网,mcu)