关于c++的一些知识点

C++知识点总结 过两天就要考C++了,这次一点都不慌张和害怕,虽然看书、做题、总结也就5、6天的时间,但三遍读书法算是比较完整的过了一遍,模拟成绩也还不错,希望能够取得好成绩。O(∩_∩)O~           看了两本书,自考课本(编号04737)和C++程序设计谭浩强版的,我比较推荐后一本,浅显易懂,知识点多,例子多,可以自己多多动手写一写。 下面对学习过程中需要注意的知识点做了一些总结和导图: 阅读详情

内存分类

在C++中,内存分成5个区,他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区。

  • 栈:在执行函数时,函数内局部变量的存储单元都可以在栈上创建,函数执行结束时这些存储单元自动被释放。栈内存分配运算内置于处理器的指令集中,效率很高,但是分配的内存容量有限。
  • 堆:就是那些由 new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个 delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
  • 自由存储区:就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。
  • 全局/静态存储区:全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
  • 常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。

堆栈区别

void f() { int* p=new int[5]; }

这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?他分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,

主要的区别由以下几点:

  1. 管理方式不同
    栈由编译器自动释放,堆必须由我们手动释放,或者程序结束后系统回收。
  2. 空间大小不同
    堆一般没有限制(32位系统理论是4G),栈有限制很小
  3. 能否产生碎片不同
    堆因为频繁的new、delete导致地址不连续,产生碎片降低程序效率。栈因为数据结构设计,先进后出,不存在此问题。
  4. 生长方向不同
    对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。
  5. 分配方式不同
    堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。
  6. 分配效率不同
    栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高。堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,然后进行返回。显然,堆的效率比栈要低得多。

内存错误的几种姿势及对策

  1. 内存分配未成功,却使用了它。需要使用assert(p!=NULL)或if (p!=NULL)进行防错
  2. 内存分配虽然成功,但是尚未初始化就引用它。需要对分配的内存进行初始化。
  3. 内存分配成功并且已经初始化,但操作越过了内存的边界。
  4. 忘记了释放内存,造成内存泄露。需要配套使用malloc和free,new和delete
  5. 释放了内存却继续使用它。
  • 程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。
  • 函数的return语句写错了,注意不要返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数体结束时被自动销毁。
  • 使用free或delete释放了内存后,没有将指针设置为NULL。导致产生“野指针”。

野指针(也就是指向不可用内存区域的指针)

如何避免:

  • 规则1:用malloc或new申请内存之后,应该立即检查指针值是否为NULL。防止使用指针值为NULL的内存。
  • 规则2:不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。
  • 规则3:避免数组或指针的下标越界,特别要当心发生“多1”或者“少1”操作。
  • 规则4:动态内存的申请与释放必须配对,防止内存泄漏。
  • 规则5:用free或delete释放了内存之后,立即将指针设置为NULL,防止产生“野指针”。

指针和数组

  • 修改内容
    指针和数组对字符串的操作很像,但是有区别,它们在内存中的存储区域不同
    如下,字符数组存储在全局数据区或栈区,内容可修改。而字符串存在常量区,只读不能改。
char a[] = “hello”;
a[0] = ‘X’;
cout << a << endl;
char *p = “world”; // 注意p指向常量字符串
p[0] = ‘X’; // 编译器不能发现该错误
cout << p << endl;
  • 内容复制和比较
    不能对数组名进行直接复制与比较,应该用strcpy和strcmp
// 数组…    
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能用 b = a;
if(strcmp(b, a) == 0) // 不能用 if (b == a)
…
// 指针…
//tip:字符串是特殊情况,测试请使用int a[]={1,2,3,4,5}
int len = strlen(a);
char *p = (char *)malloc(sizeof(char)*(len+1));
strcpy(p,a); // 不要用 p = a;因为p=a只是把a的地址赋给p,而不是内容;
if(strcmp(p, a) == 0) // 不要用 if (p == a),p==a也是比较的地址,非内容
…
  • 计算内存容量
char a[] = "hello world";
char *p = a;
// 字符数组用sizeof得到数组的总容量,注意,若a[10]则是10,动态指定的会加上一个字节的空字符
cout<< sizeof(a) << endl; // 12字节
//p是一个指针,sizeof(p)得到的是对sizeof(char *)的容量,不是p所指的内存容量。
cout<< sizeof(p) << endl; // 4字节
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针。如下示例中,不论数组a的容量是多少,sizeof(a)始终等于sizeof(char *)。
void Func(char a[100]){
    cout<< sizeof(a) << endl; // 4字节而不是100字节
}

