C++Primer-day1 第六章

练习6.22编写一个函数交换两个int指针

第一个函数以值传递的方式使用指针,所有改变都局限于函数内部,当函数执行完毕后即不会改变指针本身的值,也不会改变指针所指的内容。

第二个函数同样以值传递的方式使用指针,但是函数内部通过解引用的方式直接访问内存并修改了指针所指的内容。


第三个函数的参数形式是 int &* ,其含义是(从右向左理解),该参数是一个引用,引用的对象是内存中的一个 int 指针,使用这种方式可以把指针当成对象,交换指针本身的值。需要注意的是,最后一个函数既然交换了指针,当然解引用该指针所得的结果也会相应发生改变。


#include<iostream>
#include<string.h>
#include <vector>
#include <ctime>
using namespace std;

void SwapPointer1(int *p, int *q) {
    int *temp = p;
    p = q;
    q = temp;
    cout << "p 的值是:" << p << ",q 的值是:" << q << endl;
}

// 该函数交换指针所指的内容
void SwapPointer2(int *p, int *q) {
    int temp = *p;
    *p = *q;
    *q = temp;
    cout << "p 的值是:" << p << ",q 的值是:" << q << endl;
}

// 该函数交换指针本身的值,即交换指针所指的内存地址
void SwapPointer3(int *&p, int *&q) {
    int *temp = p;
    p = q;
    q = temp;
    cout << "p 的值是:" << p << ",q 的值是:" << q << endl;
}

int main()
{
    int a=5,b=10;
    int *r=&a, *s=&b;
    cout << "交换前:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    SwapPointer1(r, s);
    cout << "交换后:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    cout << endl;

    a = 5, b = 10;
    r = &a, s = &b;
    cout << "交换前:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    SwapPointer2(r, s);
    cout << "交换后:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    cout << endl;

        a = 5, b = 10;
    r = &a, s = &b;
    cout << "交换前:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    SwapPointer3(r, s);
    cout << "交换后:" << endl;
    cout << "r 的值是:" << r << ",s 的值是:" << s << endl;
    cout << "r 所指的值是:" << *r << ",s 所指的值是:" << *s << endl;
    return 0;
}

练习6.23

根据参数的不同,为 print 函数设计几个版本。版本的区别主要体现在对指针参数的管理方式不同。

#include <iostream>
using namespace std;

// 参数是常量整型指针
void print1(const int *p) {
    cout << *p << endl;
}

// 参数有两个,分别是常量整型指针和数组的容量
void print2(const int *p, const int sz) {
    int i = 0;
    while (i != sz) {
        cout << *p++ << endl;
        ++i;
    }
}

// 参数有两个,分别是数组的首尾边界
void print3(const int *b, const int *e) {
    for (auto q = b; q != e; ++q)
        cout << *q << endl;
}

int main() {
    int i = 0, j[2] = {0, 1};
    print1(&i);
    cout << "------- a ---------" << endl;
    print1(j);
    cout << "------- b ---------" << endl;
    print2(&i, 1);
    cout << "------- c ---------" << endl;
    // 计算得到数组 j 的容量
    print2(j, sizeof(j) / sizeof(*j));
    cout << "------- d ---------" << endl;
    auto b = begin(j);
    auto e = end(j);
    print3(b, e);

    return 0;
}

输出一个数组时,可使用如下方法

void print(const int ia[], const int sz) {
  for (size_t i = 0; i != sz; ++i)
    cout << ia[i] << endl;
}

二、含有可变形参的参数

initializer_list形参

可以接受相同类型的可变形参
存储在il中的参数是常量值

void error_msg(initializer_list<string> il)
{
    for(auto beg=il.begin();beg!=il.end();++beg)
    {
        cout<<*beg<<" ";
    }
    cout<<endl;
}

省略符形参

void foo(parm_list,...);
void foo(...);

三、函数的返回:引用返回左值

修改字符串的其中一个字符

char &get_val(string &str,const string::size_type sz)
{
    return str[sz];
}
int main()
{
    string s("a values");
    get_val(s,0)='A';
    cout<<s;
}

返回数组指针

因为数组不能被拷贝,函数不能返回数组。不过函数可以返回数组的指针或引用。
最直接的方法是使用类型别名:
typedef int arrT[10] //arrT是一个类型别名,它表示的类型是含有10个整数的数组
using arrT = int[10];//arrT的等价声
arrT* func(int i);//返回一个指向含有10个整数的数组的指针

声明一个返回数组指针的函数

要想在声明func时不使用类型别名,必须记住被定义的名字后数组的维度

    int (*p)[10]=&arr;//p是一个指针,指向含有10个整数的数组

使用尾置返回类型

    auto func(int i) -> int(*)[10];//把函数的返回类型放在了形参列表之后,可以看到返回类型是一个指针
使用decltype

