文章

C嘎嘎

1.++i 和i++的区别

  • ++i:先递增变量,然后返回递增后的值。

  • i++:先返回变量的当前值,然后递增变量。

#include <stdio.h>  
  
int main() {  
    int i = 5;  
    int j = 5;  
  
    printf("%d\n", ++i);  // 输出 6,因为i先加1然后返回  
    printf("%d\n", i);    // 输出 6,因为i已经被加1  
  
    printf("%d\n", j++);  // 输出 5,因为j的值先被返回然后加1  
    printf("%d\n", j);    // 输出 6,现在j的值是6  
  
    return 0;  
}

2.逻辑运算符

包括!(非)、&&(与)、||(或),优先级依次降低。

(非)比如!a,a的值是true,则!a的值是false。

&&(与):同True为True,其他结果全是False。

|| (或):同False为False,其他结果全是True。

3.移位运算

包括左移运算(<<)和右移运算(>>),都是二元运算符。

移位运算符左边的数是需要移位的数值,右边的数是移动的位数。

#include <iostream>  
#include <bitset> // 用于以二进制形式输出  
  
int main() {  
    int num = 0b00001111; // 或者直接写作 15,这里使用0b前缀是为了清晰表示二进制  
    int shiftedNum = num << 4; // 将num向左移动4位  
  
    // 直接输出十进制结果  
    std::cout << "Shifted number (decimal): " << shiftedNum << std::endl;  
  
    // 使用bitset输出二进制形式(需要包含<bitset>头文件)  
    std::cout << "Shifted number (binary): " << std::bitset<8>(shiftedNum) << std::endl; // 注意这里使用8位来显示  
  
    return 0;  
}

结果如下:

Shifted number (decimal): 240
Shifted number (binary): 11110000

4.强制类型转换

C++提供了四种主要的类型转换方式:

静态类型转换(Static Cast)

#include <iostream>
using namespace std;

int main()
{
   double d = 3.14;  
   int i = static_cast<int>(d); // 将double转换为int,丢失小数部分
   cout << i << endl;
	return 0;
}

运行结果:3

动态类型转换(Dynamic Cast)

class Base { virtual void dummy() {} };
class Derived : public Base { /* ... */ };
Base* b = new Derived();
Derived* d = dynamic_cast<Derived*>(b); // 将Base指针转换为Derived指针

常量类型转换(Const Cast)

const int ci = 10;
int* modifiable = const_cast<int*>(&ci); // 去除const属性

重新解释类型转换(Reinterpret Cast)

int* p = new int(65);
char* ch = reinterpret_cast<char*>(p); // 将int指针转换为char指针

5.指针

指针的本质是变量,可以是各种数据类型,定义一个指针 "*ip",其中 "ip" 需要赋于一个地址(可以用 & 符号获取其他变量的地址再赋值给 ip),而 "*ip" 是一个具体的值,即读取地址后获得的值;

 Meet in 2025/02/22 :

创建出来的指针如果不赋值,在后续代码中对其进行Delete操作会使得整个进程生成解决方案虽然不会报错,但是在逻辑上会走不通,导致生成的Dll出现卡死或者闪退的现象。

6.结构体

C嘎嘎中的结构体概念可以对应理解为Java中的类。

如下是一个简单的结构体概念,直观感受和java的类别无二致。

struct Books
{
   char  title[50];
   char  author[50];
   char  subject[100];
   int   book_id;
} book;

7.运算符重载

// 重载 + 运算符,用于把两个 Box 对象相加
      Box operator+(const Box& b)
      {
         Box box;
         box.length = this->length + b.length;
         box.breadth = this->breadth + b.breadth;
         box.height = this->height + b.height;
         return box;
      }

// 把两个对象相加,得到 Box3
   Box3 = Box1 + Box2;

8.—>在C++中的用法

在C++中,-> 运算符主要用于通过指针访问对象的成员。当你有一个指向对象的指针时,你可以使用 -> 运算符来访问该对象的成员变量或成员函数。

这里是一个简单的例子来说明 -> 运算符的用法:

#include <iostream>
#include <string>

class Person {
public:
    std::string name;
    int age;

