c_cpp
输入输出
控制台输入输出
cin>>
根据cin>>sth 中sth的变量类型读取数据,这里变量类型可以为int,float,char,char*,string等诸多类型。这一输入操作,在遇到结束符(Space、Tab、Enter)就结束,且不读取结束符,结束符留在缓存区。
cin.get(字符数组名,接收长度,结束符)
其中结束符意味着遇到该符号结束字符串读取,默认为enter,读取的字符个数最多为(长度 - 1),因为最后一个为'\0'。要注意的是,cin.get(字符数组名,接收长度,结束符)操作遇到结束符停止读取,但并不会将结束符从缓冲区丢弃。可后接一个cin.get();吸收结束符.
cin.getline(字符数组名,接收长度,结束符)
其用法与cin.get(字符数组名,接收长度,结束符)极为类似。cin.get()当输入的字符串超长时,不会引起cin函数的错误,后面若有cin操作,会继续执行,只是直接从缓冲区中取数据。但是cin.getline()当输入超长时,会引起cin函数的错误,后面的cin操作将不再执行。将结束符从缓冲区丢弃
getline(输入流,str)
istream& getline (istream& is, string& str, char delim);
istream& getline (istream&& is, string& str, char delim);
istream& getline (istream& is, string& str);
istream& getline (istream&& is, string& str);
is :表示一个输入流,例如 cin、文件流.
str :string类型的引用,用来存储输入流中的流信息。
delim :char类型的变量,所设置的截断字符;默认为'\n'。结束符从缓冲区丢弃。
上面的函数读到文件尾返回 0 ;
getchar\gets
C 库函数 int getchar(void) 从标准输入 stdin 获取一个字符(一个无符号字符)。这等同于 getc 带有 stdin 作为参数。
#include <stdio.h>
char *gets(char *s);
功能:从标准输入读入字符,并保存到s指定的内存空间,直到出现换行符或读到文件结尾为止。抛弃换行符。
参数:
s:字符串首地址
返回值:
成功:读入的字符串
失败:NULL
gets(str)与scanf("%s",str)的区别:
gets(str)允许输入的字符串含有空格
scanf("%s",str)不允许含有空格
putchar\puts
#include <stdio.h>
int puts(const char *s);
功能:标准设备输出s字符串,在输出完成后自动输出一个'\n'。
参数:
s:字符串首地址
返回值:
成功:非负数
失败:-1
printf()
在Turbo C中格式字符串的一般形式为: [标志][输出最小宽度][.精度][长度]类型 其中方括号[]中的项为可选项。
①类型类型字符用以表示输出数据的类型,其格式符和意义下表所示:
表示输出类型的格式字符
格式字符意义
d 以十进制形式输出带符号整数(正数不输出符号)
o 以八进制形式输出无符号整数(不输出前缀O)
x 以十六进制形式输出无符号整数(不输出前缀OX)
u 以十进制形式输出无符号整数
f 以小数形式输出单、双精度实数
e 以指数形式输出单、双精度实数
g 以%f%e中较短的输出宽度输出单、双精度实数
c 输出单个字符
s 输出字符串
p 输出指针(地址)
②标志
标志字符为-、+、#、空格四种,其意义下表所示:
标志格式字符
标
志
意
义
- 结果左对齐,右边填空格
+ 输出符号(正号或负号)空格输出值为正时冠以空格,为负时冠以负号
# 对c,s,d,u类无影响;对o类,
在输出时加前缀o
对x类,在输出时加前缀0x;对e,g,f 类当结果有小数时才给出小数点
③输出最小宽度
用十进制整数来表示输出的最少位数。
若实际位数多于定义的宽度,则按实际位数输出,
若实际位数少于定义的宽度则补以空格或0。
④精度
精度格式符以"."开头,后跟十进制整数。本项的意义是:如果输出数字,则表示小数的位数;如果输出的是字符,
则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分。
⑤长度
长度格式符为h,l两种,h表示按短整型量输出,l表示按长整型量输出。
scanf()
格式字符串
格式字符串的一般形式为: %[*][输入数据宽度][长度]类型
其中有方括号[]的项为任选项。各项的意义如下:
1.类型
表示输入数据的类型,其格式符和意义下表所示。
格式
字符意义
d 输入十进制整数
o 输入八进制整数
x 输入十六进制整数
u 输入无符号十进制整数
f或e
输入实型数(用小数形式或指数形式)
c 输入单个字符
s 输入字符串
2."*"符
用以表示该输入项读入后不赋予相应的变量,即跳过该输入值。
如 scanf("%d %*d %d",&a,&b);当输入为:1 2 3 时,把1赋予a,2被跳过,3赋予b。
3.宽度
用十进制整数指定输入的宽度(即字符数)。例如: scanf("%5d",&a);
输入:
12345678
只把12345赋予变量a,其余部分被截去。又如: scanf("%4d%4d",&a,&b);
输入:
12345678将把1234赋予a,而把5678赋予b。
4.长度
长度格式符为l和h,l表示输入长整型数据(如%ld) 和双精度浮点数(如%lf)。h表示输入短整型数据。
使用scanf函数还必须注意以下几点:
a. scanf函数中没有精度控制,如: scanf("%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。
b. scanf中要求给出变量地址,如给出变量名则会出错。如 scanf("%d",a);是非法的,应改为scnaf("%d",&a);才是合法的。
c. 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格,TAB或回车作间隔。C编译在碰到空格,TAB,回车或非法数据(如对"%d"输入"12A"时,A即为非法数据)时即认为该数据结束。
d. 在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
格式控制
输出精度
cout<<fixed<<setprecision(5)<<n<<endl;//输出n,保留小数点后5位;
需要包含头文件#include<iomanip>
cout << fixed; //使用一般方式输出浮点数,避免系统使用E表示法,加了后精度变为小数点后几位
cout.precision(2); //设置精度为2位,此精度为有效数字,作用域永久;
cout.setf(ios_base::showpoint); //显示小数点后面的0,通常cout会自动删去小数点结尾的0
cout.unsetf( ios::fixed ); // 去掉了fixed,所以精度恢复成整个数值的有效位数
bool k =true;
cout<<boolalpha<<k<<endl;//输出true;
string字符串
构造函数
1) string(); //创建一个空的字符串,例如string str;
2) string(const char* s); //使用字符串s初始化
3) string(const string& str); //使用一个string对象初始化另外一个string对象
4) string(int n, char c); //使用n个字符c初始化
字符串大小写转换
//将字符串s转化为小写字母
transform(s.begin(), s.end(), s.begin(), ::tolower);
//将字符串s转化为大写字母
transform(s.begin(), s.end(), s.begin(), ::toupper);
string转C串
string str="hello world!";
char * ch=new char[str.size()+1];
strcpy(ch,str.c_str());
string互转int
string to int
a.采用标准库中atoi函数。(#include <stdlib.h>)
string s = "12";
int a = atoi(s.c_str());
对于其他类型也都有相应的标准库函数,比如浮点型atof(),long型atol()等等。
b.标准库stoi函数
int stoi(const string & str)//将串按十进制转换为int
还有double型stod,float型stof,long long型stoll等。
c.采用sstream头文件中定义的字符串流对象来实现转换。
istringstream is("12"); //构造输入字符串流,流的内容初始化为"12"的字符串
int i;
is >> i; //从is流中读入一个int整数存入i中
int to string
a.采用标准库中的to_string函数。
int i = 12;
cout << std::to_string(i) << endl;
不需要包含任何头文件,应该是在utility中,但无需包含,直接使用,还定义任何其他内置类型转为string的重载函数,很方便。
b.采用sstream中定义的字符串流对象来实现。
ostringstream os; //构造一个输出字符串流,流内容为空
int i = 12;
os << i; //向输出字符串流中输出int整数i的内容
cout << os.str() << endl; //利用字符串流的str函数获取流中的内容
字符串流对象的str函数对于istringstream和ostringstream都适用,都可以获取流中的内容。
特性描述
int capacity()const; //返回当前容量(即string中不必增加内存即可存放的元素个数)
int max_size()const; //返回string对象中可存放的最大字符串的长度
int size()const; //返回当前字符串的大小
int length()const; //返回当前字符串的长度
bool empty()const; //当前字符串是否为空
void resize(int len,char c);//把字符串当前大小置为len,并用字符c填充不足的部分
赋值 =、assign
string &operator=(const string &s);//把字符串s赋给当前字符串
string &assign(const char *s);//用c类型字符串s赋值
string &assign(const char *s,int n);//用c字符串s开始的n个字符赋值
string &assign(const string &s);//把字符串s赋给当前字符串
string &assign(int n,char c);//用n个字符c赋值给当前字符串
string &assign(const string &s,int start,int n);//把字符串s中从start开始的n个字符赋给当前字符串
string &assign(const_iterator first,const_itertor last);//把first和last迭代器之间的部分赋给字符串
连接 +=、append、push_back
string &operator+=(const string &s);//把字符串s连接到当前字符串的结尾
string &append(const char *s); //把c类型字符串s连接到当前字符串结尾
string &append(const char *s,int n);//把c类型字符串s的前n个字符连接到当前字符串结尾
string &append(const string &s); //同operator+=()
string &append(const string &s,int pos,int n);//把字符串s中从pos开始的n个字符连接到当前字符串的结尾
string &append(int n,char c); //在当前字符串结尾添加n个字符c
string &append(const_iterator first,const_iterator last);//把迭代器first和last之间的部分连接到当前字符串的结尾
比较 compare
bool operator==(const string &s1,const string &s2)const;//比较两个字符串是否相等
运算符">","<",">=","<=","!="均被重载用于字符串的比较;
int
compare(const
string &s) const;//比较当前字符串和s的大小
int compare(int pos, int n,const string &s)const;//比较当前字符串从pos开始的n个字符组成的字符串与s的大小
int compare(int pos, int n,const string &s,int pos2,int n2)const;//比较当前字符串从pos开始n个字符字符串与s中pos2开始n2个字符字符串的大小
int compare(const char *s) const;
int compare(int pos, int n,const char *s) const;
int compare(int pos, int n,const char *s, int pos2) const;
compare函数在>时返回1,<时返回-1,==时返回0
子串 substr
string substr(int pos = 0,int n = npos) const;//返回pos开始的n个字符组成的字符串
使用匿名构造函数返回子串:
string (iterator begin,iterator end) //返回[begin,end);
string (&s[i],&s[j]) //返回 字符数组[i,j);
交换 swap
void swap(string &s2); //交换当前字符串与s2的值
查找 find/rfind...
int find(char c, int pos = 0) const;//从pos开始查找字符c在当前字符串的位置
int find(const char *s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
int find(const char *s, int pos, int n) const;//从pos开始查找字符串s中前n个字符在当前串中的位置
int find(const string &s, int pos = 0) const;//从pos开始查找字符串s在当前串中的位置
//查找成功时返回所在位置,失败返回string::npos的值或 -1;
int rfind(char c, int pos = npos) const;//从pos开始从后向前查找字符c在当前串中的位置
int rfind(const char *s, int pos = npos) const;
int rfind(const char *s, int pos, int n = npos) const;
int rfind(const string &s,int pos = npos) const;
//从pos开始从后向前查找字符串s中前n个字符组成的字符串在当前串中的位置,成功返回所在位置,失败时返回string::npos的值
int find_first_of(char c, int pos = 0) const;//从pos开始查找字符c第一次出现的位置
int find_first_of(const char *s, int pos = 0) const;
int find_first_of(const char *s, int pos, int n) const;
int find_first_of(const string &s,int pos = 0) const;
//从pos开始查找当前串中第一个在s的前n个字符组成的数组里的字符的位置。查找失败返回string::npos
int find_first_not_of(char c, int pos = 0) const;
int find_first_not_of(const char *s, int pos = 0) const;
int find_first_not_of(const char *s, int pos,int n) const;
int find_first_not_of(const string &s,int pos = 0) const;
//从当前串中查找第一个不在串s中的字符出现的位置,失败返回string::npos
int find_last_of(char c, int pos = npos) const;
int find_last_of(const char *s, int pos = npos) const;
int find_last_of(const char *s, int pos, int n = npos) const;
int find_last_of(const string &s,int pos = npos) const;
int find_last_not_of(char c, int pos = npos) const;
int find_last_not_of(const char *s, int pos = npos) const;
int find_last_not_of(const char *s, int pos, int n) const;
int find_last_not_of(const string &s,int pos = npos) const;
//find_last_of和find_last_not_of与find_first_of和find_first_not_of相似,只不过是从后向前查找
替换 replace
string &replace(int p0, int n0,const char *s);//删除从p0开始的n0个字符,然后在p0处插入串s
string &replace(int p0, int n0,const char *s, int n);//删除p0开始的n0个字符,然后在p0处插入字符串s的前n个字符
string &replace(int p0, int n0,const string &s);//删除从p0开始的n0个字符,然后在p0处插入串s
string &replace(int p0, int n0,const string &s, int pos, int n);//删除p0开始的n0个字符,然后在p0处插入串s中从pos开始的n个字符
string &replace(int p0, int n0,int n, char c);//删除p0开始的n0个字符,然后在p0处插入n个字符c
string &replace(iterator first0, iterator last0,const char *s);//把[first0,last0)之间的部分替换为字符串s
string &replace(iterator first0, iterator last0,const char *s, int n);//把[first0,last0)之间的部分替换为s的前n个字符
string &replace(iterator first0, iterator last0,const string &s);//把[first0,last0)之间的部分替换为串s
string &replace(iterator first0, iterator last0,int n, char c);//把[first0,last0)之间的部分替换为n个字符c
string &replace(iterator first0, iterator last0,const_iterator first, const_iterator last);//把[first0,last0)之间的部分替换成[first,last)之间的字符串
插入 insert
string &insert(int p0, const char *s);
string &insert(int p0, const char *s, int n);
string &insert(int p0,const string &s);
string &insert(int p0,const string &s, int pos, int n);
//前4个函数在p0位置插入字符串s中pos开始的前n个字符
string &insert(int p0, int n, char c);//此函数在p0处插入n个字符c
iterator insert(iterator it, char c);//在it处插入字符c,返回插入后迭代器的位置
void insert(iterator it, const_iterator first, const_iterator last);//在it处插入[first,last)之间的字符
void insert(iterator it, int n, char c);//在it处插入n个字符c
删除 erase、pop_back
iterator erase(iterator first, iterator last);//删除[first,last)之间的所有字符,返回删除后迭代器的位置
iterator erase(iterator it);//删除it指向的字符,返回删除后迭代器的位置
string &erase(int pos = 0, int n = npos);//删除pos开始的n个字符,返回修改后的字符串
s.pop_back() //删除末尾一个字符
迭代器处理 const_iterator
string类提供了向前和向后遍历的迭代器iterator,迭代器提供了访问各个字符的语法,类似于指针操作,迭代器不检查范围。
用string::iterator或string::const_iterator声明迭代器变量,const_iterator不允许改变迭代的内容。常用迭代器函数有:
- const_iterator begin()const;
- iterator begin(); //返回string的起始位置
- const_iterator end()const;
- iterator end(); //返回string的最后一个字符后面的位置
- const_iterator rbegin()const;
- iterator rbegin(); //返回string的最后一个字符的位置
- const_iterator rend()const;
- iterator rend(); //返回string第一个字符位置的前面
rbegin和rend用于从后向前的迭代访问,通过设置迭代器string::reverse_iterator,string::const_reverse_iterator实现
【"字符串流处理"】:
通过定义ostringstream和istringstream变量实现,<sstream>头文件中
例如:
string input("hello,this is a test");
istringstream is(input);
string s1,s2,s3,s4;
is>>s1>>s2>>s3>>s4;//s1="hello,this",s2="is",s3="a",s4="test"
ostringstream os;
os<<s1<<s2<<s3<<s4;
cout<<os.str();
对于string 对象进行的操作非常多,在这里调用的成员函数中也包括由点操作符和函数名组成的对象名称,同时对象通过函数的参数列表来传递。如下所示:
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1="string of many words.",s2="many"; //s1="string of many words.和"s1("string of many words.")等价。都调用了string类构造函数。
int i;
cout<<s1<<endl;
i=s1.find(s2); //调用find函数,返回s2在s1中的位置。在这里返回10,因s1在s2起始位置是10
s1.replace(i,4,"few");//调用replace函数用字符串few来替换s1中的4个字符。i的值就是find的返回值,
//many在字符串中的位置,并且表示替换从这该位置开始。
cout<<s1<<endl;
s1.erase(i,4); //删除了s1字符串中从第10个开始的4个字符
cout<<s1<<endl;
s1.insert(i,"simple "); //把字符串"simple"插入字符串s1的第10个字符的位置。
cout<<s1<<endl;
}
在调用find函数时,没有找到字符串s2将返回一个string类的数据成员npos(npos=-1),在string类中find函数被重载了,
重载后有两个参数ob1.find(ob2,index),index 是个整数值,表示从字符串ob1中开始搜索起始位置。
repace函数也可以重载,如ob1.replace(index1,num1,ob2,index2,num2);这里index1和num1是ob1中要替换的字符串和字符个数,
index2和num2是ob2中用于替换字符串下标和字符个数,replace函数返回一个引用给调用对象。
- 其他
C字符串
strlen
#include <string.h>
size_t strlen(const char *s);
功能:计算指定指定字符串s的长度,不包含字符串结束符'\0'
参数:
s:字符串首地址
返回值:字符串s的长度,size_t为unsigned int类型
char str[] = "abc\0defg";
int n = strlen(str);
printf("n = %d\n", n);
strcpy
strcpy()
#include <string.h>
char *strcpy(char *dest, const char *src);
功能:把src所指向的字符串复制到dest所指向的空间中,'\0'也会拷贝过去
参数:
dest:目的字符串首地址
src:源字符首地址
返回值:
成功:返回dest字符串的首地址
失败:NULL
注意:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。
strncpy()
#include <string.h>
char *strncpy(char *dest, const char *src, size_t n);
功能:把src指向字符串的前n个字符复制到dest所指向的空间中,是否拷贝结束符看指定的长度是否包含'\0'。
参数:
dest:目的字符串首地址
src:源字符首地址
n:指定需要拷贝字符串个数
返回值:
成功:返回dest字符串的首地址
失败:NULL
strcat
strcat()
#include <string.h>
char *strcat(char *dest, const char *src);
功能:将src字符串连接到dest的尾部,'\0'也会追加过去
参数:
dest:目的字符串首地址
src:源字符首地址
返回值:
成功:返回dest字符串的首地址
失败:NULL
strncat()
#include <string.h>
char *strncat(char *dest, const char *src, size_t n);
功能:将src字符串前n个字符连接到dest的尾部,'\0'也会追加过去
参数:
dest:目的字符串首地址
src:源字符首地址
n:指定需要追加字符串个数
返回值:
成功:返回dest字符串的首地址
失败:NULL
strcmp
strcmp()
#include <string.h>
int strcmp(const char *s1, const char *s2);
功能:比较 s1 和 s2 的大小,比较的是字符ASCII码大小。
参数:
s1:字符串1首地址
s2:字符串2首地址
返回值:
相等:0
大于:>0 在不同操作系统strcmp结果会不同 返回ASCII差值
小于:<0
strncmp()
#include <string.h>
int strncmp(const char *s1, const char *s2, size_t n);
功能:比较 s1 和 s2 前n个字符的大小,比较的是字符ASCII码大小。
参数:
s1:字符串1首地址
s2:字符串2首地址
n:指定比较字符串的数量
返回值:
相等:0
大于: > 0
小于: < 0
sprintf
sprintf()
#include <stdio.h>
int sprintf(char *str, const char *format, ...);
功能:根据参数format字符串来转换并格式化数据,然后将结果输出到str指定的空间中,直到出现字符串结束符 '\0' 为止。
参数:
str:字符串首地址
format:字符串格式,用法和printf()一样
返回值:
成功:实际格式化的字符个数
失败: - 1
sscanf
sscanf()
#include <stdio.h>
int sscanf(const char *str, const char *format, ...);
功能:从str指定的字符串读取数据,并根据参数format字符串来转换并格式化数据。
参数:
str:指定的字符串首地址
format:字符串格式,用法和scanf()一样
返回值:
成功:参数数目,成功转换的值的个数
失败: - 1
strchr
strchr()
#include <string.h>
char *strchr(const char *s, int c);
功能:在字符串s中查找字母c出现的位置
参数:
s:字符串首地址
c:匹配字母(字符)
返回值:
成功:返回第一次出现的c地址
失败:NULL
strstr
strstr()
#include <string.h>
char *strstr(const char *haystack, const char *needle);
功能:在字符串haystack中查找字符串needle出现的位置
参数:
haystack:源字符串首地址
needle:匹配字符串首地址
返回值:
成功:返回第一次出现的needle地址
失败:NULL
strtok
strtok()
#include <string.h>
char *strtok(char *str, const char *delim);
功能:来将字符串分割成一个个片段。当strtok()在参数s的字符串中发现参数delim中包含的分割字符时, 则会将该字符改为\0 字符,当连续出现多个时只替换第一个为\0。
参数:
str:指向欲分割的字符串
delim:为分割字符串中包含的所有字符
返回值:
成功:分割后字符串首地址
失败:NULL
在第一次调用时:strtok()必需给予参数str,字符串往后的调用则将参数str设置成NULL,每次调用成功则返回指向被分割出片段的指针
char a[100] = "adc*fvcv*ebcy*hghbdfg*casdert";
char *s = strtok(a, "*");//将"*"分割的子串取出
while (s != NULL)
{
printf("%s\n", s);
s = strtok(NULL, "*");
}
atoi
atoi()
#include <stdlib.h>
int atoi(const char *nptr);
功能:atoi()会扫描nptr字符串,跳过前面的空格字符,直到遇到数字或正负号才开始做转换,而遇到非数字或字符串结束符('\0')才结束转换,并将结果返回返回值。
参数:
nptr:待转换的字符串
返回值:成功转换后整数
类似的函数有:atof():把一个小数形式的字符串转化为一个浮点数。
atol():将一个字符串转化为long类型
内存操作函数
memset()
#include <string.h>
void *memset(void *s, int c, size_t n);
功能:将s的内存区域的前n个字节以参数c填入
参数:
s:需要操作内存s的首地址
c:填充的字符,c虽然参数为int,但必须是unsigned char , 范围为0~255
n:指定需要设置的大小
返回值:s的首地址
memcpy()
#include <string.h>
void *memcpy(void *dest, const void *src, size_t n);
功能:拷贝src所指的内存内容的前n个字节到dest所值的内存地址上。
参数:
dest:目的内存首地址
src:源内存首地址,注意:dest和src所指的内存空间不可重叠,可能会导致程序报错
n:需要拷贝的字节数
返回值:dest的首地址
memmove()
memmove()功能用法和memcpy()一样,区别在于:dest和src所指的内存空间重叠时,memmove()仍然能处理,不过执行效率比memcpy()低些。
void * malloc(size_t size)
1).malloc()函数会向堆中申请一片连续的可用内存空间,不初始化;
2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用malloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
3).返回值的类型为void*型, malloc()函数并不知道连续开辟的size个字节是存储什么类型数据的 ,所以需要我们自行决定 ,方法是在malloc()前加强制转 ,转化成我们所需类型 ,如: (int*)malloc(sizeof(int)*n).
4).如果size为0, 此行为是未定义的, 会发生未知错误, 取决于编译器
void free(void* ptr)
1).如果ptr没有指向使用动态内存分配函数分配的内存空间,则会导致未定义的行为。
2).如果ptr是空指针,则该函数不执行任何操作。
3).此函数不会更改ptr本身的值,因此它仍指向相同(现在已经无效)的位置(内存)
4).在free()函数之后需要将ptr再置空 ,即ptr = NULL;如果不将ptr置空的话 ,后面程序如果再通过ptr会访问到已经释放过无效的或者已经被回收再利用的内存, 为保证程序的健壮性, 一般我们都要写ptr = NULL; .
注意 : free()不能重复释放一块内存, 如:
void * calloc(size_t num,size_t size)
与malloc()函数的区别只在于, calloc()函数会在返回地址之前将所申请的内存空间中的每个字节都初始化为0 .
1).calloc()函数功能是动态分配num个大小(字节长度)为size的内存空间 .
2).若申请成功 ,,返回指向这片内存空间的指针 ,若失败 ,则会返回NULL, 所以我们在用calloc()函数开辟动态内存之后, 一定要判断函数返回值是否为NULL.
3).返回值的类型为void*型, calloc()函数虽然分配num个size大小的内存空间 ,但还是不知道存储的什么类型数据 ,所以需要我们自行决定 ,方法是在calloc()前加强制转 ,转化成我们所需类型 ,如: (int*)calloc(num, sizeof(int)).
4).如果size与num有一个或都为0, 此行为是未定义的, 会发生未知错误, 取决于编译器
void *realloc(void *p,int byte_size)
对动态开辟内存大小的调整(既可以往大调整, 也可以往小了调整) .
若p所指空间连续大小(单位字节)大于byte_size,则从首地址开始连续地扩充开辟p所指空间至byte_size字节,不对空间再次赋值,将空间地址指针返回;若p所指空间连续大小小于byte_size,则新开辟byte_size字节大小的空间,将p指向的空间内的数据复制进新空间,之后释放p所指空间(此时p为NULL),返回指向新空间的指针,此指针所指数据类型没有确定,需要强转。
举例:int *p=(int *)calloc(5,sizeof(int));
p =(int*)realloc(p,10*sizeof(int));
例解:首句中p为5*4=20字节的空间指针并按字节初始化为ascii码0,(int *)强转后才限定了指向空间的每个元素为int型。后句将p所指空间扩充为10*4=40字节的空间指针,未对其二次赋值,故此时p[0]~p[4]为0,p[5]~p[9]未初始化。
注意:realloc的第一个参数必须是动态开辟的地址,不能是静态定义的数组的地址。
- Other
文件操作
C语言文件操作
打开文件
文件打开:
FILE*pf;
if((pf=fopen("D:\\new\\ceshi.txt","rt+"))==NULL)
pf=fopen("D:\\new\\ceshi.txt","wt+");
文件指针名=fopen(文件名,使用文件方式)
在打开一个文件时,如果出错,fopen将返回一个空指针值NULL。
文件使用方式 意 义
"rt" 只读打开一个文本文件,只允许读数据 若文件不存在返回NULL
"wt" 只写打开或建立文本文件,只允许写数据,若文件存在,删除重建
"at" 追加打开一个文本文件,并在文件末尾写数据,若文件无,建立文件
"rb" 只读打开一个二进制文件,只允许读数据若文件不存在返回NULL
"wb" 只写打开或建立一个二进制文件,只允许写数据若文件存在,删除重建
"ab" 追加打开一个二进制文件,并在文件末尾写数据
"rt+" 读写打开一个文本文件,允许读和写
"wt+" 读写打开或建立一个文本文件,允许读写
"at+" 读写打开一个文本文件,允许读,或在文件末追加数 据
"rb+" 读写打开一个二进制文件,允许读和写
"wb+" 读写打开或建立一个二进制文件,允许读和写
"ab+" 读写打开一个二进制文件,允许读,或在文件末追加数据
第一个参数的几种形式:
FILE *fp_passwd = NULL;
//相对路径:
//打开源文件所在目录下passdw.txt文件:
FILE *fp_passwd = fopen("passwd.txt", "r");
`
//打开源文件所在目录下的test目录下的passwd.txt文件
fp_passwd = fopen(". / test / passwd.txt", "r");
//打开源文件所在目录上一级目录的test目录下的passwd.txt文件
fp_passwd = fopen(".. / test/passwd.txt", "r");
//绝对路径:
//打开C盘test目录下一个叫passwd.txt文件
fp_passwd = fopen("c:/test/passwd.txt","r");
文件关闭:
fclose(pf);
fclose(文件指针)
读写文件
读字符函数fgetc
字符变量=fgetc(文件指针);
ch=fgetc(pf);
写字符函数fputc
fputc(字符量,文件指针);
fputc(ch,pf);
#include <stdio.h>
char * fgets(char * str, int size, FILE * stream);
功能:从stream指定的文件内读入字符,保存到str所指定的内存空间,直到出现换行字符、读到文件结尾或是已读了size - 1个字符为止,最后会自动加上字符 '\0' 作为字符串结束。
参数:
str:字符串
size:指定最大读取字符串的长度(size - 1)
stream:文件指针
返回值:
成功:成功读取的字符串
读到文件尾或出错: NULL
#include <stdio.h>
int fputs(const char * str, FILE * stream);
功能:将str所指定的字符串写入到stream指定的文件中,字符串结束符 '\0' 不写入文件。
参数:
str:字符串
stream:文件指针
返回值:
成功:0
失败:-1
格式化读写函数fscanf和fprintf
这两个函数的调用格式为: fscanf(文件指针,格式字符串,输入表列); fprintf(文件指针,格式字符串,输出表列); 例如:
fscanf(fp,"%d%s",&i,s);
fprintf(fp,"%d%c",j,ch);
数据块读写函数fread和fwrite
C语言还提供了用于整块数据的读写函数。 可用来读写一组数据,如一个数组元素,一个结构变量的值等。读数据块函数调用的一般形式为: fread(buffer,size,count,fp); 写数据块函数调用的一般形式为: fwrite(buffer,size,count,fp); 其中buffer是一个指针,在fread函数中,它表示存放输入数据的首地址。在fwrite函数中,它表示存放输出数据的首地址。 size 表示数据块的字节数。count 表示要读写的数据块块数。fp 表示文件指针。
检测文件
rewind(文件指针); 它的功能是把文件内部的位置指针移到文件首。
fseek函数用来移动文件内部位置指针,其调用形式为: fseek(文件指针,位移量,起始点);当用常量表示位移量时,要求加后缀"L"。"起始点"表示从何处开始计算位移量,规定的起始点有三种:文件首 0,当前位置 1和文件尾 2。
文件结束检测函数feof函数调用格式: feof(文件指针);
功能:判断文件是否处于文件结束位置,如文件结束,则返回值为1,否则为0。
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);
功能:移动文件流(文件光标)的读写位置。
参数:
stream:已经打开的文件指针
offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。当用常量表示位移量时,要求加后缀"L"
whence:其取值如下:
SEEK_SET or 0 文件开头
SEEK_CUR or 1 当前位置
SEEK_END or 2 文件末尾
返回值:
成功:0
失败:-1
#include <stdio.h>
long ftell(FILE *stream);
功能:获取文件流(文件光标)的读写位置。
参数:
stream:已经打开的文件指针
返回值:
成功:当前文件流(文件光标)的读写位置
失败:-1
#include <stdio.h>
void rewind(FILE *stream);
功能:把文件流(文件光标)的读写位置移动到文件开头。
参数:
stream:已经打开的文件指针
返回值:
无返回值
#include <sys/types.h>
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
功能:获取文件状态信息
参数:
path:文件名
buf:保存文件信息的结构体
返回值:
成功:0
失败-1
删除文件、重命名文件名//不能打开文件
#include <stdio.h>
int remove(const char *pathname);
功能:删除文件
参数:
pathname:文件名
返回值:
成功:0
失败:-1
#include <stdio.h>
int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改为newpath
参数:
oldpath:旧文件名
newpath:新文件名
返回值:
成功:0
失败: - 1
更新缓存区
#include <stdio.h>
int fflush(FILE *stream);
功能:更新缓冲区,让缓冲区的数据立马写到文件中。
参数:
stream:文件指针
返回值:
成功:0
失败:-1
System();
头文件"stdlib.h"
带路径的要使用绝对路径,用"/"或者"\\"分隔;带中文名的要使用GBK编码
例:
System("D:/DEV/devcpp.exe")://打开D:/DEV中的devcpp.exe
如果正确执行会0;否知返回其它非0数;
c++文件操作
打开模式
in:打开文件时做读操作;
out:打开文件时做写操作;
app:在每次写之前找到文件尾;
ate:打开文件后立即将文件定位在文件尾;(与ios::app存在区别)
trunc:打开文件时清空已存在的文件流;
binary:以二进制模式进行IO操作;(默认时采用的是 文本文件模式)
ios::out 写打开文件,无文件则创建,有则清空原文件
ios::in 读打开文件,无文件则打开失败,有则打开文件,不清空文件
ios::in|ios::out 读写打开文件,无则打开失败,有则不清空打开;文件指针在开头
ios::in|ios::app 读写打开文件,无则创建,有则打开,不清空,文件指针在开头,但只能在文件尾写
ios::out|ios::app 写打开文件,无则创建,有则打开,不清空,文件指针在开头,但只能在文件尾写,不能读
当文件同时以in out模式打开时,不会清空已有数据;
如果只使用了out,而没有指定in模式,则会清空已有数据;
如果使用了trunc,不论是否使用了in,都会清空已有的数据(前提是有ios::out);
ifstream 对象 提供默认 文件模式 ios::in;
ofstream 对象提供默认 文件模式 ios::out|ios::trunc;
fstream 对象不提供默认的 文件模式 因此只能自己敲了;
读写文件步骤
1包含头文件
#include <fstream>
2创建流对象
ofstream ofs;
3打开文件
ofs.open("文件路径",打开方式);
4读写数据
ofs << "写入的数据";
5关闭文件
ofs.close();
读写操作
* put()
put()函数向流写入一个字符,其原型是ofstream &put(char ch),使用也比较简单,如file1.put('c');就是向流写一个字符'c'。
* get()
get()函数比较灵活,有3种常用的重载形式:
1.一种就是和put()对应的形式:ifstream &get(char &ch);功能是从流中读取一个字符,结果保存在引用ch中,如果到文件尾,返回空字符。如file2.get(x);表示从文件中读取一个字符,并把读取的字符保存在x中。
2.另一种重载形式的原型是: int get();这种形式是从流中返回一个字符,如果到达文件尾,返回EOF,如x=file2.get();和上例功能是一样的。
3.还有一种形式的原型是:ifstream &get(char *buf,int num,char delim='\n');这种形式把字符读入由 buf 指向的数组,直到读入了 num 个字符或遇到了由 delim 指定的字符,如果没使用 delim 这个参数,将使用缺省值换行符'\n'。例如:
* getline()
fstream in("text.txt",ios::in|ios::out);
string s;
getline(in,s);
* 读写数据块
要读写二进制数据块,使用成员函数read()和write()成员函数,它们原型如下:
read(unsigned char *buf,int num);
write(const unsigned char *buf,int num);
read()从文件中读取 num 个字节到 buf 指向的缓存中,如果在还未读入 num 个字符时就到了文件尾,可以用成员函数 int gcount();来取得实际读取的字符数;而 write() 从buf 指向的缓存写 num 个字符到文件中,值得注意的是缓存的类型是 unsigned char *,有时可能需要类型转换。
例:
unsigned char str1[]="I Love You";
int n[5];
ifstream in("xxx.xxx");
ofstream out("yyy.yyy");
out.write(str1,strlen(str1));//把字符串str1全部写到yyy.yyy中
in.read((unsigned char*)n,sizeof(n));//从xxx.xxx中读取指定个整数,注意类型转换
in.close();out.close();
检测文件
- 成员函数eof()用来检测是否到达文件尾,如果到达文件尾返回非0值,否则返回0。原型是int eof();
- if(!in) //in为ifstream;判断文件是否打开成功;
- 成员函数streampos tellg(); 返回文件get指针绝对位置
streampos tellp(); 返回文件put指针绝对位置
- C++的文件定位分为读位置和写位置的定位,对应的成员函数是 seekg()和 seekp();seekg()是设置读位置,seekp是设置写位置。它们最通用的形式如下:
basic_istream& seekg(streampos pos); //将g指针定位到绝对位置pos
basic_istream& seekg(streamoff offset,seek_dir origin);
basic_ostream& seekp(streamoff offset,seek_dir origin);
streamoff定义于 iostream.h 中,定义有偏移量 offset 所能取得的最大值,seek_dir 表示移动的基准位置,是一个有以下值的枚举:
ios::beg: 0 文件开头
ios::cur: 1 文件当前位置
ios::end: 2 文件结尾
指针和引用
数组的指针 (*)[]
类型 (*变量名) [大小] = 初值;
例:
int b[3][4];
int (*pb)[4] = b;//定义pb指针,指向int[4];pb用法与b一样;
指针的数组 *[]
类型 * 数组名 [大小]={};
例:
int b[3][4];
int * p[]={b[0],b[1],b[2]};//定义一个指针数组,数组元素是指针;
向函数中传递二维数组
int arr[3][4];
//1.
void f(int * a,int row,int col)
{
for(int i=0;i<row;i++)
for(int j=0;j<col;j++)
cout<<a[i*col+j]; //a[i][j];
}
f((int*)arr,3,4);
//2.
void f2(int(*a)[4],int row)//void f2(int a[][4],int row)
{
for(int i=0;i<row;i++)
for(int j=0;j<4;j++)
cout<<a[i][j]; //a[i][j];
}
f2(arr,3);
//3.
void f3(int **a,int row,int col)
{
for(int i=0;i<row;i++)
for(int j=0;j<4;j++)
cout<<a[i][j]; //a[i][j];
}
int * p[]={arr[0],arr[1],arr[2]};
f3(p,3,4);
函数的指针 (* )()
返回类型 (*指针名) (形参表) = 函数名;
void testFunc(int a,int b){cout<<a<<b;}
void (*fun)(int,int)=testFunc; //定义指向testFun的函数指针
//为函数指针类型取别名:
typedef void (*F)(int ,int);
using F=void(*)(int ,int);
函数指针的数组 (* [])()
返回类型 (*数组名[大小])(形参)={函数1,...};
void (*funArr[])(int,int)={fun1,fun2.fun3};
//或先用using 说明函数指针的类型别名
using F=void (*)(int,int);
F funArr[]={fun1,fun2,...};
数组的引用 (&)[]
类型 (&引用变量名)[大小] = 数组名;
int a[]={1,2,3};
int (&ra)[3]=a;
auto & ra=a;//可用auto简化;
指针的引用 *&
类型 *&引用名 = 指针名;
int * p;
int *&rp = p;//rp是指针p的引用;
auto & rp=p;//可用auto简化;
注:引用的指针就是原变量的指针;
函数的引用 (& )()
返回类型 (& 引用名) (形参表) = 函数名;
const指针和引用
const 修饰指针变量
int a=10;
const int * p1 =&a; //指针所指内容不可改变
int * const p2 = &a; //指针指向不可改变
const int * const p3 =&a; //指针所指内容和指针指向都不可改变
const int * 和 int * 可定义重载函数
int * const 和 int * 不可定义重载函数
函数调用匹配规则:
const指针实参只能匹配const指针形参
非const指针形参优先匹配非const指针形参,没有则匹配const指针形参
函数可返回const指针,它要求返回值只能赋值给一个const指针。
常量左值引用
一般来说,左值引用变量 只能引用变量和命名常量,不能引用字面值和右值表达式。但const修饰的左值引用变量 (常量左值引用) 可以引用字面值或右值表达式。
const引用往往作为函数形参,调用方可用相同类型的任意左值或右值表达式,但在函数体中不能改变被引用的值。
Lambda表达式
[捕获](形参表){函数体}
① 函数对象参数;
[],标识一个Lambda的开始,这部分必须存在,不能省略。函数对象参数是传递给编译器自动生成的函数对象类的构造函数的。函数对象参数只能使用那些到定义Lambda为止时Lambda所在作用范围内可见的局部变量(包括Lambda所在类的this)。函数对象参数有以下形式:
- 空。没有使用任何函数对象参数。
- =。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是值传递方式(相当于编译器自动为我们按值传递了所有局部变量)。
- &。函数体内可以使用Lambda所在作用范围内所有可见的局部变量(包括Lambda所在类的this),并且是引用传递方式(相当于编译器自动为我们按引用传递了所有局部变量)。
- this。函数体内可以使用Lambda所在类中的成员变量。
- a。将a按值进行传递。按值进行传递时,函数体内不能修改传递进来的a的拷贝,因为默认情况下函数是const的。要修改传递进来的a的拷贝,可以添加mutable修饰符。
- &a。将a按引用进行传递。
- a, &b。将a按值进行传递,b按引用进行传递。
- =,&a, &b。除a和b按引用进行传递外,其他参数都按值进行传递。
- &, a, b。除a和b按值进行传递外,其他参数都按引用进行传递。
② 操作符重载函数参数;
标识重载的()操作符的参数,没有参数时,这部分可以省略。参数可以通过按值(如:(a,b))和按引用(如:(&a,&b))两种方式进行传递。
③ 可修改标示符;
mutable声明,这部分可以省略。按值传递函数对象参数时,加上mutable修饰符后,可以修改按值传递进来的拷贝(注意是能修改拷贝,而不是值本身)。
- other
Class
类和对象
类的定义
class 类名{ 访问权限: 属性 / 行为 };
class缺省是私有private;struct中缺省公有public
三部分次序随意,但private只有在第一部分才可省略;
对复杂的成员函数,在类中先给出原型定义,在类外再说明具体实现:
返回类型 类名::成员函数名(形参表){ 函数体 }
类成员的可见性
private:只允许本类成员函数访问,数据变量往往说明为私有
protected:能被本类和子类成员访问,其他类不能访问
public:对外可见,成员函数一般作为公有成员
类的数据成员
类中的数据成员可以是任意类型,除了自身类,但自身类的指针可作为该类的成员。
类中可对数据成员直接初始化。C11支持。
多个数据变量之间不能重名。
类定义在前,使用在后。如果类A中使用了类B,而且类B也使用了类A,就需要"前向引用说明" ,即类的原型说明,"class A;"
类的成员函数
如果一个数据成员可以改变,往往用一个setXxx(一个形参)函数来实现,"xxx"就是数据成员的名字,而且形参类型往往与数据成员的类型一致。
如果一个数据成员可读,往往用一个getXxx()函数来返回这个数据成员,"xxx"就是数据成员的名字,返回类型往往与数据成员的类型一致。
如果希望一个数据成员是只读的(read only),即不能改变,那么该数据成员应设为私有,再设计一个公有的getXxx()函数来读取它。
如果要从已有数据成员中计算并返回一个值,往往用一个getXxx()函数来实现。如果函数返回bool类型,往往用isXxx()函数来实现。
对象的创建
Per p1; //默认构造
Per p2(10); //有参构造
Per p3(p2); //拷贝构造
Per p4=Per(p2); //显式调用拷贝构造
Per(); //匿名对象
类与对象的关系,this指针
在运行时刻,类是静态的,不能改变的。一个类的标识就是类的名称。一个对象就是某个类的一个实例,对象是动态的,具有一定的生存周期。
一个类的多个对象分别持有自己的数据成员,相互独立,但成员函数的空间有多个对象共享,并没有独立的拷贝。
每个非静态成员函数都隐含持有指向当前对象的一个指针,即this指针
如果函数形参与数据成员重名,就需要this指针来区别。
如果函数要返回当前对象的引用,就需要return *this;
类的成员
构造函数
构造函数: 类名 (形参表):成员初始化表{ };
委托构造函数: 类名 (形参表):类名(实参表){ }; //本函数将构造委托给 类名(实参表);
拷贝构造函数: 类名 (const 类名&对象名):成员初始化表{ };
拷贝赋值函数: 类名& operator= (const 类名&对象名){函数体;return *this; };
移动构造函数: 类名 (类名&&对象名):成员初始化表{ };
移动赋值函数: 类名& operator= (类名&&对象名){函数体;return *this; };
析构函数: ~类名(){}; //若类的数据成员含有指针,且构造函数中用new动态申请,此时就需要自定义析构函数,用delete回收。
转换构造函数: 类名(其他类型){};//持有单个不同于本类型的形参,可实现隐式的自动类型转换,将其他类型的数据转换为本类的对象。加关键字explicit修饰函数可避免转换构造函数被隐式调用。
构造函数的用法
构造函数调用规则如下:
* 如果用户定义有参构造函数,c++不在提供默认无参构造,但是会提供默认拷贝构造
* 如果用户定义拷贝构造函数,c++不会再提供其他构造函数
调用移动构造函数 A a2 = move(a1);
调用移动赋值函数 a2 = move(a1); //若移动函数不存在,这调用拷贝函数;
特殊成员函数的显式控制:
A () = default; //显式要求编译器生成默认缺省构造函数;
A (const A &) = delete;
A& operator=(const A&) = delete; //显式要求编译器不生成拷贝构造、赋值函数
复合类
创建一个符合对象时,先调用成员对象的构造函数,再调用复合对象的构造函数。若有多个成员对象时,其构造函数的执行顺序与成员对象的说明顺序有关,而与成员初始化列表中的顺序无关。
析构函数的执行顺序与构造函数严格相反,即先执行复合对象的析构,再执行成员对象的析构。
静态成员
静态成员变量
* 所有对象共享同一份数据
* 在编译阶段分配内存
* 类内声明,类外初始化
* 受封装性和访问权限的控制
* 不管派生多少次,都只有一份;
静态成员函数
* 所有对象共享同一个函数
* 静态成员函数没有this指针,只能访问静态成员变量
* 若静态成员函数在类外实现,就不能加static和访问控制修饰符;
class AA{
static int a; //类内声明
};
int AA::a; //类外初始化,默认为0;不加static和访问控制修饰符
const修饰成员函数
常函数:
* 成员函数后加const后我们称为这个函数为常函数
* 常函数内不可以修改成员属性,只能调用const成员函数
* 成员属性声明时加关键字mutable后,在常函数中依然可以修改
*若在类外实现const成员函数,应保持const,否则编译器认为是定义重载函数
常对象:
* 声明对象前加const称该对象为常对象
* 常对象只能调用常函数
*常对象不能修改成员变量的值,但是可以访问,可以修改mutable修饰成员变量。
*形参带const限定也被作为函数基调的一部分,可以用来定义重载函数
类成员的指针
数据成员指针:
类型 类名::*指针变量名 = &类名::数据成员名
通过指针访问数据成员:
对象指针 ->* 数据成员指针变量名
对象引用 .* 数据成员指针变量名
成员函数指针:
返回类型 (类名::*指针变量名)(形参表) = 类名::成员函数名
通过指针调用成员函数:
对象指针 ->*成员函数指针变量名 (实参表);
对象引用 .* 成员函数指针变量名 (实参表);
例:
class A{
public:
int a;
void fun(int a){cout<<a<<end;}
};
A a1{10};
int A::*pa1 = &A::a; //定义数据成员指针 pa1 指向 A::a;
cout << a1 .* pa1 << endl; //通过数据成员指针访问该成员;
void (A::*pf)(int) = A::fun; //定义成员函数指针 pf 指向 A::fun
a1.*pf (5); //通过成员函数指针调用该函数
类的继承
继承的定义
class 子类名 : 继承方式1 父类1,继承方式2 父类2 {子类扩展成员};
继承方式缺省为 private, 基类私有派生类不可访问(无论何种方式继承);
公有继承时,父类保护、公有成员在子类中是 保护、公有成员。
保护继承时,父类的保护、公有在子类中为保护。
私有继承时,父类的公有、保护、在子类中全为私有。
派生类的构造和析构
派生类的构造函数的一般格式为:
子类名 (形参表): 基类名(实参表), ... , 成员名(实参表), ... {构造函数体};
如果派生类自定义拷贝构造或移动构造,应在初始化列表中显式调用基类的拷贝构造或移动构造函数。如果没有显式调用,就会隐式调用基类的缺省构造函数。派生类缺省实现的拷贝构造函数和移动构造函数分别调用各基类的拷贝函数和移动函数(递归理解这个过程)。
初始化列表中调用基类的函数格式如下:
- 拷贝构造函数: 基类名 (被拷贝对象)
- 移动构造函数: 基类名(move(被拷贝对象))
赋值函数体中调用基类函数格式如下:
- 拷贝赋值函数: 基类名::operator= (被拷贝对象);
- 移动赋值函数: 基类名::operator=(move(被拷贝对象));
C++11允许派生类"继承"基类的构造函数以避免重复代码。派生类中使用一种using 说明语句: using 基类名::基类名; 来继承基类的带参构造函数。
派生类构造过程:
①先逐层先上调用父类构造函数,执行完成顺序相反,最上层的基类先执行完。
②再调用各成员对象的构造函数(按成员对象的说明顺序),若成员对象是继承来的,则进入第(1)步。
③最后执行派生类自己的构造函数。
派生类的析构过程与构造过程正相反。派生类的析构函数仅对自己扩展的数据成员负责。
- 继承同名问题
* 访问子类同名成员 直接访问即可
* 访问父类同名成员 加作用域限制
成员访问表达格式如下:
对象或引用.类名::数据成员
对象或引用.类名::成员函数(实参表);
支配规则:
支配规则确定了基类和派生类之间的同名成员的访问规则,查找定位成员的过程是从派生类到基类查找,即向上查找。
导入基类中指定成员:
using 基类名::成员名;
被导入成员因为非私有成员;被导入的数据成员不能与本类成员重名;被导入的成员函数只需说明函数名,无需说明形参,以导入该函数名所有的重载形式;
菱形继承
虚基类说明格式:
class 派生类名: virtual 继承方式 基类名 {};
关键字virtual与继承方式的位置无关,但必须位于基类名之前,且virtual只对紧随其后的一个基类起作用,该基类无论经过多少次派生,在其派生对象中只有该基类的一个子对象。
例:
class A{public:int a;};
class B1:virtual public A{};
class B2:public virtual A{};
class C:public B1,public B2{public:A };
注意,上面对类A的两个派生关系都要加virtual;而类C的继承关系就不需要加virtual,这是因为虚基类可以继承。
从虚基类直接或间接派生出的派生类中的构造函数的成员初始化列表中都要列出对其虚基类构造函数的调用(若未列出,就隐含调用该基类的缺省构造函数);但只有当前派生类的构造函数真正执行了虚基类的构造函数,保证对虚基类的子对象只初始化一次。虚基类的构造函数只能被调用一次。
派生类不能从其虚基类中继承构造函数。
多态
多态的基本概念
多态分为两类
* 静态多态: 函数重载 和 运算符重载 属于静态多态,复用函数名
* 动态多态: 派生类和虚函数实现运行时多态
静态多态和动态多态区别:
* 静态多态的函数地址早绑定 - 编译阶段确定函数地址
* 动态多态的函数地址晚绑定 - 运行阶段确定函数地址
多态满足条件
* 有继承关系
* 子类重写父类中的虚函数
多态使用条件
* 父类指针或引用指向子类对象
重写:函数返回值类型 函数名 参数列表 完全一致称为重写
虚函数
虚函数是用virtual修饰的成员函数,说明该函数可以被派生类重写。
构造函数、静态成员函数和友元函数不能修饰为虚函数。
含有虚函数的类称为多态类。
非多态类:无虚函数
多态类:有虚函数
is_polymorphic<Ty>::value==false
is_polymorphic<Ty>::value==true
只能静态转换,编译期执行,可取代传统C转换。
static_cast<目标类型>(被转换指针或引用)
静态转换的源和目标没有限制必须为多态类
①向上转换:标准转换或隐式转换
②向下转换:静态转换或代传统C转换。
静态转换或动态转换,但动态转换更安全
dynamic_cast<目标类型>(被转换指针或引用)
动态转换的源必须是多态类,对目标则无限制。
①向上转换:标准转换或隐式转换
②向下转換:静态转换或动态转换
typeid(指针) 得到指针类型,即使用类型
typeid(引用)得到所引用对象实际类型
typeid(指针)得到指针类型,即使用类型
成员函数中调用虚函数,执行改写后的虚函数;(多态)
构造函数、析构函数中调用虚函数,执行本类自己的虚函数,(静态绑定)
虚析构和纯虚析构
如果用"delete 基类指针;"来撤销一个派生类对象时,就只执行基类的析构函数,而不执行派生类的析构函数。
解决方式:将基类中的析构函数改为虚析构或者纯虚析构
虚析构和纯虚析构共性:
*可以解决父类指针释放子类对象
*都需要有具体的函数实现
虚析构和纯虚析构区别:
*如果是纯虚析构,该类属于抽象类,无法实例化对象
虚析构语法:
virtual ~类名(){}
纯虚析构语法:
virtual ~类名() = 0;
类名::~类名(){}
基类中的虚构函数虽可定义为纯虚函数,但类外必须提供一个实现。
定义纯虚函数的一般格式为:
virtual 返回类型 函数名 (形参表) = 0;
如果一个类含有纯虚函数,那么此类就称为抽象类,抽象类不能直接实例化创建对象,其派生类应该以改写形式提供纯虚函数的具体实现。纯虚函数可以定义指针或引用,通过这些指针或引用能调用纯虚函数。
override、final、abstract
派生类如果明确要改写某个虚函数,C++11建议应显式说明,对该函数添加 override 修饰,让编译器检查是否合法有效。
virtual void fun(int) override {};
显式说明抽象,即在类名后用 abstract 说明,显式说明的类无论其是否有纯虚函数,都作为抽象类。例: class base abstract{ };
如果一个虚函数修饰为 final ,派生类就不能再改写它。
纯虚函数和非虚函数不能修饰为final。
virtual void fun(int) final {};
如果一个类修饰为final,该类就不能再说明派生类。例: class base final{ };
运算符重载
友元friend
友元的目的就是让一个函数或者类 访问另一个类中私有成员
友元的三种实现:
*全局函数做友元
*类做友元
*成员函数做友元
例:
class A;
class B{public:int visit(A *aa);};
class A{
int a;
public:
friend void PUT(A *aa) { cout << aa->a; };//全局函数做友元
friend class C;//类做友元,C的所有成员函数都可视为A的友元;
friend int B::visit(A *aa);//成员函数做友元
};
int B::visit(A*aa){return aa->a;}
友元运算符
只能用全局函数来实现,双目运算符中右操作数为对象,左操作数为非同类。
friend ostream& operator<< (ostream &out ,类名&obj);//cout<<obj;
friend istream& operator>> (istream &in, 类名&obj);//cin>>obj;
friend 类名 operator + (int i,类名&obj)// i+obj;
普通运算符
成员函数和全局函数都可实现
例:
class A{
int a;
public:
A(){};
A(int i):a(i){};
//成员函数
A& operator++(){a++;return*this;}//前置自增
A operator++(int){A tp=*this;a++;return tp;}//后置自增
A operator +(const A&aa){return A(this->a+aa.a); }
bool operator >(const A&aa){return a>aa.a;}
A& operator+=(const A&aa){a+=aa.a;return*this;}
//全局友元函数
friend A& operator++(A&aa){aa.a++;return aa;}//前置自增
friend A operator++(A&aa,int){A tp=aa;aa.a++;return tp;}//后置自增
friend A operator +(const A&a1,const A&a2){return A(a1.a+a2.a); }
friend bool operator >(const A&a1,const A&a2){return a1.a>a2.a;}
friend A& operator+=(A&a1,const A&a2){a1.a+=a2.a;return a1;}
friend ostream& operator<<(ostream&out,A&aa){
out<<aa.a;
return out;}
friend istream& operator>>(istream&in,A&aa){
in>>aa.a;
return in;}
};
特殊运算符
只能成员函数来实现,当前对象作为单目运算符的操作数或双目运算符的左操作数。
拷贝赋值: 类名& operator= (const 类名&对象名){函数体;return *this; };
移动赋值: 类名& operator= (类名&&对象名){函数体;return *this; };
类型转换 operator 目标类型 (){return 目标类型;};//目标类型就是返回类型;
下标运算符:返回类型 & operator[](形参){ };
仿函数: 返回类型 operator()(形参表){ };
例:
class INT
{
int d;
public:
INT (int i):d(i){}
operator string(){return to_string(d);}//类型转换
int operator[](int i){return d+i;}//下表访问
void operator()(string s){cout<<"仿函数"<<s;}//仿函数
};
int main()
{
INT in(10);
cout<<string(in)<<endl;
cout<<in[10]<<endl;
in("sd");
}
STL容器
vector容器
vector基本概念
功能:vector数据结构和数组非常相似,也称为单端数组
数组是静态空间,而vector可以动态扩展
动态扩展:并不是在原空间之后续接新空间,而是找更大的内存空间,然后将原数据拷贝新空间,释放原空间
vector容器的迭代器是支持随机访问的迭代器
vector构造函数
vector<T> v; //采用模板实现类实现,默认构造函数
vector(v.begin(), v.end());//将v[begin(), end())区间中的元素拷贝给本身。
vector(n, elem); //构造函数将n个elem拷贝给本身。
vector(const vector &vec); //拷贝构造函数。
vector赋值操作
vector& operator=(const vector &vec);//重载等号操作符
assign(beg, end); //将[beg, end)区间中的数据拷贝赋值给本身。
assign(n, elem); //将n个elem拷贝赋值给本身。
vector容量和大小
empty(); //判断容器是否为空
capacity(); //容器的容量
size(); //返回容器中元素的个数
resize(int num); //重新指定容器的长度为num,若容器变长,则以默认值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除。
resize(int num, elem); //重新指定容器的长度为num,若容器变长,则以elem值填充新位置。
//如果容器变短,则末尾超出容器长度的元素被删除
vector插入和删除
push_back(ele); //尾部插入元素ele
pop_back(); //删除最后一个元素
insert(const_iterator pos, ele); //迭代器指向位置pos插入元素ele
insert(const_iterator pos, int count,ele);//迭代器指向位置pos插入count个元素ele
insert(pos,beg,end); //在pos位置插入[beg,end)区间的数据,无返回值。
erase(const_iterator pos); //删除迭代器指向的元素
erase(const_iterator start, const_iterator end);//删除迭代器从start到end之间的元素
clear(); //删除容器中所有元素
vector数据存取
at(int idx); //返回索引idx所指的数据
operator[]; //返回索引idx所指的数据
front(); //返回容器中第一个数据元素
back(); //返回容器中最后一个数据元素
vector互换容器
swap(vec);//将vec与本身的元素互换
//收缩内存
vector<int>(v).swap(v); //匿名对象
vector预留空间
reserve(int len); //容器预留len个元素长度,预留位置不初始化,元素不可访问。
功能: 减少vector在动态扩展容量时的扩展次数
deque容器
deque基本概念
双端数组,可以对头端进行插入删除操作。
deque容器的迭代器也是支持随机访问的。
deque与vector区别:
vector对于头部的插入删除效率低,数据量越大,效率越低。
deque相对而言,对头部的插入删除速度回比vector快。
vector访问元素时的速度会比deque快,这和两者内部实现有关。
deque函数
与vector函数相同
特有:
push_front(elem); //在容器头部插入一个数据
pop_front(); //删除容器第一个数据
stack容器
stack基本概念
stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口
栈中只有顶端的元素才可以被外界使用,因此栈不允许有遍历行为,不支持迭代器
栈中进入数据称为 --- 入栈 push
栈中弹出数据称为 --- 出栈 pop
stack常用接口
构造函数:
stack<T> stk; //stack采用模板类实现,stack对象的默认构造形式
stack(const stack &stk); //拷贝构造函数
赋值操作:
stack& operator=(const stack &stk); //重载等号操作符
数据存取:
push(elem); //向栈顶添加元素
pop(); //从栈顶移除第一个元素
top(); //返回栈顶元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小
queue容器
queue基本概念
Queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。
队列容器允许从一端新增元素,从另一端移除元素
队列中只有队头和队尾才可以被外界使用,因此队列不允许有遍历行为,不支持迭代器
队列中进数据称为 --- 入队 push
队列中出数据称为 --- 出队 pop
queue常用接口
构造函数:
queue<T> que; //queue采用模板类实现,queue对象的默认构造形式
queue(const queue &que); //拷贝构造函数
赋值操作:
queue& operator=(const queue &que); //重载等号操作符
数据存取:
push(elem); //往队尾添加元素
pop(); //从队头移除第一个元素
back(); //返回最后一个元素
front(); //返回第一个元素
大小操作:
empty(); //判断堆栈是否为空
size(); //返回栈的大小
list容器
list基本概念
功能:将数据进行链式存储
链表(list)是一种物理存储单元上非连续的存储结构,数据元素的逻辑顺序是通过链表中的指针链接实现的
链表的组成:链表由一系列结点组成
结点的组成:一个是存储数据元素的数据域,另一个是存储下一个结点地址的指针域
STL中的链表是一个双向循环链表
由于链表的存储方式并不是连续的内存空间,因此链表list中的迭代器只支持前移和后移,属于双向迭代器,不支持随机访问
list的优点:
采用动态存储分配,不会造成内存浪费和溢出
链表执行插入和删除操作十分方便,修改指针即可,不需要移动大量元素
list的缺点:
链表灵活,但是空间(指针域) 和 时间(遍历)额外耗费较大
List有一个重要的性质,插入操作和删除操作都不会造成原有list迭代器的失效,这在vector是不成立的。
list插入与删除
* push_back(elem);//在容器尾部加入一个元素
* pop_back();//删除容器中最后一个元素
* push_front(elem);//在容器开头插入一个元素
* pop_front();//从容器开头移除第一个元素
* insert(pos,elem);//在pos位置插elem元素的拷贝,返回新数据的位置。
* insert(pos,n,elem);//在pos位置插入n个elem数据,无返回值。
* insert(pos,beg,end);//在pos位置插入[beg,end)区间的数据,无返回值。
* clear();//移除容器的所有数据
* erase(beg,end);//删除[beg,end)区间的数据,返回下一个数据的位置。
* erase(pos);//删除pos位置的数据,返回下一个数据的位置。
* remove(elem);//删除容器中所有与elem值匹配的元素。
list数据存取
* front(); //返回第一个元素。
* back(); //返回最后一个元素。
//cout << L1.at(0) << endl;//错误 不支持at访问数据
//cout << L1[0] << endl; //错误 不支持[]方式访问数据
//list容器的迭代器是双向迭代器,不支持随机访问
//迭代器不可以跳跃访问,即使是+1,只能自增或自减
set/multiset容器
set基本概念
简介:所有元素都会在插入时自动被排序,不支持随机访问
本质:set/multiset属于关联式容器,底层结构是用二叉树实现。
set和multiset区别:
set不允许容器中有重复的元素,multiset允许容器中有重复的元素.
set构造与赋值
构造:
set<T> st; //默认构造函数:
set(const set &st); //拷贝构造函数
赋值:
set& operator=(const set &st); //重载等号操作符
set大小与交换
size(); //返回容器中元素的数目
empty(); //判断容器是否为空
swap(st); //交换两个集合容器
set插入与删除
insert(elem); //在容器中插入元素。
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(elem); //删除容器中值为elem的元素。
set查找与统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数,set中为0或1;
set与multiset的区别
set不可以插入重复数据,而multiset可以
set插入数据的同时会返回插入结果,表示插入是否成功
multiset不会检测数据,因此可以插入重复数据
pair对组创建
pair<type, type> p ( value1, value2 );
pair<type, type> p = make_pair( value1, value2 );
map/multimap容器
map基本概念
简介:
map中所有元素都是pair
pair中第一个元素为key(键值),起到索引作用,第二个元素为value(实值)
所有元素都会根据元素的键值自动排序
本质:
map/multimap属于关联式容器,底层结构是用二叉树实现。
优点:
可以根据key值快速找到value值
map和multimap区别:
map不允许容器中有重复key值元素,multimap允许容器中有重复key值元素
map构造和赋值
构造:
map<T1, T2> mp; //map默认构造函数:
map(const map &mp); //拷贝构造函数
赋值:
map& operator=(const map &mp); //重载等号操作符
map插入和删除
insert(elem); //在容器中插入元素。m.insert(pair<int, int>(1, 10));
emplace(键,值) //在容器中添加一个元素,等同 m[键]=值;
clear(); //清除所有元素
erase(pos); //删除pos迭代器所指的元素,返回下一个元素的迭代器。
erase(beg, end); //删除区间[beg,end)的所有元素 ,返回下一个元素的迭代器。
erase(key); //删除容器中值为key的元素。
map查找和统计
find(key); //查找key是否存在,若存在,返回该键的元素的迭代器;若不存在,返回set.end();
count(key); //统计key的元素个数
- 其他
STL算法
遍历
for_each
功能描述:
- 实现遍历容器
函数原型:
- for_each(iterator beg, iterator end, _func);
// 遍历算法 遍历容器元素
// beg 开始迭代器
// end 结束迭代器
// _func 函数名或者函数对象的实例 一个参数无返回
tansform
功能描述:
- 搬运容器到另一个容器中。
- 搬运的目标容器必须要提前开辟空间,否则无法正常搬运
函数原型:
- transform(iterator beg1, iterator end1, iterator beg2, _func);
//beg1 源容器开始迭代器
//end1 源容器结束迭代器
//beg2 目标容器开始迭代器
//_func 函数名或者函数对象的实例 传入第一个容器的参数,返回值填充进第二个容器
transform(first1, last1, first2, first3, _func);
_func:传入两个参数(第一个容器的值,第二个容器的值),返回值填充进第三个容器。
查找
find
函数原型:
- find(iterator beg, iterator end, value);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代end()
// beg 开始迭代器
// end 结束迭代器
// value 查找的元素
find_if
功能描述:
按条件查找元素
函数原型:
find_if(iterator beg, iterator end, _Pred);
// 按条件查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// _Pred 函数或者谓词(返回bool类型的仿函数)
search
search(beg,end,beg2,end2);
在[beg,end)区间查找子序列[beg2,end2);
发现返回第一个容器首元素位置的迭代器,未发现则返回end.
search(beg,end,beg2,end2,_op);
_op:返回区间[beg,end)中和[beg2,end2)中对应元素使_op(elem,elem2)都为true的元素区间的首元素位置的迭代器,未发现则返回end.
adjacent_find
功能描述:
- 查找相邻重复元素
函数原型:
- adjacent_find(iterator beg, iterator end);
// 查找相邻重复元素,返回相邻元素的第一个位置的迭代器
// beg 开始迭代器
// end 结束迭代器
binary_search
功能描述:
- 查找指定元素是否存在
函数原型:
- bool binary_search(iterator beg, iterator end, value);
// 查找指定的元素,查到 返回true 否则false
// 注意: 在无序序列中不可用
// beg 开始迭代器
// end 结束迭代器
// value 查找的元素
count
功能描述:
- 统计元素个数
- 统计自定义数据类型时候,需要配合重载 operator==
函数原型:
- count(iterator beg, iterator end, value);
// 统计元素出现次数
// beg 开始迭代器
// end 结束迭代器
// value 统计的元素
count_if
功能描述:
- 按条件统计元素个数
函数原型:
- count_if(iterator beg, iterator end, _Pred);
// 按条件统计元素出现次数
// beg 开始迭代器
// end 结束迭代器
// _Pred 谓词 一个参数,返回bool值
排序
sort
功能描述:
- 对容器内元素进行排序
函数原型:
- sort(iterator beg, iterator end, _Pred);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// _Pred 谓词 两个参数,返回bool值
random_shuffle
功能描述:
- 洗牌 指定范围内的元素随机调整次序
- 使用时记得加随机数种子
函数原型:
- random_shuffle(iterator beg, iterator end);
// 指定范围内的元素随机调整次序
// beg 开始迭代器
// end 结束迭代器
merge
功能描述:
- 两个容器元素合并,并存储到另一容器中
- 目标容器需要提前开辟空间
函数原型:
- merge(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 容器元素合并,并存储到另一容器中
// 注意: 两个容器必须是有序的
// beg1 容器1开始迭代器 // end1 容器1结束迭代器
// beg2 容器2开始迭代器 // end2 容器2结束迭代器
// dest 目标容器开始迭代器
reverse
功能描述:
- 将容器内元素进行反转
函数原型:
- reverse(iterator beg, iterator end);
// 反转指定范围的元素
// beg 开始迭代器
// end 结束迭代器
拷贝和替换
copy
功能描述:
- 容器内指定范围的元素拷贝到另一容器中
- 目标容器记得提前开辟空间
函数原型:
- copy(iterator beg, iterator end, iterator dest);
// 按值查找元素,找到返回指定位置迭代器,找不到返回结束迭代器位置
// beg 开始迭代器
// end 结束迭代器
// dest 目标起始迭代器
replace
功能描述:
- 将容器内指定范围的旧元素修改为新元素
- replace会替换区间内满足条件的元素
函数原型:
- replace(iterator beg, iterator end, oldvalue, newvalue);
// 将区间内旧元素 替换成 新元素
// beg 开始迭代器
// end 结束迭代器
// oldvalue 旧元素
// newvalue 新元素
replace_if
功能描述:
- 将区间内满足条件的元素,替换成指定元素
函数原型:
- replace_if(iterator beg, iterator end, _pred, newvalue);
// 按条件替换元素,满足条件的替换成指定元素
// beg 开始迭代器
// end 结束迭代器
// _pred 谓词
// newvalue 替换的新元素
swap
功能描述:
- 互换两个容器的元素
- 注意交换的容器要同种类型
函数原型:
- swap(container c1, container c2);
// 互换两个容器的元素
// c1容器1
// c2容器2
算数生成
accumulate
功能描述:
- 计算区间内 容器元素累计总和
- accumulate使用时头文件注意是 numeric
函数原型:
- accumulate(iterator beg, iterator end, value);
// 计算容器元素累计总和
// beg 开始迭代器
// end 结束迭代器
// value 起始值,为0;
fill
功能描述:
向容器区间中覆盖指定的元素
函数原型:
fill(iterator beg, iterator end, value);
// 向容器中填充元素
// beg 开始迭代器
// end 结束迭代器
// value 填充的值
generate
功能描述:
- 向容器区间中覆盖 函数返回值
函数原型:
- generate(iterator beg, iterator end, _func);
// 向容器中填充元素
// beg 开始迭代器
// end 结束迭代器
// _func 函数名或者函数对象的实例 无参,返回值填充进容器
集合算法
set_intersection
功能描述:
- 求两个容器的交集
函数原型:
- set_intersection(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的交集
// 注意:两个集合必须是有序序列,//取两个里面较小的值给目标容器开辟空间
//set_intersection返回值既是交集中最后一个元素的位置
// beg1 容器1开始迭代器 // end1 容器1结束迭代器 // beg2 容器2开始迭代器 // end2 容器2结束迭代器 // dest 目标容器开始迭代器
set_union
功能描述:
- 求两个集合的并集
函数原型:
- set_union(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的并集
// 注意:两个集合必须是有序序列 //取两个容器的和给目标容器开辟空间
// beg1 容器1开始迭代器 // end1 容器1结束迭代器 // beg2 容器2开始迭代器 // end2 容器2结束迭代器 // dest 目标容器开始迭代器
set_difference
功能描述:
求两个集合的差集
函数原型:
set_difference(iterator beg1, iterator end1, iterator beg2, iterator end2, iterator dest);
// 求两个集合的差集
// 注意:两个集合必须是有序序列
// beg1 容器1开始迭代器 // end1 容器1结束迭代器 // beg2 容器2开始迭代器 // end2 容器2结束迭代器 // dest 目标容器开始迭代器
list特有算法
关系算法
- a
MySQL使用C语言查询
常用api
1 MYSQL *mysql_init(MYSQL *mysql)
功能: 分配或初始化MYSQL对象。
参数:mysql
待初始化的MYSQ对象,将对象地址传入,NULL指针,该函数将分配、初始化、并返回新对象。否则,将初始化对象,并返回对象的地址。
用法实例:
MYSQL mysql;
mysql_init(&mysql); // 或者 mysql = mysql_init(NULL);
2 MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag)
功能:尝试与运行在主机上的MySQL数据库引擎建立连接,这个函数参数很多啊。看名字就可以是什么意思了
参数:mysql 前面一个函数的返回的mysql实例句柄。host 要连接的数据库的主机,可以是ip地址或主机名。user 表示登录数据库的用户名
passwd 登录的密码。db 就是访问的数据库。port mysql的tcp/ip端口默认是3306。unix_socket 表示连接类型。client_flag 暂时为0即可。(一般win下后两个参数为NULL,0)
3 int mysql_query(MYSQL *mysql,const char *query)
功能:根据query查询语句执行查询数据库
参数: mysql mysql的实例。query 查询语句字符串可以是 增删修查
返回值: 成功返回0,失败返回非0
4 MYSQL_RES *mysql_store_result(MYSQL *mysql)
功能:得到查询的结果集,对于成功检索了数据的每个查询(SELECT、SHOW、DESCRIBE、EXPLAIN、CHECK TABLE等),必须调用mysql_store_result()或mysql_use_result()
参数:mysql前面的mysql实例
返回值:成功返回MYSQL_RES结构体,该结构体中保存查询的结果
5 unsigned int mysql_num_fields(MYSQL_RES *result)
参数:前面返回的结果地址;
返回结果集中的行数。
6 MYSQL_ROW mysql_fetch_row(MYSQL_RES *result)
功能:mysql_store_result得到的结果结构中提取一行,并把它放到一个行结构中。
返回值:下一行的一个MYSQL_ROW结构当数据用完或发生错误时返回null
7 void mysql_free_result(MYSQL_RES *result)
释放由mysql_store_result()、mysql_use_result()、mysql_list_dbs()等为结果集分配的内存。完成对结果集的操作后,必须调用mysql_free_result()释放结果集使用的内存。
8 void mysql_close(MYSQL *mysql)
关闭前面打开的连接。
综上 返回值 若是指针,NULL表示失败, 其他表示成功
若是整形,0表示成功,其他表示失败;
示例
MYSQL sql;
mysql_init(&sql);
MYSQL_ROW row;
if (mysql_real_connect(&sql, "localhost", "root", "root", "mydatabase", 3306, NULL, 0)) //连接到mysql
{
cout << "\n\t-----MySQL已连接----" << endl;
}
if (!mysql_query(&sql, "select * from student")) //若查询成功返回0,失败返回随机数
{
cout << "\n\t ----查询成功----" << endl;
}
MYSQL_RES * rs = mysql_store_result(&sql); //将查询到的结果集储存到result中
int num = mysql_num_fields(rs); //将结果集列数存放到num中
while ((row = mysql_fetch_row(rs))) //遇到最后一行,则中止循环
{
for (int i = 0; i < num; i++) //利用for循环,输出该行的每一列
{
cout << row[i]<< "\t"; //row是MYSQL_ROW变量,可以当做数组使用,i为列数
}
cout << endl;
}
mysql_free_result(rs); //释放结果集所占用的内存
mysql_close(&sql); //关闭与mysql的连接
- other
其他
复制
int a[5]={1,2,3,4,5};
int b[5]={6,7,8,9,0};
将数组b复制到数组a中:
memcpy(a,b,sizeof(b));//方法1,#include <string.h>
memmove(a,b,sizeof(b));//方法2,#include <string.h>
std::copy(b,b+sizeof(b),a);//方法3
可变参数函数
#include<stdarg.h>
double average(int num,...){
va_list arglist;//定义参数列表
//初始化参数列表,第一个参数为 参数列表,第二个参数为 可变参数...前面一个形参
va_start(arglist,num);
double sum = 0 ;
for(int i = 0 ; i<num ; i++)
sum+=va_arg(arglist,int);//循环读取参数列表中的int参数
/*va_arg宏的第2个参数不能被指定为char、short或者float类型。
因为char和short类型的参数会被转换为int类型,而float类型的参数会被转换为double类型 ……*/
va_end(arglist);//释放参数内存
return sum / num;
}
//c++11的初始化列表
double avrg(initializer_list<int> alist){
double sum =0;
for(auto it = alist.begin(); it!=alist.end();it++)
sum += *it;
return sum / alist.size();
}
cout<<average(6 ,1,2,3,4,5,6)<<endl;
cout<<avrg( { 1,2,3,4,5,6 } )<<endl;
模板
函数模板
语法:
template<typename T>
函数声明或定义
解释:
template --- 声明创建模板
typename --- 表面其后面的符号是一种数据类型,可以用class代替
T --- 通用的数据类型,名称可以替换,通常为大写字母
调用:
//1、自动类型推导
mySwap(a, b);
//2、显示指定类型
mySwap<int>(a, b);
注意:
* 自动类型推导,必须推导出一致的数据类型T,才可以使用
* 模板必须要确定出T的数据类型,才可以使用
普通函数和函数模板的调用规则:
* 如果函数模板和普通函数都可以实现,优先调用普通函数
* 可以通过空模板参数列表来强制调用函数模板
* 函数模板也可以发生重载
* 如果函数模板可以产生更好的匹配,优先调用函数模板
类模板
语法:
template<class T>
类声明或定义
类模板与函数模板区别主要有两点:
* 类模板没有自动类型推导的使用方式
* 类模板在模板参数列表中可以有默认参数
类模板中成员函数和普通类中成员函数创建时机是有区别的:
* 普通类中的成员函数一开始就可以创建
* 类模板中的成员函数在调用时才创建
类模板实例化出的对象,向函数传参的方式 :
1. 指定传入的类型 --- 直接显示对象的数据类型
2. 参数模板化 --- 将对象中的参数变为模板进行传递
3. 整个类模板化 --- 将这个对象类型 模板化进行传递
//类模板
template <class T1,class T2>
class MYC{T1 t1;T2 t2;};
//1.指定传入的类型
void func1(MYC<int, int> & m){};
//2.参数模板化
template <class T1,class T2>
void func2(MYC<T1,T2> & m){};
//3.整个类模板化
template <class T>
void func3(T & t){};
当类模板碰到继承时,需要注意一下几点:
* 当子类继承的父类是一个类模板时,子类在声明的时候,要指定出父类中T的类型
* 如果不指定,编译器无法给子类分配内存
* 如果想灵活指定出父类中T的类型,子类也需变为类模板
类模板中的成员函数类外实现:
//类模板中成员函数类外实现
template<class T1, class T2>
class Person {
public:
//成员函数类内声明
Person(T1 name, T2 age);
void showPerson();
public:
T1 m_Name;
T2 m_Age;
};
//构造函数 类外实现
template<class T1, class T2>
Person<T1, T2>::Person(T1 name, T2 age) {
this->m_Name = name;
this->m_Age = age;
}
//成员函数 类外实现
template<class T1, class T2>
void Person<T1, T2>::showPerson() {
cout << "姓名: " << this->m_Name << " 年龄:" << this->m_Age << endl;
}
类模板分文件编写:
问题:
类模板中成员函数创建时机是在调用阶段,导致分文件编写时链接不到
解决:
方式1:直接包含.cpp源文件
方式2:将声明和实现写到同一个文件中,并更改后缀名为.hpp,hpp是约定的名称,并不是强制
控制台操作
检测键盘输入
可以用kbhit()函数和或getch()函数。
kbhit()的用法:
#include <coio.h>
程序执行到kbhit()时,等待输入,但是不会停止而是继续运行,有输入时kbhit()才就返回一个非零值,否则返回0。
getch()直接读取一个字符,不用等回车键。
设置光标位置
SetConsoleCursorPosition函数
#include <conio.h> #include <Windows.h>
typedef struct _COORD {
SHORT X;
SHORT Y;
} COORD
COORD coord;
coord.X=10;
coord.Y=20;
SetConsoleCursorPosition( GetStdHandle(STD_OUTPUT_HANDLE),coord);
//将控制台光标移到(x,y)处;
设置光标信息
SetConsoleCursorInfo函数
typedef struct _CONSOLE_CURSOR_INFO {
DWORD dwSize;
WINBOOL bVisible;
} CONSOLE_CURSOR_INFO
CONSOLE_CURSOR_INFO cci;
cci.dwSize=sizeof cci;
cci.bVisible=FALSE;//可见
SetConsoleCursorInfo(GetStdHandle(STD_OUTPUT_HANDLE),&cci);
//设置光标信息 大小,,可见,
Sleep
#include <windows.h>
函数原型
void Sleep(DWORD dwMilliseconds);
参数为毫秒
system()
头文件"stdlib.h"
带路径的要使用绝对路径,用"/"或者"\\"分隔;带中文名的要使用GBK编码
例:
System("D:/DEV/devcpp.exe")://打开D:/DEV中的devcpp.exe
如果正确执行会0;否知返回其它非0数;
全局字体颜色
system("color 70");//产生黑字白底
system("color 07");//产生白字黑底
color命令详细介绍
局部字体颜色
void setColor(short BackGroundColor=0, short ForeColor=7)
{
HANDLE handle=GetStdHandle(STD_OUTPUT_HANDLE);//获取当前窗口句柄
SetConsoleTextAttribute(handle,ForeColor+BackGroundColor*0x10);//设置颜色
}
设置默认的控制台前景和背景颜色。
第一个对应于背景,第二个对应于前景。
0 = 黑色 8 = 灰色
1 = 蓝色 9 = 淡蓝色
2 = 绿色 A = 淡绿色
3 = 浅绿色 B = 淡浅绿色
4 = 红色 C = 淡红色
5 = 紫色 D = 淡紫色
6 = 黄色 E = 淡黄色
7 = 白色 F = 亮白色
防止头文件重复包含
为了避免同一个文件被include多次,C/C++中有两种方式,一种是 #ifndef 方式,一种是 #pragma once 方式。
方法一:
#ifndef SOMEFILE_H
#define SOMEFILE_H
// 声明语句
#endif
方法二:
#pragma once
// 声明语句
#include <time.h>
clock_t clock();//计算处理器所用时间 毫秒
time_t mktime(struct tm * timeptr); //将tm类型的指针转换为time_t类型
time_t time(time_t * timer);取得系统当前时间,形参用来保存结果;实参为NULL,返回但不保存。
char * asctime(const struct tm * timeptr);
char * ctime(const time_t *timer);//通过time_t类型的指针返回一个日期,时间的字符串
struct tm * gmtime(const time_t *timer); //将日历时间(time_t)转化为世界标准时间(即格林尼治时间),并返回一个tm结构体来保存这个时间
struct tm * localtime(const time_t * timer);// 将time_t指针转化为tm指针;
tm结构包括:
tm_year:从1900开始; tm_mon:0-11,1月为0;
tm_day:1-31; tm_hour:0-23;
tm_min:0-59; tm_sec:0-59;
tm_wday:0-6,周日为0; tm_yday:0-365,1月1日为0;
time_t l_t=time(NULL);
tm * pt=localtime(&l_t);
hour=pt->tm_hour;
min=pt->tm_min;
sec=pt->tm_sec;
#include <cctype>
加入这个头文件就可以调用以下函数:
1、isalpha(x) 判断x是否为字母
2、isdigit(x) 判断x是否为数字
3、islower(x) 判断x是否为小写字母
4、isupper(x) 判断x是否为大写字母
5、isalnum(x) 判断x是否为字母或数字
6、ispunct(x) 判断x是否为标点符号
7、isspace(x) 判断x是否为空格
对于以上函数,如果x符合条件的话,均会返回true,否则返回false
还有以下函数:
1、toupper(x) 如果x是小写字母,将其转换成大写字母
2、tolower(x) 如果x是大写字母,将其转换成小写字母
g++编译命令
g++ main.cpp -o main 直接将mian.cpp编译成main.exe
-L 要链接的库所在目录
-l 指定链接时需要的动态库,隐含命名规则,即在前加lib,在后加.dll确定库文件名
例:libmysql.dll
-lmysql 或直接用libmysql.dll
-I 要链接的头文件目录
示例:
g++ main.cpp -o main -I include -L lib -lmysql
小知识
正数 原码,反码,补码相同
对于负数,反码符号位不变,其他位取反。 补码:反码+1;
while(cin.get()!='\n');
cin.sync() ; 清空输入缓存区;
system("pause"); 按任意键继续
system("cls"); 清屏
swap(a,b);//交换数据或数组a.b;
字符大小写转换: ch=ch^32;
C语言字符串'\'续行;
- other