C++ 语法基础(三)(三)

简介: C++ 语法基础(三)(三)

指针与函数

知识点


如何在函数中修改传入变量的值?一种常用的方法是使用指针。

void swap(int *a, int *b) 
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
int main() 
{
    int a = 1, b = 2;
    swap(&a, &b);
}


image.png

求解一元二次方程的函数

知识点


image.png

//题目描述:
//请实现函数 `int SolveQuadratic(double a, double b, double c, double *px1,  double *px2)`,返回一元二次方程 ax^2 + bx + c = 0 的两个根,并返回不同的根的个数。如果 a=0,返回3。
//输入:无
//输出:
//2
//2 1
#include <iostream>
int SolveQuadratic(double a, double b, double c, double *px1, double *px2)  
{ 
  double disc, sqrtDisc;
  if(a == 0) return 3;
  disc = b * b - 4 * a * c;
  if( disc < 0 ) return 0;
  if ( disc == 0 ) { *px1 = -b /(2 * a); return 1;}
  sqrtDisc = sqrt(disc);
  *px1 = (-b + sqrtDisc) / (2 * a);
  *px2 = (-b - sqrtDisc) / (2 * a);
  return 2;
}
int main()
{
  double x1, x2;
  std::cout << SolveQuadratic(1, -3, 2, &x1, &x2) << std::endl;
  std::cout << x1 << " " << x2 << std::endl;
}

返回指针的函数

知识点


指向全局变量的指针可以作为函数返回值。


但局部变量不可以。局部变量的生命周期为:声明时,直到当前作用域(花括号)结束。函数返回后,该局部变量对应内存被回收,指向它的指针无意义。


指向new得到的内存的指针可以作为返回值。该内存在堆上分配,直到被delete之前都不会被回收。


int tmp;
int *func1 (int val) 
{
    int a;
    int *b = new int [2];
    // return &val; // err: val的生命周期在return时就结束了
    // return &a; // err: 同上
    // return b; // ok
    return &tmp; // ok
}

image.png


引用概念

知识点


引用可以理解为变量的"别名"。同时,也可以理解为一个type *const指针,即指针指向的对象的值可变,但指针本身的地址不可变。在指针的基础上,引用省略了取地址和解引用。


对引用的操作(求值,修改等)始终绑定在原对象上。


int a = 1;
int &b = a; // a == 1; b == 1
a = 2; // a == 2; b == 2
b = 3; // a == 3; b == 3
cout << sizeof(b) << endl; // 4; 和sizeof(a)相同
cout << &b << ' ' << &a << ' ' << (&a == &b) << endl; // true; a和b的地址是一样的
int *c = &b; // 此时c指向a
cout << sizeof(c) << endl; // 64位系统上为8; 和上面sizeof(b)做对比


image.png

image.png

引用传递

知识点


如果想在函数内修改一个对象(而非数组)的值,传引用是更现代的方式。


引用的另一个作用是可以让函数返回多个值。此时,只要传入多个引用,然后把他们当作返回值修改即可。


void swap(int &a, int &b) 
{
    int tmp = a;
    a = b;
    b = tmp;
}
void return2value(int &a, int &b) 
{ //returns {3, 4}
    a = 3, b = 4;
}
int main() 
{
    int a, b;
    return2value(a, b);
    swap(a, b);
    cout << a << ' ' << b << '\n';
}


即使不需要修改传入对象的值,按引用传递往往也是更高效的方式。此时,常常使用常量引用传递。


这对于大对象的传递尤其有效。按值传递大对象时,会导致对整个对象的拷贝,可能很慢;而按引用传递时,开销仅仅为传一个指针(64 位计算机中为 8 bytes)。


因此,大多数时候,C++的函数参数为常量引用或普通引用,非引用/指针的参数出现较少。