指针作为参数传递

如果函数的参数是一个指针,不要指望用该指针去申请动态内存。如下示例中,Test函数的语句GetMemory(str, 200)并没有使str获得期望的内存,
str依旧是NULL,为什么?

void GetMemory(char *p, int num){
    p = (char *)malloc(sizeof(char) * num);
}
void Test(void){
    char *str = NULL;
    GetMemory(str, 100); // str 仍然为 NULL
    strcpy(str, "hello"); // 运行错误
}

毛病出在函数GetMemory中。编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是 _p,编译器使 _p=p。如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p申请了新的内存,只是把 _p所指的内存地址改变了,但是p丝毫未变。所以函数GetMemory并不能输出任何东西。事实上,每执行一次GetMemory就会泄露一块内存,因为没有用free释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”,见示例:

void GetMemory2(char **p, int num){
    *p = (char *)malloc(sizeof(char) * num);
}
void Test2(void){
    char *str = NULL;
    GetMemory2(&str, 100); // 注意参数是 &str,而不是str
    strcpy(str, "hello");
    cout<< str << endl;
    
    free(str);
}

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态内存。这种方法更加简单,见示例:

char *GetMemory3(int num){
    char *p = (char *)malloc(sizeof(char) * num);
    return p;
}
void Test3(void){
    char *str = NULL;
    str = GetMemory3(100);
    
    strcpy(str, "hello");
    cout<< str << endl;
    free(str);
}

用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把return语句用错了。这里强调不要用return语句返回指向“栈内存”的指针,因为该内存在函数结束时自动消亡,见示例:

char *GetString(void){
    char p[] = "hello world";
    return p; // 编译器将提出警告
}
void Test4(void){
    char *str = NULL;
    str = GetString(); // str 的内容是垃圾
    cout<< str << endl;
}

用调试器逐步跟踪Test4,发现执行str = GetString语句后str不再是NULL指针,但是str的内容不是“hello world”而是垃圾。
如果把上述示例改写成如下示例,会怎么样?

char *GetString2(void){
    char *p = "hello world";
    return p;
}
void Test5(void){
    char *str = NULL;
    str = GetString2();
    cout<< str << endl;
}

野指针

“野指针”不是NULL指针,是指向“垃圾”内存的指针。人们一般不会错用NULL指针,因为用if语句很容易判断。但是“野指针”是很危险的,if语句对它不起作用。 “野指针”的成因主要有三种:

  1. 指针变量没有被初始化。任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的同时应当被初始化,要么将指针设置为NULL,要么让它指向合法的内存。例如:
char *p = NULL;
char *str = (char *) malloc(100);
  1. 指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。
  2. 指针操作超越了变量的作用域范围。这种情况让人防不胜防,示例程序如下:
class A{
    public:
    void Func(void){ cout << “Func of class A” << endl; }
};
void Test(void){
    A *p;
    {
    A a;
    p = &a; // 注意 a 的生命期
    }
    p->Func(); // p是“野指针”
}

函数Test在执行语句p->Func()时,对象a已经消失,而p是指向a的,所以p就成了“野指针”。但奇怪的是我运行这个程序时居然没有出错,这可能与编译器有关。

malloc/free和new/delete

malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见示例:

class Obj{
    public :
  Obj(void){ cout << “Initialization” << endl; }
  ~Obj(void){ cout << “Destroy” << endl; }
  void Initialize(void){ cout << “Initialization” << endl; }
  void Destroy(void){ cout << “Destroy” << endl; }
};
void UseMallocFree(void){
    Obj *a = (obj *)malloc(sizeof(obj)); // 申请动态内存
    a->Initialize(); // 初始化
    //…
    
    a->Destroy(); // 清除工作
    free(a); // 释放内存
}
void UseNewDelete(void){
    Obj *a = new Obj; // 申请动态内存并且初始化
    //…
    delete a; // 清除并且释放内存
}

类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。函数UseNewDelete则简单得多。
所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,结果也会导致程序出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。

内存耗尽

