c++知识整理 编程模块

最近开始看c++primer,做一点记录!

函数的基础知识:

要使用函数,必须完成:

1. 提供函数的定义

2.提供函数原型

3.调用函数

了解一下,函数是如何返回它的返回值:

函数会通过将返回值复制到指定的cpu寄存器或者内存单元中来将其返回,随后,调用程序将查看该内存单元,在这一过程中,返回函数和调用函数必须就该内存中存储的数据的类型达成一致,函数原型将返回值类型告知调用程序,而函数的定义决定被调函数应该返回什么类型的数据。在原型中提供与定义中相同的信息似乎有些多余,但是这样做确实有道理。例如要让信差从办公室的桌上取走一些物品,则向信差和办公室的同事都交代自己的意图,将提高信差完成这项工作的概率(C++ primer)

函数原型与函数定义:

函数原型: 即函数声明

函数定义: 函数的定义

1. c++中为什么需要提供函数原型

原型描述了函数到编译器的接口,也就是说,它将函数的返回值类型和参数的类型,数量告诉编译器。

例如函数原型:

double cube(double x);

首先,原型会告诉编译器cube()函数有一个double类型的参数,如果程序没有提供这样的参数,原型将让编译器能够捕获到这种错误。在cube()函数完成计算之后,将把返回值放到指定的位置(CPU寄存器或者内存中),然后调用函数(main()函数)将从这个指定的位置取得返回值,由于原型中指出了函数cube的返回值类型为double,因此编译器知道应该检索多少个字节以及如何解释它们。

这样做可以解决编译时的效率问题,如果没有函数原型,编译器在编译过程中在文件中查找函数的定义的话,会比较耗时,同时在查找的过程中会停止对main()函数的编译,而且函数很可能不在文件中,这样,编译器将无权访问函数代码。

所以,综上所述,原型可以确保以下几点:

1. 编译器正确处理函数的返回值

2. 编译器检查使用的参数数目是否正确

3. 编译器检查使用的参数类型是否正确

上述在编译阶段进行的原型化称为静态类型检查(static type checking)

函数和数组

数组做为函数的指针,将数组的首地址作为参数传递给函数,,节省可复制整个数组所需的内存空间和时间。尤其是在遇到大的数组的时候。

int sum(int array[], int size)
{
    int M = sizeof array;     // sizeof array返回的是指向数组元素的指针 的字节数(大小) 
    // 指针本身并没有指出数组的长度
} 
int main(int argc, char *argv[])
{
    const int arraySize = 8;
    int cookies[arraySize] = {1,2,3,4,5,6,7,8};
    int L = sizeof cookies;     // 这里返回的是整个数组的长度(字节数) 
    int total = sum(cookies, arraySize);    
    return 0;
}

由于上面的原因,所以数组作为函数参数的时候必须显式的传递数组的大小

// 可以选择数组的起始和结束的位置
int total = sum(cookies+3, 4);   // 起始位置, 数组的长度  

在数组作为函数的参数时,使用const保护数组:

函数使用普通参数时,这种保护机制将自动实现,因为参数按值传递,使用的是实参的副本,但是当参数为数组时,应该考虑如何避免参数被修改。

// 使用const保护数组参数
void show_array(const double arr[], int n);

使用const限制数组参数,这意味着原始数组不一定得是常量,而是意味着不能再show_array()函数中修改数组中元素的值。

将数组的区间作为参数传入函数,在STL中,使用 “超尾” 的概念来指定区间,如数组名是指向数组第一个元素的指针,数组大小i为n,则数组名+n是执行数组中最后一个元素的后一个位置,所以会有“超尾”这个概念。

例如对之前的sum()函数做修改:

#include <iostream>
#include <algorithm>
using namespace std;

int sum(const int* begin, const int* end)
{
    int total = 0;
    const int* pt;   // pt指向常量的指针 
    for(pt=begin; pt<end; pt++)
    {
        total += *pt;
    } 
    return total;
}