如果我们知道函数返回的指针将指向哪个数组,可以使用此关键字声明返回类型
decltype并不负责把数组类型转换成对应的指针,所以decltype的结果是个数组,要想表示arrPtr返回指针还必须在函数声明时加一个*符号

    int odd[]={1,3,5,7,9};
    int even[]={0,2,4,6,8};
//返回一个指针,该指针指向含有5个整数的数组
decltype(odd) *arrPtr(int i)
{
    return (i%2)? &odd:&even;    
}
练习6.36

编写一个函数声明,返回数组的引用并且该数组包含10个string对象

    string (&func())[10];

func()表示调用func函数无须任何实参,(&func())表示函数的返回结果是一个引用,(&func())[10]表示引用的对象是一个维度为10的数组,string (&func())[10]表示数组的元素是string对象

练习6.37

使用类型别名:

typedef string arr[10];
arr &func();

使用尾置返回类型:

auto func() ->string (&)[10];

使用 decltype 关键字:

string str[10];
decltype(str) &func();
练习6.38

数组也是一个对象,所以可以定义数组的引用。要想为数组引用赋值,只需要把数组名赋给该引用即可

int odd[] = {1, 3, 5, 7, 9};
int even[] = {0, 2, 4, 6, 8};
// 返回一个引用,该引用所引的对象是一个含有 5 个整数的数组
decltype(odd) &arrPtr(int i){
    return (i % 2) ? odd : even;		// 返回数组的引用
}

重载

const_cast和重载

//比较两个string对象的长度,返回较短的那个引用
const string &shorterString(const string &s1,const string &s2)
{
    return s1.size()<=s2.size()?s1:s2;
}

这个函数的参数和返回类型都是const string的引用。我们可以对两个非常量的string实参调用这个函数,返回结果仍是const string的引用。因此需要一个新的shortString函数,当它的实参不是常量时,得到的结果是一个普通的引用

string &shorterString(string &s1,string &s2)
{
    auto &r=shorterString(const_cast<const string&>(s1),
                          const_cast<const string&>(s2));
    return const_cast<string&>(r);
}

特殊语言用途特性

1、默认实参

某函数有这样一种形参,在函数的很多次调用中它们都被赋予一个相同的值,我们把这个反复出现的值称为函数的默认实参。调用含有默认实参的函数时,可以包含该实参,也可以省略该实参。
形式如下

typedef string::size_type sz;
string screen(sz ht=24,sz wid=80,char backgrnd='');

我们可以使用多个函数来调用该函数
只能省略尾部实参

string window;
window=screen();//screen(24,80,' ');
window=screen(66);//screen(66,801,' ');
window=screen('?');//screen(63,80,' ');

调用传递一个字符值是合法的,‘?’是一个char,函数最左侧的形参的类型string::size_type是一种无符号整数类型,发生隐式转换,作为height值传递给函数,‘?’对应的十六进制0x3F,十进制63

局部变量不能作为默认实参。只要表达式的类型能转换成形参所需的类型,表达式就能作为默认实参。

sz wd=80;
char def=' ';
sz ht();
string screen(sz=ht(),sz=wd,char=def);
string window=screen(); //ht(),80,' '

用作默认实参的名字在函数声明所在的作用域内解析

void f2()
{
    def='*';
    sz wd=100;
    window=screen();//调用screen(ht(),80,'*')
}

在函数内部改变了def值,所以对screen的调用将会传递这个更新过的值。另一方面,虽然我们的函数还声明了一个局部变量用于隐藏外层的wd,但是该局部变量与传递给screen的默认实参没有任何关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
《C Primer》是一本针对C语言初学者的经典教材,第五版的第六章主要介绍了函数的基本概念和用法。 在第六章中,教材首先介绍了函数的定义和调用。函数是具有独立功能的代码块,可以通过函数名来调用。函数由函数头、参数、函数体和返回值组成。函数头包括函数类型、函数名和参数类型,参数用于接收传递给函数的值,返回值用于将结果返回给调用者。 接着,教材详细介绍了函数的参数传递方式,包括按值传递、按引用传递和按指针传递。按值传递是指将参数的值复制给形参,按引用传递是指将参数的引用传递给形参,而按指针传递是将参数的地址传递给形参。不同的传递方式在函数内部对参数进行操作时,会对应不同的结果。 此外,教材还讲解了函数的返回值和函数的调用顺序。函数的返回值类型由函数头中的类型确定,可以是任意类型。当函数需要返回多个值时,可以使用结构体或指针进行返回。函数的调用顺序决定了函数执行的顺序,即哪个函数先执行,哪个函数后执行。 在函数的实现过程中,教材介绍了函数的定义和声明、局部变量和全局变量、递归等内容。通过这些知识点的学习,读者可以了解到函数的具体细节和一些实践技巧。 总的来说,第五版的第六章通过清晰的语言和丰富的例子,循序渐进地讲解了函数的基本概念和用法。学完这一章,读者将能够理解函数的定义、调用、参数传递、返回值和实现等方面的知识,为之后的学习和实践打下坚实的基础。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值