C++笔记一(C语言基础)

1.变量命名规则

1.1 标识符可由三类字符:字母、下划线、数字组成;标识符只能由字母或下划线开头;标识符不能具有二义性;标识符有长度要求,在起定的名字中!超出长度规定的部分将被截掉。

2.部分基础数据类型

2.1 常用数据类型长度
bool : 1个字节
char :1个字节
int:4个字节
float:4个字节
double:8个字节
可以使用sizeof()获取长度。

2.2 bool类型
bool类型非0为真(true),0为假(false)。并且bool型数据true参与算术运算时会转为int值1,比如:

bool b = 2;//此时b为真
b = b-1;//此时b为假

其实函数返回类型为bool类型时,一般用于判断,与if一起,且默认返回true。例如:

bool func()
{
	return true;	//布偶类型return一定要写全,因为默认返回true。
}
int main()
{
	bool a = func();
	if(a)
		cout<<"true"<<endl;
	return 0;
}

2.3 字符串类型

//C风格字符串
char str[] = "hello,树先生!";
cout<< str << endl;
//string 类型
string str2 = "hello,树先生!";
cout << str2 << endl;

2.4 转义字符

反斜杠+字母。eg: \t(跳到下一个TAB位置);\n(下一行)等等,其中转义字符应该在双引号中。

3.作用域

3.1 一般使用大括号分隔。(比如函数的大括号、循环的大括号等)

3.2 名字的有效区域始于名字的声明语句,结束于作用域的末端。(有点“向内兼容”的味道)

3.3 举例:在主函数main中声明的变量可以在main内使用,但像在for循环中定义的变量(比如for(int i=0;i<100;i++)中的i)却只能在循环中有效,这就是块作用域。如果在所有花括号外定义的变量则有全作用域,可以在整个程序内使用。

4.冒泡排序(循环与数组)

4.1 思路:相邻两个元素比较大小,大的放在后面,从左到右进行一次后,所有元素中最大的一个数便在最后。再次进行上诉操作,第二大的数便在倒数第二个位子上。以此类推,直到排序结束。

4.2 几个要点:为了方便称呼,便将从左到右进行的排序称为一次行排序,假设有N个元素进行排序。第一点:第一次行排序需要进行N-1次大小比较,那么第m次行排序需要进行N-m次。第二点:一共需要N-1次行排序。

4.3 实现:第一:需要一个双层循环,内层循环为一次行排序,外层循环为行排序次数,即第i次行排序。第二:外层循环思路为4.2中第二点,即从1到N-1(包括N-1),用i表示此时次数。第三:内层循环思路为4.2中第一点,即从0开始到N-i。内层循环中添加一个比较大小的代码即可,相邻两个元素比较大小。

for(int i=1;i<=N-1;i++) //一共需要N-1次行排序 ,只是第i次行排序 
{
    for(int j=0;j<N-i;j++) //第i次行排序需要进行N-i次比较 
    {
        if(b[j]>b[j+1])
        {
             int k;
             k=b[j];
             b[j]=b[j+1];
             b[j+1]=k;
        } 
    }
}

5.数组

5.1 声明:一维数组 eg: int a[3],有三个元素分别是a[0]、a[1]、a[2] 。即声明时[]里的常量为元素个数,而数组使用时下标从0开始。
定义eg:

int arr[10];
int arr1[5]={1,2,3,4,5};
int arr2[] = {1,2,3,4,5};

5.2 一维数组数组名的用处
其一,由sizeof(arr)/sizeof(arr[0])可以得到元素个数
其二,可以获得数组首地址,cout<<arr<<endl; 或者cout << &arr[0]<<endl;

数组名是常量,不可赋值。例如 arr = 1;就会报错!

5.3 二维数组同上,例如a[1][2],就是一行两列,里面的元素为a[0][0]和a[0][1]。
定义可以确定大小后,用双层循环初始化 。也可以用如下方式:

int arr[2][3]={1,2,3,4,5,6}  //此种方式可以省略行数但不可省略列数
int arr1[2][3]=
{    
     {1,2,3}    
     {1,2,3}
}	//此种方式最直观,优先考虑

二维数组的数组名用处同一维数组,思路基本一样。