    void introduce() {
        std::cout << "Hello, my name is " << name << " and I am " << age << " years old." << std::endl;
    }
};

int main() {
    // 创建一个 Person 对象,并通过指针指向它
    Person* ptr = new Person{"Alice", 30};

    // 使用 -> 运算符访问成员变量
    std::cout << "Name: " << ptr->name << std::endl;
    std::cout << "Age: " << ptr->age << std::endl;

    // 使用 -> 运算符调用成员函数
    ptr->introduce();

    // 释放动态分配的内存
    delete ptr;

    return 0;
}

在这个例子中,我们首先创建了一个 Person 类的对象,并通过 new 运算符将其地址赋给了一个指针 ptr。然后,我们使用 -> 运算符来访问该对象的 nameage 成员变量,以及调用 introduce 成员函数。

需要注意的是,当你使用 new 运算符动态分配内存时,你需要在适当的时候使用 delete 运算符来释放这块内存,以避免内存泄漏。在上面的例子中,我们在程序结束前释放了 ptr 指向的内存。

另外,虽然 -> 运算符在指针访问对象成员时非常有用,但在可能的情况下,更推荐使用引用或智能指针(如 std::shared_ptrstd::unique_ptr)来管理对象的生命周期和内存,以减少内存泄漏和悬挂指针的风险。

9.C++中的句柄

句柄的解释请参考下列CSDN文章:

什么是句柄?为什么会有句柄?HANDLE

10.C++中的union联合体

#include <iostream> 
using namespace std;
 union myun 
{ 
  int k; 
  struct { int x; int y; int z; }u; 
}a; 
int tmain(int argc, TCHAR* argv[]) { 
  a.u.x =4; 
  a.u.y =5; 
  a.u.z =6; 
  a.k = 0; 
  cout<<a.u.x<<" "<<a.u.y<<" "<<a.u.z<<" ";
 return 0; }

这段代码的输出结果是:

复制

0 5 6

代码分析

  1. 联合体(Union)的定义

    cpp

    复制

    union myun {
        int k;
        struct { int x; int y; int z; } u;
    } a;
    • myun 是一个联合体,联合体的特点是所有成员共享同一块内存空间。

    • 联合体 myun 有两个成员:

      • int k:一个整数。

      • struct { int x; int y; int z; } u:一个包含三个整数的结构体。

  2. 内存布局

    • 联合体 myun 的大小是 sizeof(int),即 4 个字节(在大多数系统中)。

    • 结构体 u 的大小是 3 * sizeof(int),即 12 个字节。

    • 由于联合体的所有成员共享同一块内存,ku 的成员 xyz 会共享内存。

  3. 代码执行过程

    cpp

    复制

    a.u.x = 4;
    a.u.y = 5;
    a.u.z = 6;
    a.k = 0;
    • a.u.x = 4;:将联合体的内存的前 4 个字节(x)设置为 4。

    • a.u.y = 5;:将接下来的 4 个字节(y)设置为 5。

    • a.u.z = 6;:将接下来的 4 个字节(z)设置为 6。

    • a.k = 0;:将联合体的前 4 个字节(ku.x)设置为 0。

  4. 内存覆盖

    • a.k = 0; 执行时,它会覆盖 a.u.x 的值,因为 ku.x 共享同一块内存。

    • 因此,a.u.x 的值变为 0,而 a.u.ya.u.z 的值保持不变。

  5. 输出结果

    cpp

    复制

    cout << a.u.x << " " << a.u.y << " " << a.u.z << " ";
    • a.u.x 的值是 0(被 a.k = 0; 覆盖)。

    • a.u.y 的值是 5。

    • a.u.z 的值是 6。

总结

由于联合体的成员共享内存,a.k = 0; 覆盖了 a.u.x 的值,因此输出结果为 0 5 6

开启新对话

11.类型定义语句--typedef

typedef 是 C/C++ 中的一个关键字,用于为已有的数据类型定义一个新的名称(别名)。它的主要作用是增强代码的可读性和可维护性,尤其是在处理复杂的数据类型时。


1. 基本语法

