目录
1 基础知识
字节:计算机主存被划分的编号单元,字节可以保存8位二进制 数字位(0、1)
地址:字节的编号
指令
指令:计算机CPU执行某种操作的命令编码
程序:计算机能识别和执行的指令集合
机器指令:计算机能直接识别和接受的二进制代码(只含0、1)
编程语言分类
低级语言、高级语言
低级语言:机器语言、符号语言
机器语言:机器指令的集合
符号语言 = 汇编语言:
ADD加、SUB减、LD传送
高级语言:
FORTRAN(fortran)、ALGOL(数值计算)、
BASIC、QBASIC、
上面是非结构化语言(符合语法规则即可,规范不严格,可读性差)
下面是结构化语言(顺序 分支 循环,结构清晰、便于编写 阅读 维护)
COBOL(商业管理)、C
下面是面向对象语言
C++、Visual Basic、Java
编辑,预编译,编译,链接,调试运行
源程序:高级语言写的程序
目标程序:机器指令的程序(二进制0 1,文件后缀.obj)
c、c++程序设计步骤:
编辑,预编译,编译,链接,调试运行
编辑程序:程序代码的输入
预编译程序:代码文本的替换,是整个编译过程的最先做的工作
编译程序:把源程序转换为目标程序,
用到了预处理器(= 预处理程序、预编译器)
作用:对源程序进行检查,有无语法错误
过程:预编译+正式编译
预编译执行:define、include
编译执行:typedef
链接处理:
计算机不能直接执行目标文件,只能执行 可执行程序(后缀是.exe)
编译针对一个源程序,链接的是多个源程序文件,即使只有1个源程序文件,也要连接,因为要与函数库连接(scanf)
标准程序:
#include<。。。>
int main()
{
...
return 0;
}
2 编程知识
除法 - 整数与浮点数
5/9=0
5.0/9=5/9.0=5.0/9.0 != 0
大小写转换
a+32:小写转大写
x*=y+8 等价与 x=x*(y+8)
x%=y+8 等价与 x=x%(y+8)
函数声明:
#include<。。。>
int main()
{
void max(int,int),min(int,int);
等价于
void max(int x, int y),min(int x, int y);
...
return 0;
}
void max(int a, int b)
{}
void min(int a, int b)
{}
变量存储方式:
静态存储、动态存储
内存:运行时用
存储空间:不参与运行,就是放数据的
存储空间:
程序区、静态存储区、动态存储区
静态存储区:
全局变量、
静态局部变量(用static)、静态外部变量(用static的全局)、
外部变量(无static,可在别文件用extern引用的)
动态存储区:
函数形参、无static的自动变量、
函数调用的现场保护和返回地址
局部变量存储类别:
auto自动(动态存储)
static静态(静态存储,分局部和全部静态)
register寄存器(该变量会放在CPU寄存器中,速度变快)
全局变量存储类别:
extern外部的
其它文件在声明该外部函数时不能初始化定义
#include<。。。>
int main()
{
extern int a;
void max(int,int),min(int,int);
等价与
void max(int x, int y),min(int x, int y);
...
return 0;
}
int a;
// 该行以下是a的作用域,以上不是,
在上面某行加入extern int a;可以解决!
void max(int a, int b)
{}
void min(int a, int b)
{}
我直接在所有函数前声明全局变量就好了!就不必去用extern了
在不同文件用同一全局变量就要用extern了
该文件的全局变量不希望别的文件使用,就可以 static int a;
static局部变量:
在静态存储区,程序运行期间不释放
static全局变量:
对于外文件不可见
内存的4个区:
全局数据区(全局变量,静态变量,字符串常量)
代码区(函数 代码)
栈区(运行函数而分配的形参,局部变量,返回地址)
堆区(动态内存分配new, malloc, calloc)
函数:
用户使用角度:库函数、自定义的函数
形式上:无参函数、有参函数
定义函数时,其参数是形参
主函数调用子函数,其括号的参数是实参
实参的值传给形参,形参的改变不影响实参!
数据类型 char和int可以相互兼容
数组名作实参,传递的是第一个元素的地址
一般int变量的实参和形参,互不影响是因为实参和形参它俩地址不同,自然互不影响
但数组名做地址 作为实参,形参的接收的就是地址,它俩地址相同,会相互影响
int swap1(int *x, int *y)传值
{
int p;//不能是int *p;指针为初始化,在下面用*p,*p是不可预见的
p=*x;*x=*y;*y=p;//交换功能,不加*,在主函数就会交换失败
}
int swap2(int &x, int &y)传址
{
int p;
p=x;x=y;y=p;
}
int main()
{
...
int *p1, *p2;
p1=&a;
p2=&b;
swap1(p1, p2);
swap2(a, b);
}
内部函数:
static int max(int a, int b){}
不能被其它文件调用
外部函数:
调用其它文件的函数
extern int max(int a, int b);
声明一下
数组
数组:有序数据的集合
int a[10]={0}
:全部元素为0
int a[10]={1,2}
:其它元素默认为0
int a[]={1,2}
等价与 int a[2]={1,2}
字符数组默认’\0’
指针数字默认NULL
int a[3][4]={1,2,3,4}
等价于
int a[][4]={1,2,3,4}
字符串在字符数组中
字符串存在字符数组中,以 ‘\0’ 结束
char s[]="sdd";
等价与
char s[]={"sdd"};
此时:字符串长度+1 = 字符数组字节数 = 4
其末尾是’\0’
若char s[]={'s', 'd', 'd'};
则字节数为3
字符数组输入
多个%s时,空格区隔相邻字符串
数据类型 a[常量n]
数组名a就是其数组起始地址,不用加&
strlen(字符数组)=字符串长度,不含末尾’\0’
strlwr(字符串):转小写
strupr(字符串):转大写
一维数组形参 其大小说明可省略
void max(int a[]){}
多维数组形参 其第一维大小说明可省略
void max(int a[][5]){}
void max(int a[][6][5]){}
引用和指针:
引用:
是对象别名,用来传输较大的数据变量
不占用新的地址,节省内存开销
间接访问更安全
所以引用&只能声明,不存在定义
1 声明时必须初始化,即引用b是谁的别名
初始化后不能改变为别的对象的别名
2 数组元素不能是引用
int a[3]; int (&t)[3] = a;
√
int & ref[3] = { 2, 3, 5};
×
注意到引用是别名
数组元素是引用,
引用又是别的对象别名,一系列别的对象恰好物理地址连续?
3 不能对引用进行引用(引用不能嵌套引用,没有引用的引用)
常引用:
引用本就不能改变=常const
常引用是 不能通过引用修改对象值
对象依然修改
指针和数组:
int *p=&a;
→
p=&a; *p=a;
指针赋值用变量,别直接用整数100
指针已经和变量a赋过值 这时可以用100给指针赋值,此时a也为100了
一维数组:
a[0]=*a=*(a+0)
a[i]=*(a+i)
二维数组:
&a[1]=(a+1)
(第1行首地址)
&a[i]=(a+i)
(第i行首地址)
a[0]=*(a+0)=*a
(a[0][0]
地址)
a[1]=*(a+1)
(a[1][0]
地址=第1行首地址)
a[i]=*(a+i)
(a[i][0]
地址=第i行首地址)
a[0]+1=*(a+0)+1
(a[0][1]
地址)
a[1]+1=*(a+1)+1
(a[1][1]
地址)
a[i]+j=*(a+i)+j
(a[i][j]
地址)
&a[0][0]=a[0]+0
a[0][0]=*(a[0]+0)
a[0][1]=*(a[0]+1)=*(*(a+0)+1)=*(*a+1)= a[i][j]=*(a[i]+j)=*(*(a+i)+j)
a+1
是第1行首地址
*(a+1)
是a[1][0]
的地址
两者等价!
char *a="sdfg"; √
char *a;
a="sdfg"; √
char a[14];
a="sdf"; ×
char a[14]="sdf"; √
int *p[4];指针数组,暗含二维的意思
int (*p)[4];一维普通整数数组
int *p[4] 等价与 int *(p[4])
数组p的每个元素是指针
作用于数组a的函数:
int *p = min_element(a, a+6);
a[0]至a[5]最小元素的指针给指针p
注意是a[5]!
int *p = msx_element(a, a+7);
a[0]至a[6]最大元素的指针给指针p
注意是a[6]!
random_shuffle(a, a+7);
a[0]至a[6]随机打乱
int *p = find(a, a+8, key);
a[0]至a[7]中元素值为key的元素的指针给指针p
指针和字符串
char *p=“sfgs”;
cout<< p;是输出字符串
(void *)p是字符串地址
字符串 清空与free()?
typedef struct // 变长
{
char *ch;
int length;
}Str;
free(str.ch) 程序会崩溃
直接 str.ch = NULL; 即可
free(链表节点指针) 而不是 字符指针
字符串赋值
字符串指针直接赋值即可
#include<bits/stdc++.h>
#define num 100
using namespace std;
int main()
{
//1
char *p, b = 's';
p = &b;
cout << "1" << endl;
cout << *p << endl << endl;
//2
char *a ="abcdefg";
cout << "2" << endl;
cout << "输出字符: " << *a << endl;
cout << "第二次输出字符: " << *(a + 1) << endl;
cout << "输出字符串: " << a << endl << endl;
//3
char q[num] = "asd" ;
cout << "3" << endl;
cout << *q << endl;
cout << q << endl << endl;
//4
char q1[num] = {'s','d','f','\0'}; // <=> char q1[num] = "sdf";
cout << "4" << endl;
cout << *q1 << endl;
cout << q1 << endl << endl;
//5
char q2[num];
strcpy(q2, "asd");
cout << "5" << endl;
cout << *q2 << endl;
cout << q2 << endl << endl;
return 0;
}
错误操作:
char *q3;
strcpy(q3, "asd");
strcpy(*q3, "asd");
cout << "5.2" << endl;
cout << *q3 << endl;
cout << q3 << endl << endl;
函数指针 指针函数:
函数指针:
函数代码有被分配存储空间,其起始地址为这个函数的指针
int (*p)(int,int)
是指针,是指向函数的指针,
函数有两个int参数,函数返回int数据(函数代码首地址)
函数指针的作用:
函数指针可用作形参,在不同函数的不同组合时可以用
指针函数:
int *p(int a, intb){}
返回一个指针,即数组
一般函数返回一个值,可能不够用,我希望返回一系列值,用指针函数!
#include<stdio.h>
void main()
{
float score[][4]={{60,70,80,90},{56,89,67,88},{34,78,90,66}};
float *search(float (*point)[4],int n);
float *p;
int i,n;
printf("输入学生学号n:(0=<n<=2)");
scanf("%d",&n);
printf("the score of No.%d are:\n",n);
p=search(score,n);
for(i=0;i<4;i++)
printf("%5.2f ",*(p+i));
putchar('\n');
}
float *search(float (*point)[4],int n)
{
float *pt;
pt=*(point+n);
return pt;
}
指针常量 常量指针
指针常量 指针变量:
指针常量:
指针类型的常量,该指针不能变,
即指针是常量 不能变
指针指向的对象值可以变
int b=5;
int * const p=&b;
p不能变,即 p只能指向b了
然后*p=6; √
b=9; √
指针变量:就是一般的指针
常量指针:
指向常量的指针
即:指向常量的指针变量
const int b;
int const b; (等价)
const int *p=&b;
int const *p=&b;(等价)
p可以更改指向别的对象c
非常量指针q=常量指针p ×
常量指针p=非常量指针q √
常指针常量:
指向常量的 指针常量
const int b;
const int * const p=b;
int const * const p=b;
左值 右值
左值表达式代表对象本身,
右值表达式代表对象的值
左值:在赋值运算符左侧,其值可以改变,永久对象,可被取地址,如:有名称的变量、函数形参(栈中的对象)
右值:在赋值运算符右侧,其值可以改变,临时对象(即将销毁),不可取地址,如:字面常量(1、2…等)、匿名对象(临时对象)以及函数的返回值
std::move(str)
显式将一个左值str转换为右值
*与&
&跟左值,不能式变量表达式(a+1)
&(a+1) ×
&a √
typedef
typedef int b;
int新名字是b
b h;就是int h;
3 冷知识
C语言exe文件闪退
main函数末尾的return
前加上:
system("pause");
或getchar();
这样生成的exe文件就不会闪退了
0和’\0’
在dev c++中0='\0'
!
#include<bits/stdc++.h>
using namespace std;
int main()
{
int p = (0 == '\0');
cout << " 0 == '\\0' = " << p << endl;
}
未满的整型数组,求真实元素个数?
1 手动记录
2 需要约定一个数字作为结束标记
原因:
数组的未满部分,对于计算机来说,全部存0或随机数,
手动初始化、calloc数组,未满部分就是0
malloc,未满部分就是随机数
sizeof
char str[] = “abcdef”; → sizeof(str) = = 7,因为str的类型是char[7],
也有sizeof(“abcdef”) == 7,因为"abcdef"的类型是const char[7]。
char ptr = “abcdef”; → sizeof(ptr) == 4,因为ptr的类型是char。
char str2[10] = “abcdef”; → sizeof(str2) = = 10,因为str2的类型是char[10]。
void func(char sa[100], int ia[20], char * p);
→ sizeof(sa) == sizeof(ia) == sizeof( p) == 4,
因为sa的类型是char*, ia的类型是int*,p的类型是char*
时间函数
均用到库函数:#include<time.h>
程序用时
参考于:https://blog.csdn.net/homewm/article/details/80302534
2个方法:
clock() / CLOCKS_PER_SEC
精确到毫秒
time(NULL) start, end ; difftime(end, start)
精确到秒
法1 clock() / CLOCKS_PER_SEC
精确到毫秒
clock()
:CPU运行的时钟周期数
CLOCKS_PER_SEC
:1秒CPU运行的时钟周期数
clock_t start, end;
start = clock();
end = clock();
(double)(end - start) / CLOCKS_PER_SEC
#include<bits/stdc++.h>
using namespace std;
int main()
{
clock_t start, end;
start = clock();
int i;
for(i = 0; i < 100000000; i++)
rand();
end = clock();
cout << "程序用时 " << (double)(end - start)/CLOCKS_PER_SEC << "s" << endl << endl;
system("pause");
return 0;
}
法2 time(NULL) start, end ; difftime(end, start)
精确到秒
time(NULL)
:得到当前日历时间或者设置日历时间
start = time(NULL);
end = time(NULL);
difftime(end, start)
#include<bits/stdc++.h>
using namespace std;
int main()
{
clock_t start, end;
start = time(NULL);
srand(time(NULL));
int i, b = 0;
for(i = 0; i < 100000000; i++)
rand();
end = time(NULL);
cout << "程序用时 " << difftime(end, start) << "s" << endl << endl;
system("pause");
return 0;
}
时间
小时、tm结构体、现在时间 localtime、gmtime、asctime
小时:
time_t seconds;
seconds = time(NULL);
seconds/3600 :自 1970-01-01 起的小时数
下面的结构体tm
不用手动定义:
struct tm
{
int tm_sec; /* 秒,范围从 0 ~ 59 */
int tm_min; /* 分,范围从 0 ~ 59 */
int tm_hour; /* 小时,范围从 0 ~ 23 */
int tm_mday; /* 一月中的第几天,范围从 1 ~ 31 */
int tm_mon; /* 月,范围从 0 ~ 11(注意) */
int tm_year; /* 自 1900 年起的年数 */
int tm_wday; /* 一周中的第几天,范围从 0 到 6 */
int tm_yday; /* 一年中的第几天,范围从 0 到 365 */
int tm_isdst; /* 夏令时 */
};
tm_year是1900年后的年数,真实年数 = tm_year +1900
夏令时:
夏季天亮早的,人为将时间调快一小时,早起早睡,减少照明量,充分利用光照资源,节约照明用电
得到现在时间:
2个办法:
法1
time_t t;
time(&t); 获取当前时间
cout << ctime(&t) <<endl;
法2:
最简代码!:
time_t t;
time(&t); 获取当前时间
struct tm *lt;
lt=localtime(&t); 等价于 lt=gmtime(&t); 转换为时间结构
cout << asctime(lt) <<endl;
strftime
:
size_t strftime( char *strDest, size_t maxsize, const char *format, const struct tm *timeptr);
其中format
:
%a 星期几的简写
%A 星期几的全称
%b 月分的简写
%B 月份的全称
%c 标准的日期的时间串
%C 年份的后两位数字
%d 十进制表示的每月的第几天
%D 月/天/年
%e 在两字符域中,十进制表示的每月的第几天
%F 年-月-日
%g 年份的后两位数字,使用基于周的年
%G 年分,使用基于周的年
%h 简写的月份名
%H 24小时制的小时
%I 12小时制的小时
%j 十进制表示的每年的第几天
%m 十进制表示的月份
%M 十时制表示的分钟数
%n 新行符
%p 本地的AM或PM的等价显示
%r 12小时的时间
%R 显示小时和分钟:hh:mm
%S 十进制的秒数
%t 水平制表符
%T 显示时分秒:hh:mm:ss
%u 每周的第几天,星期一为第一天 (值从0到6,星期一为0)
%U 第年的第几周,把星期日做为第一天(值从0到53)
%V 每年的第几周,使用基于周的年
%w 十进制表示的星期几(值从0到6,星期天为0)
%W 每年的第几周,把星期一做为第一天(值从0到53)
%x 标准的日期串
%X 标准的时间串
%y 不带世纪的十进制年份(值从0到99)
%Y 带世纪部分的十进制年份
%z,%Z 时区名称,如果不能得到时区名称则返回空字符。
%% 百分号
time_t a;
time( &a);
struct tm *b;
b= localtime( &a);
char buffer[80]; sizeof(buffer) = 80
以年月日_时分秒的形式表示当前时间:
strftime(buffer, 80, "%Y%m%e_%H%M%S", b); 80 对应 buffer[80]
mktime
:
mktime(struct tm *timeptr)
timeptr
转为time_t
类型,若失败则返回 -1
int ret;
struct tm a;
a.tm_year = 2001 - 1900;
a.tm_mon = 7 - 1;
a.tm_mday = 4;
a.tm_hour = 0;
a.tm_min = 0;
a.tm_sec = 1;
a.tm_isdst = -1;
ret = mktime(&a);
if( ret == -1 )
{
printf("错误:不能使用 mktime 转换时间。\n");
}
else
{
char buffer[80];
strftime(buffer, sizeof(buffer), "%c", &a);
print(buffer);
}