Welcome to C/C++ System Program’s documentation!¶
目录¶
语法基础¶
目录¶
c语言篇¶
C语言基础语法学习目标
- 32个关键字
- 9种控制流
- 34种运算符
目录¶
我们在学习每一种编程语言时,都会先学习写一个hello world
的demo程序,下面我们将从这个小demo程序来窥探一下我们C程序的程序结构
#include <stdio.h>
int main()
{
/* 我的第一个 C 程序 */
int ret = 0;
printf("Hello, World! \n");
return ret;
}
以上是一个可以输出hello world
的简单C代码,该代码的组成部分为:
- 预处理指令:
#include <stdio.h>
告诉C编译器在实际编译之前需要将stdio.h
文件内容复制包含到当前文件 - 函数:
int main()
是C程序的执行入口;每个C程序有且只能有一个main函数
- 语句&表达式:
return 0
会终止main()函数
并返回0;在C中,每个语句必须以分号结束。它表明一个逻辑实体的结束 - 常量&变量:
ret
是一个int型变量
、hello world!\n
是一个字符串常量 - 注释:
/*...*/
将会被编译器忽略,这里放置程序的注释内容
从另外一个角度来看demo程序,该程序是由各种令牌组成:关键字
、标识符
、常量
、运算符
C标识符是用来标识常量名
、变量名
、函数名
或任何其他用户自定义的名称。一个标识符以字母A-Z
或a-z
或下划线 _
开始,后跟0个或多个字母、下划线和数字(0-9)
需要注意的是;
- C标识符内不允许出现标点字符,比如
@
、$
和%
- C是区分大小写的编程语言;
Manpower
和manpower
是两个不同的标识符
C关键字就是C中的保留字,它们被赋予特殊意义。这些保留字不能作为常量名,变量名或其它标识符名称。上述demo中的include
、int
、return
都是C关键字。C中一共有32个关键字,详解可见C关键字
C常量可以是数值常量0
;也可以是字符串常量hello world
C运算符有很多,它是构成表达式、语句、代码块的重要组成部分。详解可见C运算符
最后特别容易忽视的就是C中的空格
,我们在编程中,为了规范,不同字段之间都会以空格隔开,便于编译器识别解释各字段的含义
int ret = 0;
数据类型的本质:固定内存大小的别名
数据类型的作用:
- 决定了定义变量的内存存储大小
数据类型的误区:
- 它不能决定定义变量的内存存储形式
- 它不能决定定义变量的内存存储模式
现代整个计算机科学都是建立在二进制基础上,所有数据都是以0
和1
组合形式进行存储和传输的。这里的0或1
就是1bit
在通信领域,网络带宽传输速度的基本单位是比特(bits)
然而在计算即领域,磁盘存储数据的基本单位是字节(Bytes==1字节等于8比特);不同数据类型变量的存储大小是不一样的,例如:char类型
变量64位系统下的存储大小是1字节, short类型
变量64位系统下的存储大小是2字节。具体相关介绍可见数据类型详解目录表
需要注意的是:
- c语言没有规定数据类型(例如int类型)的具体大小,数据类型的具体大小和系统有关;同一系统下,同一种数据类型的大小是相同的
- 数据类型只是一个声明,并没有占用内存空间,只有当使用数据类型定义一个变量时,变量才占用空间,其所占用空间大小由数据类型决定,数据类型声明的内存大小和变量所占的内存大小都可以使用
sizeof
关键字来获取
在计算机中,我们都是以补码
的形式存储并运算数据。采用补码存储的最大作用就是减法也可以通过加法器实现
- 所有正数都是以补码的形式存放====正数的补码与其原码、反码都相同,都是正数的二进制,最高位为0
- 所有负数都是以补码的形式存放====最高位为1保存不变,其它位都和原码相反,转换成反码;然后在反码的基础上加1,转换成补码
既然所有正数和负数都是以补码的形式进行存储,那么此时存储方式有两种:
小端模式
:数据的高字节保存在内存的高地址中,而数据的低字节保存在内存的低地址中,这种存储模式将地址的高低和数据位权有效地结合起来,高地址部分权值高,低地址部分权值低[Unix服务器CPU ]大端模式
:数据的高字节保存在内存的低地址中,而数据的低字节保存在内存的高地址中,这样的存储模式有点儿类似于把数据当作字符串顺序处理:地址由小向大增加,而数据从高位往低位放[x86/ARM]
对0x11223344
的两种存储模式如下:

需要注意的是:大端模式和小端模式只与CPU架构相关;同一CPU架构下存储模式是相同的
数值型可分为两大类:
类型关键字 | 类型说明 | 存储大小(linux) | 值范围 | 示例 |
---|---|---|---|---|
short | 有符号短整型 | sizeof(short)=2[x86和x86_64] | -32768~32767 | short a = 20; |
unsigned short | 无符号短整型 | sizeof(unsigned short)=2[x86和x86_64] | 0~65535 | unsigned short a = 20; |
int | 有符号整型 | sizeof(int)=2[x86]/4[x86_64] | -32768~32767或-2147483648~2147483647 | int a = 20[10进制]/020[8进制]/0x20[16进制]; |
unsigned int | 无符号整型 | sizeof(unsigned int)=2[x86]/4[x86_64] | 0~65535或0~4294967295 | unsigned int a = 20; |
long | 有符号长整型 | sizeof(long)=4[x86]/8[x86_64] | -2147483648~2147483647或-9223372036854775808~9223372036854775807 | long a = 20; |
unsigned long | 无符号长整型 | sizeof(unsigned long)=4[x86]/8[x86_64] | 0~4294967295或0~1777777777777777777777 | unsigned long a = 20; |
long long | 有符号长长整型 | sizeof(long long)=8[x86和x86_64] | -9223372036854775808~9223372036854775807 | long long a = 20; |
unsigned long long | 无符号长长整型 | sizeof(unsigned long long)=8[x86和x86_64] | 0~1777777777777777777777 | unsigned long long a = 20; |
整型数 | 对应类型 |
---|---|
100 | int |
100u | unsigned int |
100l | long |
100ul | unsigned long |
100ll | long long |
100ull | unsigned long long |
整型数据存在两种类型的溢出
- 符号位溢出:超过有符号数的最大值
- 最高位溢出:超过无符号数的最大值
注意:在所有数值类型中(包括浮点型),计算机CPU处理int类型数据的效率是最高的,int只是占用较大内存而已
类型关键字 | 类型说明 | 存储大小(linux) | 值范围 | 精度 |
---|---|---|---|---|
float | 单精度浮点数 | sizeof(float)=4[x86_64] | 1.2E-38~3.4E+38 | 6位小数 |
double | 双精度浮点数 | sizeof(double)=8[x86_64] | 2.3E-308~1.7E+308 | 15位小数 |
long double | 长双精度浮点数 | sizeof(long double)=16[x86_64] | 3.4E-4932~1.1E+4932 | 18位小数 |
在头文件float.h
定义了浮点型相关的宏;通过这些宏可以输出浮点类型占用的存储空间以及它的范围值
#include <stdio.h>
#include <float.h>
int main()
{
printf("Storage size for float : %d \n", sizeof(float));
printf("Minimum float positive value: %E\n", FLT_MIN );
printf("Maximum float positive value: %E\n", FLT_MAX );
printf("Precision value: %d\n", FLT_DIG );
return 0;
}
类型关键字 | 类型说明 | 存储大小(linux) | 值范围 | 示例 |
---|---|---|---|---|
char | 有符号字符型 | sizeof(short)=1[x86和x86_64] | -128~127 | char a = ‘a’/0x61 |
unsigned char | 无符号字符型 | sizeof(unsigned short)=1[x86和x86_64] | 0~255 | unsigned char a = 0xff |
在C中我们可以将char
和unsigned char
理解成1 Bytes的整型数
===对照ASCII码表
char a = 'a'
、char a = 0x61
、int a = 97
三者是等价的
注意
'a'
:单引号表示字符"a"
:双引号表示字符串
C语言支持数组数据结构,它的本质是:一段连续的存储相同类型变量的内存空间
所有的数组都是由连续的内存位置组成。最低的地址对应第一个元素,最高的地址对应最后一个元素。一个数组中的所有元素的数据类型都是相同的,都是整型、浮点型、字符型
数组可以分为:
接下来我们将从以下几个方面来学习一维数组
一维数组声明定义的格式是:type ArrayName [ArraySize];
- type:数组元素的数据类型
- ArrayName:数组的数组名
- ArraySize:数组元素的数量
例如:int a[10];
定义了一个名为a的数组,该数组有10个成员 每个成员都是int类型
- 该定义开辟了
sizeof(int)*10=40
字节的连续内存空间,依次连续存放a[0]~a[9]10个元素 - 数组名
a
是一个常量[不能当做变量被赋值],它代表数组首元素的地址 - 此时该数组并没有被初始化,每个元素的值是随机的
定义数组时,ArraySize可以是变量,其定义方法可以是
//方法一:c99之后的编译器才支持
int i = 10;
int a[i];
//方法二:标准c写法
#define NUM 10
int a[NUM];
注意:char a[10];
a
表示数组首元素的地址,即a[0]
元素的地址,a+1
表示a[1]
的地址&a
表示整个数组的首地址,&a+1
表示跳过了整个数组
当定义一个数组时只是分配了相应的内存空间,内存空间存储的依然是之前的随机值,此时需要通过初始化存放我们想要存储的数据;数组初始化的方式有:
int a[4] = {1,2,3,4};
:a[0]~a[3]的值分别初始化为1,2,3,4;每个元素都占用4字节空间int a[4] = {1,3};
:a[0]初始化为1,a[1]初始化为3,a[2]~a[3]初始为0;每个元素都占用4字节空间int a[] = {1,2,3};
:数组a只有3个元素,分别初始化为1,2,3;每个元素都占用4字节空间int a[4] = {0};
:a[0]~a[3]都初始化为0;每个元素都占用4字节空间char a[4] = {0}
:a[0]~a[3]都初始好为0;每个元素都占用1字节空间char a[6] = "hello"
:a[0]~a[5]分别初始化为’h’/’e’/’l’/’l’/’o’/‘0’;每个元素占用1个字节。注意:数组元素的个数一定要大于字符串常量的长度,因为字符串是以‘0’结尾,”hello”实际是以’h’/’e’/’l’/’l’/’o’/‘0’存放的char a[10] = "hello"
:a[0]~a[5]分别初始化为’h’/’e’/’l’/’l’/’o’/‘0’,剩余元素都初始为0;每个元素都占用1个字节空间
数组是通过索引下标的方式来访问元素的,以下demo代码就是访问数组每个元素然后打印输出
#include <stdio.h>
int main(){
int i;
int a[] = {1,2,3,4,5};
for(i=0; i < sizeof(a)/sizeof(a[0]); i++) {
printf("%d\n",a[i]);
}
return 0;
}
上述demo代码中:
sizeof(a)
表示数组a在内存中占用大小sizeof(a[0])
表示数组a的首元素在内存中占用大小[即每个元素在内存中占用大小]sizeof(a)/sizeof(a[0])
表示数组元素的个数
算法思路
- 第一次遍历整个数组n个元素,通过相邻值比较将最大值放到最右边
- 第二次遍历剩下的n-1个元素,通过相邻值比较将最大值放到最右边
- 依次循环遍历,直到遍历最后1个元素
代码实现如下:
#include <stdio.h>
int main(){
int i, j;
int a[] = {34,5,124,624,3,65,};
int count = sizeof(a)/sizeof(a[0]);
for(i = 0;i < count;i++){
for(j = 1;j < count-i;j++){
if(a[j] > a[j-1]){
int tmp = a[j-1];
a[j-1] =a[j];
a[j] = tmp;
}
}
}
for(i=0; i < count; i++) {
printf("%d\n",a[i]);
}
return 0;
}
接下来我们同样从以下几个方面来学习一维数组
二维数组声明定义的格式是:type ArrayName [LineNum][RowNum];
- type:数组元素的数据类型
- ArrayName:数组的数组名
- LineNum:一维数组的个数,可以形象的理解为行数
- RowNum:一维数组元素的个数,可以形象的理解为列数
例如:int a[2][10];
定义了一个名为a的二维数组,该数组有2个一维数组a[0]和a[1],每个一维数组有10个成员 每个成员都是int类型,这些成员是连续存放的,逻辑上可以理解为2行10列。a[0]/a[1]分别是两个一维数组的数组名,也是两个数组首元素的地址
- 该定义开辟了
sizeof(int)*10*2=80
字节的连续内存空间,依次连续存放a[0][0]~a[0][9]和a[1][0]~a[1][9]20个元素 - 二维数组名
a
、一维数组名a[0]
和a[1]
是一个常量,不能当做变量被赋值 - 此时该数组并没有被初始化,每个元素的值是随机的
注意:int a[2][10];
a[0]
代表第一行数组的首元素地址,即a[0][0]
的地址,a[0]+1
表示a[0][1]
的地址a[1]
代表第二行数组的首元素地址,即a[1][0]
的地址,a[1]+1
表示a[1][1]
的地址a
代表第一行数组的首地址,即a[0]
的地址,a+1
表示a[1]
的地址&a
代表整个二维数组的地址,&a+1
表示跳过了整个二维数组a+i
表示第i行数组的首地址,相当于一维数组名使用&取地址*(a+i)
表示第i行数组首元素的地址,相当于一维数组名*(a+i)+j
表示第i行第j列元素的地址,等价于&a[i][j]
*(*(a+i)+j)
表示第i行第j列元素的值,等价于a[i][i]
当定义一个数组时只是分配了相应的内存空间,内存空间存储的依然是之前的随机值,此时需要通过初始化存放我们想要存储的数据;数组初始化的方式有:
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
:初始化了3个一维数组a[0]~a[2]
int a[3][4] = {{1,2,3,4},{5,6,7,8}};
:初始化了3个一维数组a[0]~a[2]
,a[2]所有元素都是0int a[3][4] = { 0 };
:初始化了3个一维数组a[0]~a[2]
,所有一维数组的所有元素都是0int a[][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12},{0}};
:初始化了4个一维数组a[0]~a[3]
char a[][10] = {"my","name","is","anony"};
:初始化了4个一维数组a[0]~a[3]
,使用int a[][]
会直接报语法错误
二维数组同样是通过索引下标的方式来访问元素的,以下demo代码就是访问二维数组每个元素然后打印输出
#include <stdio.h>
int main(){
int i, j;
int a[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};
int line_index = sizeof(a)/sizeof(a[0]);
int row_index = sizeof(a[0])/sizeof(a[0][0]);
for(i=0; i < line_index; i++) {
for(j=0; j < row_index; j++)
{
printf("%d\n",a[i][j]);
}
}
return 0;
}
上述demo代码中:
sizeof(a)
表示二维数组a在内存中占用大小sizeof(a[0])
表示一维数组a[0]在内存中占用大小[即每个一维数组在内存中占用大小]sizeof(a[0][0])
表示一维数组a[0]首元素在内存中占用大小[即每个元素在内存中占用大小]sizeof(a)/sizeof(a[0])
表示一维数组的个数sizeof(a[0])/sizeof(a[0][0])
表示一维数组数组元素的个数
多维数组在实际工作中很难用到,最多只用到二维数组。在这里我只简单介绍一下。
例如:int a[2][4][10];
定义了一个三维数组a
- 该数组包含两个二维数组
a[0]
和a[1]
- 二维数组
a[0]
包含4个一维数组a[0][0]/a[0][1]/a[0][2]/a[0][3]
- 二维数组
a[1]
包含4个一维数组a[1][0]/a[1][1]/a[1][2]/a[1][3]
- 每个一维数组都包含10个int类型的元素
三维数组同样是通过索引下标的方式来访问元素:a[0][0][0]表示三维数组的第一个元素
三维数组的排序同样可以使用冒泡排序:先将多维数组放到一个一维数组中,然后对一维数组进行排序,最后将一维数组放回多维数组中
字符串是内存中一段连续的char空间,以'\0'
结尾:例如"a"
表示'a'
和'\0'
注意:'a'
表示int类型或char类型;"a"
才表示字符串类型
字符串的定义初始化方法有两种
- 字符char数组
- 字符char指针
# 字符char数组
# 字符串的长度一定不能超过char数组的长度,因为字符串是以'\0'结尾的,必须保证char数组的最后一个元素的值是'\0'
# 所以在定义char数组时就将其初始化为0
char a[10]={‘h’,’e’,’l’,’l’,’o’}; # 初始化时,元素'o'后面的元素初始化为0即'\0',所以它也是一段连续的char空间,并以'0'结尾
char a[10]="hello"; # 初始化时,a[0]~a[4]分别被初始化'h'/'e'/'l'/'l'/'o',剩余元素初始为0
char a[10]={ 0 }; # 初始化时,所有元素都初始为0
char a[]="hello"; # 初始化时,数组a有6个元素a[0]~a[5],分别为'h'/'e'/'l'/'l'/'o'/'\0'
# 字符char指针
char *p = "hello"; # 定义一个char类型的指针指向字符串
# char指针可以引用char数组
char b[10] = "hello";
char *p = b;
下面有一个demo,实现功能是:去掉给定字符串后面的空格
#include <stdio.h>
int main(){
char a[] = "hello world a ";
int index=sizeof(a)/sizeof(a[0])-2;//数组a的最后一个元素是字符串结尾的‘0’,所以需要从倒数第二个元素开始遍历的
while(a[index]){
if(a[index] != ' '){
a[index+1] = 0;
break;
}
index--;
}
printf("%s\n",a);
}
学习C语言的指针既简单又有趣。通过指针,可以简化一些C编程任务的执行,还有一些任务,如动态内存分配,没有指针是无法执行的。所以,想要成为一名优秀的 C 程序员,学习指针是很有必要的。
指针也是一种数据类型,它也是固定内存大小的别名
- 32位系统下,指针是一个4字节的无符号整数
- 64位系统下,指针是一个8字节的无符号整数
使用指针类型定义的变量也是一种变量,叫做指针变量,它是一段可读可写的连续内存空间的别名;指针变量存储的是一个内存地址,该地址指向另一块内存地址[即另一个变量的地址]
说到内存地址,内存的最小单位是Byte
,操作系统使用malloc
等手动分配内存库函数分配内存的最小单位是4kB(windows下)
对于内存,每个Byte
都有一个唯一的编号,这个编号就是内存地址,即每个内存地址对应一个1Byte
空间大小
接下来我们将从如下几个方面来学习指针
34种运算符
函数需要包含头文件 函数在调用前必须声明或者定义 一个C程序必须也只能有一个main函数
关键字不需要包含头文件 32个关键字
c++语言篇¶
C++是对C的继承和拓展
目录¶
我们在学习每一种编程语言时,都会先学习写一个hello world
的demo程序,下面我们将从这个demo程序来窥探一下我们C++程序的程序结构
//方法一
#include <iostream>
int main(void)
{
int a=0;
std::cout << "hello world" << std::endl;
std::cin >> a;
std::cout << "a=" << a <<std::endl;
return 0;
}
//方法二
#include <iostream>
using std::cout;//声明iostream库里命令空间std中的cout
using std::endl;//声明iostream库里命令空间std中的endl
using std::cin;//声明iostream库里命令空间std中的cin
int main(void)
{
int a=0;
cout << "hello world" << endl;
cin >> a;
cout << "a=" << a <<endl;
return 0;
}
//方法三
#include <iostream>
using namespace std;//声明iostream库里的命令空间std
int main(void)
{
int a=0;
cout << "hello world" << endl;
cin >> a;
cout << "a=" << a << endl;
return 0;
}
上述示例中
std
:指iostream库里的标准命令空间cout
:指标准输出设备cin
:指标准输入设备endl
:指换行符
以上是输出hello world
和接收输入的简单C++代码,继承了C的预处理指令、函数、语句、表达式、运算符、变量、数据类型、关键字、注释等基本语法,同时也拓展了相关语法:
- 头文件在被include包含预处理时,不需要加
.h
后缀 - 声明定义变量时引入了
namespace
命名空间的概念,明确指定变量的作用范围 <<
、>>
运算符通过运算符重载分别指定输出流方向和输入流方向
此处只是列举了C++对C的部分扩展,详细的扩展表如下:
命令空间的本质
- 指定标识符的作用域,实现资源的隔离
命令空间的类型
- 默认命令空间==全局作用域
- 标准命令空间==标准作用域
- 自定义命令空间==自定义作用域
命令空间的定义
命令空间的引用