如果在申请动态内存时找不到足够大的内存块,malloc和new将返回NULL指针,宣告内存申请失败。通常有三种方式处理“内存耗尽”问题。

  1. 判断指针是否为NULL,如果是则马上用return语句终止本函数。例如:
void Func(void){
    A *a = new A;
    if(a == NULL)
        return;
    …
}
  1. 判断指针是否为NULL,如果是则马上用exit(1)终止整个程序的运行。例如:
void Func(void){
    A *a = new A;
    if(a == NULL){
        cout << “Memory Exhausted” << endl;
        exit(1);
    }
    …
}
  1. 为new和malloc设置异常处理函数。例如Visual C++可以用_set_new_hander函数为new设置用户自己定义的异常处理函数,也可以让malloc享用与new相同的异常处理函数。详细内容请参考C++使用手册。
    上述 (1)、(2) 方式使用最普遍。如果一个函数内有多处需要申请动态内存,那么方式 (1) 就显得力不从心(释放内存很麻烦),应该用方式 (2) 来处理。
    很多人不忍心用exit(1),问:“不编写出错处理程序,让操作系统自己解决行不行?”
    不行。如果发生“内存耗尽”这样的事情,一般说来应用程序已经无药可救。如果不用exit(1) 把坏程序杀死,它可能会害死操作系统。道理如同:如果不把歹徒击毙,歹徒在老死之前会犯下更多的罪。
    有一个很重要的现象要告诉大家。对于32位以上的应用程序而言,无论怎样使用malloc与new,几乎不可能导致“内存耗尽”。对于32位以上的应用程序,“内存耗尽”错误处理程序毫无用处。这下可把Unix和Windows程序员们乐坏了:反正错误处理程序不起作用,我就不写了,省了很多麻烦。
    必须强调:不加错误处理将导致程序的质量很差,千万不可因小失大。
void main(void){
    float *p = NULL;
    while(TRUE){
        p = new float[1000000];
        cout << “eat memory” << endl;
        if(p==NULL)
            exit(1);
    }
}

malloc/free的使用要点

函数malloc的原型如下:

void * malloc(size_t size);

用malloc申请一块长度为length的整数类型的内存,程序如下:

int *p = (int *) malloc(sizeof(int) * length);

我们应当把注意力集中在两个要素上:“类型转换”和“sizeof”。
*malloc返回值的类型是void*,所以在调用malloc时要显式地进行类型转换,将void\ *转换成所需要的指针类型。
*malloc函数本身并不识别要申请的内存是什么类型,它只关心内存的总字节数。我们通常记不住int, float等数据类型的变量的确切字节数。例如int变量在16位系统下是2个字节,在32位下是4个字节;而float变量在16位系统下是4个字节,在32位下也是4个字节。最好用以下程序作一次测试:

cout << sizeof(char) << endl;
cout << sizeof(int) << endl;
cout << sizeof(unsigned int) << endl;
cout << sizeof(long) << endl;
cout << sizeof(unsigned long) << endl;
cout << sizeof(float) << endl;
cout << sizeof(double) << endl;
cout << sizeof(void *) << endl;

在malloc的“()”中使用sizeof运算符是良好的风格,但要当心有时我们会昏了头,写出 p = malloc(sizeof§)这样的程序来。
函数free的原型如下:

void free( void * memblock );

为什么free函数不象malloc函数那样复杂呢?这是因为指针p的类型以及它所指的内存的容量事先都是知道的,语句free§能正确地释放内存。如果p是NULL指针,那么free对p无论操作多少次都不会出问题。如果p不是NULL指针,那么free对p连续操作两次就会导致程序运行错误。

new/delete的使用要点

运算符new使用起来要比函数malloc简单得多,例如:

int *p1 = (int *)malloc(sizeof(int) * length);
int *p2 = new int[length];

这是因为new内置了sizeof、类型转换和类型安全检查功能。对于非内部数据类型的对象而言,new在创建动态对象的同时完成了初始化工作。如果对象有多个构造函数,那么new的语句也可以有多种形式。例如:

class Obj{
    public :
    Obj(void); // 无参数的构造函数
    Obj(int x); // 带一个参数的构造函数
    …
}
void Test(void){
    Obj *a = new Obj;
    Obj *b = new Obj(1); // 初值为1
    …
    delete a;
    delete b;
}

如果用new创建对象数组,那么只能使用对象的无参数构造函数。例如:

