(一)C的基本类型:包括两种类型:整数类型和浮点类型。
类型 存储大小 值范围
char 1 字节 -128 到 127或 0 到 255
unsigned char 1 字节 0 到 255
signed char 1 字节 -128 到 127
int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
long 4 字节 -2,147,483,648 到 2,147,483,647
unsigned long 4 字节 0 到 4,294,967,295
float 4 字节 1.2E-38 到 3.4E+38 6 位小数
double 8 字节 2.3E-308 到 1.7E+308 15 位小数
long double 16 字节 3.4E-4932 到 1.1E+4932 19 位小数
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。您可以使用 sizeof 运算符。
表达式 sizeof(type) 得到对象或类型的存储字节大小。
计算各数据类型存储大小
#include <stdlib.h>
int main()
{
char c = 'a';
short i = 88;
int k = 888;
long m = 9999;
float f = 1.28f;
double d = 3.689;
printf("char内存大小:%d\n",sizeof(c));
printf("short内存大小:%d\n", sizeof(i));
printf("int内存大小:%d\n", sizeof(k));
printf("long内存大小:%d\n", sizeof(m));
printf("float内存大小:%d\n", sizeof(f));
printf("double内存大小:%d\n", sizeof(d));
system("pause");
return 0;
}
(二)枚举类型:它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
enum{
monday=10,//默认为0 赋值后为10
saturday//默认为1 赋值后为11
};
int main(){
printf("%d \n",monday);
system("pause");
return 0;
}
(三)void类型:类型说明符 void 表明没有可用的值。
通常用于以下三种情况下:
1.函数返回为空 例如 void exit (int status);
2.函数参数为空 例如 int rand(void);
3.指针指向 void 例如,内存分配函数 void *malloc( size_t size )
(四)派生类型:指针类型、数组类型、结构类型、共用体类型和函数类型。
printf()函数
printf() 用于格式化输出到屏幕。printf() 函数在 “stdio.h” 头文件中声明。
printf()函数的调用格式为:printf(“<格式化字符串>”, <参量表>);
输出格式如下:
d 以十进制形式输出带符号整数(正数不输出符号)
o 以八进制形式输出无符号整数(不输出前缀0)
x,X 以十六进制形式输出无符号整数(不输出前缀Ox)
u 以十进制形式输出无符号整数
f 以小数形式输出单、双精度实数
e,E 以指数形式输出单、双精度实数
g,G 以%f或%e中较短的输出宽度输出单、双精度实数
c 输出单个字符
s 输出字符串
p 输出指针地址
lu 32位无符号整数
数组的定义
// C 语言的数组,在申明的时候就必须确定大小和基本类型。
int[10] intArray; //java
int intArray[10]; //C
数组是存储在stack 栈 ,stack的大小是有限制的,超过限制则需要动态申请数组存放在堆里面
//申请40M数组空间 4byte *1024 *1024 *10 = 40M
int intArray=(int )malloc(sizeof(int) * 1024 * 1024 * 10);
指针
int main(){
int a = 10;
int * p=&a;//p代表int *指针 *p代表int型数据a的值
*p = *p + 10;
printf("*p:%d,a:%d\n",*p,a);
int y = 1 + *p;
printf("y:%d\n",y);
*p += 1;
printf("*p:%d,a:%d\n", *p, a);
(*p)++;//*p所指向的内存地址的变量值 加1
printf("*p:%d,a:%d ,p的地址:%#x\n", *p, a,p);
*p++;//p的值 内存地址增加一个类型单位
printf("*p:%d,a:%d ,p的地址:%#x\n", *p, a, p);
system("pause");
return 0;
}
输出值为:
*p:20,a:20
y:21
*p:21,a:21
*p:22,a:22 ,p的地址:0xeffe84
*p:-858993460,a:22 ,p的地址:0xeffe88
注意:& 取地址操作符。运用在内存中的对象上面,即变量与数组元素
* 间接寻址或者引用运算符
数组与指针
注意:数组变量名就是数组的首地址,通过数组下标所能完成的任何操作都可以通过指针来实现。
int main(){
int a[5];
int *p = a;//将指针p指向数组的首地址
int i;
//a+5表示a移动到数组的第五个位置 每移动一个地址的长度为变量类型所占的位数 并不是数字加1
//通过指针给数组赋值
for ( i = 0; p < a+5; p++)
{
*p = i;
i++;
}
//打印数组元素
for ( i = 0; i < 5; i++)
{
printf("a[%d]:%d \n",i,a[i]);
}
system("pause");
return 0;
}
指针变量
int main(){
int a = 10;
int *p = &a;
//p存储的是变量a的内存地址
printf("p的值:%#x\n",p);
p++;//这里p存储的内存地址增加了一个int型大小的值 int 4字节 p的值+4
printf("p的值:%#x\n", p);
char c = 'a';
char *cp = &c;
printf("cp的值:%#x\n", cp);
cp++;//这里char 代表一个字节 cp的值+1
printf("cp的值:%#x\n", cp);
system("pause");
return 0;
}
输出值如下:
p的值:0xcffb8c
p的值:0xcffb90
cp的值:0xcffb77
cp的值:0xcffb78
指针和函数参数
实现两个数的交换
//c语言中函数 参数为值传递 a,b为新的变量与调用函数时的a,b不相同 不能实现交换原值
void swap(int a,int b){
int temp;
temp = a;
a = b;
b = temp;
}
//将对指针变量a,b的地址进行了切换 指针指向内存地址的值没有改变
void swap2(int * a,int *b){
int *temp;
temp = a;
a = b;
b = temp;
}
//将指针a,b所指向内存地址上面的值进行了改变 可以实现交换
void swap3(int * a, int *b){
int temp;
temp = *a;
*a = *b;//将指针a所指向的内存地址上的值 修改为指针b所指向的变量的值
*b = temp;
}
int main(){
int a, b;
a = 10;
b = 5;
printf("a:%d,b:%d\n",a,b);
swap(a,b);//c值传递 不能实现交换
printf("交换后的值a:%d,b:%d \n", a, b);
swap2(&a,&b);//不能实现交换
printf("交换后的值a:%d,b:%d \n", a, b);
swap3(&a, &b);//交换成功
printf("交换后的值a:%d,b:%d \n", a, b);
system("pause");
return 0;
}
输出值为:
a:10,b:5
交换后的值a:10,b:5
交换后的值a:10,b:5
交换后的值a:5,b:10
注意:函数必须先声明再使用或者函数定义在使用的前面,才能正确调用
指针数组和数组指针
指针数组: 是一个数组 里面存储的是指针类型的数据
数组指针: 是一个指针 ,这个指针指向了数组的首地址
//函数先声明再使用
void sort(char*name[],int n);
int main(){
//定义指针数组 存储的是char类型指针的一组数据
char *name[] = {"sxx","dss","bww","wee"};
sort(name,4);
//打印排序后的数组
for (int i = 0; i < 4; i++)
{
printf("%s\n",name[i]);
}
//数组指针 指针p2指向的数组的首地址
char c[5] = {'a','b','c','d','e'};
//char(*p2)[5] = c;//c数组第一个元素的首地址
char(*p2)[5] = &c;//&c 是整个数组的首地址
system("pause");
return 0;
}
//冒泡排序
void sort(char *name[], int n){
char *temp;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n-1-i; j++)
{
//strcmp字符串首字母比较大小
if (strcmp(name[j],name[j+1])>0){
temp = name[j];
name[j] = name[j + 1];
name[j + 1] = temp;
}
}
}
}
二级指针
二级指针,存储的值是一个一级指针的内存地址
int main(){
int a = 100;
int *p=&a;
//二级指针存储的是一级指针地址 *p2得到的是p的值 **p2得到的是a的值
//**p2==*(*p2)==*p==a
int **p2 = &p;
printf("a的值:%d\n",a);
printf("*p的值:%d\n",*p);
printf("**p2的值:%d\n",**p2);
system("pause");
return 0;
}
输出值为:
a的值:100
*p的值:100
**p2的值:100
函数指针
函数名是函数在内存中的首地址,可以将函数赋值给对应类型的指针
void 类型的指针:类似于java object
void* 包括 int * / char * /float*等
int plus(int a,int b){
return a + b;
}
int minus(int a,int b){
return a - b;
}
int plus2(int *a,int *b){
return *a + *b;
}
int minus2(char *a,char *b){
return 0;
}
int main(){
int a = 8;
int b = 10;
int result;
//函数指针
int(*calc)(int a, int b);
int(*calc2)(void *a,void*b);
//calc = plus;
calc = minus;
result = calc(3, 5);
//calc2 = (int(*)(void *,void *))plus2;//将void*强制类型转换int*
calc2 = (int (*)(void *, void *))minus2;
result = calc2(&a,&b);
printf("minus:%#x\n", minus);
printf("result:%d\n", result);
system("pause");
return 0;
}
注意:下面两个表达式完全不同 取决于运算符优先级
int (calc2) (void *a, void ); //函数指针
int* p(int a, int b); //一个返回值是int * 的函数 等同于(int*) p(int a, int b)
内存分配
分成三个区存储
程序区:存储程序代码二进制文件
静态存储区:存储全局变量和静态变量
动态存储区:
栈区:内存由编译器自动分配,自动释放
堆区:主要用于程序动态分配内存控件,需要手动释放内存
动态申请内存的方法
void* malloc(size_t size)//分配内存的单元是 字节, 大小 size,分配连续的内存如果申请失败,返回值是NULL
void* calloc(size_t _Count,size_t _Size)//申请 _Count 个 大小为_Size 的连续空间,这个连续空间的大小是_Size,整块空间可能不连续,如果申请失败,返回值是NULL
同时,它会初始化值为0
void * realloc(void * ptr, int len); //不常用
动态申请的内存一定要收到释放
free(*p)释放内存
注意:申请的内存必须手动释放,并置为NULL,不能重复释放 ,申请和释放要一一对应
C语言中的字符串
c语言中没有字符串类型,c语言中的字符串都用字符数组和字符指针表示
操作字符串方法
char *strcpy(char *destin, char *source);
拷贝一个字符串到另一个
char *strcat(char *destin, char *source);
字符串拼接函数
char *strchr(char *str, char c);
在一个串中查找给定字符的第一个匹配之处
int strcmp(char *str1, char *str2);
串比较 看Asic码,str1>str2,返回值 > 0;两串相等,返回0
int strncmpi(char *str1, char *str2, unsigned maxlen);
将一个串中的一部分与另一个串比较, 不管大小写
int stricmp(char *str1, char *str2);
以大小写不敏感方式比较两个串
int strcspn(char *str1, char *str2);
在串中查找第一个给定字符集内容的段
char *strdup(char *str);
将串拷贝到新建的位置处
char *strerror(int errnum);
返回指向错误信息字符串的指针
char *strnset(char *str, char ch, unsigned n);
将一个串中的所有字符都设为指定字符
char *strpbrk(char *str1, char *str2);
在串中查找给定字符集中的字符
char *strrchr(char *str, char c);
在串中查找指定字符的最后一个出现
char *strrev(char *str);
串倒转
double strtod(char *str, char **endptr);
将字符串转换为double型值
long strtol(char *str, char **endptr, int base);
将串转换为长整数
char *strupr(char *str);
将串中的小写字母转换为大写字母
void swab (char *from, char *to, int nbytes);
交换字节
int main(){
//'\0'表示结束 不再读取后面的字符
char ch[] = { 'c', 'h', 'i', 'n', 'a', '\0' };
char ch1[10] = {'c','h','i','n','a','\0','r'};
char ch2[20] = "china";//编译器默认会在末尾添加‘\0’
ch2[0] = 's';//改变元素值
/*char * ch3 = "china";
//系统分配一个常量区 , char * ch3没有初始化, 将常量区 "china"的首地址赋值给ch3
//这里不能对ch3的内容进行修改 因为ch3中存储的是常量,常量是不可修改的
ch3[0] = 's';*/
//初始化一个指针
char * ch3 = (char *)malloc(sizeof(char));
//使用strcpy方法给ch3赋值
strcpy(ch3,"china");
ch3[0] = '2';
printf("ch:%s\n",ch);
printf("ch1:%s\n", ch1);
printf("ch2:%s\n", ch2);
printf("ch3:%s\n", ch3);
system("pause");
return 0;
}
输出内容如下:
ch:china
ch1:china
ch2:shina
ch3:2hina
结构体
一系列不同类型的数据的结合
结构体名代表的只是结构体类型,没有内存空间, 结构体中的成员可以单独使用
struct Student
{
char name[20];
int age;
} Lucy = {"Lucy",20};//全局
//匿名结构体 锁定结构体的变量数量 只能在定义时声明变量
struct
{
char name[20];
int age;
int classId;
} stu3,stu4;
int main(){
//结构体初始化
struct Student stu1 = {"Lucy",20};
struct Student stu2;
strcpy(stu2.name, "张三");
stu2.age = 88;
printf("%s,%d \n",stu1.name,stu1.age);
printf("%s,%d \n", stu2.name, stu2.age);
system("pause");
return 0;
}
结构体数组
struct Student{
char name[20];
int age;
};
int main(){
//以结构体数据为单位根据数据位置匹配
struct Student stu[3] = { { "lucy", 30 }, { "lilei", 32 }, { "hann", 35 } };
struct Student s[5];
//结构体数组赋值
for (int i = 0; i < 5; i++)
{
s[i].age = 100 + i;
strcpy(s[i].name,"aa");
}
//输出结构体数组的值
for (int i = 0; i < 5; i++)
{
printf("name:%s,age:%d\n",s[i].name,s[i].age);
}
system("pause");
return 0;
}
结构体指针
struct Student{
char name[20];
int age;
};
int main(){
struct Student stu[3] = { { "lucy", 30 }, { "lilei", 32 }, { "hann", 35 } };
//结构体指针
//struct Student *stud=stu;
//初始化结构体指针
struct Student *stud;
stud = (struct Student *)malloc(sizeof(struct Student) * 4);
printf("%#x",stud);
memset(stud,0,sizeof(struct Student)*4);//赋值为0
for (int i = 0; i < 4; i++)
{
//给结构体成员age赋值 ->
(stud+i)->age = 100 + i;
//给结构体成员name赋值 ->
strcpy((stud+i)->name, "bb");
/*stud[i].age = 100 + i;
strcpy(stud[i].name,"cc");*/
}
for (int i = 0; i <4; i++)
{
printf("name:%s,age:%d\n", (stud+i)->name, (stud+i)->age);
}
system("pause");
return 0;
}
在结构体中添加函数指针成员变量
struct Man{
int age;
char *name;
int(*Msg)(char *,int);//函数指针
};
//打印信息
int message(char*str,int age){
printf("str:%s,age:%d\n",str,age);
return 0;
}
int main(){
struct Man man;
man.age = 40;
man.name = "李健";
man.Msg = message;//将函数首地址函数名message赋值给指针
//调用函数
man.Msg(man.name,man.age);
system("pause");
return 0;
}
在结构体中添加结构体指针变量
实现单链表
//定义一个结构体
struct Node{
int data;
Node * next;//结构体指针
};
//在单链表的末尾添加一个数据
int enqueNode(Node * head,int data){
//创建新的节点
struct Node * node = (struct Node *)malloc(sizeof(struct Node));
//可能申请不到内存 判空
if ( node == NULL){
return 0;
}
//给新节点赋值next 为NULL代表是最后一个节点
node->data = data;
node->next = NULL;
//找到最后一个节点
Node *p = head;
while (p->next!=NULL){
p=p->next;//切换到下一个节点
}
//将找到的最后一个节点next指向新建的节点
p->next = node;
return 1;
}
int main(){
Node * list;
list = (Node*)malloc(sizeof(Node));
list->data = 0;
list->next = NULL;
//从最后添加数据
for (int i = 0; i < 10; i++)
{
enqueNode(list,i+1);
}
//遍历打印数据
while (list->next != NULL){
printf("%d\n",list->data);
list = list->next;
}
system("pause");
return 0;
}
typedef指令
取别名,并没有创建新的数据类型,只是给现有类型创建了别名
typedef int _in;
typedef char * string;//定义一个string字符串别名
typedef int(*PFI)(char *, char *);
typedef Tnode * Treeptr;
int fun(char *, char *){
return 0;
};
typedef struct Tnode{
char *word;
int count;
/*Tnode * left;
Tnode * right;*/
Treeptr left;
Treeptr right;
} BinaryTreeNode;//结构体别名
int main(){
_in a = 20;
printf("a:%d\n",a);
string str = "hello world";
printf("str:%s\n", str);
PFI pf= fun;
BinaryTreeNode *node = (BinaryTreeNode *)malloc(sizeof(BinaryTreeNode));;
system("pause");
return 0;
}
union共用体
将不同的数据类型数据放在同一块内存上 占用内存为成员中占用最大的那一个内存 大小
//占用的内存为f占用的内存大小
union MyUnion{
int a;
char c;
float f;
};
int main(){
MyUnion uni;
uni.a = 10;
uni.c = 'a';//覆盖之前a的值
uni.f = 1.33f;//覆盖之前a,c值
//成员之间存储内存地址相同
printf("a:%#x,c:%#x,f:%#x \n",&uni.a,&uni.c,&uni.f);
printf("a: %d, c: %c, f:%f \n", uni.a, uni.c, uni.f);
system("pause");
return 0;
}
文件操作
文件分两部分:1.控制信息 2.内容信息;文本文件图片文件等都是二进制文件
读取文件过程:从磁盘上读取 根据文件控制信息解码到文件缓冲区 再加载到内存空间
文本文件读取操作
int main(){
//文件读取
char * path = "C:\\Users\\lenovo\\Desktop\\test.txt";
//如果存在文件则打开,不存在则新建文件 path文件路径 "r"表示读文件操作
FILE *fp = fopen(path,"r");
char buff[1024];
//fgets获取内容 存储到buff数组中 每次获取1024个单位数据
while (fgets(buff, 1024, fp))
{
printf("%s",buff);
}
//关闭文件流
fclose(fp);
//写文件
char * path = "C:\\Users\\lenovo\\Desktop\\test.txt";
//如果存在文件则打开,不存在则新建文件 path文件路径 "w"表示写文件操作
FILE *fp = fopen(path, "w");
if (fp==NULL){
printf("file is null");
return 0;
}
char *text = "今天下大雨,冷哦";
//写入文本
fputs(text,fp);
//关闭文件流
fclose(fp);
system("pause");
return 0;
}
二进制文件读写操作
int main(){
char * read_path = "C:\\Users\\lenovo\\Desktop\\LogViewPro.exe";
char * write_path = "C:\\Users\\lenovo\\Desktop\\LogViewPro_write.exe";
//获取需要读取的文件 rb读二进制文件 wb写二进制文件
FILE *readFile = fopen(read_path,"rb");
FILE *writeFile = fopen(write_path,"wb");
char buff[50];
int len = 0;
//fread读取文件
while ((len = fread(buff, sizeof(char), 50, readFile))!=0)
{ //fwrite写入文件 每次写入的长度为读取的数据长度
fwrite(buff,sizeof(char),len,writeFile);
}
//关闭文件流
fclose(readFile);
fclose(writeFile);
system("pause");
return 0;
}
获取文件大小
int main(){
char * read_path = "C:\\Users\\lenovo\\Desktop\\a.jpg";
FILE *fp = fopen(read_path,"r");
if (fp==NULL){
return 0;
}
//fseek移到文件末尾(SEEK_END)并偏移0 指向文件末尾
fseek(fp,0,SEEK_END);
//fseek移动到相对于SEEK_SET位置偏移100
//fseek(fp,100,SEEK_SET);
//通过ftell获取文件大小
long fileSize = ftell(fp);
printf("%ld\n",fileSize);
system("pause");
return 0;
}
对文件简单的加密解密实例
void encode(char normal_path[],char encode_path[]){
FILE* normal_fp = fopen(normal_path,"r");
FILE* encode_fp = fopen(encode_path,"w");
int ch;
//循环判断是否到文件末尾
while ((ch=fgetc(normal_fp))!=EOF)
{
fputc(ch^7,encode_fp);
}
fclose(normal_fp);
fclose(encode_fp);
}
void decode(char encode_path[], char decode_path[]){
FILE* encode_fp = fopen(encode_path, "r");
FILE* decode_fp = fopen(decode_path, "w");
int ch;
//循环判断是否到文件末尾
while ((ch = fgetc(encode_fp)) != EOF)
{
fputc(ch ^ 7, decode_fp);
}
fclose(decode_fp);
fclose(encode_fp);
}
int main(){
char *normal_path = "C:\\Users\\lenovo\\Desktop\\test.txt";
char *encode_path = "C:\\Users\\lenovo\\Desktop\\test_encode.txt";
char *decode_path = "C:\\Users\\lenovo\\Desktop\\test_decode.txt";
encode(normal_path,encode_path);
decode(encode_path,decode_path);
system("pause");
return 0;
}
预处理
C 语言执行的流程:
一,组成程序的每个源文件通过编译过程分别转换成目标代码(object code)
二,各目标文件由连接器 捆绑在一起,形成一个单一而完整的可执行文件
而编译过程又由如下过程组成:
1.预处理器处理,在这个阶段,预处理器在源代码上执行一些文本操作。例如:用实际值代替由 #define指令定义的符号以及读入由#include指令包含的文件的内容。
2.源代码经过解析,判断它的语句的意思,这个阶段会产生绝大多数错误和警告信息的地方,随后便生成目标代码。
执行阶段:
1.程序必须载入到内存中。
2.程序执行
3.程序执行的最后一个阶段就是程序终止。
Define指令
定义:”#define 标识符 字符串”
“#define宏名(参数) 字符串”
意义:就是宏替换的作用,让 标识符替换 字符串,这只是一种简单的文本替换,预处理程序对它不作任何检查。如有错误,只能在编译已被宏展开后的源程序时发现。
注意事项:
宏名一般用大写字母表示,以便于与变量区别。
宏定义末尾不必加分号,否则连分号一并替换。
宏定义可以嵌套。
宏定义不分配内存,变量定义分配内存。
预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
“#include <> 或者 #include “” 区别”
< > 和 “ ” 区别在于: 使用<>表示在包含文件目录中去查找 (包含目录是由用户在设置环境时设置的include 目录“解决方案管理器 -> 属性-> 配置属性 -> VC++ 目录”),而不是在当前源文件目录去寻找; 使用双引号就是先从当前源文件目录中查找。
条件编译
“#ifdef #endif 成对出现”
第一种方式
“#ifdef 标识符 (或者#if defined)”
程序段1
“#else ”
程序段2
“#endif”
或者
“#ifdef 标识符 (或 #if defined 标识符)”
程序段
“#endif”
第二种方式
“#ifndef”
#else
“#endif”
第三种方式
“#if”
“#else”
“#endif”
在实践中的作用:
1.屏蔽跨平台差异
在大规模开发过程中,特别是跨平台和系统的软件里,可以在编译时通过条件编译设置编译环境。
“#ifdef WINDOWS”
#define MYTYPE long
“#else”
#define MYTYPE float
“#endif”
2.包含程序功能模块
“#ifdef FLV”
Include “fastleavec”
“#endif”
当不许向别的用户提供该功能,则在编译之前将首部的FLV 加一下横线即可
3.开关调试信息
4.避开硬件的限制
有时一些具体的应用环境的硬件不同,但限于条件本地缺乏这种设备,可绕过硬件直接写出预期结果
5.防止头文件重复包含
头文件可以被头文件或者C文件包含。由于头文件包含可以嵌套,C文件就有可能多次包含同一个文件; 或者不同的C文件都包含同一个头文件,编译时就可能出现重复包含的问题。
#include "A.txt"//自定义头文件
#define NUM 5//宏定义 等价替换 NUM=5
#define MAX(x,y) (x>y?x:y)//将MAX(x,y)替换成(x>y?x:y)
#define M 10
//条件编译
//#ifndef M
#ifdef M
printf("%d\n",110);
#else
printf("%d\n",120);
#endif
printf("结束");
int main(){
for (int i = 0; i < NUM; i++)
{
printf("%d\n",i);
}
int max = MAX(8,5);
printf("max:%d\n", max);
system("pause");
return 0;
}