C实现动态数组
存储学生信息,要求顺序存储可逐个添加信息,减少内存浪费。
#include
#include //字符串处理
#include //内存分配
struct Stu{ //单个学生结构体
int id; // 学号
char name[20]; //姓名
};
struct Stu_arr{//学生组织结构体
struct Stu *pstu; //指向结构体的指针
int size;
};
void init(struct Stu_arr *p_ss);
void destroy(struct Stu_arr *p_ss);
void push_back(struct Stu_arr *p_ss, struct Stu *p_stu);
void print(struct Stu_arr *p_ss);
int main(){
struct Stu_arr ss;
init(& ss); //初始化
Struct Stu stu1;
stu1.id = 1;strcpy(stu1.name, "张三");
push_back(&ss, &stu1);
print(&ss);
stu1.id = 5; strcpy(stu1.name, "mike");
push_back(&ss, &stu1);
print(&ss);
destroy(&ss); //清理内存
system("pause");
return 0;
}
//初始化
void init(struct Stu_arr *p_ss) {
p_ss->size = 0;
p_ss->pstu = NULL;
}
void destroy(struct Stu_arr *p_ss) {
if (p_ss->pstu) //
free(p_ss->pstu);//释放内存
}
void push_back(struct Stu_arr *p_ss, struct Stu *p_stu) {
struct Stu* p_new = (struct Stu*)malloc((p_ss->size + 1)
* sizeof(struct Stu));
memcpy(p_new, p_ss->pstu, p_ss->size * sizeof(struct Stu));
memcpy(p_new + p_ss->size, p_stu, sizeof(struct Stu));
free(p_ss->pstu);
p_ss->pstu = p_new;
p_ss->size++;
}
void print(struct Stu_arr *p_ss) {
for (int i = 0; i < p_ss->size; i++) {
printf("(%d,%s)\t", (p_ss->pstu)[i].id, (p_ss->pstu)[i].name);
}
printf("\n");
}
C++
使用c++中的标准库类型vector可以很轻松的完成任务。
不需要管理内存分配,对不同的类型都可以处理
使用c++中 string标准库类型string替代c中的字符数组类,编程更加自如
#include
#include
#include
using namespace std;
struct Stu{
int id;
string name;
};
int main(){
vector ss;
Stu stu1;
stu1.id = 1; stu1.name = "张三";
ss.push_back(stu1);
stu1.id =5; stu1.name = "mike";
ss.push_back(stu1);
for(int i = 0; i < ss.size(); i++){
cout<<"("<
C++对C的扩展(命名空间:作用域)
在相同作用域,同名变量只可以定义一次
不同作用域中,同名变量课重复定义
只有新定义的起作用
实际上,不同作用域的同名变量所占有的空间是不同的。
#include
int x = 10;
void fun(){
int x = 20;
printf(" fun()" x = %d\n,x);
}
int main(){
int x = 30;
{
int x = 40;
printf("main(){}: x =%d\n",x);
}
fun();
printf("main():x = %d\n",x);
return 0 ;
}
C++对C的扩展(命名空间:引入原因)
在大型项目过程中,经常会用到多家公司提供的类库,或者协作开发的多个小组之间,可能会使用同名的函数或者全局变量,从而造成冲突。
基本语法:
namespace 空间名字{
变量;
函数;
}(注意此处没有分号)
基本用法:
空间名字::变量名/函数名
命名空间分割了全局命名空间(::)
每个命名空间都是一个作用域
#include
namespace A {
int x = 10;
void print() { printf("A::x=%d\n", x); }
}
namespace B {
int x = 20;
void print() { printf("B::x=%d\n", x); }
}
int x = 30;
void print() { printf("x=%d\n", x); }
int main() {
int x = 40;
printf("main:x=%d\n", x); //局部变量x 40
printf("全局:x=%d\n", ::x); //全局的x 30
printf("命名空间A::x=%d\n", A::x);//A中的x 10
printf("命名空间B::x=%d\n", B::x);//B中的x 20
A::print(); //10
B::print(); //20
print(); //30
return 0;
}
命名空间:使用方法
- 空间名字::变量名/函数名
- using 空间名字::变量名/函数名
- using namespace 空间名字
第一种方法推介,肯定不会有错
第2中种用法每次只能引入一个成员
第3种用法会打开该空间中所有的成员(变量/函数等),谨慎使用
#include
namespace A {
int x = 10, y = 20;
}
int x = 30;
int main() {
//打开了A, A中的名字被“添加”到全局作用域
{
using namespace A;
y++; //此处y是 A::y (ok)
//x++; //此处的x可能是::x也可能是A::x (错误)
::x++; //指明了是全局作用域的x (ok)
A::x++; //指明了是 A::x (ok)
int x = 31; //当前的局部变量x (ok)
x++; //此处的x 是上面的局部变量 31 (ok)
}
{
using A::x;
x++; //此处的x是 A::x (ok)
}
return 0;
}
#include
namespace A {
int x = 10, y = 20;
}
namespace B {
int x = 30;
}
int main() {
{
using namespace A;
using namespace B;
y++; // ok
}
{
using namespace A;
using namespace B;
//x++; //A B 中都有x, 二义性 (错误)
}
{
using namespace A;
int x; //定义局部变量 (ok)
}
{
using A::x;
//int x; //错误
}
return 0;
}
在实际使用中,要避免二义性错误,直接使用
空间名字::变量函数的写法是最保险的
命名空间:嵌套连续
#include
using namespace std;
namespace A{
int a1 = 1;
inr a2= 2;
namespace A1{
int x1 = 3;
int y1 = 4;
}
}
int main(){
//方法1: A::A1::作用域符
cout <
#include
using namespace std;
namespace A{
int a1=1;
int a2 = 2;
}
namespace A{// 同名空间自动合并
int b1 = 3;
int b2 = 3;
}
int main(){
return 0 ;
}
C++对C的扩展(输入输出:格式化)
#include
int main() {
int a = 12345;
double f = 123.4567;
//默认输出整数
printf("a=%d===\n", a);
//占8格,右对齐
printf("a=%8d===\n", a);
//占8格,左对齐
printf("a=%-8d===\n", a);
//默认输出浮点数,小数显示6位
printf("f=%f===\n", f);
//占10格,小数显示2位,右对齐
printf("f=%10.2f===\n", f);
//占10格,小数显示2位,左对齐
printf("f=%-10.2f===\n", f);
return 0;
}
#include
#include
using namespace std;
int main() {
int a = 12345;
double f = 123.4567;
//默认输出整数
cout << "a=" << a << "===" << endl;
//占8格,右对齐
cout << "a=" << setw(8) << a << "===" << endl;
//占8格,左对齐
cout << "a=" << setw(8) << setiosflags(ios::left)
<< a << "===" << endl;
//默认输出浮点数,有效位数显示6位
cout << "f=" << f << "===" << endl;
//占10格,小数显示2位,右对齐
cout << "f=" << setw(10) << setprecision(2)
<< setiosflags(ios::fixed) << setiosflags(ios::right)
<< f << "===" << endl;
return 0;
}
字符串
C代码:输入输出字符串
# include
int main(){
char str[10] = {0};
scanf("%s",str);
printf("%s\n", str);
return 0;
}
问题1:输入字符串有空格,无法处理
问题2:输入长度超过字符数组长度,不安全
以下代码解决问题:
#include
#include
int main() {
char str[10] = { 0 };
fgets(str, sizeof(str), stdin); // stdin 标准输入文件
if (str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = '\0';
printf("%s\n", str);
return 0;
}
#include
using namespace std;
int main() {
char str[10] = { 0 };
cin.getline(str, sizeof(str));
cout << str << endl;
return 0;
}
C++代码
#include
using namespace std;
int main(){
char str[10] = {0};
cin>>str;
cout<
问题1、2都存在
using namespace std;
int main(){
char str[10] = {0};
cin.getline(str,sizeof(str));
cout <
}
C++基本内置类型
新增bool类型,取值是真(true),false(假)
基本类型转换
#include
using namespace std;
int main(){
// int 和 bool 转换(1,0)
int i = 3;
bool b = true;
i = b ; // bool 转 int
cout << " i = " << i <
其中,1 - 2 运算转换成1 + (-2)
1的原码和补码都为0.......1;
-2 的原码为1.....10,原码转换成补码:先转换成反码然后在加1:为111111...01 +1 = 11111...10
所以:1+(-2)= 0......1 + 111...10 = 111...111
变量的声明
变量可以多次声明,但只能定义一次
int i1 ; //定义
int i2 =20;//定义
extern int i3 = 30 // 定义
extern int i4; //声明
列表初始化
#include
using namspace std;
int main(){
int i1 = 0;
int i2 = {0};
int i3(0); // c++
int i4{0} // c++
return 0;
}
列表初始化: 由一组花括号括起来的初始值进行初始化,使用该方式对内置类型变量初始化时,假如存在丢失信息的风险,编译器直接报错。
#include
using namespace std;
int main(){
double fd = 3.14
int i1(fd), i2 = fd; // ok
int i3{fd}, i4 = {fd}; //error
return 0;
}
指针和引用
//空指针
// NULL 在C中定义是((void*) 0 )
// NULL 在C++中定义是0
//nullptr 在 c++中用来表示空指针
int *p = nullptr;
if(p)
cout<< "p is false"<
指针实际上是地址,指针变量用来存放指针(指针)。
指针变量也是一种变量,同样要占用一定的存储空间,指针的存储空间存放的是一个地址。
#include
using namespace std;
int main(){
int i = 100;
int &ref_i = i; // 引用
cout<< i << " " <
引用是给变量或对象起一个别名,定义时必须初始化,一旦绑定,终身不变,引用并不占有内存空间。
#include
using namespace std;
int main(){
int a = 10;
int &ra = a;
cout<<&a<<&ra<
# include
using namespace std;
void swap1(int *pa, int *pb){
int tmp = *pa;
*pa = *pb;
*pb = tmp;
}
void swap2(int &a, int &n){
int tmp = a;
a = b ;
b = tmp;
}
void swap3(int a1, int b1) {
int tmp = a;
a = b;
b = tmp;
int main(){
int a = 10,b = 20;
swap1(&a,&b);
cout<
数组的引用
#include
using namespace std;
int main(){
int a = 1,b =2 , c =3;
// ok,指针数组
int *ptr[] = {&a,&b,&c}
// 错误 在数组中存放引用是不行的
// int &r_arr[] = {&a ,&b , &c};
int arr[3] = {a,b,c};
int (*p1)[3] = &arr; //ok,数组指针,指向数组的指针
int(&r1)[3] = arr; // ok,数组的引用
return 0;
}
引用的本质,从下面的代码,可以猜测,引用好像就是一个指针,通过汇编代码的分析,可以推测引用实际上通过指针实现的,引用是指针的一种的包装:类似int * const p 这样的格式的一种包装。
#include
using namespace std;
struct Stu{
int age;
char sexl;
char name[20];
};
struct A{int &data;};
struct B{char &data;};
struct C{stu &data;};
int main(){
cout<
const 限定符
C代码
可以通过将const 常量的地址赋值给指针变量改变const常量的值,这通常是不被希望看见的
#include
int main() {
const int i = 10;
//const int i; //错误,const变量必须在定义时初始化
//i=100; //错误,const类型不能修改
int *p = &i; //将i的地址赋值给指针p( 在C中ok)
*p = 20; //通过指针修改const int i的值
printf("i=%d,*p=%d\n", i, *p); // 20 20
return 0;
}
在C++中增强了对const的限制,不允许以上类似C的操作
#include
using namespace std;
int main() {
const int i = 10;
//int *pi = &i; 编译错误(C++中不行)
const int *pi = &i; //ok
//*pi = 20; 编译错误(指向常量的指针无法修改常量)
//下面尝试,强转转换来修改常量:
int *pi2 = (int*)&i; //将常量i的地址强转为int *
*pi2 = 20; //将pi2指针指向的地址内容修改为20
//观察 *pi2 和 i 对应的内存地址是否一样:
cout << "pi2=" << pi2 << " &i=" << &i << endl;
//观察 *pi2 和 i 的值
cout << "*pi2=" << *pi2 << " i=" << i << endl;
//输出 *pi2=20 i=10
//思考:为什么会出现这样的结果??
//一定要这么做,怎么做?
volatile const int ii = 10; //使用volatile关键字
int *pii = (int*)ⅈ
*pii = 20;
cout << "*pii=" << *pii << " ii=" << ii << endl;
//输出 *pii=20 ii=20
return 0;
}
默认情况下,const对象仅在文件内有效,假如要在多个文件中生效,则const变量不管是声明还是定义,都加上extern关键字。
const与引用
#include
using namespace std;
int main() {
const int i = 10;
//int &ri = i; 错误,非常量引用指向常量
int ii = 20;
const int &rii = ii; //OK
//rii = 30; 错误,常量引用无法修改值
double fd = 1.23;
//int &r = fd; 错误
const int &r = fd; //OK
//观察 fd 和 r 的值
cout << fd << " " << r << endl;
//观察 fd 和 r 的地址
cout << &fd << endl;
cout << &r << endl;
return 0;
}
以上代码中 r 绑定了一个临时量
const int temp = fd;
const int &r = temp;
指针与const
#include
using namespace std;
int main() {
int i = 10, j = 30;
int *p1 = &i; //无const限定
*p1 = 20; //可以改变指向变量的值(i-->20)
p1 = &j; //可以改变指向的变量(p1指向了j)
//指向常量的指针
const int *p2; //const在*前面(也可写int const *p2)
p2 = &i; //p2 的指向 可以改变 (意味着p2不是常量)
p2 = &j;
//*p2 = 100; 错误,*p2改不了值(意味着*p2是常量)
//常量指针(指针本身是常量)
int * const p3 = &i; //const在*后面
//p3 = &j; 错误,p3的指向不能改变(p3是常量)
*p3 = 100; //OK, *p3可以修改
//指向常量的常量指针
const int * const p4 = &i; //两个const
//p4 = &j; 错误
//*p4 = 100; 错误
return 0;
}
指向常量的指针
const int *p2 //const在* 前面,也可以写成 int const *p2
其中,p2
的指向可以改变,意味着p2
不是常量,但是 *p2
不能改变,意味着*p2
是常量
所以 上面 const 限定的是 *p2
常量指针
int * const p3 = &i; // const在*之后
// p3 = &j ; // 错误 p3的指向不能改变(p3是常量)
* p3 = 100; // OK,*p3 可以修改
指向常量的常量指针
const int * const p4 = &i ;//两个const
// p4 = &j; 错误
// * p4 = 100; 错误
return 0 ;
顶层const :指针本身是常量(常量指针int *const p1
)
底层const :指正指向的对象是常量(指向常量的指针 const int *p2
)
数据类型 struct 和 class
#include
using namespace std;
class player {
public:
player(int level = 0, int hp = 0)
:level(level), hp(hp) {}
void train(int nums) {
int killnums = hp > nums ? nums : hp;
level += killnums; hp -= killnums;
cout << "练级:长了 " << killnums << " 级。";
cout << "当前:level=" << level << ",hp=" << hp << endl;
}
void pk(player &another) {
int power1 = level * 100 + hp;
int power2 = another.level * 100 + another.hp;
if (power1 >= power2)
printf("You win!\n");
else
printf("You loss!\n");
}
private:
int level; //等级
int hp; //hp值
};
int main() {
player p1(1, 100); player p2(2, 50);
p1.train(6);
p2.train(10);
p1.pk(p2);
system("pause");
return 0;
}
字符串
C语言
#include
#include
int main() {
//字符数组
char str1[20] = "abcde"; //初始化
char str2[20] = { 'a','b','c' };//初始化
//str2 = "abc"; 错误
char str3[20];
str3[0] = 'a'; str3[1] = 'b'; str3[2] = '\0';
//字符指针
char *pstr = "bcd"; //将常量字符串的地址赋给pstr
pstr = "def";
pstr = str1;
pstr[0] = 'x'; //通过指针修改
*(pstr + 1) = 'y'; //通过指针修改
printf("str1=%s\n", str1); // 输出xycde
//字符串长度
printf("str1长度= %d\n", strlen(str1)); //5
//字符串拷贝
printf("str1=%s\n", strcpy(str1, "ddd"));//ddd
//字符串连接
printf("str1=%s\n", strcat(str1, str2)); //dddabc
//字符串比较
if (strcmp(str2, str3) > 0)
printf("%s > %s\n", str2, str3);
else if(strcmp(str2, str3) == 0)
printf("%s == %s\n", str2, str3);
else
printf("%s < %s\n", str2, str3);
//字符串查找
strcpy(str2, "--ab=="); //str3: "ab"
printf("%s\n", strstr(str2, str3)); //ab==
return 0;
}
C++
#include
#include
using namespace std;
int main() {
//std::string
std::string str1("abc"); //初始化
string str2 = "bcd"; //初始化
str2 = "defg"; //可以直接赋值
str2 = str1; //可以直接赋值
const char *pstr = str2.c_str(); //转c风格字符串
str2[0] = 'X'; //可以直接下标访问操作
str2.at(1) = 'Y'; //可以 at 访问操作
cout <<"str2=" << str2 << endl; //XYc
//求字符串长度
cout << str2.size() << endl;
cout << str2.length() << endl;
//strlen(str1); 错误
cout << strlen(str2.c_str()) << endl; //正确
//字符串连接
str2 = str2 + str1 + "!!";
cout << "str2=" << str2 << endl; //XYcabc!!
//字符串比较 (str1: abc)
cout << str2.compare(str1) << endl; //-1
cout << (str2 < str1) << endl; //1
//字符串查找
cout << str2.find(str1) << endl; //3
//字符串提取
string str3 = str2.substr(3, 3);
cout << str3 << endl; //abc
return 0;
}
vector
#include
#include
#include
using namespace std;
int main() {
//std::vector的结构
std::vector vec11; // [ 1, 3, 9 ...]
vector vec22; // [ "abc", "play", "C++" ]
vector> vec33; // [ [1,3,9..],[2,3,4..], ... ]
vector> vec44; // [ ["hello","C",..],["C++","abc",..],... ]
//vector的初始化
vector vec1 = { 1,2,3 };
vector vec2{ 1,2,3 }; //列表初始化
vector vec3 = vec1; //vec1拷贝给 vec3
vector vec4(10); //初始化10个元素,每个元素都是0
vector vec5(10, -1); //初始化10个元素,每个元素都是-1
vector vec6(10, "hi"); //初始化10个元素,每个元素都是 "hi"
//判断是否为空
cout << vec1.empty() << endl; //0
//元素个数
cout << vec1.size() << endl; //3
//添加元素在最后面
vec1.push_back(100);
cout << vec1[vec1.size() - 1] << endl; //100
//弹出元素在最后面
vec1.pop_back();
cout << vec1[vec1.size() - 1] << endl; //3
//直接下标访问元素
cout << vec1[1] << endl; //2
vec1[1] = 10;
cout << vec1[1] << endl; //10
// vector vec6(10, "hi")
vec6[0][1] = 'X';
cout << vec6[0] << endl; //hX
//遍历(类似遍历数组)
for (int i = 0; i < vec1.size(); i++)
cout << vec1[i] << " "; // 1 10 3
cout << endl;
return 0;
}
标准库类型vector表示对象的集合,其中所有的对象类型必须相同,因为vector 容纳着其他对象,所以被称为容器,vector是一个类模板。
auto 类型说明符
#include
using namespace std;
int main() {
//1.auto 变量必须在定义时初始化,类似于const
auto i1 = 0; auto i2 = i1;
//auto i3; //错误,必须初始化
//2.如果初始化表达式是引用,则去除引用语义
int a1 = 10;
int &a2 = a1; // a2是引用
auto a3 = a2; // a3是int类型,而不是引用
auto &a4 = a1; // a4是 引用
//3.去除顶层const
const int b1 = 100;
auto b2 = b1; // b2 是 int
const auto b3 = b1; // b3是 const int
//4.带上底层const
auto &b4 = b1; // b4 是 const int 的引用
//5.初始化表达式为数组时,推导类型为指针
int arr[3] = { 1,2,3 };
auto parr = arr; //parr 是 int * 类型
cout << typeid(parr).name() << endl;
//6.表达式为数组且auto带上&,推导类型为数组
auto &rarr = arr; //rarr 是 int [3]
cout << typeid(rarr).name() << endl;
//7.函数参数类型不能是 auto
//func(auto arg); //错误
//8.auto并不是一个真正的类型,编译时确定
//sizeof(auto); 错误
return 0;
}
迭代器:返回指针
#include
#include
#include
using namespace std;
int main() {
vector vs =
{ "all","people","like","c++" };
for (vector::iterator i =
vs.begin(); i != vs.end(); i++)
cout << *i << " ";
cout << endl;
for (auto i = vs.begin(); i != vs.end(); i++)
cout << *i << " ";
cout << endl;
for (auto &s : vs)
cout << s << " ";
cout << endl;
return 0;
}