Obj *objects = new Obj[100]; // 创建100个动态对象

不能写成:

Obj *objects = new Obj[100](1);// 创建100个动态对象的同时赋初值1

在用delete释放对象数组时,留意不要丢了符号‘[]’。例如:

delete []objects; // 正确的用法
delete objects; // 错误的用法

指针和引用

指针指向一块内存,它的内容是所指内存的地址;引用是某块内存的别名。

  1. 从现象上看,指针在运行时可以改变其所指向的值,而引用一旦和某个对象绑定后就不再改变。这句话可以理解为:指针可以被重新赋值以指向另一个不同的对象。但是引用则总是指向在初始化时被指定的对象,以后不能改变,但是指定的对象其内容可以改变。

  2. 从内存分配上看,程序为指针变量分配内存区域,而不为引用分配内存区域,因为引用声明时必须初始化,从而指向一个已经存在的对象。引用不能指向空值。

  3. 从编译上看,程序在编译时分别将指针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变指向的对象(指针变量中的值可以改),而引用对象不能改。这是使用指针不安全而使用引用安全的主要原因。从某种意义上来说引用可以被认为是不能改变的指针。

  4. 不存在指向空值的引用这个事实,意味着使用引用的代码效率比使用指针的要高。因为在使用引用之前不需要测试它的合法性。相反,指针则应该总是被测试,防止其为空。

因此如果你有一个变量是用于指向另一个对象,但是它可能为空,这时你应该使用指针;如果变量总是指向一个对象,你的设计不允许变量为空,这时你应该使用引用。

  1. 理论上,对于指针的级数没有限制,但是引用只能是一级。如下:
int** p1;         // 合法。指向指针的指针
int*& p2;         // 合法。指向指针的引用
int&* p3;         // 非法。指向引用的指针是非法的
int&& p4;         // 非法。指向引用的引用是非法的
  1. 指针是一个实体,而引用仅是个别名;

  2. 引用使用时无需解引用(*),指针需要解引用;

  3. 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终

  4. 引用没有 const,指针有 const,const 的指针不可变;

  5. 引用不能为空,指针可以为空;

  6. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身(所指向的变量或对象的地址)的大小;
    typeid(T) == typeid(T&) 恒为真,sizeof(T) == sizeof(T&) 恒为真,但是当引用作为成员时,其占用空间与指针相同(没找到标准的规定)。

  7. 指针和引用的自增(++)运算意义不一样;

常量指针(指向常量的指针,值不能变,但是地址可以变)

int i = 10;
const int *p = &i; //=》 int const *p = &i
*p = 20;    //error
 p = &num1  //success

常量引用(指向常量的引用)

int i = 10;
const int &p = i;
p=20; //error

指针常量(表示指针本身是常量。在定义指针常量时必须初始化,地址不能变,但是值可以变)

int i = 10;
int j = 20;
int* const q = &i; //error,没有初始化
int* const p = &i; //success
p   =  &num1; //error
*p  =  num1; //success

没有引用常量的说法,因为引用本身就是不可变的

常量指针常量(地址和值都不能改变)

int i =10;
const int* const p = &i;

指针和引用传递参数

    1.  指针传递参数本质上是值传递的方式,它所传递的是一个地址值。
     2. 值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。
     3. 值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。

     4. 引用传递过程中,被调函数的形式参数也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址。
     5. 被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。

     6. 引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,如果改变被调函数中的指针地址,它将影响不到主调函数的相关变量。如果想通过指针参数传递来改变主调函数中的相关变量, 那就得使用指向指针的指针,或者指针引用。

     7. 从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。
     8. 而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

最后,总结一下指针和引用的相同点和不同点:

相同点:

  • 都是地址的概念;
  • 指针指向一块内存,它的内容是所指内存的地址;而引用则是某块内存的别名。

不同点:

  • 指针是一个实体,而引用仅是个别名;
  • 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;
    *引用没有const,指针有const,const的指针不可变;
    具体指没有int& const a这种形式,而const int& a是有的,前者指引用本身即别名不可以改变,这是当然的,所以不需要这种形式,后者指引用所指的值不可以改变)
    * 引用不能为空,指针可以为空;
    * “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;
    * 指针和引用的自增(++)运算意义不一样;
  • 引用是类型安全的,而指针不是 (引用比指针多了类型检查)

