c++知识整理 编程模块

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-1 02:08   49   0

最近开始看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;
}




运行结果:

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;
}

运行结果:

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

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP