关于常量指针与指针常量

const int *p=&a ; 

const 修饰的是取 的操作,因此取星操作不能改变,就是 p的值(p所指的内容)不可改变 ,但指针的方向可以改变。

int * const p = &a ; 

const修饰的是p这个指针,因此这个指针的指向固定了,但是指针所指的内容可以改变。

char *p="hello";
p="aa";//报错

这种方法初始化的字符串是常量字符串,不能修改,相当于const char *p=”hello”。

char p[]="hello";
p[0]='a';
cout<<p<<endl;

这是变量,可以修改。

char *p=new char[6];
p="hello";

下面是应用举例:

char* test(char* x) {
x[2] = '\0';//标记点
return x;
}
int main() {
    char* x = (char*)"svgc";
    cout << test(x);
}

会报错,因为传入的char*地址指向了一个字符串常量

char* test(char* x) {
    x[2] = '\0';
    return x;

}
int main() {
    char x[] = "svgc";
    cout << test(x);
}

这个就不会报错

指针与别名接收栈空间返回值

int test01()
{
    int a=10;
    return a;
}

int main()
{
    int &ref = test01();
    cout<<"ref = "<< ref << endl;
    cout<<"ref = "<< ref << endl;
}

输出结果: ref = 10
ref = 2425255

原因,拿别名指向返回值,但test01()执行完后,该返回值所在空间被释放了,编译器会默认保留返回值一次。
同理:

int test01()
{
    int a=10;
    return &a ;
}

int main()
{
    int *ref = test01();
    cout<<"ref = "<< *ref << endl;
    cout<<"ref = "<< *ref << endl;
}

输出结果: ref = 10
ref = 2425255
也是一个原因。其本质都是指针指向了一个被释放的栈空间而编译器只保留了一次。

引用和指针区别

定义上的区别

指针和引用主要有以下区别:

  1. 引用必须被初始化,但是不分配存储空间。指针不声明时初始化,在初始化的时候需要分配存储空间。
  2. 引用初始化后不能被改变,指针可以改变所指的对象。
  3. 不存在指向空值的引用,但是存在指向空值的指针。
    注意:引用作为函数参数时,会引发一定的问题,因为让引用作参数,目的就是想改变这个引用所指向地址的内容,而函数调用时传入的是实参,看不出函数的参数是正常变量,还是引用,因此可能引发错误。所以使用时一定要小心谨慎。

  从概念上讲。指针从本质上讲就是存放变量地址的一个变量,在逻辑上是独立的,它可以被改变,包括其所指向的地址的改变和其指向的地址中所存放的数据的改变。

  而引用是一个别名,它在逻辑上不是独立的,它的存在具有依附性,所以引用必须在一开始就被初始化,而且其引用的对象在其整个生命周期中是不能被改变的(自始至终只能依附于同一个变量)。

  指针和引⽤都是⼀种内存地址的概念,区别呢,指针是⼀个实体,引⽤只是⼀个别名。 在程序编译的时候,将指针和引⽤添加到符号表中。

  1. 指针它指向⼀块内存,指针的内容是所指向的内存的地址,在编译的时候,则是将“指针变量名-指针变量的地址”添加到符号表中,所以说,指针包含的内容是可以改变的,允许拷贝和赋值,有 const 和⾮ const 区别,甚⾄可以为空, sizeof 指针得到的是指针类型的⼤⼩。
  2. ⽽对于引⽤来说,它只是⼀块内存的别名,在添加到符号表的时候,是将”引用变量名-引⽤对象的地址”添加到符号表中,符号表⼀经完成不能改变,所以引⽤必须⽽且只能在定义时被绑定到⼀块内存上,后续不能更改,也不能为空,也没有 const 和⾮ const 区别。
    具体举例:https://zhuanlan.zhihu.com/p/140966943

    传参上的使用

      在C++中,指针和引用经常用于函数的参数传递,然而,指针传递参数和引用传递参数是有本质上的不同的:

  指针传递参数本质上是值传递的方式,它所传递的是一个地址值。值传递过程中,被调函数的形式参数作为被调函数的局部变量处理,即在栈中开辟了内存空间以存放由主调函数放进来的实参的值,从而成为了实参的一个副本。值传递的特点是被调函数对形式参数的任何操作都是作为局部变量进行,不会影响主调函数的实参变量的值。(这里是在说实参指针指向的地址值不会变,但地址的内容可能会变,调用的函数对传入指针指向的地址做备份,可以改变该地址内存的内容,但不能影响实参指针指向的地址)
  而在引用传递过程中, 被调函数的形式参数虽然也作为局部变量在栈中开辟了内存空间,但是这时存放的是由主调函数放进来的实参变量的地址(传进被调函数的东西是变量别名,但备份值传递的值是实参的地址!传递指针中传入被调函数的东西是指针指向的地址,备份的也是指针指向的地址)。被调函数对形参的任何操作都被处理成间接寻址,即通过栈中存放的地址访问主调函数中的实参变量。正因为如此,被调函数对形参做的任何操作都影响了主调函数中的实参变量。
  引用传递和指针传递是不同的,虽然它们都是在被调函数栈空间上的一个局部变量,但是任何对于引用参数的处理都会通过一个间接寻址的方式操作到主调函数中的相关变量。而对于指针传递的参数,被调函数只是复制了指针指向的地址,并不能改变实参指向的地址。引用传递的实参指的是变量本身,指针传递的实参指的是指针指向的地址,因此引用传递可以改变实参;指针传递不能改变实参(指针指向的地址),但能改变这个地址对应的内容。