一个有意思的例子

/*一个有意思的例子
常量不能修改,const_cast去除常量修饰,打印出来地址一样,但值却不一样。
原因:编译器优化导致;加上volatile 修饰后发现 a的地址为1?
*/
 const int a = 1;    //volatile const int a=1,可以确保不让编译器优化,每次去内存取值
 int *p = const_cast<int*>(&a);
 *p = 2;
 cout << "value a="<< a << endl;
 cout << "value *p=" <<*p << endl;
 cout << "address a=" <<&a << endl;
 cout << "address p=" <<p << endl;

请关注我的订阅号

订阅号.png

GESP C++ 各等级详细知识点汇总 函数声明与定义- 实参与形参,参数作用域- 参数传递方式:值传递、引用传递( 阅读详情

相关推荐

C++算法竞赛入门常用知识点

C++算法竞赛入门常用知识点

无限进步 1262

C++入门基础(下)

2022-05-17- 摘要 总结 目录 文章目录2022-05-17-摘要总结目录@[toc]内联函数概念特性内联函数的缺点一些其他不足auto关键字auto简介详细使用规则auto不能推导类型的场景基于范围的for循环范围for的语法范围for的使用条件新的指针空值nullptr C++11 内联函数 C++中函数的使用我们已经比较清楚了,与C语言中函数的使用大多相同,主要是增加了重载的特性,对C语言的函数的一些缺陷做了一些补充。 那么对于一些比较简单却又经常使用的功能,我们在C语言中常常使用宏来替

安安的博客 1149

C++面试知识点汇总

本博文是本文历时3个月,收集各种资料,汇总的C++面试题,欢迎各位博友评价。

zhangkkit的博客 5168

c++基础

C++基础 第一章、概述 1、在学习C++编程前,首先来重复一个基本的问题:程序由什么组成、算法的5大特征、以及面向对象的5大原则? 答:程序=数据结构+算法 算法的5个基本特征:确定性、有穷性、输入、输出、可行性。 确定性:算法的每一步骤必须有确切的定义; 有穷性:算法的有穷性是指算法必须能在执行有限个步骤之后终止; 输入:一个算法有0个或多个输入,以刻画运算对象的初始情况,所谓0个输入是指算法...

rsyrtg的博客 544

众灵科技24路舵机控制器二次开发实战(1)-STM32串口指令控制详解

本文详细介绍了如何通过STM32单片机对众灵科技24路舵机控制器进行二次开发,实现串口指令控制。文章从硬件连接、通信协议解析到STM32代码实战,手把手指导开发者打通控制链路,并重点讲解了如何调用预设动作组驱动六足机器人,为后续集成传感器、无线遥控等高级功能奠定基础。

weixin_29188997的博客 483

Windows 10/11下彻底卸载Chrome的终极指南(附一键清理注册表脚本)

本文提供了一份在Windows 10/11系统中彻底卸载Google Chrome浏览器的终极指南。针对标准卸载后用户数据与注册表项残留的问题,文章详细介绍了从应用程序层、用户数据层到系统集成层的三层深度清理方法,并重点提供了安全高效的注册表清理脚本,确保浏览器所有痕迹被完全移除,解决重装冲突并释放磁盘空间。

elastic6hunter的博客 781

<C++> 编译器优化问题

上面代码首先调用了test1函数构造了aa对象,然后使用值返回用aa对象拷贝构造了一个临时对象,然后调用拷贝构造函数构造出来了a1对象,所以一共调用了一次构造两次拷贝构造。如果对象比较大就会拷贝这个对象一次,然后再用拷贝出来的临时对象作为返回值,所以临时对象一定不在test栈帧中,一般是在需要接受test返回值的栈帧之中,临时对象具有常属性,只读不可修改。编译器的优化行为一般发生在一个表达式中,如果产生了一个临时对象,然后又用这个临时对象去构造一个对象,可能优化成直接去构造这个对象,就不会产生临时对象了。

weixin_61522065的博客 425

Claude Code+Cursor+Claude 4三剑合璧!5步搞定Magentic-UI的OAuth2.0集成,开发效率飙升500%,新手秒变全栈工程师

本文详细介绍了如何利用Claude Code、Cursor和Claude 4三款AI工具高效集成Magentic-UI的OAuth2.0认证系统。通过5个步骤的实战演练,开发者可快速完成从环境搭建、项目分析、代码生成到前端集成的全过程,显著提升开发效率。文章还提供了JWT令牌管理、安全配置及常见问题解决方案。

weixin_29234509的博客 453

编译器优化对函数的影响

编译器如gcc,可以指定不同的优化参数,在某些条件下,有些函数可能会被优化掉。 先写一个简单的测试文件: cat test.c inline void foo(void) { } 用命令 gcc -g -c test.c 编译成目标文件。 用 nm检查函数符号: $ nm -C test.o 00000000 T foo 现在使用 命令 gcc -g -c -O2 t

小猪爱拱地 1592

unity中判断是否联网,判断网络状态的两种方式

unity中判断是否联网,判断网络状态的两种方式

wla2996gpy032的博客 1876

如何使用JMeter进行大模型的压力测试?

使用JMeter进行大模型的压力测试,核心是通过模拟高并发请求,测试大模型在极限负载下的性能表现(如响应时间、吞吐量、错误率、资源瓶颈等)。通过以上步骤,可系统性评估大模型在高压下的性能极限,为优化模型(如量化、蒸馏)、扩容硬件(增加GPU)或调整接口限流策略提供数据依据。)、请求方式(POST)、认证方式(API Key放在Header)、请求参数(作用:避免瞬间高压压垮系统,更精准找到瓶颈点。等)、响应格式(同步/流式)。需获取:API地址(如。通过CSV文件引入不同。

alankuo的专栏 787

编译器的优化真是太难了!

一个编译器的好坏,主要是看这个编译器优化的效果是否良好,但曾有一位倍受编译器困扰的人曾说过:“优化设计是科学,但应用的顺序是艺术。”原文链接:https://faultlore.com/blah/oops-that-was-important/声明:本文为 优快云 翻译,未经允许,禁止转载。作者 |Aria Beingessner译者 | 弯月出品 | 优快云(ID:优快云news)编译器...

优快云资讯 3796

ARM CoreLink 系列 5.1.1 -- CI-700 System Address Map 】

所有的CHI 命令都包含一个 Source ID 和 Target ID, 其中 Source ID 可以来自于 RN Node, Target ID 可以来自于HN node或者其他 SN node. 其中 Target ID 是通过 SAM解析传入的地址得到的。对于 CI-700中的所有 RN Node 中的 SAM 多会进行配置,因为RN Node 访问的是整个地址空间,不同的地址空间会有不同的属性(I/O, DRAM),所以会对RN SAM 配置多个不同属性的区域。

CodingCos的博客 770

C/C++面试笔试知识点总结

C/C++面试问题 1 const关键字的作用? 1)欲阻止一个变量被改变,可使用const,在定义该const变量时,需先初始化,以后就没有机会改变他了; 2)对指针而言,可以指定指针本身为const,也可以指定指针所指的数据为const,或二者同时指定为const; 3)在一个函数声明中,const可以修饰形参表明他是一个输入参数,在函数内部不可以改变其值; 4)对于类的成员函数,有时候必须指定其为const类型,表明其是一个常函数,不能修改类的成员变量; 5)对于类的成员函数,有时候必须指定其返回值为