int main(int argc, char *argv[])
{
    const int arraySize = 8;
    int cookies[arraySize] = {1,2,3,4,5,6,7,8};
    cout << sum(cookies, cookies+arraySize) << endl;;
    return 0;
}

关于const对指针的限制:
1. 可以用const来修饰指针,让指针指向一个常量对象,防止使用指针来修改指向元素的值

但是要注意:const *pt这样的声明,并不意味着pt指向的值实际就是一个常量,而实意味着对pt而言,这个值是一个常量,不能y用pt来修改他:

int age = 20;    // age不是一个常量 
const int* pt = &age;   // pt指向的age值是一个常量。不能修改:
age += 10;    // 可以修改age的值
*pt += 10;    // 不能修改age的值 

2.第二种使用const的方式使得无法修改指针的值:

int age = 20;    // age不是一个常量 
int* const pt = &age;   // 指针不能被修改,只能指向age
int sage = 80;
pt = &sage;     // 禁止这样修做! 

3. 第三种,可以声明指向const对象的const指针:

int age = 20;    // age不是一个常量 
const int* const pt = &age;   // 指针不能被修改,只能指向age,且不能通过指针修改age的值 

函数,二维数组

// 理解二维数组作为函数参数的原理

int data[3][4] = {{1,2,3,4},{2,3,4,5},{5,6,7,8}};
int sum(int arr[][4], int size)
{
    // 二维矩阵作为函数的参数
    // 参数arr相当于一个指针 ,指向数组的第一个元素,
    //这个数组的每一个元素都有四个元素组成,所以需要传入二维数组的列数
    //因此 arr[i]是一个由四个元素组成的数组的数组名
    int total = 0;
    for(int i=0; i<size; i++)
    {
        for(j=0; j<4; j++)
        {
            total += arr[i][j];
        }
    } 
    return total;
}

函数与c-风格字符串

包含字符,但是不以空值字符结尾的char类型的数组只是数组,而不是字符串。意味着不必将字符串的长度传递给函数。

#include <iostream>
#include <algorithm>
using namespace std;


unsigned int c_in_str(const char* str, char target)  // 寻找字符串中特定字符的个数 
{
    unsigned int count = 0;
    while(*str)    // != '\0'
    { 
        if(*str == target)
        {
            count++;
        }
        str++;
    }
    return count;
}

int main(int argc, char *argv[])
{
    char ghost[15] = "helloworld";
    char* str = "helloworld";
    cout << c_in_str(ghost, 'o') << endl;
    cout << c_in_str(str, 'l') << endl;
    return 0;
}

返回c-风格的字符串

#include <iostream>
#include <algorithm>
using namespace std;

char* build_str(char ch, int n)
{
    char* pstr = new char[n+1];     // new动态分配内存 
    pstr[n] = '\0';   // 字符串结尾
    while(n> 0)
    {
        n--;
        pstr[n] = ch;
    } 
    return pstr;
}
int main(int argc, char *argv[])
{
    char* p = build_str('M', 8);
    cout << p << endl;
    delete []p;     // 释放内存    
    return 0;
}

函数和结构体:

可以将一个结构赋值给另一个结构,也可以按值传递结构,就像普通变量一样。结构作为函数的参数,可以按值传递,但是按值传递的操作实际上函数中使用的是结构的副本(会将结构进行复制), 所以如果结构包含的数据量比较大的话,则复制结构需要增加内存需求,降低系统的运行速度。所以一般采用 pass by address 或者 pass by reference

#include <iostream>
#include <algorithm>
using namespace std;

// 定义结构
struct travel_time
{
    int hour;
    int minute;

    travel_time(int hour, int minute)
    {
        this->hour = hour;
        this->minute = minute;
    }
    travel_time()
    {
        this->hour = 0;
        this->minute = 0;
    }

};

const int minutes_perH = 60;
travel_time sum(travel_time, travel_time);    // 计算时间之和 
void show_time(travel_time);     // 显示时间 

