目录
目录
C语言基础
介绍
C语言=>汇编语言=>机器语言=>cpu执行
java=>字节码=>解释器=>cpu执行
#include <stdio.h>
int main()
{
printf("I love fishC.com\n");
print\
f("\n\
*\n\
***\n\
*****\n\
*******\n");
return 0;
}
变量
变量名:
只能用英文字母和数字或者下划线组成
第一个字符必须是字母或者下划线
变量名区分大小写
#include <stdio.h>
int main()
{
int a;
char b;
float c;
double d;
a = 520;
b = 'F';
c = 3.14;
d = 3.141592653;
printf("小红帽公司创办于2010年的%d\n",a);
printf("I love %cishC.com!\n",b);
printf("圆周率是:%.2f\n",c);//%。2f指精确到小数点后两位的f浮点数
printf("精确到小数点后9位的圆周率是:%11.9f\n",d);//%11.9f指精确到小数点后九位,且占位宽度位11的f浮点数
return 0;
}
字符型:'a'
整型:520
单精度浮点型:float
双精度浮点型:double
常量
整型常量:520,1314
实型常量:3.14,5.12
字符常量
-普通字符:'L','o','v','e'
转义字符
\a 响铃bell
\b 退格
\f 换页
\n 换行
\r 回车
\t 水平制表,跳到下一个tab位置
\v 垂直制表
\\ 表示\本身
\' 表示单引号
\" 表示双引号
\? 表示问号
\0 表示空字符null
\ddd 1到3位八进制数所代表的任意字符
\xhh 1到2位十六进制数所代表的任意字符
\ 表示本行延续至下一行
%d 整型
%f 浮点型
%c 字符型
%s 字符串型
%u 无符号unsigned型
%p 地址
定义符号常量
#define 标识符 常量
#include <stdio.h>
#define URL "http://www.baidu.com"
#define NAME "百度"
#define BOSS "喝饱水"
#define YEAR 2020
#define MONTH 5
#define DAY 20
int main()
{
printf("%s成立于%d年%d月%d日\n",NAME,YEAR,MONTH,DAY);
printf("%s是%s创立的……\n",NAME,BOSS);
printf("%s的域名是%s\n",NAME,URL);
return 0;
}
标识符
只能由英文字母,数字或下划线组成
首字符必须是字面或下划线
区分大小写,不能用关键字来命名标识符
字符串常量
"Hello World"\\实际上在字符串最后还追加了一个\0来追加一个空字符,代表该字符串结束
数据类型
基本类型:整型(short int,int,long int,long long int),浮点型(float,double,long double),字符类型(char),布尔类型(_Bool),枚举类型(enum)
指针类型
构造类型:数组类型,结构类型,联合类型
空类型
sizeof运算符
可以用于获得数据类型或表达式的长度;
-sizeof(object); //sizeof(对象);
-sizeof(type_name); //sizeof(类型);
-sizeof object; //sizeof 对象;
#include <stdio.h>
int main()
{
int i;
char j;
float k;
i = 123;
j = 'C';
k = 3.14;
printf("size of int is %d\n",sizeof(int));
printf("size of i is %d\n",sizeof(i));
printf("size of char is %d\n",sizeof(char));
printf("size of j is %d\n",sizeof(j));
printf("size of float is %d\n",sizeof(float));
printf("size of k is %d\n",sizeof(k));
return 0;
}
signed和unsigned
[signed] short [int]:可以存放负数
unsigned short [int]:不能存放负数
#include <stdio.h>
int main()
{
short i;//默认带符号位
unsigned short j;
i = -1;
j = -1;
printf("%d\n",i);
printf("%d\n",j);
return 0;
}
字节-比特位:1Byte == 8bit
#include <stdio.h>
#include <math.h>
int main()
{
int result = pow(2,32) - 1;//对于32位的整型变量,在signed类型的存储单元中,左边第一位表示符号位,所以表示值的只有31个bit
unsigned int result1 = pow(2,32) - 1;//无符号位的可以表示32个bit
printf("result = %d\n",result);
printf("result1 = %u\n",result1);
return 0;
}
基本数据类型的取值范围:
数据类型 | 字节数 | 取值范围 |
char | 1 | -128~127 |
unsigned char | 1 | 0~255 |
short | 2 | -32768~32767 |
unsigned short | 2 | 0~65535 |
int | 4 | |
unsigned int | 4 | |
long | 4 | |
unsigned long | 4 | |
long long | 8 | |
unsigned long long | 8 | |
float | 4 | |
double | 8 | |
long double | 12 |
字符
#include <stdio.h>
int main()
{
char a ='C';
unsigned char height = 170;//必须用unsigned,这时候取值范围才对
printf("%c = %d\n",a,a);//%d对应的是C的ASCII码,字符类型就是一个特殊的整型
printf("阿大的身高是%d\n",height);
return 0;
}
字符串
char 变量名[数值];
#include <stdio.h>
int main()
{
char a[6] = {'F','i','s','h','C','\0'};//尾部加上\0代表字符串结束 ,也可以直接用双引号引出字符串常量"FishC",不用加\0
printf("%c\n",a[1]);//可以对字符串提取其索引,获得对应位置上的字符,此处是i
printf("%s\n",a);
printf("Hello\n");
return 0;
}
运算符
算数运算符(高):
运算符 | 名称 | 例子 | 结果 |
+ | 加法(双目) | ||
- | |||
* | |||
/ | |||
% | |||
+ | 正号(单目) | ||
- |
目:
1+2(操作数 运算符 操作数),有几个操作数就是几目
表达式:用运算符和括号将操作数连接起来的式子
1+2
'a'+'b'
a+b
#include <stdio.h>
#include <math.h>
int main()
{
int i,j,k;
i = 1 + 2;
j = 1 + 2 * 3;
k = i + j + -1 + pow(2,3);//3 + 7 +(-1)+8=17
printf("i = %d\n",i);
printf("j = %d\n",j);
printf("k = %d\n",k);
return 0;
}
类型转换:操作数类型不同时,占位少的转换成占位多的,如下:
1+2.0 == 1.0+2.0 整型转换成浮点型
#include <stdio.h>
int main()
{
printf("整数输出:%d\n",1 + 2.0);//整型就应该转换成浮点型去输出,用%d强制转换会使其报错
printf("浮点数输出:%f\n",1+2.0);//此处就不会报错,输出3.0
printf("整数输出:%d\n",1 + (int)2.2);//此处可以将2.2强制转换为整型再参加运算,转换为int型的时候,不是进行四舍五入,而是直接去除小数位
return 0;
}
关系运算符(中):
优先级相同(高):
<(小于)
<=(小于等于)
>(大于)
>=(大于等于)
优先级相同(低):
==(等于)
!=(不等于)
关系表达式:只会返回一个逻辑值,1or0,表示True or False
用关系运算符将两边的变量、数据或表达式连接起来,称为关系表达式
1
a>b
a'a'+'b'
(a=3)>(b=5)
#include <stdio.h>
int main()
{
int a =5,b = 3;
printf("%d\n",1 < 2);
printf("%d\n",a > b);
printf("%d\n",a <= b + 1);
printf("%d\n",'a' + 'b' <= 'c');
printf("%d\n",(a = 3) > (b =5));
return 0;
}
逻辑运算符(低):
运算符 | 含义 | 优先级 | 举例 | 说明 |
! | 逻辑非 | 高 | ||
&& | 逻辑与 | 中 | ||
|| | 逻辑或 | 低 |
逻辑表达式:用逻辑运算符将两边的变量、数据或表达式连接起来,称为逻辑表达式
3>1 && 1
3+1 || 2==0
!(a+b)
!0 + 1 < 1 || !(3+4)
'a' - 'b' && 'c'
#include <stdio.h>
int main()
{
int a =5 ,b = 3;
printf("%d\n",3 > 1 && 1 < 2);
printf("%d\n",3 + 1 || 2 == 0);
printf("%d\n",!(a + b));
printf("%d\n",!0 + 1 < 1 || !(3 + 4));//!0 => 1 + 1 => 2 < 1 => 0 || !7 => 0 || 0 => 0
printf("%d\n",'a' - 'b' && 'c');
(a = 0) && (b = 5);
printf("a = %d,b = %d\n",a,b);//从a的值的大小就可以给出逻辑运算结果,所以不需要进行后面的运算了,故b=5不会执行,b仍为3
(a = 1) || (b = 5);
printf("a = %d,b = %d\n",a,b);//称为短路求值
return 0;
}
优先级与结合性表
循环条件结构
if语句结构
……//其他语句
if (表达式1)
{
……//表达式1的逻辑值为真所执行的语句、程序块
}
else if (表达式2)
{
……//表达式2的逻辑值为真所执行的语句、程序块
}
else
{
……//逻辑值都为假所执行的语句、程序块
}
……//其他语句
#include <stdio.h>
int main()
{
int i;
printf("您老贵庚啊:");
scanf("%d",&i);
if (i >= 18)
{
printf("进门左拐!\n"); //{}把几条语句组成一个程序块
}
else if (i < 18 && i >= 10)
{
printf("进门右拐!\n");
}
else
{
printf("慢走不送!\n");
}
return 0;
}
switch语句结构
……//其他语句
switch (表达式)
{
case 常量表达式1:语句或程序块1
case 常量表达式2:语句或程序块2
……
case 常量表达式n:语句或程序块n
default:语句或程序块n+1
}
……//其他语句
#include <stdio.h>
int main()
{
char ch;
printf("请输入成绩:");
scanf("%c",&ch);//输入ch的值
switch (ch)
{
case 'A': printf("你的成绩在90分以上!\n"); break;//如果是A,就执行后面的语句
case 'B': printf("你的成绩在80~90分之间!\n"); break;
case 'C': printf("你的成绩在70~80分之间!\n"); break;
case 'D': printf("你的成绩在60~70分之间!\n"); break;
case 'E': printf("你的成绩在60分以下!\n"); break;
default: printf("请输入有效的成绩!\n"); break;//如果以上都不是,就执行后面的语句
}
return 0;
}
嵌套结构:
#include <stdio.h>
int main()
{
int a,b;
printf("请输入两个数:");
scanf("%d %d",&a,&b);//输入值时,格式为23 32
if (a != b)
{
if (a > b)
{
printf("%d > %d\n",a,b);
}
else
{
printf("%d < %d\n",a,b);
}
}
else
{
printf("%d = %d\n",a,b);
}
return 0;
}
悬挂else:
#include <stdio.h>
int main()
{
char isRain,isFree;
printf("是否有空?(Y/N)\n");
scanf("%c",&isFree);
getchar();//把回车输入进getchar()中去 ,否则这个回车会被下面的scanf获取
printf("是否下雨?(Y/N)\n");
scanf("%c",&isRain);
if (isFree == 'Y')
{
if (isRain == 'Y')//此处没有放置{},也执行成功了
{
printf("记得带伞哦!\n");
}
}
else
{
printf("女神没空~\n");
}
return 0;
}
while循环结构
入口条件循环:
while (表达式)
循环体
出口条件循环:
do
循环体
while (表达式);
#include <stdio.h>
int main()
{
int count = 0;//初始化计数器
printf("输入字符:");
while (getchar() != '\n')//循环条件:getchar != '\n'时,执行以下循环体,直到输入回车即停止
{
count = count + 1;//更新计数器
}
printf("你总共输入了%d个字符!\n",count);
return 0;
}
for循环与break的应用
for (表达式1;表达式2;表达式3)//表达式可以省略,;不能省略
//暴力求解素数
#include <stdio.h>
int main()
{
int i,num;
bool flag = 1;
printf("请输入一个整数:");
scanf("%d",&num);
for (i = 2;i < num / 2;i++)
{
if (num % i == 0)
{
flag = 0;
break;//如果没有break,本循环会一直执行下去,直到跳出循环条件为止,加了break后,只要执行一次本表达式就会自动跳出循环,减少资源消耗
}
}
if (flag)
{
printf("%d是一个素数!\n",num);
}
else
{
printf("%d不是一个素数\n",num);
}
printf("i = %d\n",i);//输入100000,没有break时,i=50000,有break时,i=2
return 0;
}
循环嵌套:
#include <stdio.h>
int main()
{
int i,j;
for (i = 1;i <= 9;i++)
{
for (j = 1;j <= i;j++)
{
printf("%d*%d=%-2d ",i,j,i*j);//%-2d中的2指补足数据到2位,-表示数据左对齐
}
putchar('\n');
}
return 0;
}
break与continue语句:
//printf在不同位置的输出效果不同以及break的作用
#include <stdio.h>
int main()
{
int i,j;
for (i = 0;i < 10;i++)
{
for (j = 0;j < 10;j++)
{
if (j == 3)
{
break;//注意break只能跳到一层循环
}
printf("i = %d;j = %d \n",i,j);
}
//printf("i = %d;j = %d \n",i,j);
}
//printf("i = %d;j = %d \n",i,j);
return 0;
}
--------分割线----------
//continue的用法
#include <stdio.h>
int main()
{
int i;
for (i = 0;i < 10;i++)
{
if (i % 2)
{
continue;//对于for来说,continue后,直接到i++
}
}
/*while (i < 10)
{
if (i % 2)
{
continue;//这个continue会自动跳过i++,进行下一次循环,导致i无法自增,陷入死循环
}
i++;
}*/
printf("i = %d \n",i);
return 0;
}
拾遗
复合赋值运算符:
a = a + 1 a += 1(+-*/%均可)
自增自减运算符:
a++与++a
a--与--a
逗号运算符:
i=1;
j=2; => i=1,j=2;
若a = 3,5;则结果为
a = 3;
5;
注意逗号运算符优先级很低,甚至低于赋值运算符,且其运算顺序是从左到右。
#include <stdio.h>
int main()
{
int i = 5,j;
j = ++i;//先计算再赋值,++i,i=6,j=i,j=6
printf("i = %d,j = %d\n",i,j);
i = 5;
j = i++;//先赋值再计算,j=i,j=5,i++,i=6
printf("i = %d,j = %d\n",i,j);
a = 3,5;//注意,此时a = 3;
printf("a = %d",a);
a = (3,5);
printf("a = %d",a);//在括号里的值,选择最后的那个值赋给变量a,a=5
return 0;
}
条件运算符:
exp1?exp2:exp3;
exp1是条件表达式
如果结果为真,返回exp2
如果结果为假,返回exp3
if (a > b)
max = a;
else
max = b;
<=>
max = a > b ? a : b;
goto语句
#include <stdio.h>
int main()
{
int i = 5;
while (i++)
{
if (i > 10)
{
goto A;//直接跳到A处
}
}
A: printf("Here,i = %d\n",i);
return 0;
}
数组
数组定义:
类型 数组名[元素个数];
int a[6];//一个int是4字节,4*6=24字节
char [24];//一个char是1字节,1*24=24字节
double c[3];//一个double是8字节,8*3=24字节
数组不能动态定义
注意不能这样:int a[n];//[]中必须都是常量,不能是变量n,现在的C99可以支持
数组[元素个数];
int a[5];//定义一个有5个元素的数组
a[0];//访问a数组中的第一个元素
a[4];//访问a数组中的第五个元素
a[5];//报错,超出数组范围
//使用循环访问数组
int a[10];
for (i = 0;i < 10;i++)
{
a[i] = i;
}
------分割线-------
#include <stdio.h>
#define NUM 10//宏定义NUM = 10
int main()
{
int s[NUM];
int i,sum = 0;//初始化数值
for (i = 0;i < 10; i++)
{
printf("请输入第%i位同学的成绩:",i + 1);//i+1代表在数组中的位数
scanf("%d",&s[i]);//向数组中填入第i个数值
sum += s[i];//sum开始递增
}
printf("成绩录入完毕,该次考试的平均分是:%.2f\n",(double)sum / NUM);//计算出平均值
return 0;
}
数组初始化
int a[10] = {1,2};//第一个数为1,第二个数为2,其余数均为0
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int a[] = {1,2,3,4,5,6,7,8,9,0};//可以自动判断出a数组的长度并填充,这里可以偷懒
C99新特性:int a[10] = {[3] = 3}
#include <stdio.h>
int main()
{
int a[10] = {[3] = 3,[5] = 5,[8] = 8};//C99
int i;
for (i = 0;i < 10;i++)
{
printf("%d\n",a[i]);
}
printf("%d\n",sizeof(a));//sizeof求数组a所占用的内存
return 0;
}
------分割线------
//一个小demo
#include <stdio.h>
int main()
{
int n,i;
printf("请输入字符的个数:\n");
scanf("%d",&n);
char a[n+1];//共n+1个元素,包括结尾的\0
printf("请开始输入字符:\n");
getchar();//把上面的换行符吸收,并释放掉
for (i = 0;i < n;i++)//本循环中,共填充了n个元素
{
scanf("%c",&a[i]);
}
a[n] = '\0';//a[n]就是第n+1个元素,作为字符串的结尾,以\0结束
printf("你输入的字符串是:%s\n",a);
return 0;
}
字符数组
char str1[10] = {'F','i','s','h','C','\0'};//初始化字符数组的每个元素
char str1[] = {'F','i','s','h','C','\0'};//不写元素个数也可以
char str1[] = {"FishC"};//使用字符串常量初始化字符数组,{}也可以省略
字符串处理函数
strlen:获取字符串长度
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "I love FishC.com!";
printf("sizeof str = %d\n",sizeof(str));//指的是字符串中的尺寸,包含结束符\0
printf("strlen str = %u\n",strlen(str));//strlen的返回值是unsigned int ,指的是字符串的长度,不包含结束符\0
return 0;
}
strcpy和strncpy:拷贝字符串
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Original String";
char str2[] = "New String";
char str3[100];
char str4[] = "to be or not to be";
char str5[40];
strcpy(str1,str2);//将str2拷贝到str1上,且将str2的结束符\0也拷贝过去了,提示其按点结束 ,str1的长度必须足以容纳str2,否则会溢出
strcpy(str3,"Copy Successful");
strncpy(str5,str4,5);//限制拷贝字符数为5,但这5个字符中不包含结束符
str5[5] = '\0';//加上结束符
printf("str1:%s\n",str1);
printf("str2:%s\n",str2);
printf("str3:%s\n",str3);
printf("str5:%s\n",str5);
return 0;
}
strcat与strncat:连接字符串
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "I love";
char str2[] = "FishC.com!";
strcat(str1," ");//将str1与空格连接
strcat(str1,str2);//再将其与str2连接
//strncat也可以限制拷贝字符数,且会自动追加结束符
printf("str1:%s\n",str1);
return 0;
}
strcmp与strncmp:比较字符串
#include <stdio.h>
#include <string.h>
int main()
{
char str1[] = "Love!";
char str2[] = "Love!";
if (!strcmp(str1,str2))//str1与str2的ASCII码完全相等,则为0,若str1的ASCII码大
//返回大于0的值,若str2的ASCII码大,返回小于0的值
{
printf("两个字符串完全一致\n");
}
else
{
printf("两个字符串存在差异\n");
}
return 0;
}
二维数组
类型 数组名[常量表达式][常量表达式]
int a[6][6];//6*6,6行6列
char b[4][5];//4*5,4行5列
a[0][0];//访问a数组的第一行第一列的元素
//用二维数组实现矩阵转置
#include <stdio.h>
int main()
{
int a[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};//第一维度可以不写,其他维度不行,比如a[][4]可以计算出来,a[3][]不行
int i,j;
//int b[3][4] = {[0][0] = 1,[1][1] = 2,[2][2] = 3};C99新特性 ,可以按索引赋值,其他位置默认为0
//printf(b[3][4]);
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("%d ", a[i][j]);
}
printf("\n");
}
for (i = 0; i < 4; i++)
{
for (j = 0; j < 3; j++)
{
printf("%d ", a[j][i]);
}
printf("\n");
}//获得上矩阵的转置
return 0;
}
指针
根据存储的值的地址,来找到对应的值
变量的地址 | 存放的值 |
10000 | 'H' |
10001 | 'i' |
10002 | 's' |
10003(d变量的地址在这里,并按其类型占4个字节来向下获取3个字节) | 123 |
10004 | |
10005 | |
10006 |
左边是内存地址,地址就是一个索引,右边是对应内存地址上的存储的值,比如char型变量占用一个字节,也就是占用一个地址,int型变量占用四个字节,也就是占用四个地址。
注意:在内存中完全没有必要存放变量名
因为变量名是为了方便使用而定义的,只有人和编译器知道,当你访问某个变量的时候,编译器会根据变量所在的地址以及变量的类型,来访问特定范围内的数据
只有编译器知道 | |
变量 | 变量的地址 |
a | 10000 |
b | 10001 |
c | 10002 |
实际上也就是访问到变量所在的地址,再根据变量类型,确定其占多少个字节 | |
d | 10003 |
指针和指针变量:指针变量的地址里存着一般变量的地址,这个指针变量的地址叫指针
指针变量 | 指针变量的地址 |
pa | 11000 |
指针变量的地址(指针) | 存放的值(一般变量的地址) |
11000(这4个字节称为一个指针,可以存放一个地址) | 10000(比如这里存放的就是变量a的地址) |
11001 | |
11002 | |
11003 |
根据10000这个地址,就可以获取到一般变量的地址与存放的值了
定义指针变量:
类型名 *指针变量名
char *pa;//定义一个指向字符型的指针变量,意思是指向a这个字符型变量
int *pb;//定义一个指向整型的指针变量
取址运算符与取值运算符:
若要获取某个变量的地址,可以使用取地址运算符(&):
char *pa = &a;
若要访问指针变量指向的数据,可以使用取值运算符(*):
printf("%c,%d\n",*pa,*pb);
#include <stdio.h>
int main()
{
char a = 'F';
int f = 123;
char *pa = &a;//这里的*是指定义指针变量,&是取址运算符
int *pb = &f;
*pa = 'C';//这里的*pa就是指a的值,因为这里的*是取值运算符,此时就是利用取值运算符来对a进行间接的赋值
*pb += 1;
printf("a = %c\n",*pa);//这里的*也是取值运算符
printf("f = %d\n",*pb);
printf("sizeof pa = %d\n",sizeof(pa));//32位输出是4,意思是pa存储的是变量a的地址,占4个字节,但64位输出是8,原因不详
printf("sizeof pb = %d\n",sizeof(pb));
printf("the addr 0f a is: %p\n",pa);//%p指的是地址类型的数据,pa是指针变量,其取值是a的地址,也可以直接使用&a来寻址
printf("the addr of f is: %p\n",pb);
return 0;
}
避免访问未初始化的指针
#include <stdio.h>
int main()
{
int *a;//没有初始化的指针,就是野指针,它在栈里的地址是随机分配的,但又是合法的
*a = 123;//此时相当于向一个随机的地址进行赋值,可能会覆盖到系统的其他代码,此时系统会强制终止,若没有覆盖其他代码且赋值成功,出现问题也会非常难以排查,因为地址是随机分配的
return 0;
}
指针和数组
#include <stdio.h>
int main()
{
int a;
int *p = &a;//将a的地址赋值给指针变量p
printf("请输入一个整数:");
scanf("%d",&a);//给a传入具体的值
printf("a = %d\n",a);
printf("请重新输入一个整数:");
scanf("%d",p);//p与&a类似
printf("a = %d\n",a);//这里输出的就是a的值
return 0;
}
定义数组
#include <stdio.h>
int main()
{
char str[128];//定义数组str
printf("请输入阿大的域名:");
scanf("%s",str);
//printf("阿大的域名是: %s\n",str);
printf("阿大的域名是: %p\n",str);//数组名是数组第一个元素的地址,实际上指针也就是存储了某个变量的第一个地址
printf("阿大的域名是: %p\n",&str[0]);//如果一个变量存储的是另一个变量的地址叫指针,如果一个变量存储的是这个变量本身的地址叫数组
//数组名 = 数组的第一个元素的地址 = 数组的地址
return 0;
}
指向数组的指针
当指针指向数组元素的时候,我们可以对指针变量进行加减运算,这样做的意义相当于指向距离指针所在位置向前或向后第n个元素。
#include <stdio.h>
#include <string.h>
int main()
{
char a[] = "You";
int b[5] = {1,2,3,4,5};
float c[5] = {1.1,2.2,3.3,4.4,5.5};
double d[5] = {1.1,2.2,3.3,4.4,5.5};
char *str = "I love You!";//定义字符指针变量
int i,length;
length = strlen(str);
for (i = 0;i < length;i++)
{
printf("%c",str[i]);//说明用数组的下标法也能访问指针变量
}
printf("\n");
char *p = a;//该指针p指向的是第一个地址,也就是字符串的第一个字节处,此处的值是F
printf("*p = %c,*(p+1) = %c,*(p+2) = %c\n",*p,*(p+1),*(p+2));//在此基础上,进行指针运算,使其偏移,p+1对应的位置在后面1个字节处,即取值为i处
printf("*b = %d,*(b+1) = %d,*(b+2) = %d\n",*b,*(b+1),*(b+2));//数组像一种特殊的指针,所以直接对数组用指针法访问元素,竟也能成功
/*
printf("a[0] -> %p,a[1] -> %p,a[2] = %p\n",&a[0],&a[1],&a[2]);//dev c中地址以00开头,但也是16进制,含义与0x一样,原因不详
printf("b[0] -> %p,b[1] -> %p,b[2] = %p\n",&b[0],&b[1],&b[2]);//显然可以看出char是一个字节,float与int是4字节,而double是8字节
printf("c[0] -> %p,c[1] -> %p,c[2] = %p\n",&c[0],&c[1],&c[2]);
printf("d[0] -> %p,d[1] -> %p,d[2] = %p\n",&d[0],&d[1],&d[2]);
*/
return 0;
}
注意:p+1并不是简单地将地址加1,而是指向数组的下一个元素,比如int型就会跳过4个字节,到达下一个元素处。
#include <stdio.h>
#include <string.h>
int main()
{
char str[] = "I love You!";
char *target = str;
int count = 0;
while (*target++ != '\0')//str是数组的第一元素的地址,它不可改变,换成指针变量target后,就可以改变了,而左值必须是一个变量
{
count++;
}
printf("总共有%d个字符!\n",count);//最后
return 0;
}
//左值lvalue指用于识别或定位一个存储位置的标识符,且是可改变的,可以把左值理解成变量,右值理解为常量
注意:数组名只是一个地址,而指针是一个左值。
指针数组和数组指针
int *p1[5];//指针数组,显然[]运算符比*运算符优先级高,所以先被定义为数组,但又有*,指的是数组的每个元素都存放一个指针变量
#include <stdio.h>
#include <string.h>
int main()
{
int a = 1;
int b = 2;
int c = 3;
int d = 4;
int e = 5;
int *p1[5] = {&a,&b,&c,&d,&e};//将地址传给指针变量
int i;
for (i = 0;i < 5;i++)
{
printf("%d\n",*p1[i]);//把指针变量的值输出,这里的*是取值符
}
return 0;
}
-------分割线-------
#include <stdio.h>
#include <string.h>
int main()
{
char *p1[5] = {"01.Hadoop","02.Zookeeper","03.Hive","04.Kafka","05.Flink"};//数组中的每个元素都是指针变量,指向的是一个字符数组,该指针存放的就是该字符数组首个字符的地址
int i;
for (i = 0;i < 5;i++)
{
printf("%s\n",p1[i]);//当用%s时,取出的就是该地址及其后面所有字符,当用%d时,取出的是首字符所在的地址,当用*p1[i]时,取出的是首字符。
}
return 0;
}
int (*p2)[5];//数组指针,()与[]优先顺序一样,但是结合性是从左到右,所以它先被定义为指针,再成为数组形式。
数组指针是一个指针,它指向的是一个数组,指针数组中的元素指针,指向的其实是数组的第一个元素的地址,这之间有很大的区别。
#include <stdio.h>
#include <string.h>
int main()
{
int temp[5] = {1,2,3,4,5};//定义一个数组
int (*p2)[5] = &temp;//将该数组首元素地址的地址&temp传给数组指针,不能直接写出数组名temp(数组名=数组的第一个元素的地址)
/*
(*p2)[5]指的是指向数组的指针,所以这是一个指针,应该赋值给这个指针变量一个数组地址,也就是&temp(取址符加数组名),同时数组名=数组首元素地址,所以也就是数组首元素地址的地址
*/
int i;
for (i = 0;i < 5;i++)
{
printf("%d\n",*(*p2 + i));//p2指向的是数组temp的地址即数组首元素地址,*p2就是p2取值,即得到temp数组首元素地址,*(*p+i)就是temp首元素后第i个元素地址上的值,也就是首元素后的第i个元素
}
return 0;
}
二维数组
#include <stdio.h>
int main()
{
int array[4][5] = {0};//array[4][5]是指向包含5个元素的数组的指针
printf("sizeof int: %d\n",sizeof(int));
printf("array: %p\n",array);//array默认为是数组array[0]的地址
printf("array+1: %p\n",array + 1);//array+1可以看成array[1]的地址,也就是第二行数组首元素的地址(数组名=数组的地址=数组首元素的地址)
return 0;
}
array[4][5]的存储依然是在一列中的,但是为方便理解,写成如下形式,
array[0][0] | array[0][1] | array[0][2] | array[0][3] | array[0][4] |
array[1][0] | array[1][1] | array[1][2] | array[1][3] | array[1][4] |
array[2][0] | array[2][1] | array[2][2] | array[2][3] | array[2][4] |
array[3][0] | array[3][1] | array[3][2] | array[3][3] | array[3][4] |
#include <stdio.h>
int main()
{
int array[4][5] = {0};//array[4][5]是指向包含5个元素的数组的指针
int i,j,k = 0;
for (i = 0;i < 4;i++)
{
for (j = 0;j < 5;j++)
{
array[i][j] = k++;
}
}
printf("*(array+1):%p\n",*(array + 1));//对array+1进行解引用,%p就是取array的第二行数组的首元素地址
printf("array[1]:%p\n",array[1]);//用%p可以取array的第二行数组的首元素地址
printf("&array[1][0]:%p\n",&array[1][0]);//直接取array的第二行数组的首元素地址
printf("**(array+1):%d\n",**(array+1));//取第二行数组首元素的值(是该地址上对应的值)
printf("*(*(array+1)+3):%d\n",*(*(array+1)+3));//相当于在第二行首元素地址上进行指针运算,向后偏移3位,得到首元素后第三个元素的地址,再取值得到首元素后第三个元素的值
printf("array[1][3]:%d\n",array[1][3]);//*(array+1)+3 == &array[1][3]
//*(array + i) + j == &array[i][j]
//*(array + i) == array[i]
//*(*(*(array + i) + j) + k) == array[i][j][k]
return 0;
}
初始化二维数组是可以偷懒的:
int array[][3] = {{0,1,2},{3,4,5}};//其中行数是可以省略的
关于数组指针与二维数组的关系:
int (*p)[3] = array;//array本身指的是第一行子数组本身
#include <stdio.h>
int main()
{
int array[2][3] = {{0,1,2},{3,4,5}};
int (*p)[3] = array;
printf("**(p+1):%d\n",**(p+1));
printf("**(array+1):%d\n",**(array+1));
printf("array[1][0]:%d\n",array[1][0]);
printf("*(*(p+1)+2):%d\n",*(*(p+1)+2));//p是指针变量,其值是数组的地址,p+1就是进行了指针运算,偏移到了后面1个数组上,就是第二个数组的地址
//*(p+1)就是取了这个数组出来,类似于数组名,也就是数组的首元素地址,再对其求偏移,可得到后面第2个元素的地址*(p+1)+2,另外进行取值得到第二个数组的第三个元素
printf("*(*(array+1)+2):%d\n",*(*(array+1)+2));
printf("array[1][2]:%d\n",array[1][2]);
return 0;
}
void指针
void指针称为通用指针,就是可以指向任意类型的数据。也就是说,任何类型的指针都可以赋值给void指针。
#include <stdio.h>
int main()
{
int num = 1024;
int *pi = #
char *ps = "Love";
void *pv;
pv = pi;
printf("pi:%p,pv:%p\n",pi,pv);
printf("*pv:%d\n",*(int *)pv);//(int *)是改变指针类型的操作,这里讲void指针强转为int指针,再加解引用,也就是取值符
//因为void指针不能解引用!!!
pv = ps;
printf("ps:%p,pv:%p",ps,pv);
printf("*pv:%s",(char *)pv);
return 0;
}
NULL指针
#define NULL ((void *)0);//相当于宏定义,空指针,不指向任何数据,因为地址0通常不存储数据,所以把它定义成一个void指针
当不知道要将指针初始化为什么地址时,先初始化为NULL,这样就可以解引用之前先检查是否为NULL,为NULL则报错,不会像野指针一样,不报错难以排查
NUL与NULL不同,NULL用于指针和对象,表示控制,指向一个不被使用的地址;而NUL表示'\0'表示字符串的结尾。
#include <stdio.h>
int main()
{
int *p1;
int *p2 = NULL;
printf("%d\n",*p1);//野指针会乱指一气,但随机地址如果没有存放别的值,就会随机输出,但不会报错,难以排查
printf("%d\n",*p2);//但NULL指针是非法的,不会无法排查
return 0;
}
指向指针的指针
#include <stdio.h>
int main()
{
int num = 520;
int *p = #//p保存的是num的地址
int **pp = &p;//pp保存的是p的地址
printf("num:%d\n",num);
printf("*p:%d\n",*p);//对p做取值,即num的地址
printf("**p:%d\n",**pp);//对pp取值得到p的值num的地址,再进行一次取值,得到num的取值
printf("&p:%p,pp:%p\n",&p,pp);//&p对p进行取址,pp看pp这个指针里存放的哪个变量的地址,其实pp=&p
printf("&num:%p,p:%p,*pp:%p\n",&num,p,*pp);//&num对num进行取址,p看p这个指针里面存放的是哪个变量的地址,*pp对pp进行解引用,也就是求保存的地址对应的变量的值,即p的值
//实际上我发现,直接返回pp本身的值,也就是pp存放的p的地址,进行*pp,也就是对pp进行解引用,得到的是p的取值,也就是p存放的num的地址
return 0;
}
指针数组与指向指针的指针
#include <stdio.h>
int main()
{
int array[][4] = {
{0,1,2,3},
{4,5,6,7},
{8,9,10,11}};
int (*p)[4] = array;//数组指针,是一个指向数组的指针,p就是指向二维数组array的指针,其偏移量就是二维数组的元素长度4
int i,j;
for (i = 0;i < 3;i++)
{
for (j = 0;j < 4;j++)
{
printf("%2d",*(*(p + i) + j));
}
printf("\n");
}
return 0;
}
常量和指针
普通的:520,'a',3.14
宏定义的:#define PRICE 520;
const关键字修饰符:const int price = 520;
指向常量的指针:
指针可以修改为指向不同的常量
指针可以修改为指向不同的变量
可以通过解引用来读取指针指向的数据
不可以通过解引用修改指针指向的数据
#include <stdio.h>
int main()
{
/*
const float pi = 3.14;//定义一个只可读的常量pi
printf("%f\n",pi);
//pi = 3.1415;//修改会报错
*/
int num = 520;
const int cnum = 880;
const int *pc = &cnum;
printf("cnum:%d,&cnum:%p\n",cnum,&cnum);
printf("*pc:%d,pc:%p\n",*pc,pc);
pc = #//修改pc的指向,指向num的地址
printf("num:%d,&num:%p\n",num,&num);
printf("*pc:%d,pc:%p\n",*pc,pc);
num = 1024;//通过修改num的值,来修改pc指向的地址的值
printf("num:%d,&num:%p\n",num,&num);//但是指针不能被修改,如*p = 1024;是不允许的
printf("*pc:%d,pc:%p\n",*pc,pc);
return 0;
}
指向非常量的常量指针
指针自身不可被修改
指针指向的值可以被修改
指向常量的常量指针:
指针自身不可被修改
指针指向的值也不可被修改
#include <stdio.h>
int main()
{
int num = 520;
const int cnum = 880;
int * const p = #
*p = 1024;
printf("*p:%d\n",*p);
p = &cnum;//指向常量的指针是不可以被修改的,这里会报错
printf("*p:%d\n",*p);
return 0;
}
函数
函数定义:
类型名 函数名(参数列表)
{
函数体
}
函数声明:
所谓声明,就是告诉编译器我要使用这个函数,现在没有找到它的定义不要紧,请不要报错,待会会补上
#include <stdio.h>
//编写一个函数sum,由用户输入参数n,计算1+2+..+n的结果并返回
int sum(int n);//形参
int sum(int n)
{
int result = 0;
do
{
result += n;
} while(n-- > 0);
return result;
}
int main()
{
int n,result;
printf("请输入n的值:");
scanf("%d",&n);
result = sum(n);//实参传入
printf("1+2+...+(n-1)+n的结果是:%d\n",sum(n));
return 0;
}
参数与指针
传值与传址
版本1:传值
#include <stdio.h>
//数据的互换,直接进行数值的传入
void swap(int x,int y);
void swap(int x,int y)
{
int temp;
printf("In swap,互换前:x = %d,y = %d\n",x,y);
temp = x;
x = y;
y = temp;
printf("In swap,互换后:x = %d,y = %d\n",x,y);
}
int main()
{
int x = 3,y = 5;
printf("In main,互换前:x = %d,y = %d\n",x,y);
swap(x,y);
printf("In main,互换后:x = %d,y = %d\n",x,y);//传值的时候,这里不会被函数的变化所改变
return 0;
}
版本2:传址
#include <stdio.h>
//数据的互换
void swap(int *x,int *y);//声明x,y指针
void swap(int *x,int *y)//对*x,*y两个指针传入地址
{
int temp;
printf("In swap,互换前:x = %d,y = %d\n",*x,*y);//*x,*y即对x,y进行解引用,获取传入的&x,&y对应的值,注意这里的x,y的不同
temp = *x;//依然解引用获取x的取值,传给int型变量temp
*x = *y;//解引用:将y的取值传给x
*y = temp;//将temp变量的值传给y的解引用
printf("In swap,互换后:x = %d,y = %d\n",*x,*y);//解引用后打印x,y的数值
}
int main()
{
int x = 3,y = 5;
printf("In main,互换前:x = %d,y = %d\n",x,y);
swap(&x,&y);//传入x,y的地址
printf("In main,互换后:x = %d,y = %d\n",x,y);//传址的话,就可以改变这里的值了
return 0;
}
用数组传参数
#include <stdio.h>
void get_array(int a[10]);
void get_array(int a[10])
{
int i;
a[5] = 520;//会修改到main函数中,因为数组也有指针的性质
for (i = 0;i < 10;i++)
{
printf("a[%d] = %d\n",i,a[i]);
}
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
int i;
get_array(a);
for (i = 0;i < 10;i++)
{
printf("a[%d] = %d\n",i,a[i]);
}
return 0;
}
关于传入函数中的数组究竟是不是数组本身???
#include <stdio.h>
void get_array(int a[10]);
void get_array(int a[10])//这里就是一个地址
{
printf("sizeof b:%d\n",sizeof(a));//所以打印出来只占4个字节,就是int型地址的内存
}
int main()
{
int a[10] = {1,2,3,4,5,6,7,8,9,10};
printf("sizeof a:%d\n",sizeof(a));//这里打印出来的是数组的长度,也就是10个int型元素的内存,10*4 = 40个字节
get_array(a);
return 0;
}
可变参数
#include
va_list//一个类型,va就是可变参数的意思
va_start//3个宏定义
va_arg
va_end
感觉可变参数的目的就是,使参数的个数可以随意调整
#include <stdio.h>
#include <stdarg.h>
int sum(int n,...);//...指的是参数个数不确定
int sum(int n,...)
{
int i,sum = 0;
va_list vap;//用vap_list这个类型来定义参数列表,vap相当于一个字符指针
va_start(vap,n);//定义之后,把vap传给va_start这个宏,传入参数n,这里就相当于对字符指针进行计算而已
for (i = 0;i < n;i++)
{
sum += va_arg(vap,int);//用va_arg来获取后面的每一个参数的值,就是...代指的参数 ,并且说明参数类型为int
}
va_end(vap);//用va_end来关闭参数列表
return sum;
}
int main()
{
int result;
result = sum(3,1,2,3);
printf("result1 = %d\n",result);
result = sum(5,1,2,3,4,5);
printf("result2 = %d\n",result);
result = sum(6,1,2,3,-1,99,1000);
printf("result3 = %d\n",result);
return 0;
}
指针函数与函数指针
指针函数
#include <stdio.h>
char *getWord(char c);
char *getWord(char c)//指针函数
{
switch(c)
{
case 'A':return "Apple";//实际上返回的是"Apple"中'A'的地址啦
case 'B':return "Banana";
case 'C':return "Cat";
case 'D':return "Dog";
default:return "None";
}
}
int main()
{
char input;
printf("请输入一个字母:");
scanf("%c",&input);
printf("%s\n",getWord(input));//这里getWord(input)应该给出一个字符串,通常是没有类型来定义字符串的,都是用char类型的指针来定义字符串
//因为char类型的指针是指向一个字符,如果用它指向字符串的第一个字符,然后字符串的截止于'\0'这个空字符的,因而知道首字符也就知道了字符串本身了
return 0;
}
不要返回局部变量的指针
#include <stdio.h>
char *getWord(char c);
char *getWord(char c)
{
char str1[] = "Apple";//函数内部定义的变量是局部变量,出了函数就什么不是了
char str2[] = "Banana";
char str3[] = "Cat";
char str4[] = "Dog";
char str5[] = "None";
switch(c)
{
case 'A':return str1;//注意如果是局部变量就不能返回出函数去,但是字符串可以,为何呢,因为字符串的存储区域是在一个永久的区域里的,不在这个函数中
case 'B':return str2;
case 'C':return str3;
case 'D':return str4;
default:return str5;
}
}
int main()
{
char input;
printf("请输入一个字母:");
scanf("%c",&input);
printf("%s\n",getWord(input));
return 0;
}
函数指针:指向函数的指针
指针函数 -> int *p();
函数指针-> int (*p)();
#include <stdio.h>
int square(int);
int square(int num)//接收到&num
{
return num * num;
}
int main()
{
int num;
int (*fp)(int);//第一个int是返回值类型,(int)是参数类型,fp这个指针指向的是一个函数
printf("请输入一个整数:");
scanf("%d",&num);//接收的是num的地址,是一个整型
fp = □//把这个函数名squre赋给指针fp,其实函数名就是指这个函数的地址啦,square = &square
printf("%d * %d = %d\n",num,num,(*fp)(num));
return 0;
}
一个例子:将函数指针作为参数传入
#include <stdio.h>
int add(int,int);
int sub(int,int);
int calc(int (*fp)(int,int),int,int);
int add(int num1,int num2)
{
return num1 + num2;
}
int sub(int num1,int num2)
{
return num1 - num2;
}
int calc(int (*fp)(int,int),int num1,int num2)//用用函数指针作为传入的参数!!!
{
return (*fp)(num1,num2);
}
int main()
{
printf("3 + 5 = %d\n",calc(add,3,5));
printf("3 - 5 = %d\n",calc(sub,3,5));
return 0;
}
将函数指针作为返回值
#include <stdio.h>
int add(int,int);
int sub(int,int);
int calc(int (*fp)(int,int),int,int);
int (*select(char))(int,int);//(*select(char))是指一个叫select的函数,有一个参数,是char型,再仔细看
//实际上(*)(int,int)这里就相当于是一个函数指针
int add(int num1,int num2)
{
return num1 + num2;
}
int sub(int num1,int num2)
{
return num1 - num2;
}
int calc(int (*fp)(int,int),int num1,int num2)
{
return (*fp)(num1,num2);
}
int (*select(char op))(int,int)//那么这里返回了啥呢,我觉得就是返回了一个函数指针(*)(int,int)
{
switch(op)
{
case '+':return add;
case '-':return sub;
}
}
int main()
{
int num1,num2;
char op;
int (*fp)(int,int);
printf("请输入一个式子(如1+3):");
scanf("%d%c%d",&num1,&op,&num2);
fp = select(op);
printf("%d %c %d = %d\n",num1,op,num2,calc(fp,num1,num2));
return 0;
}
局部变量与全局变量
不同函数的变量不能互相访问
#include <stdio.h>
int main()
{
int i = 520;
printf("before,i = %d\n",i);
for (int i = 0;i < 10;i++)//作用范围只在for语句中,所以不会和外面的i冲突,并且也不能改变外面的i的值
{
printf("%d\n",i);
}
printf("after,i = %d\n",i);
return 0;
}
全局变量
在函数中定义的叫局部变量,在函数外定义的,叫外部变量或全局变量;
全局变量可以被本程序中其他函数所共用。
#include <stdio.h>
void a();
void b();
void c();
int count;//全局变量,它会自动初始化为0
void a()
{
count++;
}
void b()
{
count++;
}
void c()
{
int count;//注意这里定义了同名的局部变量,所以下面的count++的行为被看成局部变量count进行的,不会被其他函数记录下来
count++;
}
int main()
{
a();//记录1次
b();//记录1次
c();//未记录
b();//记录1次
printf("小郭今天被抱了%d次\n",count);//在各个函数中全局变量的行为,均被记录下来
return 0;
}
先使用全局变量,后面再定义
#include <stdio.h>
void func();
void func()
{
extern int count;//extern int意思是先跟编译器说明,这是要先使用后面才定义的全局变量,不要报错
count++;
}
int count;//全局变量,它会自动初始化为0
int main()
{
func();
printf("%d\n",count);
return 0;
}
尽量少定义全局变量,因为全局变量要保留到程序结束才释放,占用内存大;另外若与局部变量重名多,会降低可读性。
作用域与链接属性
作用域:
当变量被定义在程序的不同位置时,它的作用范围不一样,也就是作用域不一样;
C语言的作用域:
代码块作用域:{}内就是一个代码块,一般就是从定义变量开始到右边的花括号为止
文件作用域:任何在代码块外声明的标识符都具有文件作用域,作用范围是从声明位置开始到文件结尾处,函数名也具有文件作用域
原型作用域:原型作用域只适用于那些在函数原型中声明的参数名。函数在声明的时候可以不写参数的名字(但参数类型是必须要写上的),其实函数原型的参数名还可以随便写一个名字,不必与形式参数相匹配(当然这样做没有任何意义)。
函数作用域:函数语句只适用于goto语句的标签,作用域将goto语句的标签限制在同一个函数内部,以及防止出现重名标签。
定义和声明:
定义就是编译器为变量申请空间并填充值,可以理解成int i = 10,赋值了就是定义了;声明是编译器知道变量被定义在其他地方了,int i,这种没赋值的可以理解成声明,但是别的地方没有进行定义的话,也会直接赋值为0啦。
局部变量既是定义,又是声明;
定义只能一次,声明可以有多次。
链接属性:
external(外部的):多个文件中声明的同名标识符表示同一个实体,一般默认文件作用域的标识符(如全局变量、函数)都是external属性,就是允许标识符跨文件访问,无论在不同文件中声明多少次,表示的都是同一实体
internal(内部的):单个文件中声明的同名标识符表示同一个实体,用static获取这种链接属性
none(无):声明的同名标识符被当作独立不同的实体
另外,只有文件作用域的标识符才有external或internal标识符,其他作用域都是none属性。
static关键字:用来修改拥有文件作用域的标识符的链接属性,可以将其默认的external->internal
生存期和存储类型
生存期:
静态存储期:文件作业域的变量属于静态存储,会在程序关闭之前,一致占据存储空间
自动存储期:会在程序运行中变化其存储空间,或释放其存储空间
存储类型:存储变量值的内存类型
自动变量auto:代码块中声明的变量就是auto的存储类型,用关键字auto描述,形式参数,局部变量都是默认的auto存储类型,可以加也可以不加;
寄存器变量register:该变量有可能被存放于CPU的寄存器中,它与auto的一致点在于,都拥有代码块作用域,自动存储器和空链接属性,但注意寄存器变量不能被取址运算符&获取地址,因为CPU寄存器地址不允许获取;
静态局部变量static:可以用来修饰局部变量,使其获得静态存储期,本来很快被释放,现在会保存到程序结束才释放
#include <stdio.h>
void func();
void func()//按理说count会执行完一次本函数即释放
{
static int count = 0;//静态局部变量,初值默认为0,让count获得了静态存储期,不会被释放了,要等到程序结束才释放
//每次都会留下上次保留的count值,而不是重置为初值0
printf("%d\n",count);//若不加static,就全是0,若加了static,就是输出0,1,2,...,9
count++;
}
int main()
{
int i;
for (i = 0;i < 10;i++)
{
func();
}
return 0;
}
extern:
typedef:
快速排序
递归的思想用来一次次地重复过程,直到排完
#include <stdio.h>
void quick_sort();
void quick_sort(int array[],int left,int right)
{
int i = left,j = right;
int temp;
int pivot;
pivot = array[(left + right) / 2];
while (i <= j)
{
//从左到右找到大于等于基准点的元素
while (array[i] < pivot)
{
i++;
}
//从右到左找到小于等于基准点的元素
while (array[j] > pivot)
{
j--;
}
//如果i <= j,则互换
if (i <= j)
{
temp = array[i];
array[i] = array[j];
array[j] = temp;
i++;
j--;
}
}
if (left < j)//从右向左的j如果没有到达最左端,就递归下去,直到排到最左边
{
quick_sort(array,left,j);
}
if (i < right)//这是一样的,如果从左向右的i没有到达最右端,也就一直递归下去,直到排到最右边
{
quick_sort(array,i,right);
}
}
int main()
{
int array[] = {73,108,22,3,567,8907,4,23,678,890,1679,2356,76432,23456,654,3129,1008};
int i,length;
length = sizeof(array) / sizeof(array[0]);
quick_sort(array,0,length - 1);
printf("排序后的结果是;");
for (i = 0;i < length;i++)
{
printf(" %d",array[i]);//按排序后的顺序打印数组元素
}
putchar('\n');//打印换行符
return 0;
}
动态内存管理
以下都是堆函数:
malloc:申请动态内存空间
void *malloc(size);//void类型指针可以修改成任何类型的数据,如果调用失败返回为NULL,另外如果字节数参数size为0,则返回值也为NULL,但不意味着调用失败;注意,void *可以省略不写
malloc函数向系统申请分配size个字节的内存空间,并返回一个指向这块空间的指针。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
ptr =(int *)malloc(sizeof(int));//相当于为指针变量ptr申请了一个int整型的空间(4字节)
//并将这块空间的地址传给指针ptr,又因为返回的是void *指针,所以转化为int *指针
if (ptr == NULL)//判断是否调用malloc成功,不是NULL就成功
{
printf("内存分配失败");
exit(1);//退出程序,它和malloc需要加载stdlib头文件
}
printf("请输入一个整数:");
scanf("%d",ptr);//输入的值存放在为指针ptr申请的内存空间中
printf("你输入的整数是%d\n",*ptr);
free(ptr)//释放malloc申请的空间
return 0;
}
free:释放动态内存空间
void free(void *ptr);
free函数释放ptr指针参数指向的内存空间,该内存空间必须是malloc、calloc或realloc函数申请的,如果ptr指向NULL,则不执行任何操作,注意该函数不会改变ptr的值,也就是它仍指向原来的地方,只是该空间变成了非法空间,也就是ptr变成了类似于野指针。。。
!!!!!!关于内存泄露
因为C语言没有自己的GC垃圾回收机制,所以每次malloc一次空间,用完就要立刻free掉它,如果一直不释放空间并堆积下去,会耗尽内存,导致宕机!!!CPU会主动杀死该进程。
!!!!!!关于内存地址丢失
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *ptr;
int num = 123;
ptr =(int *)malloc(sizeof(int));//相当于为指针变量ptr申请了一个int整型的空间(4字节)
//并将这块空间的地址传给指针ptr,又因为返回的是void *指针,所以转化为int *指针
if (ptr == NULL)//判断是否调用malloc成功,不是NULL就成功
{
printf("内存分配失败");
exit(1);//退出程序,它和malloc需要加载stdlib头文件
}
printf("请输入一个整数:");
scanf("%d",ptr);//输入的值存放在为指针ptr申请的内存空间中,注意scanf("数据类型",地址表列),也就是按照地址输入值的!!!
//这里指针变量ptr存的就是指向的地址,这个操作相当于向这个地址里存值了
printf("你输入的整数是%d\n",*ptr); //输出是按照具体的值打印的,解引用指针ptr,就得到ptr指向的内存中存的值
ptr = #//让ptr指向num的地址处,malloc的内存就没有指针指向它了,它变成了垃圾内存。。。
printf("你输入的整数是%d\n",*ptr); //打印出来的是后面的num的值
free(ptr);//因为free只能释放malloc申请的内存空间,发现ptr指向的不是malloc申请的空间了,直接报错!!!
return 0;
}
malloc还可以申请一块任意尺寸的内存空间
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int *ptr = NULL;
int num,i;
printf("请输入待录入整数的个数:");
scanf("%d",&num);
ptr = (int *)malloc(num * sizeof(int));
for (i = 0;i < num;i++)
{
printf("请录入第%d个整数:",i+1);
scanf("%d",&ptr[i]);
}
printf("你录入的整数是:");
for (i = 0;i < num;i++)
{
printf("%d ",ptr[i]);
}
putchar('\n');
free(ptr);
return 0;
}
初始化内存空间
以mem开头的函数被编入字符串标准库,函数的声明包含在string.h这个头文件中;
memset:使用一个常量字节填充
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define N 10
int main(void)
{
int *ptr = NULL;
int i;
ptr = (int *)malloc(N * sizeof(int));
if (ptr == NULL)
{
exit(1);
}
memset(ptr,0,N * sizeof(int));//初始化一下内存空间,在申请的每个单位空间中填充常量0
for (i = 0;i < N;i++)
{
printf("%d ",ptr[i]);
}
putchar('\n');
free(ptr);
return 0;
}
memcpy:拷贝内存空间
memmove:拷贝内存空间
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int *ptr1 = NULL;
int *ptr2 = NULL;
//第一次申请的内存空间
ptr1 = (int *)malloc(10 * sizeof(int));
//进行若干操作后发现ptr1申请的内存空间不够用了
//第二次申请的内存空间
ptr2 = (int *)malloc(20 * sizeof(int));
//将ptr1的数据拷贝到ptr2中
memcpy(ptr2,ptr1,10);//memcpy(目标,源,数量);
//释放ptr1
free(ptr1);
//对ptr2申请的内存空间进行若干操作
//释放ptr2
free(ptr2);
return 0;
}
calloc:申请并初始化一系列内存空间
int *ptr = (int *)calloc(8,sizeof(int));//直接一步申请空间并初始化
等价于
int *ptr = (int *)malloc(8 * sizeof(int));//先申请空间
memset(ptr,0,8 * sizeof(int));//再初始化
realloc:重新分配内存空间
void *realloc(void *ptr,size_t size);
一般要求新分配的内存空间更大,否则可能造成数据丢失。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
int i,num,count = 0;
int *ptr = NULL;
do
{
printf("请输入一个整数(输入-1表示结束):");
scanf("%d",&num);//给num传入一个值
count++;//count自增
ptr = (int *)realloc(ptr,count * sizeof(int));//给ptr传入count个int型数值,就给ptr分配count个int所需空间
if (ptr == NULL)
{
exit(1);
}
ptr[count - 1] = num;//将num的值传给ptr
} while(num != -1);
printf("输入的整数分别是:");
for (i = 0;i < count;i++)//按顺序打印出来
{
printf("%d ",ptr[i]);
}
putchar('\n');
//释放ptr
free(ptr);
return 0;
}
宏定义
#define:宏定义相当于预编译,就是在编译之前就进行机械替换,然后宏定义不能加";"。
#include <stdio.h>
#define R 6371
#define PI 3.14
#define V PI * R * R * R * 4 / 3
int main(void)
{
printf("地球的体积是:%.2f\n",V);
return 0;
}
//含参数的宏定义
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))//为什么这里都要加()?
//因为如果输入x+1,y+1,不加括号是这样:x+1 > y+1 ? x+1 : y+1,想象一下,如果标识符优先级按顺序还好,如果结合性错误,就会出现意想不到的后果
//加了()就可以避免了
int main(void)
{
int x,y;
printf("请输入2个整数:");
scanf("%d%d",&x,&y);
printf("较大的数是%d\n",MAX(x,y));
return 0;
}
#undef:终止宏定义作用域
结构体
struct 结构体名称
{
结构体成员1;
结构体成员2;
结构体成员3;
....
};
定义结构体变量
struct 结构体名称 结构体变量名;
#include <stdio.h>
struct Book
{
char title[128];
char author[40];
float price;
unsigned int date;
char pulisher[40];
};
//} book;//在这里定义是全局变量
int main(void)
{
struct Book book;
printf("请输入书名:");
scanf("%s",book.title);
printf("请输入作者:");
scanf("%s",book.author);
printf("书名:%s\n",book.title);
printf("作者:%s\n",book.author);
return 0;
}
初始化结构体变量
struct Book book = {
"大话数据结构",
"程杰"
};
struct Book book = {
.title = "大话数据结构",
.author = "程杰"
};
#include <stdio.h>
struct Date//定义结构体变量
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;//定义Date结构体型变量
char publisher[40];
} book = {
"<大话数据结构>",
"程杰",
59.0,
{2017,11,11},//结构体就用{ , , }输入
"清华大学出版社"
};//在这里初始化全局变量book
int main(void)
{
printf("书名:%s\n",book.title);
printf("作者:%s\n",book.author);
printf("价格:%.2f\n",book.price);
printf("出版日期:%d-%d-%d\n",book.date.year,book.date.month,book.date.day);//层层索引,访问取值
printf("出版社:%s\n",book.publisher);
return 0;
}
结构体数组
定义1:
struct 结构体名
{
结构体成员;
} 数组名[长度];
定义2:
struct 结构体名
{
结构体成员;
};
struct 结构体名 数组名[长度];
初始化结构体数组:
struct Book book[2] = {
{"《大话数据结构》","程杰",59.0,{2017,11,11},"清华大学出版社"},
{"《深度学习》","伊恩",49.5,{2016,11,11},"人民邮电出版社"}
};
结构体指针
struct Book *pt;
pt = &book;//与数组不一样,,需要取址运算
通过结构体指针访问结构体成员:
(*结构体指针).成员名 //*解引用变成结构体,然后.运算,访问成员
结构体指针->成员名 //效果一样,但是方便
#include <stdio.h>
struct Date
{
int year;
int month;
int day;
};
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;//定义Date结构体型变量
char publisher[40];
} book = {
"<大话数据结构>",
"程杰",
59.0,
{2017,11,11},//结构体就用{ , , }输入
"清华大学出版社"
};//在这里初始化全局变量book
int main(void)
{
struct Book*pt;//定义结构体指针
pt = &book;//结构体指针要传入结构体的地址
printf("书名:%s\n",(*pt).title);
printf("作者:%s\n",(*pt).author);
printf("价格:%.2f\n",(*pt).price);
printf("出版日期:%d-%d-%d\n",(*pt).date.year,(*pt).date.month,(*pt).date.day);//结构体指针访问结构体的成员变量1
printf("出版社:%s\n",(*pt).publisher);
printf("********************分隔符***********************\n");
printf("书名:%s\n",pt->title);
printf("作者:%s\n",pt->author);
printf("价格:%.2f\n",pt->price);
printf("出版日期:%d-%d-%d\n",pt->date.year,pt->date.month,pt->date.day);//结构体指针访问结构体的成员变量2
printf("出版社:%s\n",pt->publisher);
return 0;
}
结构体赋值
#include <stdio.h>
int main(void)
{
struct Test
{
int x;
int y;
} t1,t2;
t1.x = 3;//结构体成员变量可以直接赋值
t1.y = 4;
t2 = t1;//同类型的结构体之间也可以直接赋值
printf("t2.x = %d,t2.y = %d\n\n",t2.x,t2.y);
}
传递结构体变量
#include <stdio.h>
//声明一个结构体Date
struct Date
{
int year;
int month;
int day;
};
//声明一个结构体Book
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;//结构体嵌套
char publisher[40];
};
//定义一个函数getInput
struct Book getInput(struct Book book)//这里的返回值类型是struct Book,函数名getInput,形参为Book型结构体,命名为book
{
printf("请输入书名:");
scanf("%s",book.title);//这里可以直接对传入的形参的变量进行修改
printf("请输入作者:");
scanf("%s",book.author);
printf("请输入价格:");
scanf("%f",&book.price);
printf("请输入日期:");
scanf("%d-%d-%d",&book.date.year,&book.date.month,&book.date.day);
printf("请输入出版社:");
scanf("%s",book.publisher);
return book;//这个值返回给b
}
//定义一个函数printBook
void printBook(struct Book book)
{
printf("书名:%s\n",book.title);
printf("作者:%s\n",book.author);
printf("价格:%.2f\n",book.price);
printf("日期:%d-%d-%d\n",book.date.year,book.date.month,book.date.day);
printf("出版社:%s\n",book.publisher);
}
//main函数
int main(void)//这里也可以看出传入参数列表是void,返回值类型为int,函数名是main
{
struct Book b;//定义一个Book型结构体变量b
printf("输入一本书的信息...\n");
b = getInput(b);//这是结构体实参,传递给函数getInput的结构体形参,在这里Book型结构体变量接收到了getInput返回出来的结构体book
putchar('\n'); //打印换行
printf("录入完毕,现在开始打印验证...\n");
printf("打印一本书的信息...\n");
printBook(b);//相当于又是调用函数printBook,一样的传参,但是这次不需要返回值
return 0;
}
关于结构体指针怎么用
#include <stdio.h>
//声明一个结构体Date
struct Date
{
int year;
int month;
int day;
};
//声明一个结构体Book
struct Book
{
char title[128];
char author[40];
float price;
struct Date date;//结构体嵌套
char publisher[40];
};
//定义结构体指针
void getInput(struct Book *book);
void printBook(struct Book *book);
//定义一个函数getInput
void getInput(struct Book *book)//指针接收地址
{
printf("请输入书名:");
scanf("%s",book->title);//对指针指向的结构体的成员变量进行输入赋值
printf("请输入作者:");
scanf("%s",book->author);
printf("请输入价格:");
scanf("%f",&book->price);//
printf("请输入日期:");
scanf("%d-%d-%d",&book->date.year,&book->date.month,&book->date.day);
printf("请输入出版社:");
scanf("%s",book->publisher);
}
//定义一个函数printBook
void printBook(struct Book *book)//关于结构体指针
{
printf("书名:%s\n",book->title);//给对应的变量输入赋值
printf("作者:%s\n",book->author);
printf("价格:%.2f\n",book->price);
printf("日期:%d-%d-%d\n",book->date.year,book->date.month,book->date.day);
printf("出版社:%s\n",book->publisher);
}
//main函数
int main(void)
{
struct Book b;
printf("输入一本书的信息...\n");
getInput(&b);//为指针传入结构体地址
putchar('\n');
printf("录入完毕,现在开始打印验证...\n");
printf("打印一本书的信息...\n");
printBook(&b);
return 0;
}
单链表
一个节点[信息域(存放节点的内容)+指针域(用来指向下一个节点)];
先是头指针指向第一个节点,节点类似于数组里的元素,包括信息域和指针域,信息域用来存放内容,指针域则是用来指向下一个节点的,直到最后一个节点的指针域指向NULL结束。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
//定义一个单链表
struct Book
{
char title[128];//信息域
char author[40];
struct Book *next;//指针域,指向一个和它一模一样的节点
};
void getInput(struct Book *book)//接收到实参,开始对各变量进行传值
{
printf("请输入书名:");
scanf("%s",book->title);//传入值
printf("请输入作者:");
scanf("%s",book->author);
}
//头插法在链表中插入一个节点
void addBook(struct Book **library)//这是一个指向头指针的指针,这里想要传入的是头指针library的地址 、
//指针要修改谁的值,就传入谁的地址
//int *pb = &book;//这其实就是传入book的地址,那如果可以改变book的地址,自然可以改变对应的book的内容了,如果book是一个指针,就是上面的情形了
{
struct Book *book,*temp;//定义出来结构体指针,*book是指新创的节点指针,而*temp是用来中转的临时节点
book = (struct Book *)malloc(sizeof(struct Book));//为book申请了内存空间,其实就是一个地址
if (book == NULL)
{
printf("内存分配失败");
exit(1);
}
getInput(book);//把指针变量中保存的地址“它址”作为实参传给getInput的指针形参,book指向的内存地址中有值了!
if (*library != NULL)//如果头指针不指向NULL,说明链表中已经有节点了,这时候指向新节点,要用*temp来临时保存老节点,防止丢失它的地址
{
temp = *library;//library保存的是老节点指针(结构体指针)地址,解引用出来就是老节点指针,传给临时指针变量temp(也是结构体指针)
*library = book;
book->next = temp;
}
else//若头指针指向的是NULL,说明链表为空
{
*library = book;//此时就让头指针直接指向book节点
book->next = NULL;//book的next指向NULL,也就是指针域指向NULL
}
}
void printLibrary(struct Book *library)
{
struct Book *book;
int count = 1;
book = library;
while (book != NULL)
{
printf("Book%d:",count);
printf("书名:%s",book->title);
printf("作者:%s",book->author);
book = book->next;
count++;
}
}
void releaseLibrary(struct Book *library)
{
while (library != NULL)
{
free(library);
library = library->next;
}
}
int main(void)
{
struct Book *library = NULL;//头指针(指向指针的指针),指向的是NULL,表示空的单链表,可以理解成library指针变量中了保存下一个节点(节点就是指针)的地址,对library一层解引用可以得到下一节点的值
int ch;
while (1)
{
printf("请问是否需要录入书籍信息(Y/N):");
do
{
ch = getchar();
} while (ch != 'Y' && ch != 'N');
if (ch == 'Y')
{
addBook(&library);//要改动头指针的值(头指针的指向),要给指向头指针的指针传入头指针的地址进行修改
}
else
{
break;
}
}
printf("请问是否需要打印图书信息(Y/N):");
do
{
ch = getchar();
} while (ch != 'Y' && ch != 'N');
if (ch == 'Y')
{
printLibrary(library);
}
releaseLibrary(library);
return 0;
}
typedef
给数据类型起别名:typedef int integer; typedef int *ptrint;//像这样可以替换基本数据类型和指针
它与#define的机械替换不同,它就是直接对数据类型进行替换,并且这种替换是封装过的
还可以给结构体起别名:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct Date
{
int year;
int month;
int day;
} DATE;//给结构体起别名
int main(void)
{
DATE *date;//可以用别名啦
date = (DATE *)malloc(sizeof(DATE));
if (date == NULL)
{
printf("内存分配失败\n");
exit(1);
}
date->year = 2022;
date->month = 3;
date->day = 16;
printf("今天的日期是:%d-%d-%d",date->year,date->month,date->day);
return 0;
}
共用体
union 共用体名称
{
共用体成员1;
共用体成员2;
共用体成员3;
......
};
共用体是多个成员使用同一个地址,同一时间只能使用一个成员;共用体所需空间至少要可以存储最大成员
#include <stdio.h>
#include <string.h>
union Test//共用体可以没有名称
{
int i;
double pi;
char str[6];
};//声明共用体
int main(void)
{
union Test test;//定义共用体变量
//union Test test = {520};//初始化第一个值
//union Test test = {.i = 520};//初始化指定成员
//union Test test = test1;//共用体传递初始化
test.i = 520;
test.pi = 3.14;
strcpy(test.str,"FishC");
printf("add of test.i:%p\n",&test.i);//事实上,他们的地址一样
printf("add of test.pi:%p\n",&test.pi);
printf("add of test.str:%p\n",&test.str);
printf("add of test.i:%d\n",test.i);//打印值会互相覆盖
printf("add of test.pi:%.2f\n",test.pi);
printf("add of test.str:%s\n",test.str);//只有最后这个值可以正确打印
return 0;
}
枚举类型
如果变量只有几种可能的值,就可以将其定义为枚举类型。
声明枚举类型:
enum 枚举类型名称{枚举值名称,枚举值名称,...};
定义枚举变量:
enum 枚举类型名称 枚举变量1,枚举变量2,...;
#include <stdio.h>
#include <time.h>
int main(void)
{
enum Week {sun,mon,tue,wed,thu,fri,sat};//这里的mon,tue等叫枚举常量,不赋值就默认从0开始初始化
enum Week today;//定义这个枚举变量,它可以有以上几种取值
struct tm *p;
time_t t;
time(&t);
p = localtime(&t);
today = p->tm_wday;
switch(today)
{
case mon:
case tue:
case wed:
case thu:
case fri:
printf("工作\n");
case sat:
case sun:
printf("放假\n");
default:
printf("error\n");
}
}
C++特性
命名空间的使用(嵌套,合并,以及命名污染)
namespace xjt
{
int printf = 1;
int rand = 2;
int Add(int a,int b)
{
return a + b;
}
namespace xjt2 //命名空间namespace的嵌套
{
int a = 0;
int Sub(int a,int b)
{
return a - b;
}
}
}
namespace xjt //相同名称的namespace会默认合并到一起
{
int a = 3;
int b = 1;
}
#include <iostream>
int main()
{
printf("%p\n",xjt::rand); //注意printf是默认取的命名空间中的printf的地址的
}
//另外需要注意,直接采用using namespace xjt;会默认所有变量首选为命名空间中的变量,如果命名空间中有的变量是与关键字重合的,会造成污染。
//那我们就只需要完成using namespace xjt::rand;指定好要用的命名就行了,不要盲目全部导入
函数小tips
#include <iostream>
using namespace std;
void func(int a,int b = 1); //声明函数时,添加了默认值
void func(int a,int b) //定义函数时就不要再修改默认值了,不然g++编译器会编译错误
{
cout << a << endl;
cout << b << endl;
}
int main()
{
func(1,1);
func(1);//效果与func(1,1)是一样的,因为缺省了b = 1
}
函数重载
#include <iostream>
using namespace std;
//对形参个数或数据类型不同的函数来说,可以根据传入的实参来实现重载函数的调用
void func(double a)
{
cout << a << endl;
}
//但注意,只有返回类型不同的函数并不能实现函数重载
void func(int a,int b)
{
cout << a << endl;
cout << b << endl;
// cout << c << endl;
}
int main()
{
func(1.1);
func(1,1);
}
引用&
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int &b = a; //引用a,也就是给a取个别名b,但是b没有自己开辟出内存空间,它用的还是a的内存空间,由此可知,改变b的话,a也会跟着改变
//但是注意,在定义的时候&才是取别名,在其他地方&b指的是取出b的地址
cout << a << endl;
cout << b << endl;
b = 2; //可以改变一下b的值实验一下看看
cout << a << endl;
cout << b << endl;
}
指针(解引用)*
#include <iostream>
using namespace std;
int main()
{
int a = 1;
int *c = &a;//&引用其实就是取址符,就是给定变量,取其地址,这里相当于把&a也就是a的地址,赋给指针变量c,c存储的就是a变量的地址了
cout << c << endl;//这里其实会输出指针变量c的值,也就是a变量的内存地址
cout << &a << endl;//可以和上面对比一下,是一样的值
cout << (int *)&a << endl;//&a是a的地址,将其转换为指针变量的类型,也就是变成一个地址的格式,(int *)不在乎其后面的数值是什么,作用只是获取与该数值相等的地址而已,如果已经是地址了,就不必再加(int *)了
cout << *c << endl;//*在这里其实就是取值符,什么取值符,就是给定地址,取出该地址上的变量值,*又叫解引用
cout << a << endl;//c作为储存a的地址的指针变量,对其进行解引用得到的结果就是a的值
}
基础知识:
变量就是开辟的空间的内容,对变量进行处理,就是对所开辟的内存空间的内容进行处理;
64位平台:
整型:short 2bytes,int 4bytes,long 8bytes
字符型:char 1bytes(计算机只认识它的ASCII码值,不管是啥,都看成ASCII码)
实型(浮点数):float 4bytes,double 8bytes,指数类型123e3,指的是123*10^3
有符号数和无符号数:
有符号数(默认有符号数),最高位为1为负数,最高位为0为正数,这个位不算数据,后面的7位用来计算数据;
如1111 1111,为-127,0111 1111,为127,又因为1000 0000和0000 0000都分别为-0和+0,-0没有意义,所以直接用1000 0000来代表-128,所以八位二进制的取值范围-128~127;
无符号数(unsigned),所有位都是数据位,0000 0000~1111 1111,代表的范围就是0~255。
十六进制-->二进制:
0x1d3c,十六进制的一位换算成二进制四位,c=12=8+4+0+0=1100,3=0+0+2+1=0011,d=13=8+4+0+1=1101,1=0+0+0+1=0001,故而
0x1d3c = 0001 1101 0011 1100。
无符号数:原码==反码==补码;
有符号数的正数:原码==反码==补码
有符号数的负数:反码 == 原码符号位不变,其他按位取反,补码==反码+1
补码的意义:统一了0的编码。+-0的补码一致,均为0000 0000,解决了正负数原码相加出错的问题,补码就不会出错
#include <iostream>
#include <bitset>
using namespace std;
int main(int argc,char *argv[])
{
// cout默认以十进制输出
cout<<0b00001010<<endl;
cout<<0123<<endl;
cout<<0xab<<endl;
// cout需要用bitset<位数>(数值),一般输出位数包括8,16,32
cout<<bitset<16>(-10)<<endl; //以16位二进制形式输出-10,即1111111111110110
//cout需要用oct来输出八进制
cout<<oct<<0123<<endl;
//cout需要用hex输出十六进制
cout<<hex<<0xab<<endl;
return 0;
}
'\0'空字符,'\b'删除
"abc"中有四个字符,分别是'a','b','c','\0',出现'\0'就代表字符串结束。
关键字:
const int data; //const修饰data为只读变量,必须被初始化,且不能被赋值
存储在"符号常量表"中,不会立即给data开辟空间,对data取地址时,才会为他开辟空间,
#include <iostream>
#include <string>
using namespace std;
int main(){
int a = 10;
const int data = a; //当用变量对const变量初始化时,data就不会进入符号常量表,用常量对const变量初始化,data就会进入符号常量表暂存
int *p = (int *)&data;
*p = 2000;//a变量肯定就是指向a变量的地址对应的内存,p指针变量保存的是a变量的地址,*p变量就是指向p变量中存储的地址对应的内存,所以*p与a等价
cout << "*p=" << *p << endl;
cout << "data=" << data << endl;//若符号常量表中有值,就输出符号常量表中的值,没有进入符号常量表,就输出地址中的值
};
寄存器:
register int data = 0;//data暂存进入cpu中的寄存器内
不能对寄存器变量取地址,因为它不在内存中。。。
对于使用频率高的变量,我们采用寄存器变量,这样就不用总是与内存交互了,可以速度快点。
但是,也不一定就会放进寄存器,这其实是编译器决定的,修饰符只能作为参考,对于频繁使用的变量,即便没有register修饰,也会被编译器放入寄存器中。
强制访问内存:
volatile int data = 0;
假如某个变量频繁使用,那么就会被编译器放入寄存器中,然后很长时间才会与内存交互一次,这对于内存中变量会快速更新的情况来说,寄存器中的数据可能无法及时被同步过来,导致数据出现误差。
这目的就是防止编译器的自动优化。。。
sizeof关键字:
用来测量数据类型的内存长度。
typedef给已有的类型取个别名:
先声明,再使用,最后定义,只能用于全局变量
#include <iostream>
using namespace std;
//提前变量声明
extern float l_var;
int main(){
//先使用
cout<<"l_var"<<l_var<<endl;
};
//后定义
float l_var = 0;
对于多文件编译来说,需要用extern来声明另一个文件中定义的变量或函数,这样才能在本文件中调用另一个文件中的变量或函数~
结构体作为参数,再在函数内对该参数进行去sizeof()容易出问题