weixin_42538789的博客 1万+

杰理SDK UART 使用教程

本文介绍了JL701n芯片的UART串口通信使用教程,重点讲解了UART2的初始化配置、数据收发和中断处理功能。教程详细说明了代码结构、核心功能函数、配置参数和使用步骤,包括UART初始化、数据发送和接收中断处理等关键操作。同时提供了完整的API接口说明和注意事项,强调了中断处理限制、缓冲区管理、引脚配置等重要细节。最后给出了基本使用流程和高级任务处理示例,并针对常见问题提供了故障排除建议。本教程适用于需要在JL701n芯片上实现可靠串口通信的开发者。

qq_42014563的博客 1817

FPGA控制AD5754实现模数转换Verilog代码与测试激励

本文提供了使用FPGA控制AD5754芯片实现数字信号转模拟信号的Verilog代码和测试激励。AD5754芯片简介:AD5754是一款16位数字信号转模拟信号的芯片。通信方式:FPGA与AD5754之间通过SPI接口进行通信,SPI时钟clk的最大频率为30MHz。模拟输出:AD5754提供了四路模拟输出。工作方式:当前AD5754采用单极性工作方式。指令刷新:根据现有理解,同轴的速度和电流指令应同时刷新。利用LDAC引脚控制模拟信号的刷新,但需实际验证。CLR信号验证。

