内部数据类型:int char bool float double
还可以通过数组int[]、指针int&、引用int*等来定义基于上面这些数据类型以及其他外部数据类型的变异类型。
字符型由一个字节(8bit)组成,可以看作为整数的子集,所以运算可以参与到整型数中去,只要不超过其范围。
C-串是以一个全0位字节作为结束符的字符序列。C-串的空间长度为字符串长度加1,C-串的类型准确说来是const char*。
C-串是字符指针,比较C-串时,会因空间位置的不同而不同。
C-串的复制问题:
char* str1="hello"; char* str2=str1;//str1 str2共享"hello"空间 //数组之间不能复制 char a1[6]="hello"; char a2[6]=a1;//错误
总之,C库函数设计了一系列C-串库函数,例如
string是一种自定义的类型,可以方便执行C-串不能直接执行的一切操作。它处理空间占用问题是自动的,需要多少,用多少。
C-串是const char*类型的
//通过循环读入单词操作,将各种字符过滤 for (string s; cin>>s; ) cout<<s<<" "; //通过getline一次读入,getline将行末的回车符滤掉 string s; getline(cin,s); //逐个字符输入 for(char ch;(ch=cin.get())!='\n'; ) cout<<char(ch);
如果一个文件,有若干行,不知道每行有几个整数,要输出每行整数和。只能用getline逐行读入到string中,但string变量分离若干整数比较吃力,所以用string流。
#include<iostream> #include<sstream> #include<fstream> using namespace std; int main(){ ifstream in("aaa.txt"); for(string s;getline(in,s); ){ int a,sum=0; for(istringstream sin(s); sin>>a; sum+=a); cout<<sum<<endl; } }
数组定义是具有编译确定意义的操作,它分配固定大小的空间,就像变量定义一样明确。因此元素个数必须是由编译时就能够定夺的常量表达式。
int array[5]={1,2,3};
只要动用了花括号,就是实施了初始化。如果初始值个数不足规定的元素个数,则后面的元素全为0。
字符数组与其他数组有一点书写上的特殊性,字符数组初始化的三种形式,
对于没有初始化的数组,分两种情况:
1、全局数组,在外部定义或有static修饰,元素全填0
2、静态数组,局部数组,在函数内部定义,值不确定
int array[2][3]={{1,2},{4}};
vector是向量类型,它是一种对象实体,具有值,可以看作是变量。可以容纳许多其他类型的相同实体,所以称之为容器。Vectors是C++STL(标准模版类库)的重要一员,使用时需要包括头文件vector。
4种定义方式:
vector<int> a(10); vector<int> b(10,1); vector<int> c(b); vector<int> d(b.begin(), b.begin()+3);
向量元素位置也属于一种类型,称为遍历器。遍历器不单表示元素位置,还可以在容器中前后挪动。每种容器都有对应的遍历器。向量中的遍历器类型:vector<int>::iterator。因此有两种循环控制方式:
for(int i=0;i<a.size();++i) cout<<a[i]<<" "; for(vector<int>::iterator it=a.begin();it!=a.end();++it) cout<<*it<<" ";
调用对象a的成员函数:
读入一个文件aaa.txt的数据到向量中,文件中为一些整数。要判断向量中的元素有多少个两两相对的数对。
#include<iostream> #include<fstream> #include<vector> using namespace std; int main(){ ifstream in("aaa.txt"); vector<int> s; for(int a; in>>a; ) s.push_back(a); int pair=0; for(int i=0;i<s.size()-1;++i){ for(int j=i+1;j<s.size();++j) if(s[i]==s[j])pair++; } }
因为不知道文件元素个数,所以无法用数组来处理,也无法在向量中确定元素个数,但可以先创建一个空向量,然后不断往向量中添加元素。向量操作中有一个性能问题,如果频繁扩展容量,就要显著增加向量操作的负担,因为扩容意味着分配更大空间,复制原空间到现空间,删除空间。
向量不是每次扩展都要扩容,向量中预留了一部分未用元素供扩展之用,可以用capacity()查看。
二维向量中,可以使用vector中的swap操作来交换两个向量,其中只要做地址交换工作,而不用再创建一个向量、赋值、再赋值。
文件aaa.txt中有一些行,每行中有一些整数,可以构成一个向量。设计一个排序程序,使得按从短到长的顺序输出每个向量。
#include <iostream> #include <vector> #include <sstream> #include <fstream> using namespace std; typedef vector<vector<int>> Mat; Mat input(); void mySort(Mat& a); void print(const Mat& a); //------------------------- int main() { Mat a=input(); mySort(a); print(a); }//------------------------ Mat input(){ ifstream in("aaa.txt"); for(string s;getline(in,s);){ vector<int> b; istringstreaming sin(s); for(int ia;sin>>ia;){ b.push_back(ia); } a.push_back(b); } return a; }//------------------------- void mySort(Mat& a){ for(int pass=1;pass<a.size();pass++){ for(int i=0;i<a.size()-pass;i++){ if(a[i+1].size()<a[i].size()) a[i].swap(a[i+1]); } } }//------------------------ void print(const Mat& a){ for(int i=0;i<a.size();i++){ for(int j=0;j<a[i].size();j++){ cout<<a[i][j]<<" "; } cout << endl; } }//========================
c++拥有 在运行时获得变量或对象的地址和通过地址操纵数据的能力。
指针本身也有对应的指针类型int** iip。
一个*只能修饰一个指针,所以:
int* ip,iq; //ip为指针变量,iq为整型变量 int* ip,*iq;
指针变量在不致引混淆的情况下也称为指针。
指针可以赋值,也可以在定义指针时初始化,赋值或初始化的值是同类型实体的地址:
int* ip; int iCount = 18; int *iPtr = &iCount;//初始化 ip=&iCount;//赋值
指针的0值不是表示指向地址0的空间,而是表示空指针,即不指向任何空间。指针只有指向具体的实体,才能使间访操作具有意义。
重解释转换
float f=34.5; int* ip reinterpret_cast<int*>(&f);
指针变量所占的空间大小总是等于整型变量的大小。
指针的加减多数用在数组这种连续的又是同类型的元素的序列空间中。可以把数组起始地址赋给指针,数组名本身是表示元素类型的地址,所以可以直接将数组名赋给指针。
int iArray[6]; for(int i=0;i<6;++i) iArray[i]=i*2; for(int* ip=iArray;ip<iArray+6;ip+=1) cout<<ip<<": "<<*ip<<endl;
作为指针ip每次循环都只加1,而不是假想的4,但元素地址却是以4递增的。指针的增减是以该类型的实体大小为单位的。
指针的常量性:
指针常量constant pointer:指针值不能修改的指针
常量指针 pointer to constant:指向常量的指针
引用定义时必须初始化,这是它与指针根本不同的地方。给引用初始化的总是一个内存实体。
修改引用值,就是修改实体值,就是修改对应变量值,而引用的地址操作也就是所代表的实体地址操作,是不会有任何改变的。
引用与指针的差别:指针操控两个实体,一个指针值,一个是指向的值,因此指针可以改变关联的实体,即指向的实体。而引用只能操控一个实体。
与指针比较,引用隐去了地址操作,引用封锁了这种地址的可修改性,使得间访操作相对来说更安全了,高级编程多用引用,低级编程多用指针。