语法与数据结构基础
如何通过函数修改实参的值
1.改变正常变量的实参
#include<stdio.h>
int main(void){
int i =10;
f(&i);
printf("%d\n",i);
}
void f(int *p){
*p = 99 ; //调用函数传递元素的地址,更改其实参值。
}
// 指针是唯一能够将调用函数中的局部变量传入到主函数中的方法
// 最终的输出结果为 99 通过指针将数据覆盖到实参的原位置,从而改变了实参值。
2.改变指针变量的实参
//改变指针变量的实参
#include<stdio.h>
// unsafe !!!
int main(void){
int i =10;
int *p = &i; //int *p ; p = &i;
printf("%d\n",p);
f(&p); //直接传数值到函数f()只能作为形参,不能改变实参值,只能传递地址元素。
printf("%d\n",p);
}
void f(int **q){ // 为了配合&p的数据类型,为指针的指针。
*q = (int *)0xFFFFFFFF; //调用函数传递元素的地址,更改其实参值。
// *q 理解为q的实参(q为指针的指针,因此q的实参其实也是指针(地址))
}
形参与实参
#include<stdio.h>
void A(int* p){//此处p为形参
int x = 3;
p = &x;
printf("在函数中p中的地址为%d\n\n",p);
}
//形参出现在函数定义中,在整个函数体内都可以使用, 离开该函数则不能使用(形参没有确定的值,只有函数内部运行时才是存在的)
//更底层的讲,形参是使用栈保存的,函数调用时入栈。
//而实参一般直接分配内存空间,存储在堆空间之中。
int main(){
int e = 9;
int* p = &e; //此处p为实参
printf("main:p中的地址为: %d\n\n",p);
A(p);//此时传入的是地址值,只能作为形参;当传入地址时才能做实参。
printf("main:p中的地址为: %d\n\n",p);
return 0;
//main:p中的地址为: 1030875172
//在函数中p中的地址为1030875148
//main:p中的地址为: 1030875172
//假如在main方法中有一个指针变量p,它指向的内存地址为20000。现在将它传递到一个函数中,在这个函数里让它指向内存地址为20048。当函数执行完返回到main函数时,p所指向的地址还是20000,而不是20048。
读入未知数目输入
// 读入未知数目的输入
#include<iostream>
using namespace std;
int main(){
int sum =0,value;
while(std::cin >> value)
sum += value;
std::cout << "sum is "<<sum<<std::endl;
return 0;
}
类与结构体
结构体:为了表示一些复杂的数据,而普通的基本类型变量无法满足要求;因此产生了结构体 ,结构体即用户根据实际需要自己定义的复合数据类型。
struct Student st = {1000, "zhangsan", 20}
struct Student * pst = &st;
\1. st.sid
\2. pst->sid
pst 所指向的结构体变量中的 sid 这个成员
#include<stdio.h>
#include <cstring>
struct Student
{
int sid;
char name[200];
int age;
};//创建类
int main(void){
struct Student st;//创建一个名为st的对象
f(&st);
g(st);
g2(&st);
// printf("%d %s %d\n",st.sid,st.name,st.age);
return 0;
}
// 结构体变量可以相互赋值,但实参一共占208个字节,浪费空间且耗时间。
// 传指针省空间 省时间。
void g(struct Student st)
{
printf("%d %s %d\n",st.sid,st.name,st.age);
}
void g2(struct Student *st)
{
printf("%d %s %d\n",st->sid,st->name,st->age);
}
void f (struct Student *pst) //想要传入实参改变实值必须传入地址
{
(*pst).sid = 99;
// *p.sid = p->sid (普通变量).sid == (指针变量)->sid
strcpy(pst->name,"zhangsan");
pst->age = 22;
}
类:
使用类的时候必须知道:1.类的名称2.类在哪里定义3.类支持什么操作。
成员函数:成员函数是类定义的方法,有时称作类方法。
#include <iostream>
#include <cstring>
using namespace std;
class C{
public:
C(const char *s ="",int i=0,double d=1){
strcpy(dataMember1,s);
dataMember2 = i;
dataMember3 = d;
}
void memberFunction1(){
cout << dataMember1 <<"\n" << dataMember2 <<"\n" << dataMember3 <<endl;
}
void memberFunction2(int i,const char *s = "unkonwn"){
dataMember2 = i;
cout << i << "received from"<< s <<endl;// ''一个词;“” 一句
}
protected://protected:只允许本类及子类的成员函数访问
char dataMember1[20];
int dataMember2;
double dataMember3;
};
int main()
{
C object1("object1",100,2000);//, object2("object2"), object3;
//在创建的对象中调用函数。
object1.memberFunction1();
object1.memberFunction2(123,"object2");
return 0;
}
class intClass{
int storage[50];
};
class floatClass{
float storage[50];
};
//使用模板声明一个通用类,在定义对象时确定对象引用什么类型的数据。
template<class genType>
class gneClass{
genType storage[50];
};
genClass<int> intObject;
genClass<float> floatObject;
//将数组大小定义也推迟到创建对象的时候
template<class genType,int size =50> //class可用typename替换
class GneClass{
genyType storge[size];
};
genClass<int> intObject1;//use the default size;
genClass<int,100> intObject2;
genClass<float,123> floatObject;
// 使用模板定义一个交换函数
template<class genType> //class可用typename替换
void swap(genType & e11,genType &e12){
genType tmp = e11; e11 = e12; e12 = tmp;
}
swap(n,m);
// template 模板的用法
template<int N, int LO = 1, int HI = N>
struct Sqrt {
static constexpr auto mid = (LO + HI + 1) / 2;
static constexpr auto value =
(N < mid * mid) ?
Sqrt<N, LO, mid - 1>::value
:
Sqrt<N, mid, HI>::value;
};
template<int N, int M>
struct Sqrt<N, M, M> { // 终止条件为LO和HI相等
static constexpr auto value = M;
};
//类模板
template<typename T> //在模板定义语法中关键字 class 与 typename 的作用完全一样;这里 class 关键字表明T是一个类型,后来为了避免 class 在这两个地方的使用可能给人带来混淆,所以引入了 typename 这个关键字,它的作用同 class 一样表明后面的符号为一个类型。
class A {
public:
A(T y) : x(y) {}
private:
T x;
};
A<int> a(10);
继承
#include <iostream>
using namespace std;
class BaseClass {
public:
BaseClass() { } //成员变量空置
void f(const char *s = "unknown") { //定义成员函数
cout << "Function f() in BaseClass called from " << s << endl;
h();
}
protected: //基类的受保护乘员只能在派生类中调用,因此f()在Derived1Level1、Derived2Level1中调用都是合法的,但在main()中调用是非法的
void g(const char *s = "unknown") {
cout << "Function g() in BaseClass called from " << s << endl;
}
private: // 私有类中定义的函数只能在BaseClass内部定义的函数调用;使用main()或者BaseClass的派生类调用都是非法的。
void h() {
cout << "Function h() in BaseClass\n";
}
};
class Derived1Level1 : public virtual BaseClass { // :后面加public,将继承指定为公有继承;(公有继续共有,受保护继续受保护)
public:
void f(const char *s = "unknown") {
cout << "Function f() in Derived1Level1 called from " << s << endl;
g("Derived1Level1");
h("Derived1Level1"); //g来自于base类;h来自于该派生类自己定义的函数
}
void h(const char *s = "unknown") {
cout << "Function h() in Derived1Level1 called from " << s << endl;
}
};
class Derived2Level1 : public virtual BaseClass { //virtual 用于继承防止冗余;确保DerivedLevel2 仅包含BaseClass成员函数的一个副本
public:
void f(const char *s = "unknown") {
cout << "Function f() in Derived2Level1 called from " << s << endl;
g("Derived2Level1");
// h(); // error: BaseClass::h() is not accessible
}
};
class DerivedLevel2 : public Derived1Level1, public Derived2Level1 {
public:
void f(const char *s = "unknown") {
cout << "Function f() in DerivedLevel2 called from " << s << endl;
g("DerivedLevel2");
Derived1Level1::h("DerivedLevel2");
BaseClass::f("DerivedLevel2");
}
};
int main() {
BaseClass bc;
Derived1Level1 d1l1;
Derived2Level1 d2l1;
DerivedLevel2 dl2;
bc.f("main(1)");
// bc.g(); // error: BaseClass::g() is not accessible
// bc.h(); // error: BaseClass::h() is not accessible
d1l1.f("main(2)");
// d1l1.g(); // error: BaseClass::g() is not accessible 注: Derived1Level1 继承自BaseClass的g()仍然是protected,不能在main中直接调用
d1l1.h("main(3)");
d2l1.f("main(4)");
// d2l1.g(); // error: BaseClass::g() is not accessible 注: Derived2Level1 继承自BaseClass的g()仍然是protected,不能在main中直接调用
// d2l1.h(); // error: BaseClass::h() is not accessible 注: Derived2Level1 继承自BaseClass的h()仍然是private,不能在main中直接调用
dl2.f("main(5)");
// dl2.g(); // error: BaseClass::h() is not accessible
dl2.h(); //h是继承自Derived1Level1的public函数h();
return 0;
}
指针&动态分配内存
*p = 20 //这里的(*)是一个间接寻址运算符
// malloc动态分配内存
#include<stdio.h>
#include<malloc.h>
int main(void ){
int a[5] = {4,10,2,8,6};
int len;
printf("请输入你需要分配的数组长度:");
scanf("%d",&len);
int *pArr = (int *)malloc(sizeof(int)*len);
//*pArr = 4; //动态分配的数组和普通数组有一样的用法即*pArr = pArr[0]
//pArr[1] = 10;
//printf("%d %d",*pArr,pArr[1]);
//我们可以当做一个普通数组来使用
for(int i = 0;i <len;i++)
scanf("%d",&pArr[i]);
for(int i = 0;i <len;i++)
printf("%d\n",*(pArr+i));
free(*pArr); //动态数组可以随时释放节省内存,释放(4字节)*len 大小的内存。
return 0;
}
// C++中想要动态的分配合回收动态空间,主要使用new和delete两个函数
p = new int;
//指示程序向内存管理器请求足够的空间来存储一个整数,这部分内存的地址存放在p中;现在可以间接地通过指针对p指向的内存块赋值,也可以使用q = p将存储在p中的地址赋值给另一个指针。
// int *p = (int *)malloc(sizeof(int));
delete p; //回收内存;为了防止内存泄露,使用完内存后应当即时释放内存空间。
指针和数组
int n =10, *p = &n; delete p; int a[10],*q = a; // a性质和一致 delete [] q; //动态分配数组空间 //int n = 10; p = new int[n] delete [] p
指针与复制构造函数
#include <iostream> #include <cstring> using namespace std; struct Node{ char *name; int age; Node(const char *n = "",int a=0){ name = strdup(n); age = a; } }; int main() { Node node1("Roger",20),node2(node1); strcpy(node2.name,"Wendy"); node2.age = 30; cout << node1.name << ' '<<node1.age << ' '<<node2.name<<' '<<node2.age; return 0; } // 运行结果:Wendy 20 Wendy 30
// 使用新的构造函数,声明node2(node1)生成了"Roger"的·一个副本(图c),node.name指向该副本,给一个数值成员赋值并不会影响到另一个成员。 struct Node{ char *name; int age; Node(const char *n = "",int a=0){ name = strdup(n); age = a; } //Node(const Node * n)// 只传地址; n的数据结构:Node *n Node(const Node &n){ // 只传地址; n的数据结构:Node n // &n = x name = strdup(n.name);//*n.name or n->name age = n.age; } };
//入果用户没有提供赋值运算符的定义,以下操作 node1 = node2; // 就会对逐个成员进行复制,引起图(a)图(b)所示的问题 //为了避免这一问题,用户必须重载运算符,以下代码可以帮node完成这一任务。 Node & operator = (const Node &n){ if(this != 0){ if(name != 0) free(name); name = strdup(n.name); age = n.age; } return *this; } // 每个对象都可以通过指针this访问自己的地址;所以*this就是对象本身。
函数指针
// 考虑一个简单的函数 double f(double x){ return 2*x; } // 对于这个定义 f是指向函数f()的指针; *f()是函数本身 // double *f(double) 返回一个指向double值得指针 double sum(double (*f)(double),int n,int m ){//传的都是形参 double result =0; for(int i = n;i<=m;i++){ result += i; } return result; } //调用函数sum()时,需要提供一个具有double参数并返回double值的函数,这个函数既可以是自定义的也可以是内置的。 // cout << sum(f,1,5) << endl; // cout << sum(sin,3,7) << endl; //在函数sum()的定义中,第一个形参的声明 // double (*f)(double) // 意味着 f是一个指向函数的指针,该函数带有一个double参数 //示例: 在某个区间寻找连续函数的根 、 // 使用二分法求根 double root(double (*f)(double),double a,double b,double epsilon){ //第一个声明将函数 f 作为形参传入 double middle = (a+b)/2; while( middle != 0 && fabs(b-a) > epsilon){ if(f(a)*f(middle)<0) b = middle; else a = middle; middle = (a+b)/2; } return middle; }
跨函数使用内存
只有在使用动态内存,且动态内存没有free的前提下,函数调用时所占用的内存会保留。
#include<stdio.h>
struct Student{
int sid ;
int age;
};
struct Student *CreateStudent(void); //创建
void ShowStudent(struct Student *);
int main (void){
struct Student *ps; //创建一个动态数据类型的结构体
ps = CreateStudent(); //ps == p
ShowStudent(ps);
return 0;
}
void ShowStudent(struct Student *pst)
{
printf("%d %d",pst->sid,pst->age);
}
struct Student *CreateStudent(void){
struct Student *p = (struct Student *)malloc(sizeof(struct Student)); //创建了一个Student 结构体动态分配变量
p->sid = 99;
p->age = 20;
return p;
}
多态性
#include <iostream>
using namespace std;
class Class1 {
public:
virtual void f() {
cout << "Function f() in Class1\n";
}
void g() {
cout << "Function g() in Class1\n";
}
};
class Class2 {
public:
virtual void f() {
cout << "Function f() in Class2\n";
}
void g() {
cout << "Function g() in Class2\n";
}
};
class Class3 {
public:
void h() {
cout << "Function h() in Class3\n";
}
};
int main() {
Class1 object1, *p;
Class2 object2;
Class3 object3;
p = &object1;
p->f(); //Function f() in Class1
p->g(); //Function g() in Class1
p = (Class1*) &object2;
p->f(); // Function f() in Class2
p->g(); // Function g() in Class1
p = (Class1*) &object3;
// p->f(); // Abnormal program termination;
p->g(); //Function g() in Class1
// p->h(); // h() is not a member of Class1;
return 0;
}
标准模板库
函数对象
// 定义一个重载函数调用运算符的类实现同样的任务
标准模板库中的向量
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional> // greater<>
using namespace std;
template<class T>
void printVector(char *s, const vector<T>& v) {
cout << s << " = (";
if (v.size() == 0) {
cout << ")\n";
return;
}
typename vector<T>::const_iterator i = v.begin();
for ( ; i != v.end()-1; i++)
cout << *i << ' ';
cout << *i << ")\n";
}
bool f1(int n) {
return n < 4;
}
int main() {
int a[] = {1,2,3,4,5};
vector<int> v1;
printVector("v1",v1); // v1 is empty, size = 0, capacity = 0
cout << "size = " << v1.size() << ", capacity = " << v1.capacity() << endl;
for (int j = 1; j <= 5; j++)
v1.push_back(j); // v1 = (1 2 3 4 5), size = 5, capacity = 8
printVector("v1",v1);
cout << "size = " << v1.size() << ", capacity = " << v1.capacity() << endl;
vector<int> v2(3,7);
printVector("v2",v2); // v2 = (7 7 7)
vector<int>::iterator i1 = v1.begin()+1;
vector<int> v3(i1,i1+2);
printVector("v3",v3); // v3 = (2 3), size = 2, capacity = 2
cout << "size = " << v3.size() << ", capacity = " << v3.capacity() << endl;
vector<int> v4(v1);
printVector("v4",v4); // v4 = (1 2 3 4 5), size = 5, capacity = 5
cout << "size = " << v4.size() << ", capacity = " << v4.capacity() << endl;
vector<int> v5(5);
printVector("v5",v5); // v5 = (0 0 0 0 0)
v5[1] = v5[3] = 9;
printVector("v5",v5); // v5 = (0 9 0 9 0)
v3.reserve(6);
printVector("v3",v3); // v3 = (2 3), size = 2, capacity = 6
cout << "size = " << v3.size() << ", capacity = " << v3.capacity() << endl;
v4.resize(7);
printVector("v4",v4); // v4 = (1 2 3 4 5 0 0), size = 7, capacity = 10
cout << "size = " << v4.size() << ", capacity = " << v4.capacity() << endl;
v4.resize(3);
printVector("v4",v4); // v4 = (1 2 3), size = 3, capacity = 10
cout << "size = " << v4.size() << ", capacity = " << v4.capacity() << endl;
v4.clear();
printVector("v4",v4); // v4 is empty, size = 0, capacity = 10 (!)
cout << "size = " << v4.size() << ", capacity = " << v4.capacity() << endl;
v4.insert(v4.end(),v3[1]);
printVector("v4",v4); // v4 = (3)
v4.insert(v4.end(),v3[1]);
printVector("v4",v4); // v4 = (3 3)
v4.insert(v4.end(),2,4);
printVector("v4",v4); // v4 = (3 3 4 4)
v4.insert(v4.end(),v1.begin()+1,v1.end()-1);
printVector("v4",v4); // v4 = (3 3 4 4 2 3 4)
v4.erase(v4.end()-2);
printVector("v4",v4); // v4 = (3 3 4 4 2 4)
v4.erase(v4.begin(), v4.begin()+4);
printVector("v4",v4); // v4 = (2 4)
v4.assign(3,8);
printVector("v4",v4); // v4 = (8 8 8)
v4.assign(a,a+3);
printVector("v4",v4); // v4 = (1 2 3)
vector<int>::reverse_iterator i3 = v4.rbegin();
for ( ; i3 != v4.rend(); i3++)
cout << *i3 << ' '; // print: 3 2 1
cout << endl;
// algorithms
v5[0] = 3;
printVector("v5",v5); // v5 = (3 9 0 9 0)
replace_if(v5.begin(),v5.end(),f1,7);
printVector("v5",v5); // v5 = (7 9 7 9 7)
v5[0] = 3; v5[2] = v5[4] = 0;
printVector("v5",v5); // v5 = (3 9 0 9 0)
replace(v5.begin(),v5.end(),0,7);
printVector("v5",v5); // v5 = (3 9 7 9 7)
sort(v5.begin(),v5.end());
printVector("v5",v5); // v5 = (3 7 7 9 9)
sort(v5.begin(),v5.end(),greater<int>());
printVector("v5",v5); // v5 = (9 9 7 7 3)
v5.front() = 2;
printVector("v5",v5); // v5 = (2 9 7 7 3)
}