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
。然后,我们使用 ->
运算符来访问该对象的 name
和 age
成员变量,以及调用 introduce
成员函数。
需要注意的是,当你使用 new
运算符动态分配内存时,你需要在适当的时候使用 delete
运算符来释放这块内存,以避免内存泄漏。在上面的例子中,我们在程序结束前释放了 ptr
指向的内存。
另外,虽然 ->
运算符在指针访问对象成员时非常有用,但在可能的情况下,更推荐使用引用或智能指针(如 std::shared_ptr
和 std::unique_ptr
)来管理对象的生命周期和内存,以减少内存泄漏和悬挂指针的风险。
9.C++中的句柄
句柄的解释请参考下列CSDN文章:
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
代码分析
联合体(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
:一个包含三个整数的结构体。
内存布局:
联合体
myun
的大小是sizeof(int)
,即 4 个字节(在大多数系统中)。结构体
u
的大小是3 * sizeof(int)
,即 12 个字节。由于联合体的所有成员共享同一块内存,
k
和u
的成员x
、y
、z
会共享内存。
代码执行过程:
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 个字节(k
和u.x
)设置为 0。
内存覆盖:
当
a.k = 0;
执行时,它会覆盖a.u.x
的值,因为k
和u.x
共享同一块内存。因此,
a.u.x
的值变为 0,而a.u.y
和a.u.z
的值保持不变。
输出结果:
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 原类型 新类型名;
原类型
:可以是任何基本数据类型(如int
、float
等)或复杂数据类型(如结构体、指针等)。新类型名
:为原类型定义的新名称(别名)。
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
函数中,a
和b
是x
和y
的副本。函数内部交换的是
a
和b
的值,而不是x
和y
的值。因此,
x
和y
的值在函数调用前后保持不变。
#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
函数按值传递参数,导致x
和y
的值没有交换。要实现真正的交换,可以使用按引用传递或按指针传递。
按引用传递更简洁,推荐使用。
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
。