whm128的博客 244

【C++】 C++知识点总结

作者:李春港 出处:https://www.cnblogs.com/lcgbk/p/14643010.html 目录前言一、C++常用后缀二、头文件1、C++输入输出2、在C++中使用C的库函数三、指针与动态内存分配1、C2、C++四、命名空间1、作用2、定义3、使用空间成员4、命名空间嵌套5、匿名空间六、引用1、引用特点:2、引用的应用:七、函数重载八、函数缺省参数(默认参数)九、类与对象1、类2、对象十、构造和析构函数1、构造函数<1>构造函数定义及重载<2>构造函数参数列表..

nidongla的博客 935

精雕软件(JDpaint) 5.5 绿色版

精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版精雕软件(JDpaint) 5.5 绿色版

后端c++知识点总结

这一篇是C++的一些面试点的总结。 1、一个String类的完整实现必须很快速写出来(注意:赋值构造,operator=是关键)   如果对C++String不熟悉的话,先看http://www.cplusplus.com/reference/去了解一下String类常用的方法,如果想了解C语言的实现,去看一下《C语言接口与实现》(十五章 低级字符串)。   Scott Meyers在《ef

lijun538的专栏 4615

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

C++ 知识点总结 面经

C++面试常用知识点总结

Mr_atopos的博客 1209

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

【C++】modern-cpp-template - 知识点目录

Modern-cpp-template是一个C++项目模板,它提供了一个 Modern C++ 开发环境。该模板包含了许多实用工具和库,例如CMake构建系统、Doxygen文档生成工具、Google Test测试框架等。通过使用modern-cpp-template,可以快速搭建一个 Modern C++ 项目,而不必从头开始构建整个开发环境。

墨门 160

大一下cpp知识点大纲复习

操作符重载允许程序员为类或结构体定义操作符的行为。例如,对于一个表示复数的类Complex,可以通过重载操作符来实现两个复数的加法操作。这样,就可以像操作内置类型(如int或double)一样操作自定义类型。编译时多态(静态多态)通过函数重载和运算符重载实现。在编译时就已经确定调用的具体函数或运算符。运行时多态(动态多态)通过虚函数和虚继承实现。在运行时根据对象的实际类型调用相应的函数。

2401_87275185的博客 1017

C++核心知识点总结

C++核心知识点速查手册

thomaseff的博客 1856

C++ 知识点总结思维导图

C++ 知识点总结思维导图 【下载地址】C知识点总结思维导图 这是一份精心整理的C++知识点总结思维导图,由资深学习者基于自身经验绘制,涵盖C++基本概念、语法结构、面向对象编程、数据结构与算法等核心内容。采用思维导图形式,逻辑清晰,知识点之间的联系一目了然,帮助学习者快速掌握C++精髓。无论是初学者系统学习,还是有一定...

gitblog_06743的博客 464

C++知识点

C++知识点[ 2005-09-23 11:28:38 | 作者: Admin ] 字体大小: 大 | 中 | 小 这是对C++高效编程的一个总结, 很有指导作用.一、#include “filename.h”和#include 的区别#include “filename.h”是指编译器将从当前工作目录上开始查找此文件#include 是指编译器将从标准库目录中开始查找此文件二、头文件的作用加强

游戏人生的专栏 994

C++易错知识点更新

这篇博客主要记录自己C++和其他编程知识相关的易错知识点,以免时间久了又踩同样的坑。 以后将会陆续保持更新。

Werther的博客 492

cpp细碎知识点

派生类中定义一个与基类虚函数具有相同函数签名(函数名、参数列表、返回类型)的函数,这被称为重写。重写意味着派生类提供了基类虚函数的一个特定于派生类的实现。在C++中,友元函数不会被继承。这意味着如果你在一个基类中声明了一个友元函数,这个友元关系不会自动传递给派生类。每个类都必须单独声明其友元函数。虚函数)实现动态(运行时)多态性的。

2401_86517033的博客 278
上一篇: c++动静编译的区别
下一篇: linux进程调度策略详解
码哥说
博客等级 码龄8年 5645粉丝 172原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

码哥说

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值