6.函数

6.1 值传递是指函数调用时实参将数值拷贝给形参,形参与实参是两个相互独立的对象。发生值传递时,形参改变不影响实参。
实参(argument):全称为"实际参数"是在调用时传递给函数的参数~
形参(parameter):全称为"形式参数" 由于它不是实际存在变量,所以又称虚拟变量。是在定义函数名和函数体的时候使用的参数,目的是用来接收调用该函数时传入的参数.在调用函数时,实参将赋值给形参。~
值得注意的是如果是指针形参,那么效果与非引用类型还是一样的。因为形参在改变时只改变实参指的对象的值,而实参指的对象不变。也就是说我们改变的是指针指的对象的值而不是改变指针指的对象。(形参拷贝的是实参指的地址,也就是实参的“内容”即值)

void func1(int *p2)
{
     *p2=*p2+1;
     cout<<p2<<"\t"<<*p2<<endl;
}
int main()
{ 
     int a=1;
     int *p1;
     p1 = &a;
     cout<<p1<<"\t"<<*p1<<endl;
     func1(p1);
     cout<<*p1;  
     return 0;
 } 
//输出为: a的地址     1;a的地址      2;2

6.2 函数声明,如果函数的定义在主函数后面,那么就要函数声明,告诉编译器有这么个函数可以使用。

6.3 函数的分文件编写分以下四步:第一先建一个头文件;第二建一个源文件;第三头文件里放函数声明;第四源文件里放函数定义。

7.指针

7.1 指针就是为了记录地址,使用指针的目的是通过指针去访问内存。指针是一种指向另一种类型的复合类型与int、char等类似。变量提供一个具名的、可供程序操作的内存空间,在c++中变量与对象经常可以互换使用(int a; 其中int是数据类型,a是整型的一个对象或者说变量)。指针是一个对象,也可以赋值和拷贝。指针无须在定义时赋初值,但是建议初始化所有指针

7.2 指针在32位系统中所占内存空间为4字节;64位系统中为8字节。
32位指的是CPU的地址总线数目为32条,那么32位操作系统的内存地址长度就是32位,所以指针占内存空间与系统位数有关,32位就4字节(一字节等于八位),64位就8字节。那么为什么32位系统的内存最大只能是4GB呢?原因就是32位一共可以使用2的32次方个地址,而计算机最小的存储单位是字节,最小的传输单位是bit(因为通常一个ASCII码对应就是1Byte,也就是字节才是表示信息含义的最小单位,再小就没实际意义了,这与编码有关。而在传输时位是最小单位,有0、1两种状态,可用低电平高电平表示)所以一共是2的32次方Byte等于4GB。因为并不是所有的地址都用于指向存储信息,所以实际内存大小会比4GB小一些。

7.3 空指针:空指针指向内存编号为0(地址为0)的空间。空指针的作用是初始化指针注意:空指针指向的内存是不可访问的。所以,一般在使用指针时一定要先判断指针是否为空,是否指向想要执行对象的地址。空指针类型确定,所指对象不确定(《C++primer》P48中便指出空指针不指向任何对象)。比如:

	int a = 10;
	int b = 20;
	int *p = nullptr;

p是int类型空指针,但是指向a还是b不确定,如果不赋值变成空指针,指针是可以不初始化的,但是指针便会有一个不确定的值,很容易产生误操作。
C++中空指针可以有如下几种表示方法,推荐nullptr,实在不支持那就用0。

	int *p1 = NULL;
	int *p2 = 0;
	int *p3 = nullptr;