int main(int argc, char *argv[])
{
    // travel_time time1 = {12, 45}; // 如果没有定义构造函数,则用这种方法初始化结构体 
    travel_time time1(12, 45);   
    travel_time time2(1, 35);        // 定义了构造函数的初始化方法 
    show_time(sum(time1, time2)); 
    return 0;
}


travel_time sum(travel_time t1, travel_time t2)
{
    travel_time total;
    total.minute = (t1.minute + t2.minute) % minutes_perH;
    total.hour = t1.hour + t2.hour + (t1.minute + t2.minute) / minutes_perH;
    return total;
}


void show_time(travel_time t)
{
    cout << "The total time is: " << t.hour << ":" << t.minute << endl;
} 

坐标的转化:

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

struct rect
{
    double x;
    double y;

    rect(double x, double y)
    {
        this->x = x;
        this->y = y;
    }

    rect()
    {
        this->x = 0;
        this->y = 0;
    }
};     // 定义结构

struct polar
{
    double dist;
    double angle;

    polar(double dist, double angle)
    {
        this->dist = dist;
        this->angle = angle;
    }

    polar()
    {
        this->dist = 0;
        this->angle = 0;
    }
}; 


// prototype
polar rect2polar(rect r)
{
    polar p;
    p.dist = sqrt(r.x*r.x + r.y*r.y);
    p.angle = atan2(r.y, r.x);
    return p;
}

void show_polar(polar p)
{
    const double rad2degree = 180.0/3.1415;
    cout << "The distance is " << p.dist << endl;
    cout << "The degree is " << p.angle*rad2degree << endl;
}

int main(int argc, char *argv[])
{
    rect r;
    polar p;
    cout << "Enter the rect.x and rect.y: ";
    while(cin >> r.x >> r.y)
    {
        p = rect2polar(r);
        show_polar(p);
        cout << "Next two numbers:(q to quit)";
    }
    return 0;
}




运行结果:

c++知识整理   编程模块

while(cin >> x >> y), cin是一个istream对象, cin >> x也是一个istream对象,因此cin >> x >> y最终返回的也是一个istream对象(cin), 而cin在被用于测试表达式中时,将根据输入是否成功,被转换为bool型的true和false.所以如果输入q, cin将知道输入不正确,从而q将被留在队列中,并返回一个被转换为false的值。

递归与分治:

例:绘制标尺

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

// 绘制标尺
void subdivide(char ar[], int low, int high, int level)
{
    // 递归函数, 采用分治的方法 
    if(level == 0)
    {
        return;
    }
    int mid = (low + high) / 2;
    ar[mid] = '|';
    subdivide(ar, low, mid, level-1);
    subdivide(ar, mid, high, level-1);
} 


int main(int argc, char *argv[])
{
    const int LEN = 66;
    const int divs = 6;
    char ruler[LEN];
    // 初始化ruler
    ruler[LEN-1] = '\0';
    for(int i=1; i<LEN-2; i++)
    {
        ruler[i] = ' ';
    } 

    int min_index = 0;
    int max_index = LEN-2;
    ruler[min_index] = ruler[max_index] = '|';
    cout << ruler << endl;
    for(int i=1; i<=divs; i++)
    {
        subdivide(ruler, min_index, max_index, i);
        cout << ruler << endl;
        // 将数组设置为空,以便进行下一次的分割
        for(int j=1; j<LEN-2; j++)
        {
            ruler[j] = ' ';
        } 
    }
    return 0;
}

运行结果:

c++知识整理   编程模块

------------------------------------------------------------------分割线------------------------------------------------------------------

原文链接: https://www.cnblogs.com/ncepubye/p/12724029.html

欢迎关注

微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍;

也有高质量的技术群,里面有嵌入式、搜广推等BAT大佬

    c++知识整理   编程模块

原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/396549

非原创文章文中已经注明原地址,如有侵权,联系删除

关注公众号【高性能架构探索】,第一时间获取最新文章

转载文章受原作者版权保护。转载请注明原作者出处!

(0)
上一篇 2023年4月6日 上午11:23
下一篇 2023年4月6日 上午11:23

相关推荐