C++笔试总结
编程风格
函数命名:都用大写字母开始
int PointToSL(const PointENU &point, std::string *lane_id, double *s, double *l,
double *heading) const;
变量命名:小写字母加下划线_
static std::unordered_map<std::string, StopSign> lane_id_stop_sign_map_;
赋值一般规范:const表示这个值不会对其改变,&就是不用再复制拷贝一遍了
------------------------------------------const 给变量赋值的一般方法
bool ReviseMapComponent::Proc(
const std::shared_ptr<LocalizationEstimate> &localization)//代表传来的值是不能变得,只能用
{
....
PointENU point;
point.set_x(x);
point.set_y(y);
std::string lane_id;
double s = 0.0;
double l = 0.0;
double heading = 0.0;
//point的值不变,只用,要给lane_id s l heading赋值,就直接取地址用指针改变值即可,或者通过引用改变值
PointToSL(point, &lane_id, &s, &l, &heading);
//或者通过引用改变值---------------------
//PointToSL_2(point, lane_id, s, l, heading);
//或者通过引用改变值---------------------
AINFO << "lane_id:" << lane_id.c_str() << " s:" << s << " l:" << l;
return true;
}
//const表示这个值不会对其改变,&就是不用再复制一遍了
int ReviseMapComponent::PointToSL(const PointENU &point, std::string *lane_id,
double *s, double *l, double *heading) const {
...
LaneInfoConstPtr lane = nullptr;
int ret = HDMapUtil::BaseMap().GetNearestLane(point, &lane, s, l);
....
*lane_id = lane->id().id();
*heading = lane->Heading(*s);
return 0;
}
int HDMap::GetNearestLane(const common::PointENU& point,
LaneInfoConstPtr* nearest_lane, double* nearest_s,
double* nearest_l) const {
return impl_.GetNearestLane(point, nearest_lane, nearest_s, nearest_l);
}
//通过引用获取值-----------------------
int ReviseMapComponent::PointToSL_2(const PointENU &point, std::string &lane_id,
double &s, double &l, double &heading) const {
....}
虚函数
虚函数的作用是允许在派生类中重新定义与基类同名的函数,并且可以通过基类指针或引用来访问基类和派生类中的同名函数。
虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。如果父类或者祖先类中函数func()为虚函数,则子类及后代类中,函数func()是否加virtual关键字,都将是虚函数。为了提高程序的可读性,建议后代中虚函数都加上virtual关键字。
#include<iostream>
#include<stdio.h>
using namespace std;
class A
{
public:
void foo()
{
printf("1\n");
}
virtual void fun()
{
printf("2\n");
}
};
class B : public A
{
public:
void foo() //隐藏:派生类的函数屏蔽了与其同名的基类函数 ------- 这个叫做覆盖
{
printf("3\n");
}
void fun() //多态、覆盖 --这个叫做多态,因为有虚函数
{
printf("4\n");
}
};
int main(void)
{
A a;
B b;
A *p = &a;
p->foo(); //输出1
p->fun(); //输出2
p = &b;
p->foo(); //取决于指针类型,输出1
b.foo();
a.foo();
p->fun(); //取决于对象类型,输出4,体现了多态
a.foo();//1
b.foo();//3
a.fun();//2
b.fun();//4
return 0;
}
虚函数表
#include "stdafx.h"
#include<stdio.h>
#include<iostream>
using namespace std;
class A {
public:
virtual void vfunc1() { cout << "A::vfunc1()" << endl; };
virtual void vfunc2() { cout << "A::vfunc2()" << endl; };
void func1() { cout << "A::func1()" << endl; };
void func2() { cout << "A::func2()" << endl; };
private:
int data1_;
int data2_;
};
class B :public A {
public:
virtual void vfunc1() override { cout << "B::vfunc1()" << endl; };
void func2() { cout << "B::func2()" << endl; };
private:
int data3_;
};
class C :public B {
public:
virtual void vfunc1() override { cout << "C::vfunc1()" << endl; };
void func2() { cout << "C::func2()" << endl; };
private:
int data1_, data4_;
};
//演示了手动调用虚函数的过程
int main() {
B a;
typedef void(*Fun)(void);
Fun pFun = nullptr;
cout << "虚函数表地址:" << (int*)(&a) << endl;
cout << "虚函数表第1个函数地址:" << (int*)*(int*)(&a) << endl;
cout << "虚函数表第2个函数地址:" << (int*)*(int*)(&a) + 1 << endl;
pFun = (Fun)*((int*)*(int*)(&a));
pFun();
pFun = (Fun)*((int*)*(int*)(&a) + 1);
pFun();
int c;
scanf_s("%d",&c);
return 0;
}
虚函数表地址:00AFFA18
虚函数表第1个函数地址:00262B40
虚函数表第2个函数地址:00262B44
B::vfunc1()
A::vfunc2()
拷贝构造函数
必须是引用,因为他是值传递。
编译错误。在复制构造函数中传入的参数是A的一个实例。由于是传值,把形参拷贝到实参会调用复制构造函数。因此如果允许复制构造函数传值,那么会形成永无休止的递归并造成栈溢出。因此C++的标准不允许复制构造函数传值参数,而必须是传引用或者常量引用。复制构造函数的参数需要改为:const A& other。
原文链接:https://blog.csdn.net/s_lisheng/article/details/75072007
class A{
private:
int value;
public:
A(int n)
{
value = n;
}
A(A other) //错误c
{
value = other.value;
}
void Print()
{
std::cout << value << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
A a = 10;
A b = a;
b.Print();
return 0;
}
引申:拷贝构造函数在哪些情况下被调用?
(1)函数的参数为类对象且参数采用值传递方式;
(2)将类对象做为函数的返回值。
class CBook {
public:
CBook() {
cout << "constructor is called.\n";
}
~CBook() {
cout << "destructor is called.\n";
}
};
void invoke(CBook book) {
// 对象作为函数参数,如果这里加了个&就不是了,因为加了&后是引用方式传递,形参和实参指向同一块地
// 址,就不需要创建临时对象,也就不需要调用拷贝构造函数了
cout << "invoke is called.\n";
}
int main() {
CBook c;
invoke(c);
}
--------------
解答:注意拷贝构造函数在对象作为函数参数传递时被调用,注意是对象实例而不是对象引用。因此该题输出如下:
constructor is called.
invoke is called.
destructor is called. // 在invoke函数调用结束时还要释放拷贝构造函数创建的临时对象,因此这里还调用了个析构函数
destructor is called.
类占内存
总结:空的类是会占用内存空间的,而且大小是1,原因是C++要求每个实例在内存中都有独一无二的地址。(一)类内部的成员变量:普通的变量:是要占用内存的,但是要注意对齐原则(这点和struct类型很相似)。static修饰的静态变量:不占用内容,原因是编译器将其放在全局变量区。
(二)类内部的成员函数:普通函数:不占用内存。虚函数:要占用4个字节,用来指定虚函数的虚拟函数表的入口地址。所以一个类的虚函数所占用的地址是不变的,和虚函数的个数是没有关系的
#include<iostream.h>
class a {
};
class b {
};
class c:public a{
virtual void fun()=0;
};
class d:public b,public c{
};
int main()
{
cout<<"sizeof(a)"<<sizeof(a)<<endl;
cout<<"sizeof(b)"<<sizeof(b)<<endl;
cout<<"sizeof(c)"<<sizeof(c)<<endl;
cout<<"sizeof(d)"<<sizeof(d)<<endl;
return 0;
}
程序执行的输出结果为:
sizeof(a) =1
sizeof(b)=1
sizeof(c)=4
sizeof(d)=8
结构体占内存
union mywe
{
char ccc[18];
int acc;
};
struct A
{
int a;
double b;
char c;
mywe d;
};
struct B
{
double b;
char c;
int a;
};
struct C
{
int a;
char c;
double b;
};
A aa;
B bb;
C cc;
mywe dd;
printf("D = %d\n", sizeof(dd));//结果:D = 20
printf("A = %d\n", sizeof(aa));//结果:A = 40
printf("B = %d\n", sizeof(bb));//结果:B = 16
printf("C = %d\n", sizeof(cc));//结果:C = 16
cout << sizeof(int) << '\t' << sizeof(double) << '\t' << sizeof(long ) << '\t' << sizeof(short) << '\t' << sizeof(char) << '\t' << sizeof(long long) << '\t' << endl;
// 4 8 4 2 1 8
int a[4];//可以求出数组的大小
cout << sizeof(a) <<'\t'<< sizeof(a) / sizeof(a[0])<<endl;
// 16 4
int *b = new int[4];
cout << sizeof(b) << '\t' << sizeof(float);
//4 4
sizeof和strlen
char *c = "abcdef";
char d[] = "abcdef";//带休止符的
char e[] = {
'a','b','c','d','e','f' };//不带休止符的
printf("%d%d/n", sizeof(c), strlen(c));4 6
printf("%d%d/n", sizeof(d), strlen(d));7 6
printf("%d%d/n", sizeof(e), strlen(e));*/6 任意数
char a[] = "hello world";
char *p = a;
cout<< sizeof(a) << endl; // 12 字节
cout<< sizeof(p) << endl; // 4 字节
计算数组和指针的内存容量
void Func(char a[100])
{
cout<< sizeof(a) << endl; // 4 字节而不是100 字节,退化位指针
}
const和static的作用
static关键字:
1)函数体内static变量的作用范围为函数体。不同于auto变量。该变量的内存只被分配一次。因此其值在下次调用时仍维持上次的值。
2)在模块内的static全局变量可以被模块内的所有函数访问。但不能被模块外的其他函数访问。
3)在模块内的static函数只可被这一模块内的其它函数调用。这个函数的使用范围被限制在声明它的模块内。
4)在类中的static成员变量属于整个类所有,对类的所有对象只有一份复制。
5)在类中的static成员函数属于整个类所有,这个函数不接受this指针,因而只能访问类的static成员变量。
const关键字:
1)欲阻止一个变量被改变,可以使用const关键字。在定义该const变量时,通常需要对它进行初始化。因为以后就没有机会再改变它了。
2)对指针来说,可以指定指针的本身为const,也可以指定指针所指向的数为const。或二者同时为const。
3)在一个函数的声明中,const可以修饰形参,表明它是一个输入参数。在函数内不能改变其值。
4)对于类的成员函数,若指定其为const类型。则表明其是一个常量函数。不能修改类的成员变量。
5)对于类的成员函数,有时候必须指定其返回值为const类型。以使得其返回值不为“左值”。
重载和覆盖
可作为函数重载判断依据的有:参数个数、参数类型、const修饰符;
不可以作为重载判断依据的有:返回类型。
覆盖发生在父子关系中,重载发生在一个类里面。
多态和覆盖差不多,只不过基类中要有虚函数。
null 0 nullptr区别
int a = 10;
int* ptr = &a;
cout << *ptr << " " << ptr << endl;//*ptr是他所指向的值,ptr为地址
int *pptr = nullptr;
//c语言null就是(void*0),可以隐式转换其他类型包括指针和整形,C++重载时候会发生冲突,因为可以转换为其他类型。
//C++中定义null为0,但是会和整形发生重合,所以C++就定义了nullptr,他只可以转换成指针和Bool类型,不能转换为整形
cout << pptr << endl;//正确
//cout << *pptr << endl;//错误
与“零”的比较语句
分别写出BOOL,int,float,指针类型的变量a
BOOL : if ( !a ) or if(a)
int : if ( a == 0)
float : const EXPRESSION EXP = 0.000001
if ( a < EXP && a >-EXP)
pointer : if ( a != NULL) or if(a == NULL)
dynamic_cast和 static_cast的区别
C 隐式转换 显示转换
int a=0;
double b;
b=a+10;隐式转换
b=(double)a;显示转换
C++增加了四个显示转换的关键字。(C++是强类型语言)(static_cast,dynamic_cast,const_static,reinterpret_cast)
(dynamic_cast)必须要有虚函数才能进行转换,static_cast静态转换,不安全。
用法:static_cast < type-id > ( expression ),该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性
static_cast可以做上述隐式转换的事,可以部分的做显式转换的事,也可以进行继承层次的转换。它没有运行时类型检查来保证转换的安全性。它的上行转换时没问题的,但是它的下行转换时不安全的。
dynamic_cast几乎唯一的被用来处理多态。多态分为上行转换和下行转换**,上行转换没问题,子类的指针肯定可以转换成父类指针。下行转换就有问题了,如果要把父类的指针A转换成子类的指针B,如果A指向的是子类的对象,这样转没问题。但是,如果A指向的是父类的对象,正确的做法是转换应该为不成功的,因为多态里没有子类的指针指向父类的对象的。所以,在“A指向的是父类的对象,现要把A转成B”这种情况下,如果采用static_cast,不会报错,返回的是转换后的指针,但程序此时是不安全的。如果采用dynamic_cast,它会根据虚函数表找到A指向的是什么对象(这里要求父类必须要有虚函数,否则报错),如果是父类对象,返回NULL,如果是子类对象,返回转换后的指针,程序此时是安全的。所以,这就是dynamic_cast和static_cast最大的区别。**
const_cast。常量指针被转化成非常量指针,并且仍然指向原来的对象;常量引用被转换成非常量引用,并且仍然指向原来的对象;常量对象被转换成非常量对象。
reinterpret_cast。这个操作符能够在非相关的类型之间转换。操作结果只是简单的从一个指针到别的指针的值的二进制拷贝。在类型之间指向的内容不做任何类型的检查和转换
说起C++中的继承、多态、虚函数等概念,可能很多同学都有所了解,但是要说真正熟知的同学可能就不是很多了。最近在编程过程中了解到C++类型的层次转换(这就涉及到了多态和继承的相关概率),通常C语言中可以对内置类型进行强制转换,但是这样做不是很安全,在C++标准中,提供了关于类型层次转换中的两个关键字static_cast和dynamic_cast。
一、static_cast关键字(编译时类型检查)
用法:static_cast < type-id > ( expression ),该运算符把expression转换为type-id类型,但没有运行时类型检查来保证转换的安全性,它主要有如下几种用法:
(1)用于基本数据类型之间的转换,如把int转换为char,把int转换成enum,但这种转换的安全性需要开发者自己保证(这可以理解为保证数据的精度,即程序员能不能保证自己想要的程序安全),如在把int转换为char时,如果char没有足够的比特位来存放int的值(int>127或int<-127时),那么static_cast所做的只是简单的截断,及简单地把int的低8位复制到char的8位中,并直接抛弃高位。
(2)把空指针转换成目标类型的空指针
(3)把任何类型的表达式类型转换成void类型
(4)用于类层次结构中父类和子类之间指针和引用的转换。
对于以上第(4)点,存在两种形式的转换,即上行转换(子类到父类)和下行转换(父类到子类)。对于static_cast,上行转换时安全的,而下行转换时不安全的,为什么呢?因为static_cast的转换时粗暴的,它仅根据类型转换语句中提供的信息(尖括号中的类型)来进行转换,这种转换方式对于上行转换,由于子类总是包含父类的所有数据成员和函数成员,因此从子类转换到父类的指针对象可以没有任何顾虑的访问其(指父类)的成员。而对于下行转换为什么不安全,是因为static_cast只是在编译时进行类型坚持,没有运行时的类型检查,具体原理在dynamic_cast中说明。
二、dynamic_cast关键字(运行时类型检查)
用法:同static_cast
dynamic_cast主要用于类层次结构中父类和子类之间指针和引用的转换,由于具有运行时类型检查,因此可以保证下行转换的安全性,何为安全性?即转换成功就返回转换后的正确类型指针,如果转换失败,则返回NULL,之所以说static_cast在下行转换时不安全,是因为即使转换失败,它也不返回NULL。
对于上行转换,dynamic_cast和static_cast是一样的。
对于下行转换,说到下行转换,有一点需要了解的是在C++中,一般是可以用父类指针指向一个子类对象,如parent* P1 = new Children(); 但这个指针只能访问父类定义的数据成员和函数,这是C++中的静态联翩,但一般不定义指向父类对象的子类类型指针,如Children* P1 = new parent;这种定义方法不符合生活习惯,在程序设计上也很麻烦。这就解释了也说明了,在上行转换中,static_cast和dynamic_cast效果是一样的,而且都比较安全,因为向上转换的对象一般是指向子类对象的子类类型指针;而在下行转换中,由于可以定义就不同了指向子类对象的父类类型指针,同时static_cast只在编译时进行类型检查,而dynamic_cast是运行时类型检查,则需要视情况而定。下面通过代码进行说明
class Base
{
virtual void fun(){}
};
class Derived:public Base
{
};
由于需要进行向下转换,因此需要定义一个父类类型的指针Base *P,但是由于子类继承与父类,父类指针可以指向父类对象,也可以指向子类对象,这就是重点所在。如果 P指向的确实是子类对象,则dynamic_cast和static_cast都可以转换成功,如下所示:
Base *P = new Derived();
Derived *pd1 = static_cast<Derived *>(P);
Derived *pd2 = dynamic_cast<Derived *>(P);
以上转换都能成功。
但是,如果 P 指向的不是子类对象,而是父类对象,如下所示:
Base *P = new Base;
Derived *pd3 = static_cast<Derived *>(P);
Derived *pd4 = dynamic_cast<Derived *>(P);
在以上转换中,static_cast转换在编译时不会报错,也可以返回一个子类对象指针(假想),但是这样是不安全的,在运行时可能会有问题,因为子类中包含父类中没有的数据和函数成员,这里需要理解转换的字面意思,转换是什么?转换就是把对象从一种类型转换到另一种类型,如果这时用 pd3 去访问子类中有但父类中没有的成员,就会出现访问越界的错误,导致程序崩溃。而dynamic_cast由于具有运行时类型检查功能,它能检查P的类型,由于上述转换是不合理的,所以它返回NULL。
三、总结
C++中层次类型转换中无非两种:上行转换和下行转换
对于上行转换,static_cast和dynamic_cast效果一样,都安全;
对于下行转换:你必须确定要转换的数据确实是目标类型的数据,即需要注意要转换的父类类型指针是否真的指向子类对象,如果是,static_cast和dynamic_cast都能成功;如果不是static_cast能返回,但是不安全,可能会出现访问越界错误,而dynamic_cast在运行时类型检查过程中,判定该过程不能转换,返回NULL
值传递和地址传递
指针传递
指针传递本质还是值传递,
指针传递本质还是值传递,p复制了一份b的值,所以p或者b的时候,他们指向的值一样,但是是&b,和&p是不一样的,从结果中我们可以看到调用f(b)时**,传递给p的是b的内容,但是&b,和&p是不一样的**,虽然p和b都指向a。示意图如下:
调用f(&a)是将a的地址0x12ff44传递给p,则p就指向了a的内容,改变p后,a的内容自然就改变了
void f( int*p){
printf("\n%x",&p);
printf("\n%x",p);
printf("\n%x\n",*p);
*p=0xff;
}
void main()
{
int a=0x10;
printf("\n%x",&a);
printf("\n%x\n",a);
f(&a);
printf("\n%x\n",a);
}
通过指针传递的案例我们可以看到,调用f(&a)是将a的地址0x12ff44传递给p,则p就指向了a的内容,改变p后,a的内容自然就改变了,示意图如下:
指针的传递
指针传递本质还是值传递,p复制了一份b的值,所以p或者b的时候,他们指向的值一样,但是是&b,和&p是不一样的,从结果中我们可以看到调用f(b)时**,传递给p的是b的内容,但是&b,和&p是不一样的**,虽然p和b都指向a。*但是*b ,p都是取a的内容,所以本质是一样的,都是对a进行操作.示意图如下:
错误案例
当执行strcpy(str,“Hello World!”),时会报Unhandled exception in CPoint.exe:0xC0000005:Access Violation,这是因为我们参用的是指针传递,从运行结果我们可以看到str的地址为0x12ff44,当调用Allocate(str,100)时,传递给p的是str的内容也就是0,所以p为0,但是&p并不是和&str一样的,p是str的一份拷贝,所以在运行p=(char)malloc(size)时,是给p分配的100个字节,并没有给str分配字节,所以*str还是空。所以会报错*。
改正:要么用指针的引用,或者指针的指针
指针的指针
void GetMemory2(char **p, int num)
{
*p=(char *)malloc(sizeof(char) *num);//用malloc函数为p申请一块容量为num的类型为字符的内存
//p里面存的是str的地址,p指向的是str,*p指向的就是str的内容,也就是要申请的那块内存
}
void Test2(void)
{
char *str=NULL;
GetMemory2(&str,100);//注意参数是&str
strcpy(str,”hello”);
}
C和C++内存分配问题
C语言编程中的内存基本构成
C的内存基本上分为4部分:静态存储区、堆区、栈区以及常量区。他们的功能不同,对他们使用方式也就不同。
1.栈 ——由编译器自动分配释放;
2.堆 ——一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收;
3.全局区(静态区)——全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域(C++中已经不再这样划分),程序结束释放;
4.另外还有一个专门放常量的地方,程序结束释放;
(a)函数体中定义的变量通常是在栈上;
(b)用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上;
©在所有函数体外定义的是全局量;
(d)加了static修饰符后不管在哪里都存放在全局区(静态区);
(e)在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用;
(f)在函数体内定义的static表示只在该函数体内有效;
(g)另外,函数中的"adgfdf"这样的字符串存放在常量区。
C++编程中的内存基本构造
在C++中内存分成5个区,分别是堆、栈、全局/静态存储区、常量存储区和代码区;
1、栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区,里面的变量通常是局部变量、函数参数等。
2、堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。
3、全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。
4、常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。
5、代码区 (.text段),存放代码(如函数),不允许修改(类似常量存储区),但可以执行(不同于常量存储区)。
内存模型组成部分:自由存储区,动态区、静态区;
根据c/c++对象生命周期不同,c/c++的内存模型有三种不同的内存区域,即:自由存储区,动态区、静态区。
自由存储区:局部非静态变量的存储区域,即平常所说的栈;
动态区: 用new ,malloc分配的内存,即平常所说的堆;
静态区:全局变量,静态变量,字符串常量存在的位置;
注:代码虽然占内存,但不属于c/c++内存模型的一部分;
进程间通信方式和线程间通信方式
进程间通信方式:
管道( pipe ):管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。
信号量( semophore ) : 信号量是一个计数器,可以用来控制多个进程对共享资源的访问。它常作为一种锁机制,防止某进程正在访问共享资源时,其他进程也访问该资源。因此,主要作为进程间以及同一进程内不同线程之间的同步手段。
消息队列( message queue ) : 消息队列是由消息的链表,存放在内核中并由消息队列标识符标识。消息队列克服了信号传递信息少、管道只能承载无格式字节流以及缓冲区大小受限等缺点。
共享内存( shared memory ) :共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。共享内存是最快的 IPC 方式,它是针对其他进程间通信方式运行效率低而专门设计的。它往往与其他通信机制,如信号两,配合使用,来实现进程间的同步和通信。
套接字( socket ) : 套解口也是一种进程间通信机制,与其他通信机制不同的是,它可用于不同及其间的进程通信。
线程间通信方式:
锁机制:包括互斥锁、条件变量、读写锁
互斥锁提供了以排他方式防止数据结构被并发修改的方法。
读写锁允许多个线程同时读共享数据,而对写操作是互斥的。
条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
信号机制(Signal):类似进程间的信号处理
数组
初始化
int a[3] = {
0, 1, 2}; // 正确
int a[3]={
0,1,2,3}; //错误,初始化值个数大于数组大小
int a[3]={
0,,2}; //错误,初始化值被跳过
int a[3]={
0,1,}; //错误,初始化值被跳过(即使是最后一个元素,添加逗号也被认为是跳过)
int a[3]={
0}; //正确,省略初始化最后一个元素,最后省略的元素初始化为0
int a[n]={
0}; // 注意n必须为const类型,否则错误
静态 int array[100]; 定义了数组array,并未对数组进行初始化
静态 int array[100] = {
1,2}; 定义并初始化了数组array
动态 int* array = new int[100]; delete []array; 分配了长度为100的数组array
动态 int* array = new int[100](1,2); delete []array; 为长度为100的数组array初始化前两个元素
-----------------------------------------
静态 int array[10][10]; 定义了数组,并未初始化
静态 int array[10][10] = {
{
1,1} , {
2,2} }; 数组初始化了array[0][0,1]及array[1][0,1]
动态 int (*array)[n] = new int[m][n]; delete []array;
动态 int** array = new int*[m]; for(i) array[i] = new int[n]; for(i) delete []array[i]; delete []array; 多次析构
动态 int* array = new int[m][n]; delete []array; 数组按行存储
int value[9][9]; // value[i][j]的值不定,没有初始化
int value[9][9] = {
{
1,1},{
2}}; //value[0][0,1]和value[1][0]的值初始化,其他初始化为0
int (*value)[n] = new int[m][n];
delete []value; // n必须为常量,调用直观。未初始化
int* a = new int[10]; //new 分配了一个大小为10的未初始化的int数组,并返回指向该数组第一个元素的指针,此指针初始化了指针a
a = {
4, -3, 5, -2, -1, 2, 6, -2, 3}; // 错误,注意这种用大括号的数组赋值只能用在声明时,此处已经不是声明,所以出错。
例子 二位数组
int x=0,y=0;
while (cin >> x>>y)// 注意,如果输入是多个测试用例,请通过while循环处理多个测试用例
{
float** A;//以int为例
A = new float*[x];
for (int i = 0; i <x; i++)
{
A[i] = new float[y];
}
for (int i = 0; i < x; ++i)
{
for (int j = 0; j < y; ++j)
{
cin >> A[i][j];
}
}