文章目录
C++ 基础知识
1. 强制类型转换
存在原因:当数据类型由小转大可以自动完成,但是由大转小就要使用强制类型转换。
特点:
无论是自动的类型转换还是强制类型转换,都只是为了本次操作或运算而进行的临时转换,转换的结果也会保存到临时的内存空间内,不会改变数据本来的类型或者具体的值。
强制类型转换格式:
- (typename) value
- typename (value)
C++ 中有四个关键字可以实现强制类型转换:
1.1 static_cast (value)
编译器就进行了转换,可以适用于大部分的转换。
- 基本数据类型转换
- 可以用于void * 和其它指针类型转换,但不能将两个无关的指针类型进行转换。
- 用于类继承结构中基类和派生类之间指针或引用的转换,从派生类转换为基类比较安全
- 涉及左值到右值、数组到指针或函数到指针的转换,也可以通过static_cast显式执行。
1.2 dynamic_cast(value)
只能对指针和引用的进行转换,并且只用于类继承结构中基类和派生类之间指针或引用的转换(并且在基类中有虚函数才可以),可以进行向上、向下,或者横向的转换。
有一种特殊的情况就是可以把类指针转换成 void* 类型。
1.3 const_cast (value)
const_cast 不能去除变量的常量性,只能用来去除指向常数对象的指针或引用的常量性,且去除常量性的对象必须为指针或引用.
常量指针被转化成非常量指针,并且仍然指向原来的对象,常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象可能被转换成非常量对象。
1.4 reinterpret_cast(value)
将无关的类型进行转换。
以上数据类型转换在以下博客有详细举例说明,可以参考。
数据类型转换博客参考链接
2. 字符数组中sizeof() 和strlen()
strlen() 计算字符串的长度,不包括\0, 而sizeof()计算所占用的内存,包括\0.
举例如下:
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
char name[10];
cout<< "please input your name:"<<endl;
cin>>name;
cout<<"strlen of name is"<<strlen(name)<<endl;
cout<<"sizeof of name is" <<sizeof(name)<<endl;
return 0;
}
//test-----------
gege
strlen(name)=4
sizeof(name)=10
3. cin 中getline() 和get() 介绍:
cin使用空白(空格,制表符和换行符)来确定字符串的结束位置,这意味着cin在获取字符数组输入时,只能读取一个单词,因此出现cin的成员函数getline() 和get() 用来读取行。
getline() : 读取一行的输入,然后丢弃换行符。//第一个参数时数组名称,第二个参数是要读取的字符数。实际能读取的字符数是第二个参数-1.
get(xxx,size) : 读取一行的输入,将换行符丢在输入队列中。
特点:
- get(xxx,size) 会将换行符留在输入队列中,因此使用get(xxx,size) 后需要使用cin.get() 避免换行符对下一次输入影响。(如果不使用cin.get()换行符留在输入队列中,下次使用get(xxx, size)时会遇换行符直接返回,获取空字符串。)
- getline(xxx, size) 实际能接收的字符串数为size-1
- 使用cin>>xxx 按enter键后会转换为换行符留在输入队列,因此需要使用cin.get() 先获取走换行符,然后再正确输入。
- cin.get() 读取空行时,会设置失效位failbit,需要使用cin.clear()
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
char name[20];
char foot[10];
cout<<"please input your name:"<<endl;
cin.getline(name,20); //最多接收19字符
cout<<"please input your favorite foot: "<<endl;
cin.getline(foot,10);
cout<<name<<"this is your favorite foot: "<<foot<<endl;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
char name[20];
char foot[10];
cout<<"your name: ";
cin.get(name,20);
cin.get(); /* 此句至关重要,否则不会等你输入foot就打印输出*/
cout<<"favorite foot: ";
cin.get(foot,10);
cout<<name<<" this is your favorite foot: "<< foot<<endl;
return 0;
}
#include <iostream>
#include <cstring>
using namespace std;
int main(void)
{
char name[20];
int year;
cout<<"year:"<<endl;
cin>>year;
cin.get(); /*此句很重要,否则不等你输入name 自动打印最后一句输出*/
cout<<"your name: ";
cin.get(name,20);
cout<<"your name:"<<name<<" year: "<<year<<endl;
return 0;
}
4. string 类介绍:
string 类的操作:
赋值操作:str1=str2;
拼接操作:str1=str2+str3;
附加操作:str1+=str2;
比较操作:if(str1!= str2) ,再如if(str1==“test”)
I/O相关操作:getline(cin,str); 读取一行到str中;
字符串大小: str1.size() //相当于strlen();
C风格的字符串比较:strcmp(str1, str2) //=0 相同,<0 str1 在str2 前, >0 str1 在str2 后。
string 类对象使用方式和数组有很多相似,如下:
- 可以使用C-风格字符串初始化string 类对象
- 可以使用cin 将键盘输入存入string 对象
- 可以使用cout 将string 输出
- 可以使用数组表示方法访问存储在string 对象中的字符
举例说明:
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
int main()
{
string str1;
string str2="Str2_pre_";
string str3;
string str4;
cout<<"please unput str1 & str3"<<endl;
cin >> str1; //只能读取一个字符串到str1 中,遇到空格等会自动结束
cin.get(); //有该行,输入str1后输入换行符会留在输入队列,但会被cin.get() 获取不影响getline,获取输入队列中的一个字符。
getline(cin,str3); //将一行输入到str3
str3+=str1;
str4=str1+str2;
cout<<"the size of str4 is"<<str4.size()<<endl;
cout<<"str1="<<str1<<endl;
cout<<"str2="<<str2<<endl;
cout<<"str3="<<str3<<endl;
cout<<"str4="<<str4<<endl;
return 0;
}
执行结果:
please unput str1 & str3
str1_pre_
str3_pre_
the size of str4 is18
str1=str1_pre_
str2=Str2_pre_
str3=str3_pre_str1_pre_
str4=str1_pre_Str2_pre_
5.结构体,结构体数组,枚举
结构体定义和初始化:
结构体初始化:
struct student{
string name;
int score;
};
结构体变量定义和初始化:
struct student student1;
顺序初始化:
struct student student2={“xiaoming”,90};
乱序初始化:
struct student student3={
.name=“xiaomi”;
.score=30;
}
struct student student4={
.score=80;
}
参考网址:结构体定义和初始化说明
结构体数组
struct student[10];
struct student[2]={
{“xiaoge”, 6, 99},
{“xiaohan”,6,90},
}
枚举enum
enum color{
red,
black=1,
orange,
};
测试用例:
#include <iostream>
#include <cstring>
#include <string>
using namespace std;
struct student{
string name;
int yera;
float score;
};
enum color{
red,
black=1,
orange,
};
int main()
{
struct student stu_test[3]={
{"xiaohu",8,90},
{"xiaohan",10,99},
{"xiaoliu",7,88}
};
color mycl;
color yourcl;
mycl=black;
yourcl=color(2); //可以使用color(x)强制类型转换
for(int i=0;i<3;i++)
{
cout<<" name: "<<stu_test[i].name<<" year: "<< stu_test[i].yera<<" score "<< stu_test[i].score<<endl;
}
cout<<" mycolor:"<<mycl<<" yourcolor: "<<yourcl<<endl;
return 0;
}
6. 指针介绍:
指针声明和初始化:
int a=10;
int *p1 = &a; //指针声明并初始化
int *p2; //指针声明
p2 = &a; //指针赋值
指针声明和初始化注意事项:
-
指针声明时,只为指针变量分配空间,用于存储指针变量,但并未给指针变量所指定的地址数据分配空间。
因此:int *p; *p=1000; 这种使用就不正确,p并未指定地址。
-
指针变量名代表地址,通常任意类型的指针变量自身类型相同,都是表示地址。
-
每个指针变量名都需要一个*。
比如int * p1,p2; //实际p1 是int* 指针,p2是int 型变量。
-
虽然指针通常是一个整数,但C++中并不能直接将整数赋值给一个指针变量;
比如:int *p; p=0xB8000000; 这个在C++中是不允许的,需要使用强制类型转换。 如:int *p; p=(int *) 0xB8000000;
new & delete 介绍:
通常使用定义数组大小(如int arry[10]) 在编译阶段就已经分配好内存大小,但实际上通常需要运行阶段根据需要是否分配内存,以及分配多大的内存,这就需要用到new.
格式:
-
TypeName *p = new TypeName;
-
TypeName *pa = new TypeName [number];
比如:
int *p =new int; //通过new 分配int大小的内存空间,并将内存空间的地址返回给p.如此可以通过p直接操作这块内存。
double *pa = new double [20]; //通过new 按照double 类型20个元素分配内存地址,并将内存首地址返回给pa.
特点:
- 使用new 分配的内存需要使用delete 释放内存,否则可能会引起内存泄漏。
- 使用new分配则用delete, 使用new [] 分配,则使用delete[] 释放整个数组对应的内存。
- 一个指针不能delete 两次
- delete 一个空指针是可以的
指针和数组
int *pa = new int [10];
int arry[5];
int *par = arry;
特点**:
-
数组名可以当做地址,但是不能对数组名进行算数操作。
*(arry+1)
-
指针可以当做数组名 ,按照数组引用的方式使用。
pa[0],pa[1]
-
允许指针和整数相加,加1的结果等于原来地址值加上指向的对象所占用的总字节数。
par+=1;
指针数组举例:
#include<iostream>
using namespace std;
int main()
{
//int *pi = new int [10];
double arry[3]={100.9,101.1,102.2};
double *pd = arry;
double *pd2 =&arry[0];
/*
for(int i=0; i<10;i++)
{
pi[i]=i;
}
*/
cout<<"*pd= "<< *pd << endl;
cout<<"pd[0] "<<pd[0]<<"pd[1]"<<pd[1]<<endl;
pd=pd+1;
cout<<"af change pd[0]"<<pd[0]<<"pd[1]"<<pd[1]<<endl;
cout<<"arry output *arry"<<*arry<<"*(arry+1)"<<*(arry+1)<<endl;
cout<<"*pd2 = "<<*pd2<<"*(pd2+1)"<< *(pd2+1)<<endl;
return 0;
}
指针和字符串:
注意:
-
cout和多数c++表达式中,char 数组名,char指针以及引号括起来的字符串常量都被解释为第一个字符的地址。如果给cout提供第一个字符的地址,它将从字符开始打印,直到遇到空字符为止。
-
一般给cout提供一个指针,它将打印地址,如果该指针是char类型,则将打印字符串输出。
-
将字符串赋值给一个char 数组时,需要使用strcpy 或strncpy 进行拷贝。 strncpy 会多个拷贝大小的参数,避免越界。拷贝的大小<设定的大小,则会自动加\0。否则需手动加\0
指针和结构体:
可以定义指针指向结构体,也可以从堆上分配结构体类型的内存。如下:
struct xxx test1;
struct xxx * p1 = &test1;
struct xxx *p2 = new struct xxx;
如下简单例子:
#include <iostream>
using namespace std;
struct student{
char name[20];
int year;
};
int main()
{
struct student * p = new struct student;
cout<<"input name:"<<endl;
cin.get(p->name,20);
cout<<"input year: "<<endl;
cin>>(*p).year;
cout<<"name : "<<p->name<<" at year: "<<p->year;
delete p;
return 0;
}
7. 自动存储,静态存储,动态存储
自动存储是:指普通的局部变量,存储在栈上,遵循栈后进先出的原则,且栈上空间连续分配。
静态存储是:函数外全局变量或者static声明的变量,也是存储在栈上,但运行整个程序空间
动态存储是:使用new在堆上分配的空间,用户更大限度使用内存空间,但空间使用不连续。
8. const char p 和char const * p 和 char const p 的理解:
*const char p // p 是const 类型常量,这里不是说p 指针指向的必须是cosnt 常量,而是说不能通过指针p去修改p的内容。
char const * p 与cosnt char*p 是一个意思。
char const p:* 是说p 是cosnt 类型,可以通过p去修改*p的内容,但是p不能再指向别的变量/常量。
比如: static const char* const VERSION=“Test"version"test2”; //则表示VERSION 不能指向别的常量或变量,且不能通过VERSION 修改指针多指向的值。
详细可参考:const charp 和char const p 的理解
9 typedef 的用法:
typedef 函数指针的用法:
函数指针用法:
int (*pFunc)(int);
int Func1(int a){
return a;
}
PFunc =Func1;
(*PFunc)(3); // 返回3
typedef 函数指针用法:
typedef int (*pFunc)(int);
pFunc Func1; // 这里就可以使用PFunc 去定义函数指针
int Func2(int b){
return b;
}
Func1=Func2;
Func1(4);
10 define 的用法:
define 函数用法1(与连接符## 结合):
void my_put(int a){
cout <<"my put"<<a<<endl;
}
void my_test(int a){
cout<<"my test"<<a<<endl;
}
void you_test(int b){
cout<<"you_test"<<b<<endl;
}
#define what(x,a) x##_test(a) //## 就是连接符,连接两个内容在一起
#define how(x,b) my_##x(b)
void main(){
what(my,5);
what(my,6);
how(test,7);
how(put,8);
}
define 函数用法2
一般控制debug 输出,可以设置函数定义,多个参数时用args …
#define OPEN_DEBUG
#ifndef OPEN_DEBUG
#define test_printf(args...) do{}while(0)
#endif
void test_printf(int level, const char* fmt,...){
if(level>1)
vsyslog(LOG_DEBUG,fmt,ap);
}
11 va_list/va_start/va_end 用法:
va_lsit 主要解决多参数的用法。
void my_printf(const char *fmt,...){
va_list ap; //定义指向...参数的指针
va_start(ap,fmt);
vprintf(fmt,ap);
va_end(ap);
}
参考网址:va_list 相关用法参考
te