// struct的知识会在后面学到,现在只需要知道BigType是个很大的对象: sizeof(BigType) == 4000
struct BigType 
{
    int val[1000];
};
void func1(BigType a) // copies 4000 bytes
{ 
    // ...
}
void func2(const BigType &a) // copies 8 bytes
{ 
    // ...
}
//题目描述:
//实现函数`myFindCnt`,返回`vector`数组中某个值`val`第一次出现的下标。
//如果该值不存在,返回数组的长度。
//同时,要在函数的cnt参数中返回这个值出现的次数。
//输入:无
//输出:
//4
//2
#include <iostream>
#include <vector>
using namespace std;
int myFindCnt(const vector<int> &arr, int val, int &cnt)
{
    int res = arr.size();
    cnt = 0;
    for (int i = 0; i < arr.size(); ++i)
    {
        if (arr[i] == val)
        {
            if (res == arr.size())
            {
                res = i;
            }
            ++cnt;
        }
    }
    return res;
}
int main()
{
    vector<int> arr = {1, 2, 6, 1, 3, 4, 2, 4, 3};
    int cnt;
    std::cout << myFindCnt(arr, 3, cnt) << std::endl;
    std::cout << cnt << std::endl;
}

知识点


函数也可以返回变量的引用,此时和指针类似。可以返回的引用包括:


对全局变量的引用

返回函数的引用类型参数

返回函数指针类型参数解引用之后的结果

同上,不能返回局部变量的引用。

int glob;
int& func(int a, int &b, int *c) 
{
    int tmp;
    return a; // err
    return tmp; // err
    return b; // ok
    return *c; // ok: *c为引用类型
    return glob; // ok
}

image.png

相关文章
|
12天前
|
编译器 C++ 容器
C++语言的基本语法
想掌握一门编程语言,第一步就是需要熟悉基本的环境,然后就是最重要的语法知识。 C++ 程序可以定义为对象的集合,这些对象通过调用彼此的方法进行交互。现在让我们简要地看一下什么是类、对象,方法、即时变量。 对象 - 对象具有状态和行为。例如:一只狗的状态 - 颜色、名称、品种,行为 - 摇动、叫唤、吃。对象是类的实例。 类 - 类可以定义为描述对象行为/状态的模板/蓝图。 方法 - 从基本上说,一个方法表示一种行为。一个类可以包含多个方法。可以在方法中写入逻辑、操作数据以及执行所有的动作。 即时变量 - 每个对象都有其独特的即时变量。对象的状态是由这些即时变量的值创建的。 完整关键字
29 2
|
1月前
|
Java 编译器 程序员
C++中的语法知识虚继承和虚基类
**C++中的多继承可能导致命名冲突和数据冗余,尤其在菱形继承中。为解决这一问题,C++引入了虚继承(virtual inheritance),确保派生类只保留虚基类的一份实例,消除二义性。虚继承通过`virtual`关键字指定,允许明确访问特定路径上的成员,如`B::m_a`或`C::m_a`。这样,即使基类在继承链中多次出现,也只有一份成员副本,简化了内存布局并避免冲突。虚继承应在需要时提前在继承声明中指定,影响到从虚基类派生的所有后代类。**
44 7
|
1月前
|
编译器 C++ 开发者
C++一分钟之-属性(attributes)与属性语法
【7月更文挑战第3天】C++的属性(attributes)自C++11起允许附加编译器指令,如`[[nodiscard]]`和`[[maybe_unused]]`,影响优化和警告。注意属性放置、兼容性和适度使用,以确保代码清晰和可移植。示例展示了如何使用属性来提示编译器处理返回值和未使用变量,以及利用编译器扩展进行自动清理。属性是提升代码质量的工具,但应谨慎使用。
49 13
|
2月前
|
编译器 程序员 C++
C++一分钟之-属性(attributed)与属性语法
【6月更文挑战第28天】C++的属性为代码添加元数据,帮助编译器理解意图。C++11引入属性语法`[[attribute]]`,但支持取决于编译器。常见属性如`nodiscard`提示检查返回值,`maybe_unused`防止未使用警告。问题包括兼容性、过度依赖和误用。使用属性时需谨慎,确保团队共识,适时更新以适应C++新特性。通过示例展示了`nodiscard`和`likely/unlikely`的用法,强调正确使用属性能提升代码质量和性能。
52 13
|
2月前
|
编译器 C语言 C++
|
2月前
|
存储 自然语言处理 编译器
【C++语言1】基本语法
【C++语言1】基本语法
|
3月前
|
编译器 C语言 C++
C++的基本特性和语法
C++的基本特性和语法
46 1
|
3月前
|
存储 C++ 容器
【C++从练气到飞升】09---string语法指南(二)
【C++从练气到飞升】09---string语法指南(二)
|
3月前
|
存储 Linux C语言
【C++从练气到飞升】09---string语法指南(一)
【C++从练气到飞升】09---string语法指南(一)