补充:在C++最好使用nullptr避免使用NULL。
在C语言中NULL是void *类型指针(#define NULL ((void *)0)),可以隐式转换成其他类型的指针(比如int *)。但是在C++中void *类型无法进行隐式转换,那么为了解决空指针的问题便使用了0,用0来表示空指针,使NULL宏定义为0( #define NULL 0),但是C++可以重载函数,那么就有隐患了:

	void func(Type1 a, Type2 *b);
	void func(Type1 a, int b);

如上函数func发生重载,当我们调用这样调用时,Type1 a; func(a, NULL);那就出问题了,显然这里我们想传入的是一个Type2类型的空指针,可结果NULL是0,调用了第二函数。所以,我们在使用空指针时最好用0,它比起NULL更加明显地告诉我们,虽然0可以给指针赋值使其变成空指针,但是0它更是一个int类型。如果真的想调用第一个函数那该怎么办,把第二个参数0换成static_case(Type2 *)(0)。那么针对这种情况有没有解决方法呢?有!在C++11新标准中引入了nullptr,nullptr可以隐式转换成任何类型的指针。那么调用就变成了func(a, nullptr)。形参b自然就是Type2类型的空指针了。
简单说:空指针指向地址为0的特殊空间,它的指针类型是确定的,只是所指对象暂时不确定。目的是为了初始化,防止误操作。最好使用nullptr,实在不支持那就用0。

7.4void*指针。void *指针指向的对象确定,但是对象的类型不确定。比如使用malloc函数,返回的就是一个void *,需要进行类型转换比如:

int * p = (int *)malloc(sizeof(int) * 3);

void *的两个用处:
1>有很多标准C继承过来的函数会使用void * 作为参数或者返回值。比如malloc函数。但在C++中可以使用new替代。再就是像memset等函数,基本上不会使用的。
2>其他类型的指针可以隐式转换成void * 类型,再由void * 类型强制转换成其他的指针类型,所以使用void *可以接收不同的对象,但这种一个void * 的参数接收不同类型的参数,C++中基本上通过继承与多态就可以实现这种需求了。(试想一下,一个函数的参数是void * , 调用时可以是任何类型的指针,因为任何类型的指针可以隐式转换成void * , 在函数体内,再将void * 指针强制转换成具体类型的指针,但是搞得这么麻烦,C++中多态等基本就可以解决了)

void *的弊端:在void * 强转成某具体类型的指针时,如果与最开始转换成void * 指针的指针类型不同时,就会出现明明是int类型的数据,由于使用void * 强转可能变成char类型,然而指针的类型转换不是基于内容的转换,所以就可能出现对int类型的数据进行char类型的操作。(详情见《C++语言程序设计》P244)

简单说,void *是C语言遗留下的“产物”,像空指针C语言就是直接 #define NULL ((void *)0)。这些遗留下的东西,C++一般都有替代物,少用、慎用 void *,即使要用,那么一定要通过static_cast将其转换成最开始的类型(详情见《C++程序设计》P245,或者见《C++笔记之指针与数组》 )。

7.5 野指针:在实际编程中应该尽量避免出现野指针。野指针会指向一个内存,但指向哪我们不知道。所以有可能指向正在使用的空间或者不可访问的地址等。野指针产生情况:第一,指针未初始化。第二,堆区申请的空间释放后指针未置空。第三,返回局部变量的指针 如何避免?其一:指针声明后最好立即初始化。其二,使用free与delete时要置空,避免出现野指针。其三,避免指针的操作超过作用域。比如返回一个局部变量的指针。 总之使用指针时一定要确定它是否被初始化即是否指向了某一个确定的可访问的内存位置

7.6 const修饰指针,分为如下三种情况:const修饰指针、const修饰常量、const既修饰指针又修饰常量。const 对象一旦创建后其值就不能再改变,所以const对象必须初始化。

其一:const修饰指针——常量指针。int a = 10; const int *p = &a; 特点:指针的指向可以改但指针指向的值不可改。(简单说这个指针只能改指向,并且显示当前指向的值,但不可通过指针改变指向的值)
eg:

int a = 10;
const int *p = &a; //常量指针 
cout << *p << endl;	//10
a = 30;
cout << *p << endl;	//30

int b = 20;
p = &b;    //改变指针的指向 
cout << *p << endl;	//20
//*p = 30;   //此处修改了指针指向的值,所以报错 

其二:const修饰常量——指针常量。int a = 10; int * const p = &a;特点:指针的指向不可改但指针指向的值可改。
eg:

int a = 10;
int b = 20; 
int * const p = &a; //指针常量
cout<< *p << endl;
*p = 30;   //此处修改了指针指向的值
cout<< *p << endl;
//p = &b;    //改变指针的指向 ,所以报错 

其三:const既修饰指针又修饰常量。int a = 10; const int * const p = &a; 特点:指针的值与指针的指向都不可改变

补充:如何记忆?我们把*直接称呼指针,把const修饰常量。那么const int *p 不就是常量指针;int * cosnt p 不就是指针常量。特点如何记忆,谁在前面谁不可变。常量指针就是指向的值不可变,指针常量就是指针指向不可变。

7.7 指针与数组。(详情见《C++笔记之指针与数组》
在C++中指针与数组有着紧密联系。(参考《C++primer》P105)
结论:数组与指针有着密切的联系。在很多使用到数组名的情况下,编译器会自动地将其替换成一个指向数组首元素的指针。
eg:
因为通过取地址符号可以获取指向该对象的指针,而数组的元素也是对象所以可以通过&获取指向指定下标的数组元素的指针。

string num[] = {"你好","树先生","!!!"};
string *p = &num[0];  

因为使用数组名时编译器会自动将其转换成指向首元素的指针,所以p与p1一样都是指向数组num的首元素

string *p1 = num;   

这反映出一些情况下数组的操作就是指针的操作,举个例子验证一下:
当数组是auto变量的初始值时,推断得到的类型是指针而不是数组。
eg:

    int arr[5] = {1,2,3,4,5};
    auto p1(arr);
    cout<<"arr大小:"<<sizeof(arr)<<endl;  //20
    cout<<"p1大小:"<<sizeof(p1)<<endl;    //8
//    arr = 1;    //报错,数组名显然不是指针,比如上面使用sizeof时返回数组大小而不是8,而且这里同样报错
//    p1 = 1; //报错,而且编译器明确指出是int * 类型

必须指出使用auto时是指针,但是当使用decltype关键字时是数组。
eg:

decltype(arr) p2;
cout<<sizeof(p2)<<endl; //返回20,可见这里arr就是数组,而且大小都固定了
    

补充说明:auto与decltype,详细请看《c++primer》第5版P61、P62
auto是类型说明符,用它可以让编译器自己去分析表达式所属类型。
decltype是类型说明符,它的作用就是选择并返回操作数的数据类型。

8.结构体

8.1 创建结构体语法: struct 类型名称 { 成员列表 };例如struct student {int age;};
通过结构体类型创建具体实例:
struct 类型名称 s1;或者struct 类型名称 s2 = {……};或者定义结构体时就创建结构体变量

struct student
{
    int age;
    string name;
};
struct student s1;	 //第一种创建变量方式
s1.age = 10;
cout <<s1.age<<endl;
struct student s2 = {10, "asfd" };    //第二种创建变量方式
cout<<s2.name<<endl;
struct teacher
{
     int age;
}t1;     //第三种创建变量方式
t1.age = 22;
cout<<t1.age<<endl;

关键:其一定义时struct关键字不可省略
其二创建结构体变量时关键字struct可以省略
其三结构体变量通过”.“来访问成员

8.2 结构体数组
语法: struct 结构体类型名称 数组名[元素个数] = { {成员1},{成员2}…… }; 同样关键字struct可以省略。

8.3结构体指针
结构体指针使用”->“访问成员。同样关键字struct可以省略。

student stuArray[3] = {
   
   {12,"bob"},{14,"aoa"},{16,"coc"}};
cout<<stuArray[2].name<<endl;
student *stuPtr = &s2;
cout<<stuPtr->name<<endl;

8.4结构体嵌套
比如teacher结构体中有个成员是一个student结构体,其中在teacher定义时成员student的关键字struct可以省略eg: struct teacher { student s;};。最关键的是student结构体必须在teacher的前面。

8.5结构体做函数参数
也分为值传递与传地址,使用方法与之前完全一样。
注意:使用地址传递在结构体比较大时会更加节约资源,因为值传递拷贝的数据量远大于一个地址。而在使用地址传递时有可能出现修改成员数据的情况,为了防止误操作,所以可以加个const。比如:void display(const student * s1){……}。同样如果只是为了避免某一个成员出现误操作,那么在定义时就可以在相应的成员前加const关键字。比如:struct student{ const int age = 10;}。只不过const对象必须初始化。

猜你喜欢

转载自blog.csdn.net/weixin_45884870/article/details/107810261