下面是代码举例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include<iostream>
using namespace std;
//值传递
void change1(int n){
cout<<"值传递--函数操作地址"<<&n<<endl; //显示的是拷贝的地址而不是源地址
n++;
}

//引用传递
void change2(int & n){
cout<<"引用传递--函数操作地址"<<&n<<endl;
n++;
}
//指针传递
void change3(int *n){
cout<<"指针传递--函数操作地址 "<<n<<endl;
*n=*n+1;
}
int main(){
int n=10;
cout<<"实参的地址"<<&n<<endl;
change1(n);
cout<<"after change1() n="<<n<<endl;
change2(n);
cout<<"after change2() n="<<n<<endl;
change3(&n);
cout<<"after change3() n="<<n<<endl;
return true;
}

  为了进一步加深大家对指针和引用的区别,下面我从编译的角度来阐述它们之间的区别:

  程序在编译时分别将指 针和引用添加到符号表上,符号表上记录的是变量名及变量所对应地址。指针变量在符号表上对应的地址值为指针变量的地址值,而引用在符号表上对应的地址值为引用对象的地址值。符号表生成后就不会再改,因此指针可以改变其指向的对象(指针变量中的值可以改),而引用对象则不能修改。

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

★相同点:

  1. 都是地址的概念;

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

★不同点:

  1. 指针是一个实体,而引用仅是个别名;

  2. 引用只能在定义时被初始化一次,之后不可变;指针可变;引用“从一而终”,指针可以“见异思迁”;

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

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

  5. “sizeof 引用”得到的是所指向的变量(对象)的大小,而“sizeof 指针”得到的是指针本身的大小;

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

  7. 引用是类型安全的,而指针不是,(引用比指针多了类型检查)

CMake编译源文件

下面是利用CMake编译open62541库:
https://blog.csdn.net/qq_54227351/article/details/129380793?spm=1001.2101.3001.6650.2&utm_medium=distribute.pc_relevant.none-task-blog-2

  在软件开发中,库(或动态链接库)是编译好的二进制代码的集合,其中包含可重用的函数和类。当您链接一个库时,编译器会将库中的函数和类与您的源代码一起链接到一个可执行文件中。在这个可执行文件运行时,库中的函数和类就可以被调用和使用了。
  在Windows系统中,库文件通常有两种扩展名:.lib和.dll。.lib文件是静态库,其中包含了库的二进制代码,链接时会将代码静态地链接到您的应用程序中。.dll文件是动态链接库,其中包含了库的二进制代码,但是在链接时不会将代码静态地链接到您的应用程序中,而是在运行时动态地加载和链接库。
  在QT项目中,当您通过pro文件中的LIBS选项将库文件链接到您的项目中时,编译器会将库文件中的二进制代码与您的源代码链接到一起。因此,您不需要在源文件中包含库文件中的源代码文件(.c文件),因为这些文件已经被编译成了二进制代码,并且在链接时被链接到您的源代码中。

