程序设计
程序设计是给出解决特定问题程序的过程,是软件构造活动中的重要组成部分。程序设计往往以某种程序设计语言为工具,给出这种语言下的程序。程序设计过程应当包括分析、设计、编码、测试、排错等不同阶段。专业的程序设计人员常被称为程序员。
某种意义上,程序设计的出现甚至早于电子计算机的出现。英国著名诗人拜伦的女儿爱达·勒芙蕾丝曾设计了巴贝奇分析机上计算伯努利数的一个程序。她甚至还创建了循环和子程序的概念。由于她在程序设计上的开创性工作,爱达·勒芙蕾丝被称为世界上第一位程序员。
任何设计活动都是在各种约束条件和相互矛盾的需求之间寻求一种平衡,程序设计也不例外。在计算机技术发展的早期,由于机器资源比较昂贵,程序的时间和空间代价往往是设计关心的主要因素;随着硬件技术的飞速发展和软件规模的日益庞大,程序的结构、可维护性、复用性、可扩展性等因素日益重要。
另一方面,在计算机技术发展的早期,软件构造活动主要就是程序设计活动。但随着软件技术的发展,软件系统越来越复杂,逐渐分化出许多专用的软件系统,如操作系统、数据库系统、应用服务器,而且这些专用的软件系统愈来愈成为普遍的计算环境的一部分。这种情况下软件构造活动的内容越来越丰富,不再只是纯粹的程序设计,还包括数据库设计、用户界面设计、接口设计、通信协议设计和复杂的系统配置过程。
C语言
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的C语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。
二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制定了一套完整的美国国家标准语法,称为ANSI C,作为C语言最初的标准。 目前2011年12月8日,国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。
其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
C语言组成
数据类型
C的数据类型包括:整型、字符型、实型或浮点型(单精度和双精度)、枚举类型、数组类型、结构体类型、共用体类型、指针类型和空类型。
常量与变量
常量其值不可改变,符号常量名通常用大写。
变量是以某标识符为名字,其值可以改变的量。标识符是以字母或下划线开头的一串由字母、数字或下划线构成的序列,请注意第一个字符必须为字母或下划线,否则为不合法的变量名。变量在编译时为其分配相应存储单元。
数组
如果一个变量名后面跟着一个有数字的中括号,这个声明就是数组声明。字符串也是一种数组。它们以ASCII的NULL作为数组的结束。要特别注意的是,方括内的索引值是从0算起的。
指针
如果一个变量声明时在前面使用 * 号,表明这是个指针型变量。换句话说,该变量存储一个地址,而 *(此处特指单目运算符 * ,下同。C语言中另有 双目运算符 *) 则是取内容操作符,意思是取这个内存地址里存储的内容。指针是 C 语言区别于其他同时代高级语言的主要特征之一。
指针不仅可以是变量的地址,还可以是数组、数组元素、函数的地址。通过指针作为形式参数可以在函数的调用过程得到一个以上的返回值,不同于return(z)这样的仅能得到一个返回值。
指针是一把双刃剑,许多操作可以通过指针自然的表达,但是不正确的或者过分的使用指针又会给程序带来大量潜在的错误。
字符串
C语言的字符串其实就是以’\0’字符结尾的char型数组,使用字符型并不需要引用库,但是使用字符串就需要C标准库里面的一些用于对字符串进行操作的函数。它们不同于字符数组。使用这些函数需要引用头文件
文件输入/输出
在C语言中,输入和输出是经由标准库中的一组函数来实现的。在ANSI C中,这些函数被定义在头文件
标准输入/输出
有三个标准输入/输出是标准I/O库预先定义的:
stdin标准输入
stdout标准输出
stderr输入输出错误
运算
C语言的运算非常灵活,功能十分丰富,运算种类远多于其它程序设计语言。在表达式方面较其它程序语言更为简洁,如自加、自减、逗号运算和三目运算使表达式更为简单,但初学者往往会觉的这种表达式难读,关键原因就是对运算符和运算顺序理解不透不全。当多种不同运算组成一个运算表达式,即一个运算式中出现多种运算符时,运算的优先顺序和结合规则显得十分重要。在学习中,对此合理进行分类,找出它们与数学中所学到运算之间的不同点之后,记住这些运算也就不困难了,有些运算符在理解后更会牢记心中,将来用起来得心应手,而有些可暂时放弃不记,等用到时再记不迟。
先要明确运算符按优先级不同分类,《C程序设计》运算符可分为15种优先级,从高到低,优先级为1 ~ 15,除第2.13级和第14级为从右至左结合外,其它都是从左至右结合,它决定同级运算符的运算顺序。
C语言函数
函数的分类
- 库函数
- 自定义函数
库函数
C语言常用的库函数都有:IO函数、字符串操作函数、字符操作函数、内存操作函数、时间/日期函数数学函数、其他库函数
char * strcpy ( char * destination, const char * source );
int printf ( const char * format, ... );
void * memset ( void * ptr, int value, size_t num );
使用库函数,必须包含#include 对应的头文件。
自定义函数
自定义函数和库函数一样,有函数名,返回值类型和函数参数。 但是不一样的是这些都是我们自己来设计。
函数的组成:
ret_type fun_name(para1, * ){
statement;//语句项
}
ret_type :返回类型 fun_name :函数名 para1:函数参数
写一个函数可以交换两个整形变量的内容
# include <stdio.h>
void Swap1(int x, int y){
int tmp = 0;
tmp = x;
x = y;
y = tmp;
}
void Swap2(int *px, int *py){
int tmp = 0;
tmp = *px;
*px = *py;
*py = tmp;
}
int main(){
int num1 = 1;
int num2 = 2;
Swap1(num1, num2);
printf("Swap1::num1 = %d num2 = %d\n", num1, num2);
Swap2(&num1, &num2);
printf("Swap2::num1 = %d num2 = %d\n", num1, num2);
return 0;
}
函数的参数
实际参数(实参): 真实传给函数的参数,叫实参。实参可以是:常量、变量、表达式、函数等。无论实参是何种类型的量,在进行函数调用时,它们都必须有确定的值,以便把这些值传送给形参。
形式参数(形参): 形式参数是指函数名后括号中的变量,因为形式参数只有在函数被调用的过程中才实例化(分配内存单元),所以叫形式参数。形式参数当函数调用完成之后就自动销毁了。因此形式参数只在函数中有效。
上面Swap1和Swap2函数中的参数x,y,px,py 都是形式参数。在main函数中传给Swap1的num1,num2和传给Swap2函数的&num1,&num2是实际参数。
形参实例化之后其实相当于实参的一份临时拷贝。
嵌套调用
# include <stdio.h>
void new_line(){
printf("hehe\n");
}
void three_line(){
int i = 0;
for(i=0; i<3; i++){
new_line();
}
}
int main(){
three_line();
return 0;
}
链式访问
# include <stdio.h>
# include <string.h>
int main(){
char arr[20] = "hello";
int ret = strlen(strcat(arr,"bit"));
printf("%d\n", ret);
return 0;
}
函数递归
递归的两个必要条件 存在限制条件,当满足这个限制条件的时候,递归便不再继续。 每次递归调用之后越来越接近这个限制条件。
求n的阶乘:
int factorial(int n)
{
if(n <= 1)
return 1;
else
return n* factorial(n-1);
}
动态数组
动态数组,即根据实时变化,可以扩大数组大小。而这个功能的实现需要用到指针和malloc和realloc函数。
int *a = (int*)malloc(10*sizeof(int));
那么 a就相当于一个有10个元素的数组。当数据量超过10个放不下的时候,利用
a = (int*)realloc(a, 20*sizeof(int));
意思是把a的大小增加到20,而保持原来已有的数据不变。
#include<stdio.h>
#include<stdlib.h>
void DimensionalVector(){
int n,i;
int *arr;
//输入不定的值,体现了数组与指针的关系
scanf("%d",&n);
arr = (int*)malloc(sizeof(int)*n);
for(i = 0; i < n; i++){
arr[i] = i;
}
for(i = 0; i < n; i++){
printf("%d\t",arr[i]);
}
}
int main(){
DimensionalVector();
return 0;
}
结构体
结构体是一些值的集合,这些值称为成员变量。结构体的每个成员可以是不同类型的变量。
结构体的声明
struct tag{
member-list;
}variable-list;
tag 是结构体标签。
member-list 是标准的变量定义,比如 int i; 或者 float f,或者其他有效的变量定义。
variable-list 结构变量,定义在结构的末尾,最后一个分号之前,您可以指定一个或多个结构变量。
eg:描述一个学生
struct Stu{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
};
在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。以下为实例:
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
在上面的声明中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,如果令 t3=&s1,则是非法的。
结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。
//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
char string[100];
struct NODE *next_node;
};
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B; //对结构体B进行不完整声明
//结构体A中包含指向结构体B的指针
struct A
{
struct B *partner;
//other members;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
struct A *partner;
//other members;
};
结构体变量的初始化
和其它类型变量一样,对结构体变量可以在定义时指定初始值。
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "WSS", "编程语言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
执行输出结果为:
title : C 语言
author: WSS
subject: 编程语言
book_id: 123456
访问结构成员
为了访问结构的成员,我们使用成员访问运算符(.)成员访问运算符是结构变量名称和我们要访问的结构成员之间的一个句号。
可以使用 struct 关键字来定义结构类型的变量。
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 输出 Book2 信息 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
当上面的代码被编译和执行时,它会产生下列结果:
Book 1 title : C Programming
Book 1 author : Nuha Ali
Book 1 subject : C Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Zara Ali
Book 2 subject : Telecom Billing Tutorial
Book 2 book_id : 6495700
文件操作
C语言中没有输入输出语句,所有的输入输出功能都用 ANSI C提供的一组标准库函数来实现。文件操作标准库函数有:
文件的打开操作 fopen 打开一个文件
文件的关闭操作 fclose 关闭一个文件
文件的读写操作 fgetc 从文件中读取一个字符 fputc 写一个字符到文件中去 fgets 从文件中读取一个字符串 fputs 写一个字符串到文件中去 fprintf 往文件中写格式化数据 fscanf 格式化读取文件中数据 fread 以二进制形式读取文件中的数据 fwrite 以二进制形式写数据到文件中去 getw 以二进制形式读取一个整数 putw 以二进制形式存贮一个整数
文件状态检查函数 feof 文件结束 ferror 文件读/写出错 clearerr 清除文件错误标志 ftell 了解文件指针的当前位置
文件定位函数 rewind 反绕 fseek 随机定位
文件的打开
FILE *fopen(char *pname,char *mode);
按照mode 规定的方式,打开由pname指定的文件。
若找不到由pname指定的相应文件,就按以下方式之一处理:
(1) 此时如mode 规定按写方式打开文件,就按由pname指定的名字建立一个新文件;
(2) 此时如mode 规定按读方式打开文件,就会产生一个错误。
打开文件的作用是:
(1)分配给打开文件一个FILE 类型的文件结构体变量,并将有关信息填入文件结构体变量;
(2)开辟一个缓冲区;
(3)调用操作系统提供的打开文件或建立新文件功能,打开或建立指定文件; FILE *:指出fopen是一个返回文件类型的指针函数;
pname:是一个字符指针,它将指向要打开或建立的文件的文件名字符串。
mode:是一个指向文件处理方式字符串的字符指针。
返回值
正常返回:被打开文件的文件指针。
异常返回:NULL,表示打开操作不成功。
//定义一个名叫fp文件指针
FILE *fp;
//判断按读方式打开一个名叫test的文件是否失败
if((fp=fopen("test","r")) == NULL)//打开操作不成功
{
printf("The file can not be opened.\n");
exit(1);//结束程序的执行
}
文件的关闭
int fclose(FILE *fp);
2. 功能说明 关闭由fp指出的文件。此时调用操作系统提供的文件关闭功能,关闭由fp->fd指出的文件;释放由fp指出的文件类型结构体变量;返回操作结果,即0或EOF。
3. 参数说明 fp:一个已打开文件的文件指针。
4. 返回值 正常返回:0。 异常返回:EOF,表示文件在关闭时发生错误。
eg:
int n=fclose(fp);
文件的读写操作
A. 从文件中读取一个字符 1. 函数原型
int fgetc(FILE *fp);
2. 功能说明 从fp所指文件中读取一个字符。 3. 参数说明 fp:这是个文件指针,它指出要从中读取字符的文件。 4. 返回值 正常返回: 返回读取字符的代码。 非正常返回:返回EOF。例如,要从”写打开”文件中读取一个字符时,会发生错误而返回一个EOF。 5. 实例
//程序名为:display.c
//执行时可用:display filename1 形式的命令行运行。显示文件filename1中的内容。例如,执行命令行display display.c将在屏幕上显示display的原代码。
//File display program.
#include <stdio.h>
void main(int argc,char *argv[]) //命令行参数
{
int ch;
FILE *fp;//定义文件类型指针
if(argc!=2)//判断命令行是否正确
{
printf("Error format,Usage: display filename1\n");
return; //键入了错误的命令行,结束程序的执行
}
//按读方式打开由argv[1]指出的文件
if((fp=fopen(argv[1],"r"))==NULL)
{
printf("The file <%s> can not be opened.\n",argv[1]);//打开操作不成功
return;//结束程序的执行
}
//成功打开了argv[1]所指文件
ch=fgetc(fp); //从fp所指文件的当前指针位置读取一个字符
while(ch!=EOF) //判断刚读取的字符是否是文件结束符
{
putchar(ch); //若不是结束符,将它输出到屏幕上显示
ch=fgetc(fp); //继续从fp所指文件中读取下一个字符
} //完成将fp所指文件的内容输出到屏幕上显示
fclose(fp); //关闭fp所指文件
}
写一个字符到文件中去
1. 函数原型
int fputc(int ch,FILE *fp)
2. 功能说明 把ch中的字符写入由fp指出的文件中去。 3. 参数说明 ch:是一个整型变量,内存要写到文件中的字符(C语言中整型量和字符量可以通用)。 fp:这是个文件指针,指出要在其中写入字符的文件。 4. 返回值 正常返回: 要写入字符的代码。 非正常返回:返回EOF。例如,要往”读打开”文件中写一个字符时,会发生错误而返回一个EOF。
//程序名为:copyfile.c
//执行时可用:copyfile filename1 filename2形式的命令行运行,将文件filename1中的内容复制到文件filename2中去。
//file copy program.
#include <stdio.h>
void main(int argc,char *argv[]) //命令行参数
{
int ch;
FILE *in,*out; //定义in和out两个文件类型指针
if(argc!=3) //判断命令行是否正确
{
printf("Error in format,Usage: copyfile filename1 filename2\n");
return; //命令行错,结束程序的执行
}
//按读方式打开由argv[1]指出的文件
if((in=fopen(argv[1],"r"))==NULL)
{
printf("The file <%s> can not be opened.\n",argv[1]);
return; //打开失败,结束程序的执行
}
//成功打开了argv[1]所指文件,再
//按写方式打开由argv[2]指出的文件
if((out=fopen(argv[2],"w"))==NULL)
{
printf("The file %s can not be opened.\n",argv[2]);
return; //打开失败,结束程序的执行
}
//成功打开了argv[2]所指文件
ch=fgetc(in); //从in所指文件的当前指针位置读取一个字符
while(ch!=EOF) //判断刚读取的字符是否是文件结束符
{
fputc(ch,out); //若不是结束符,将它写入out所指文件
ch=fgetc(in); //继续从in所指文件中读取下一个字符
} //完成将in所指文件的内容写入(复制)到out所指文件中
fclose(in); //关闭in所指文件
fclose(out); //关闭out所指文件
}
C与C++
简述C
1967年,剑桥大学的Martin Richards对CPL语言进行了简化,于是产生了BCPL(Basic Combined Programming Language)语言。并且他用B语言写了第一个UNIX操作系统。
1972年,美国贝尔实验室的 D.M.Ritchie 在B语言的基础上最终设计出了一种新的语言,他取了BCPL的第二个字母作为这种语言的名字,这就是C语言。
C语言非常简洁,只有32个关键字,9种控制语句,34种运算符。
具体来说,C语言是一个结构化语言,重点在于数据结构和算法的实现。C程序的设计首要考虑的是如何通过一个过程,对输入(或环境条件)进行运算处理得到输出(或实现过程(事物)控制)。
C语言可以做任何用处,但最大的用处还是写写操作系统和编译器之类的。
C 语言是非常有效率的,很多时候你都需要考虑内存的管理等底层的东西。很可惜这些都需要你去花很多的时间去做。作为一种底层编程语言,可以通过指针进行很直接的内存管理,另外,很多语言都是用 C 来设计的, 比如 perl , java, python。
C语言是一种通用性的编程语言,它既具有高级语言的特点,又具有汇编语言的特点。,1978年后,C语言已先后被移植到大、中、小及微型机上,它可以作为工作系统设计语言,编写系统应用程序,也可以作为应用程序设计语言,编写不依赖计算机硬件的应用程序。它的应用范围广泛,具备很强的数据处理能力,不仅仅是在软件开发上,而且各类科研都需要用到C语言,适于编写系统软件,三维,二维图形和动画,具体应用比如单片机以及嵌入式系统开发。尽管C语言是为实现操作系统软件而设计的,但它也广泛的应用于开发便携式应用软件。
简述C++
C++是C的继续和发展。C++功能强大,可以因应开发大型应用软件。即可以面向对象,也可以兼容C,可以面向过程。
C++是一种复杂、难以掌握的语言,不仅体现在其语法,更体现在其提供了4种编程思维模型上面,包括:procedural-based, object-based, object-oriented, generic paradigm。由此C++是既有效率,又有弹性;既可以面向对象,又兼容面向过程。
C++这个词在中国大陆的程序员圈子中通常被读做“C加加”,而西方的程序员通常读做“C plus plus”,“CPP”。 它是一种使用非常广泛的计算机编程语言。C++是一种静态数据类型检查的、支持多重编程范式的通用程序设计语言。它支持过程化程序设计、数据抽象、面向对象程序设计、泛型程序设计等多种程序设计风格。
C++是最流行的编程语言之一,它的应用领域涵盖了系统软件、应用软件、驱动程序、嵌入式软件、高性能的服务器与客户端应用程序和诸如电视游戏等娱乐软件。
C和C++的联系与区别
面向过程的思路:分析解决问题所需的步骤,用函数把这些步骤依次实现。
面向对象的思路:把构成问题的事务分解为各个对象,建立对象的目的,不是完成一个步骤,而是描述某个事务在解决整个问题步骤中的行为。
从上述描述可以看出,其实面向对象和面向过程是两种思考解决问题的方式,其差异主要在于思考的角度。
C语言是面向过程的编程,它最重要的特点是函数,通过main函数来调用各个子函数。程序运行的顺序都是程序员事先决定好的。
C++是面向对象的编程,类是它的主要特点,在程序执行过程中,先由主main函数进入,定义一些类,根据需要执行类的成员函数,过程的概念被淡化了(实际上过程还是有的,就是主函数的哪些语句),以类驱动程序运行,类就是对象,所以我们称之为面向对象程序设计。面向对象在分析和解决问题的时候,将涉及到的数据和数据的操作封装在类中,通过类可以创建对象,以事件或消息来驱动对象执行处理。
C语言和C++的最大区别在于它们解决问题的思想方法不一样。C语言主要用于嵌入式领域,驱动开发等与硬件直接打交道的领域, C++可以用于应用层开发,用户界面开发等于操作系统打交道的领域。
C++既继承了C强大的底层操作特性,又被赋予了教科书式的面向对象机制。它特性繁多,有其他面向对象语言鲜见的多继承,有耐人寻味的对值传递与引用传递入木三分的区分以及const关键字,等等。C++就像是一把瑞士军刀,或者像是一个工具箱,它为你提供尽可能多的工具,多到让不熟悉它的人无所适从,让懂得如何使用它的人如鱼得水。C++的种种特性使得它非常适合用来编写底层数据结构,算法,库等,是系统软件开发以及数学模型构建等的强大武器库,被誉为工业级编程语言。
C++对C的“增强”,表现在以下几个方面:
类型检查更为严格。增加了面向对象的机制。增加了泛型编程的机制(Template)。增加了异常处理。增加了运算符重载。增加了标准模板库(STL)。增加了命名空间,避免全局命名冲突。
C++
集成开发环境(IDE)
Visual Studio (Visual C++)
C++ Builder
kDevelop
Anjuta
Code::Blocks:开放源码的全功能的跨平台C/C++集成开发环境[1] 。
Visual Mingw
Ideone
Eclipse CDT
Compilr
Code Lite
Netbeans C++
集成开发环境(IDE),功能齐全,调试功能很强,程序编好后,可以立刻在环境中调试以获得初步测试结果,然后,可以方便地做成beta版形式,拿到实际环境中进一步测试,最后做成软件发行版。
C++语法、数据类型
#include <iostream>
using namespace std;
int main(){
cout<<sizeof(char)<<endl;
cout<<sizeof(int)<<endl;
cout<<sizeof(float)<<endl;
cout<<sizeof(double)<<endl;
cout<<sizeof(bool)<<endl;
return 0;
}
Visual C++ 6.0结果:1 4 4 8 1
字符类型:char,通常用来表示单个字符和小整数
整数类型:整型int、短整型short、长整型long,分别代表不同长度的整数值
char、short、int和long类型都可以用signed和unsigned
浮点类型:浮点型float、双精度浮点型double和长双精度long、double
布尔类型:true和false
指针类型:指针持有一个对象的地址,通过指针可以间接操作这个对象。
int *pi :指向整型的指针,如指向内存地址是1000,则跨越的地址空间是1000~1003
char *pc:指向字符型的指针,如指向内存地址是1000,则只占据1000这个字节的区域
int ival = 100;
int *pi = &ival;//pi被初始化为ival的地址
int *pi2 = 0;//pi2不指向任何对象
pi2 = ival;//编译错误
double dval = 1.5;
pi = &dval;//编译错误
封装、继承、多态
类
类就是一种类型,是用户自己定义的一个类型,和内置类型如int/float/double类似, 用一个类可以去定义一个变量,即所谓的类的实例化,会得到一个object。类这个类型比较特别,它即包括了数据(数据成员),又包含了若干个操作这些数据的方法(即成员函数)。为什么需要类呢?类提供了一种对事物的抽象,增强了事物的聚合性,类让我们可以把一个事物当作一个整体去看,方便描述,方便建模。 例如:我们可以定义一个学生的类:包括了姓名/性别/年龄/学号ID等信息
class Student{
public:
int GetID();
string GetName();
string GetSex();
int GetAge();
void SetID(int ID_);
void SetName(string Name_);
void SetSex(string Sex_);
void SetAge(int Age_);
private:
string m_Name;
string m_Sex;
int m_ID;
int m_Age;
};
如上所示,我们定义了一个Student的类, 定义它之后,编译器就不光知道了Student是一个类型,而且知道了类型的一些细节,例如当使用该类型去定义一个变量(object)时,需要分配多少内存等,例如:
//输出Student类型在内存中占多少字节
cout << "Student类型的大小为:" << sizeof(Student) << endl;
//实例化一个学生对象,并命名
Student k;
k.SetName("kk");
//打印对象的名字
cout << xiaoming.GetName() << endl;
定义一个类,需要做的是:1. 声明类拥有的数据成员, 2. 声明类拥有的成员函数:
//类的内部定义
class Dog{
public:
int Age;
int GetAge() //当定义在类的内部时,默认声明为inline函数
{
return Age;
}
};
//类外部定义
class Dog{
public:
int Age;
int GetAge();
};
Dog::GetAge(){
return Age;
}
另外:
- 类成员函数通过隐式this指针访问类内部的数据成员的; 如果把this指针修饰为const,在成员函数后面加const ,这就是const 成员函数: GetAge() const
- 编译器在解析一个类,总是先把类内部的数据成员解析完毕,再去处理成员函数,因此,定义一个类时,不必关心数据成员与成员函数的先后位置,数据成员圣成员函数问题可见的。
- 一个类域也是一个作用域(用{ }括起来的部分),正因为如此, 1. 在类的外部定义成员函数时,指定类名就进入了类的作用域中了,就可以找到数据成员了; 2. 对类内的静态数据成员与静态成员函数,可以通过类名作用域访问(对非静态的不可以,因为非静态的数据成员属于具体的一个对象而不属于类,虽然非静态的成员函数实际上在多个对象之间是共享的,但是也只能通过对象名对象的指针访问,因为它们有隐式的this指针)
类的封装性
封装性就是说可以把一部分东西封装起来,不让别人看到。在C++中,类这种类型通过它的访问控制符来体现了它的封装性,包括:
public: 公有的,在类的外部放着,谁都可以看到; protected: 保护性的,只让它的子类可以看到; private: 私有的,即把它们封装在了类的内部,在类的外面是看不到的,只有类内部的人可以看到;
类的继承
如果我们想在一个类的基础上继续创建一个新类,这就用到了类的继承性。继承可以使用三个访问标志符控制:public、protectd和private。 无论哪个继承,对直接子类没有任何影响,只对子类的用户有影响。基类中的private成员无论使用哪个访问标志符,子类的用户是看不到的,基类的public与protected成员是否能让子类的用户看到由三种访问标志符控制 。
public继承: 基类内的数据成员与成员函数封装特性不变在子类中不变 protected继承: 基类内的数据成员与成员函数的public部分在子类中变为protected private继承: 基类内的数据成员与成员函数的public和protected部分变为private
基类中虚函数和纯虚函数:多态
很多时候,我们想在基类与子类中定义相同的接口(即同名函数),它们实现各自的功能,这就可以把这样的函数声明为虚函数,即virtual. 当通过类的指针与引用调用虚函数时,会发生动态绑定,即多态。如下面例子所示:
// 程序
#include<iostream>
using namespace std;
class Base {
public:
virtual void Say() {cout << "I am Base!" << endl;}
};
class Derived : public Base {
public:
virtual void Say() {cout << "I am Derived!" << endl;}
};
int main() {
Base* pA = new Base;
Derived* pB = new Derived;
pA->Say();
pB->Say();
}
// 输出
yin@debian-yinheyi:~/c$ ./a.out
I am Base!
I am Derived!
当我们不想实例化一个类时,我们可以定义一个抽象基类,它只负责提供接口。包含纯虚函数的类为抽象类。纯虚函数必须在子类中进行声明与定义。而虚函数可以不在子类中声明与定义,这时候它会像普通成员函数一样继承基类中的虚函数的实现。
class Base {
public:
virtual void Say() = 0; //纯虚函数
};
总结来说:
-
当我们要继承一个类的接口与实现时,我们在基类中定义普普通通的成员即可。
-
当我们想要继承一个类的接口与默认的实现时,我们在基类中定义为虚函数。
-
当我们只要继承一个类的接口时,我们在基类中定义为纯虚函数。
在子类中,当我们重新声明和定义虚函数时,可以加上virtual关键字(virtual只能用在类内,不可以把virtual 用在类外),也可以不加, 在c++11标准中,引入了override关键字来显示表示覆盖基类中虚函数的定义,override关键字有利于给编译器更多信息,用于查错。例如当我们在子类中定义了一个与基类中虚函数名字相同,但是参数列表不同的函数,我们本意是定义子类特有的虚函数版本,来覆盖基类中的版本。然而这时候,基类与子类中的函数是独立的,只是基类中的版本隐藏了而已。如果使用了override,编译器发现没有覆盖,就会报错。 如果我们不想让基类中的某个虚函数被覆盖掉,可以使用final关键字。(另外覆盖,即override只会发生在虚函数身上)
如果我们定义 了一个类,并且不想该类被继承,可以在定义这个类时,在类名后面加上final关键字。
class Base {
public:
virtual void Say() {cout << "I am Base!" << endl;}
virtual void Dad() final { cout << " I am your dad!" << endl;} //对该虚函数使用final关键字
};
class Derived final : public Base // 对类Derived 使用了final 关键字
{
public:
void Say() override { cout << " I am Derived!" << endl;} };
继承中的作用域
- 类本身是一个作用域,使用{ }括起来的。
- 在类的继承过程中,子类的作用域是嵌套在基类的作用域之内的(这就明白了为什么有时候子类中的成员函数会隐藏掉基类中函数,就时候如果我们想要使用被隐藏的基类函数,可以通过显示指明类名(这时可以理解为作用域名)来访问。
- 在一个类的作用域中,编译器在解析类时,它总会先解析类中声明的所以数据成员与成员函数,再去解析成员函数的定义。正因为这样的原因,无论数据成员定义在成员函数的后面还是前面,还是成员函数的顺序前后之类的, 一个成员函数总是可以找到该类的数据成员或调用其它成员函数。
class Base {
public:
virtual void Hello() { cout << "Hello, I am Base!" << endl; }
void Hi() { cout << "Hi, Hi, Base!" << endl;}
};
class Derived : public Base {
public:
void Hello() override { cout << "Hello, I am Derived!" << endl;}
void Hi() { cout << "Hi, Hi, Derived!" << endl;}
};
int main() {
Derived Derive;
Derive.Hello();
Derive.Hi();
Derive.Base::Hello(); //显示调用基类的虚函数
Derive.Base::Hi(); // 显示调用基类普通函数
Base *pBase = &Derive;
pBase->Hello(); //指针调用,进行动态绑定,即多态
pBase->Base::Hello(); //显示调用基类的虚函数
pBase->Base::Hi(); // 显示调用基类普通函数
return 0;
}
//程序输出:
Hello, I am Derived!
Hi, Hi, Derived!
Hello, I am Base!
Hi, Hi, Base!
Hello, I am Derived!
Hello, I am Base!
Hi, Hi, Base!
###