typedef 原类型 新类型名;
  • 原类型:可以是任何基本数据类型(如 intfloat 等)或复杂数据类型(如结构体、指针等)。

  • 新类型名:为原类型定义的新名称(别名)。


2. 常见用法

(1)为基本数据类型定义别名

typedef int Integer;
  • 这里将 int 定义为 Integer,之后可以使用 Integer 代替 int

  • 示例:

    Integer a = 10; // 等价于 int a = 10;

(2)为指针类型定义别名

typedef int* IntPtr;
  • 这里将 int* 定义为 IntPtr,之后可以使用 IntPtr 代替 int*

  • 示例:

    IntPtr p = nullptr; // 等价于 int* p = nullptr;

(3)为结构体定义别名

typedef struct {
    int x;
    int y;
} Point;
  • 这里定义了一个结构体,并为其起了一个别名 Point

  • 示例:

    Point p1;
    p1.x = 10;
    p1.y = 20;

(4)为函数指针定义别名

typedef int (*FuncPtr)(int, int);
  • 这里定义了一个函数指针类型 FuncPtr,指向一个返回 int 并接受两个 int 参数的函数。

  • 示例:

    int add(int a, int b) {
        return a + b;
    }
    FuncPtr f = add; // f 指向 add 函数
    cout << f(2, 3); // 输出 5

(5)为数组类型定义别名

typedef int IntArray[10];
  • 这里定义了一个长度为 10 的整型数组类型 IntArray

  • 示例:

    IntArray arr = {1, 2, 3, 4, 5}; // arr 是一个长度为 10 的整型数组

12.值调用和引用调用

值调用

  • 按值传递

    • Swap 函数中,abxy 的副本。

    • 函数内部交换的是 ab 的值,而不是 xy 的值。

    • 因此,xy 的值在函数调用前后保持不变。

#include <iostream>       
        using namespace std;
        void Swap(int a, int b);
        int _tmain(int argc, _TCHAR* argv[])
        {
                   int x=5, y=10;
                   cout<<"x="<<x<<"    y="<<y<<endl;
                   Swap(x,y);
                   cout<<"x="<<x<<"    y="<<y<<endl;
                   return 0;
        }
        void Swap(int a, int b)
        {
                   int t;
                   t=a;
                  a=b;
                  b=t;
        }

结果:

x=5    y=10
x=5    y=10

引用调用

#include <iostream>
using namespace std;

void Swap(int &a, int &b);

int _tmain(int argc, _TCHAR* argv[]) {
    int x = 5, y = 10;
    cout << "x=" << x << "    y=" << y << endl;
    Swap(x, y);
    cout << "x=" << x << "    y=" << y << endl;
    return 0;
}

void Swap(int &a, int &b) {
    int t;
    t = a;
    a = b;
    b = t;
}

结果:

x=5    y=10
x=10    y=5
  • 原代码中 Swap 函数按值传递参数,导致 xy 的值没有交换。

  • 要实现真正的交换,可以使用按引用传递或按指针传递。

  • 按引用传递更简洁,推荐使用。

13.内联函数

内联函数的基本概念

  • 定义

    • 内联函数是通过 inline 关键字声明的函数。

    • 编译器会尝试将内联函数的代码直接插入到调用处,而不是生成函数调用。

  • 目的

    • 减少函数调用的开销(如栈帧的创建和销毁)。

    • 提高程序的执行效率。

2. 内联函数的语法

在函数声明或定义前加上 inline 关键字即可:

inline 返回类型 函数名(参数列表) {
    // 函数体
}

示例:

#include <iostream>
using namespace std;

// 内联函数
inline int add(int a, int b) {
    return a + b;
}

int main() {
    int x = 5, y = 10;
    cout << "Sum: " << add(x, y) << endl; // 调用内联函数
    return 0;
}

3. 内联函数的工作原理

  • 普通函数调用

    • 调用普通函数时,程序会跳转到函数的地址,执行完后再返回调用处。

    • 这个过程涉及栈帧的创建和销毁,有一定的开销。

  • 内联函数调用

    • 编译器会尝试将内联函数的代码直接插入到调用处,避免函数调用的开销。

    • 例如,上述 add(x, y) 可能会被替换为 x + y

License:  CC BY 4.0