C++ 语言基础(一)基本结构和指针及一些常用用法


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.

特点:

  1. 使用new 分配的内存需要使用delete 释放内存,否则可能会引起内存泄漏。
  2. 使用new分配则用delete, 使用new [] 分配,则使用delete[] 释放整个数组对应的内存。
  3. 一个指针不能delete 两次
  4. 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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值