1Byte = 8 Bit
1KB = 1024Byte = 210Byte
1MB = 1024KB = 220Byte
1GB = 1024MB = 230Byte
1TB = 1024GB = 240Byte
1PB = 1024TB = 250Byte
1EB = 1024PB = 260Byte
一个字节BYTE=8位bit
数值是以补码表示的
正数的补码与原码相同
负数的补码:将该数的绝对值的二进制形式按位取反在加1
例
10的原码:00001010
取反:11110101
再加1,得-10的补码:11110110
说 明 字符型 短整型 整型 长整型 单精度浮点型 双精度浮点型
数据类型 char short int long float double
长 度 1 2 4 4 4 8
指针固定占4个字节
获取某个数据类型的长度可以使用 sizeof 操作符
============================================================
数组:
同一个数组数据类型相同;下标从零开始A[0]/A[1]......
如果要数千亿有10个变量的数组必须用循环语句逐个输出各下标变量
for(i=0;i<10;i++)
{
printf("%d",a[i];
}
二维数组
a[3][4];
a[0]--------a00 a01 a02 a03
a[1]--------a10 a11 a12 a13
a[2]--------a20 a21 a22 a23
int a[3][4]={
{1,2,3,4},{5,6,7,8},{9,10,11,12}};
==============================================================
结构体:
基本形式
struct 结构体名{
结构体所包含的变量或数组
};
struct stu{
char *name;//姓名
int num;//学号
int age;//年龄
char group;//所在学习小组
float score;//成绩
};
首先在C中定义一个结构体类型要用
typedef struct Student
{
int a;
}Stu;
于是在声明变量的时候就可:Stu stu1;
如果没有typedef就必须用struct Student stu1;来声明。
这里的Stu实际上就是struct Student的别名。
结构体变量
struct stu stu1,stu2;
定义了两个变量stu1,stu2都是stu类型,都由5个成员组成
也可以
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在学习小组
float score; //成绩
} stu1, stu2;
赋值方法
//给结构体成员赋值
stu1.name = "Tom";
stu1.num = 12;
stu1.age = 18;
stu1.group = 'A';
stu1.score = 136.5;
//读取结构体成员的值
printf("%s的学号是%d,年龄是%d,在%c组,今年的成绩是%.1f!\n", stu1.name, stu1.num, stu1.age, stu1.group, stu1.score);
return 0;
}
//结构体
struct stu{
char *name; //姓名
int num; //学号
int age; //年龄
char group; //所在小组
float score; //成绩
} stu1 = { "Tom", 12, 18, 'A', 136.5 };
//结构体指针
struct stu *pstu = &stu1;
注意,结构体变量名和数组名不同,数组名在表达式中会被转换为数组指针,而结构体变量名不会,无论在任何表达式中它表示的都是整个集合本身,要想取得结构体变量的地址,必须在前面加&,所以给 pstu 赋值只能写作:
struct stu *pstu = &stu1;
*pointer = &stu1;
获取结构体成员
pointer->memberName
指针->所取成员
(*pointer).memberName
指针。所取成员
======================================================
union 共用体名{
成员列表
};
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。
#include <stdio.h>
union data{
int n;
char ch;
short m;
};
int main(){
union data a;
printf("%d, %d\n", sizeof(a), sizeof(union data) );
a.n = 0x40;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.ch = '9';
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.m = 0x2059;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
a.n = 0x3E25AD54;
printf("%X, %c, %hX\n", a.n, a.ch, a.m);
return 0;
}
运行结果
4, 4
40, @, 40
39, 9, 39
2059, Y, 2059
3E25AD54, T, AD54
==============================================================
关系运算符 含 义
< 小于
<= 小于或等于
> 大于
>= 大于或等于
== 等于
!= 不等于
与运算(&&)
参与运算的两个表达式都为真时,结果才为真,否则为假。例如:
5&&0
5为真,0为假,相与的结果为假,也就是 0。
2) 或运算(||)
参与运算的两个表达式只要有一个为真,结果就为真;两个表达式都为假时结果才为假。例如:
10 || 0
10为真,0为假,相或的结果为真,也就是 1。
3) 非运算(!)
参与运算的表达式为真时,结果为假;参与运算的表达式为假时,结果为真。例如:
!0
0 为假,非运算的结果为真,也就是 1。
!(5>0)
5>0 的结果是1,为真,非运算的结果为假,也就是 0。
==========================================================
switch 是另外一种选择结构的语句
switch(表达式){
case 整型数值1: 语句 1;
case 整型数值2: 语句 2;
......
case 整型数值n: 语句 n;
default: 语句 n+1;
}
例题:
#include <stdio.h>
int main(){
int a;
printf("Input integer number:");
while(1)
{
scanf("%d",&a);
switch(a)
{
case 1: printf("星期一\n"); break;
case 2: printf("星期二\n"); break;
case 3: printf("星期三\n"); break;
case 4: printf("星期四\n"); break;
case 5: printf("星期五\n"); break;
case 6: printf("星期六\n"); break;
case 7: printf("星期七\n"); break;
default:printf("请重新输入\n"); break;
}
}
return 0;
}
break 是C语言中的一个关键字,专门用于跳出 switch 语句。
如果不用break,如果输入4,就会继续执行case4后面的所有语句
default 不是必须的。当没有 default 时,如果所有 case 都匹配失败,那么就什么都不执行
============================================================
max = (a>b) ? a : b;
该语句的语义是:如a>b为真,则把a赋予max,否则把b 赋予max
while(表达式){
语句块
}
先计算“表达式”的值,当值为真(非0)时, 执行“语句块”;执行完“语句块”,再次计算表达式的值,如果为真,继续执行“语句块”……这个过程会一直重复,直到表达式的值为假(0),就退出循环,执行 while 后面的代码。
for 循环的一般形式为:
for(表达式1; 表达式2; 表达式3){
语句块
}
运行过程为:
1) 先执行“表达式1”。
2) 再执行“表达式2”,如果它的值为真(非0),则执行循环体,否则结束循环。
3) 执行完循环体后再执行“表达式3”。
4) 重复执行步骤 2) 和 3),直到“表达式2”的值为假,就结束循环。
===========================================================
%d 十进制有符号整数
%u 十进制无符号整数
%f 浮点数
%s 字符串
%c 单个字符
%p 指针的值
%e 指数形式的浮点数
%X 无符号以十六进制表示的整数
%0 无符号以八进制表示的整数
%g 自动选择合适的表示法
=======================================================
@一级指针与二级指针
例子:
一个&可以抵消一个*
int i = 10;
&i = 0x01;//设i的地址为0x01
int* pi= & i=0x01;
*pi = *(&i) = i = 10;
&pi=0x02
int** ppi = &pi =0x02;
&ppi = 0x03;
*ppi = *(&pi) = pi =&i;
**ppi = *(*ppi) =*(&i) = i;
对一级指针而言,指针变量命,代表普通变量的地址
*指针变量名,代表普通变量(值)
对二级指针变量而言,指针变量名,代表一级指针变量的地址
*指针变量名,代表普通变量的地址
**指针变量名,代表普通变量(值)
=======================================================
链表
链表是结构体变量于结构体变量连接在一起(前面的尾与后面的头相连)
动态创建一个链表:动态内存申请+模块化设计
1创建链表(创建一个表头表示整个链表)
2.创建节点
3.插入节点
4.删除节点
5.打印遍历链表(测试)
创建链表
#include <stdio.h>
#include <stdlib.h>
struct Node{
int data; //数据域
struct Node *next //指针域
};
//创建链表
struct Node* createList()
{
struct Node* headNode = (struct Node*)malloc(sizeof(struct Node));
//headNode 成为了结构体变量
//变量使用前必须被初始化
headNode->next = NULL;
return headNode;
};
//创建节点
struct Node* createNode(int data)
{
struct Node* newNode = (struct Node*)malloc(sizeof(struct Node));
newNode->data = data;
newNode->next = NULL;
return newNode;
};
//插入结点,参数:插入那个链表,插入结点的数据是多少 表头插入
void instertNodeByHead(struct Node* headNode,int data)
{
//创建插入的节点
struct Node *newNode = createNode(data);
newNode->next = headNode->next;
headNode->next = newNode;
}
//打印
void printList(struct Node* headNode)
{
struct Node *pMove = headNode->next;
while(pMove)
{
printf("%d\t",pMove->data);
pMove = pMove->next;
}
printf("\n");
}
//删除链表
void delerNodeByAppoin(struct Node *headNode,int posData)
{
struct Node* posNode = headNode->next;
struct Node* posNodeFront = headNode;
if(posNode == NULL)
printf(" 无法删除链表为空\n");
else
{
while(posNode->data != posData)
{
posNodeFront = posNode;
posNode = posNodeFront->next;
if(posNode == NULL)
{
printf("没有找到相关信息,无法删除\n");
break;
}
}
posNodeFront->next = posNode->next;
free(posNode);
}
}
int main()
{
struct Node* list = createList();
instertNodeByHead(list,1);
instertNodeByHead(list,2);
instertNodeByHead(list,3);
printList(list);
delerNodeByAppoin(list,2);
printList(list);
system("pause");
return 0;
}
=======================================================
对于文件的操作
fopen(打开文件)
FILE *fopen(const char *path,const char *mode);//(文件地址,打开方式)
FILE *fp;
fp=fopen(“C:\\Users\\1.txt”,"r+")或者(“C:/Users/1.txt”,"r+");绝对路径
=======================================================
FILE *fp;//定义一个文件指针
fp=fopen("b.txt","r+")//相对路径
=========================================================
fclose(fp;)//关闭文件
========================================================
fopen函数需要两个参数
1.代表要打开的文件路径
2.代表打来文件的方式(决定程序中可以对文件做什么操作)
打开方式有如下选择:
"r" 只能查看文件内容并且从文件头开始.要求文件已经存在,否则打开失败
"r+" 比"r"多了修改功能
"w" 只能修改文件内容并且从文件头开始.
如果文件还不存在则创建文件,如果文件已经存在则删除里面所有内容.
"w+" 比"w"多了查看功能(查看功能就是读操作,读就是可以用printf打印出来,如果w没有+printf就打印不出来)
"a" 只能在文件末尾追加新内容,如果文件不存在则创建新文件,如果文件存在不对原有内容做任何修改
"a+" 比"a"多了查看功能
"b" 也是一种打开方式.它可以和上面任何一种打开方式混合使用
这种方式表示以二进制的方式对文件进行操作
注:所有文件都可以采用二进制方式进行操作.文本文件还可以采用另外一种方法进行操作(字符操作).
=================================================================
fread函数可以对文件采用二进制方式进行读操作
fwrite函数可以对文件采用二进制方式进行写操作(这两个函数可以处理任何文件,二进制和文本文件)
这两个函数都需要四个参数:
1.内存中第一个存储区的地址
2.内存中单个存储区的大小
3.希望操作的存储区个数
4.文件指针
它们的返回值表示实际操作的存储区个数
=================================================================
当要求一次存取一组数据(如,一个数组、一个结构体变量的值),fread和fwrite函数可以解决该类问题。
fread(buffer, size, count, fp);
fwrite(buffer, size, count, fp);
buffer:对于fread来说,指的是读入数据的存放地址;对于fwrite来说,是要输出数据的地址。
size:读写数据时,每笔数据的大小
count:读写数据的笔数
fp:文件指针
//文本文件
#include<stdio.h>
#include<string.h>
int main()
{
int arr[] = {5,2,85,27,945},num = 0;
char buf[10] = {};
FILE *p_file = fopen("a.txt","wb"); //二进制方式对文件操作
if(p_file)
{
for(num = 0;num <= 4;num++)
{
//将数字转换成一组字符存在字符数组中
sprintf(buf,"%d ",arr[num]);
fwrite(buf,sizeof(char),strlen(buf),p_file);
}
//printf("%s\n",buf);
fclose(p_file);
p_file = NULL;
}
}
注:sprintf函数可以将数字按照格式打印在字符串中.
//二进制文件
#include<stdio.h>
int main()
{
int arr[] = {45,23,943,426,345},size = 0;
FILE *p_file = fopen("a.bin","wb");
if(p_file)
{
size = fwrite(arr,sizeof(int),5,p_file); //size为返回值
printf("一共写入%d个存储区\n",size);
fclose(p_file);
p_file = NULL;
}
return 0;
}
示例程序:编写程序从二进制文件中得到所有数据并打印在屏幕上.
//从二进制文件中得到所有数据,并打印在屏幕上
#include<stdio.h>
int main()
{
int arr[5] = {},size = 0,num = 0;
FILE *p_file = fopen("a.bin","rb");
if(p_file)
{
size = fread(arr,sizeof(int),5,p_file);
printf("一共获得%d个存储区\n",size);
for(num = 0;num <= 4;num++)
{
printf("%d ",arr[num]);
}
printf("\n");
fclose(p_file);
p_file = NULL;
}
return 0;
}
=================================================================
文件操作练习:
1.编程程序从键盘得到多个人员信息并记录到文件中.人员信息包括id,salary和name.
人员信息个数不确定,文件内容可以追加.
2.把文件中所有人员信息打印在屏幕上.
#include<stdio.h>
#include<string.h>
typedef struct
{
int id;
float salary;
char name[10];
}person;
int main()
{
int choice;
person prs = {};
FILE *p_file = fopen("person.bin","ab");
if(p_file)
{
do{
printf("请输入id:");
scanf("%d",&(prs.id));
printf("请输入工资:");
scanf("%g",&(prs.salary));
//清理回车换行
scanf("%*[^\n]");
scanf("%*c");
printf("请输入姓名:");
fgets(prs.name,10,stdin);//stdin从键盘获取输入 (10)获取N-1个字符
if(strlen(prs.name) == 9 && prs.name[8] != '\n')
{
scanf("%*[^\n]");
scanf("%*c");
}
fwrite(&prs,sizeof(person),1,p_file);
printf("是否需要输入下一个人员信息?0表示不需要,1表示需要\n");
scanf("%d",&choice);
}while(choice);
fclose(p_file);
p_file = NULL;
}
return 0;
}
//把文件中所有人员信息打印在屏幕上
#include<stdio.h>
typedef struct
{
int id;
float salary;
char name[10];
}person;
int main()
{
person prs = {};
FILE*p_file = fopen("person.bin","rb");
if(p_file)
{
//fread函数返回值作逻辑表达式
while(fread(&prs,sizeof(person),1,p_file))
{
printf("id是%d\n",prs.id);
printf("工资是%g\n",prs.salary);
printf("姓名是%s\n",prs.name);
}
fclose(p_file);
p_file = NULL;
}
return 0;
}
=================================================================
fgets(...)从标准设备读数据。
gets(s,n,stdin);
fgets(prs.name,10,stdin);// prs是一个数组 name是其中一个变量
s: 字符型指针,指向存储读入数据的缓冲区的地址。
n: 从流中读入n-1个字符
=================================================================
字符写入函数 fputc
int fputc ( int ch, FILE *fp );
ch 为要写入的字符,fp 为文件指针。
fputc() 写入成功时返回写入的字符,失败时返回 EOF,返回值类型为 int 也是为了容纳这个负数。
例如:fputc('a', fp);
或者:char ch = 'a';
fputc(ch, fp);
例题
#include<stdio.h>
int main(){
FILE *fp;
char ch;
//判断文件是否成功打开
if( (fp=fopen("D:\\demo.txt","wt+")) == NULL )
{
puts("Fail to open file!");
exit(0);
}
printf("Input a string:\n");
//每次从键盘读取一个字符并写入文件
while ( (ch=getchar()) != '\n' )
{
fputc(ch,fp);
}
fclose(fp);
return 0;
}
while((ch=getchar())!='\n')意思就是输入的字符不是换行符,则为真,执行while里的语句块。
ch=getchar(),输入一个字符且将该值赋予给变量ch;ch=getchar())!='\n'
意思是ch不等于换行符。 ()圆括号的优先级>不等于。
getchar是读入函数的一种。它从标准输入里读取下一个字符,相当于getc(stdin)。返回类型为int型,为用户输入的ASCII码或EOF
==================================================================================
冒泡排序法
是把数字两两比较;
数字大的下沉,数字小的上浮,经过第一轮排序最后取出下沉到最下面的数字,剩下的数字继续比较,如此循环直到最后一个元素;
#include <stdio.h>
#define ARR_LEN 255 //数组长度上限
例题:
//冒泡排序
//从当前元素起,向后依次比较每一对相邻元素,若逆序则交换
//对所有元素均重复以上步骤,直至最后一个元素
// arr[]:排序目标数组;int len:元素个数
void bubbleSort(int arr[],int len)
{
int temp;
int i,j;
for(i=0;i<len-1;i++)//外循环为排序趟数,len个数进行len-1趟
for(j=0;j<len-1-i;j++)//内循环为每趟比较的次数,第一趟比较len-1次
{
if(arr[j]>arr[j+1]) //相邻元素比较,若逆序则交换(升序为左,降序为右)
{
temp = arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
int main()
{
int arr[ARR_LEN] = {3,5,1,-7,4,9,8,10,4,6};
int len = 10;
int i;
bubbleSort(arr,len);
for(i=0;i<len;i++)
printf("%d\t",arr[i]);
putchar('\n');
return 0;
}
================================================================================================
其他排序方法
1.直接选择排序(简单选择排序):
是把序列中最小的元素比较出来放在序列头部,然后剩下的元素按此方法继续比较选择;
2.直接插入排序
其方法是将序列XXXXXX(X代表元素)看作两部分,用A隔离这两个部分
如XaXXXX,a为已经排好位置的元素,A后为未排好位置的元素,然后依次取出a后的元素与a前元素比较,并放在合适的位置
初始为XaXXXX;之后为XXaXXX;
3.希尔排序(缩小增量排序)//直接插入改进版
方法是取间隔a的数进行比较,这个a称为增量,若前数大于后数,则交换位置,从头到尾完成后;
再按照a/2进行比较,若a为奇数,则按照(a+1)/2计算,知道增量为a=1时,比较完a=1这一趟结束排序
4.快速排序(在冒泡法上进行改进)
序列arr,标记头尾为i ,j,设标记量temp = arr[i] 也就是arr[0]
j开始 --,直到有arr[j]<temp,此时j找到位置了,站在此处不动,i开始动
i开始++,直到有arr[i]>temp,此时i找到位置了,站在此处不动,j再开始动
直到j与i相遇了,哎此时相遇位置的数与temp进行交换
第一次排序结束,相遇位置左侧元素便均小于temp,右方均大于temp
利用这种方法对两侧进行递归操作,直到temp实在无法拆分左右停止,次数的序列便排序完成。
6.堆排序,堆排序是基于堆的概念上设计的排序算法
堆,一般常用的也就是二叉堆,二叉堆的可以形象的比喻成一颗 完全二叉树。
而二叉堆中分为大顶堆和小顶堆。
大顶堆是节点处的值大于等于左右孩子,小顶堆是节点处的值小于等于左右孩子。
一般升序采用大顶堆,降序采用小顶堆
将待排序序列构造成一个大顶堆,此时,整个序列的最大值就是堆顶的根节点。将其与末尾元素进行交换,此时末尾就为最大值。然后将剩余n-1个元素重新构造成一个堆,这样会得到n个元素的次小值。如此反复执行,便能得到一个有序序列了
说白了,先将序列看作是一个完全二叉树,从最后一个非叶子节点开始,
(1)将结点及其叶子,进行比较,把最大的换到结点处,这样一层层的把最大的节点拱到了堆顶
(2)将堆顶元素跟末尾元素进行交换,这样就将最大值放到了序列最后
(3)此时将剩余数看作完全二叉树进行如此计算,一步一步的将最大值拱出来,剩下一个节点无左右孩子的时候,排序完成。
7.桶排序,又称桶排序
其方法主要是:
(1)从序列中找出最大值N,设置一个数组arr,并使其所有值为0,长度为N+1,其数组下标0--N也就代表了这N+1个桶
(2)将所有数据放入他所匹配的下标桶,并在放入时使得此处arr得值加1表示计数
(3)根据桶的下标取出所存在的元素,若某个桶得值为0,则直接跳过,通过桶得下标排序,通过桶的值判断有多少元素在这个桶里存放
(4)排序结束,出现结果