QT导入库可能出现的问题:
https://blog.csdn.net/yqahx/article/details/120012706


关于 char* , char[], string

char*与char[]

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;

int main()
{
const char* p = "12345";
char p[] = "12345";
}

  以上第一行代码,const char p = “12345”; 是在数据段(静态存储区)开了一块空间存”12345”,p指向的是静态区中的内容,不能通过修改p来改变其内容。C++11编译器编译这行代码时,甚至不加const也能通过编译。
  第二行代码,在数据段(静态存储区)开了一块空间存”12345”,然后编译器调用strcp()函数将其复制到函数栈中,随后释放掉数据段中的内容。

string

  string在数据结构中被称为串,通过观察其STL给出的接口函数,发现其很类似于动态数组(vector),那么用string和用vector< char>不是差不多嘛?
  其实并不是的,首先是两者被定义后被分配的位置。vector的底层是malloc()分配的地址,因此一定是被放在堆中。
  string的空间分配比较特殊,拿gcc来说,当定义的string s小于15个字符时,会被分配到栈中,而在15个字符以上时,就会被重新复制分配到堆中。
  二者的共同点是,当不断push_back超出最先给定的容量时,编译器会再次找到一块是原来二倍大的内存,将原有内容深拷贝进去。
  定义字符串时,C语言char str[] = “adadadad”;或char str[ 10 ];用C++的string时,只需要string str;相比C语言的字符串不需要提前指明str的具体大小!同时string的内部接口与STL的vector几乎一模一样,包括算法,迭代器的使用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

//string转char*
string strOutput = "Hello World";
const char* pszOutput = strOutput.c_str();//.c_str()返回的是const char*,因此不能直接给到char[]

//string转char[]
string str = "hello";
char p[8];

int i;

for(i=0; i<str.length(); ++i)
{
p[i] = str[i];
}

p[i] = '\0'; //这一步比较重要

//char*,char[]转string
const char* pszName = "liitdar";
char pszCamp[] = "alliance";

string strName;
string strCamp;

strName = pszName;
strCamp = pszCamp;

  因此说白了,string类似于char,但string类只做了string = 的重载,因此string strName = pszName可以直接等于char,但string转char 时就会发现实际上string s的只是一个类似char 的迭代器指针,因此还要用c_str()转为const char*。

  1. string 的反向迭代器reverse_iterator,是通过从后向前++来进行遍历的,rbegin()到rend()。
  2. 可以用vector< string>实现二维字符数组
  3. string .find()找到了返回首字符位置,找不到返回-1。
  4. stoi(const std::string& str, std::size_t pos = nullptr, int base = 10)可以把string转化为int,这里传一个char效果也是一样的。
  5. stoll(),string转long long 类型

仿函数 (leetcode347)

  仿函数是定义了一个含有operator()成员函数的对象,可以视为一个一般的函数,只不过这个函数功能是在一个类中的运算符operator()中实现,是一个函数对象,它将函数作为参数传递的方式来使用。

struct仿函数

1
2
3
4
5
6
7
8

struct myclass{
int operator()(int x, int y){return x+3*y}
}myobj;


int c = myobj(int a,int b)

在struct中进行了()重载。
优点:

  1. 仿函数比函数指针的执行速度快,函数指针时通过地址调用,而仿函数是对运算符operator进行自定义来提高调用的效率。
  2. 仿函数比一般函数灵活,可以同时拥有两个不同的状态实体,一般函数不具备此种功能。
  3. 仿函数可以作为模板参数使用,因为每个仿函数都拥有自己的类型。

缺点:

  1. 需要单独实现一个类。
  2. 定义形式比较复杂。