自学c语言
C 语言是一种通用的、面向过程式的计算机程序设计语言。
当前最新的 C 语言标准为 C18
前期准备
C 编译器 写在源文件中的源代码是人类可读的源。它需要"编译",转为机器语言,这样 CPU 可以按给定指令执行程序。(.c-->.exe)
下载Dev-C++ 图标长这样哦别下载错了
编写代码
1 #include <stdio.h> 2 int main(){ 3 printf("Hello World!"); 4 //system("pause"); //暂停程序 不会一闪而过 5 return 0; 6 }
保存为 hello.c 文件
进入代码目录
win+r cmd 打开命令提示符 cd到程序目录执行编译 gcc hello.c 会生成hello.exe 文件
想用vs code开发C语言点击这里
数据类型
1 基本类型:
它们是算术类型,包括两种类型:整数类型和浮点类型。
2 枚举类型:
它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
3 void 类型:
类型说明符 void 表明没有可用的值。
4 派生类型:
它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型。
存储类
存储类定义 C 程序中变量/函数的可用范围和生命周期。说明符应放置类型之前
1.auto
局部变量的默认存储类, 规定变量只能在函数内部使用
2.register
定义存储在cpu寄存器中而不是 RAM(内存) 中的局部变量
3.static
局部变量时进入和离开作用域不会进行创建和销毁
全局变量时会使变量的作用域限制在声明它的文件内
4.extern
在一个文件中声明一个全局变量或函数
#include <stdio.h> // 函数外定义变量 x 和 y int x; int y; int add(){ // 函数内声明变量 x 和 y 为外部变量 extern int x; extern int y; // 给外部变量(全局变量)x 和 y 赋值 x = 4; y = 2; return x+y; } int main() { int result; result = add(); //文件编码UTF8时中文乱码 应设为GBK printf("result 为:%d=%d+%d",result,x,y); return 0; }
运算符
7个 算术运算符 关系运算 符逻辑运算 符位运算符 赋值运算符 杂项运算符
#include <stdio.h> //不用第三个变量 A^B,异或交换值 // 这种利用位运算的交换方法只适用于整型变量,不能用于浮点型变量 int main() { int A = 60;//二进制 0011 1100 int B = 13;//二进制 0000 1101 A = A^B;//A=0011 0001 B = A^B;//B=0011 1100 A = A^B;//A=0000 1101 // A^=B^=A^=B; //简写 printf("原A=60 B=13,现A=%d,B=%d",A,B); return 0; }
enum(枚举)
一种基本数据类型 enum 枚举名 {枚举元素1,枚举元素2,……};
第一个枚举成员的默认值为整型的 0,后续枚举成员的值在前一个成员上加 1。
可以把第一个枚举成员的值定义为 1,第二个就为 2,以此类推
#include <stdio.h> #include <stdlib.h> // 枚举类型 int main() { enum Colors { red=1, blue, green }; enum Colors color; printf("%d是红色,%d是蓝色,%d是绿色,请输入数字选择喜欢的颜色",red,blue,green); scanf("%u",&color); switch(color){ case red: printf("你喜欢红色\n"); break; case blue: printf("你喜欢蓝色\n"); break; case green: printf("你喜欢绿色\n"); break; default: printf("没有你喜欢的颜色\n"); } system("pause"); }
C指针
指针指向内存地址的变量,指针变量存放的是普通变量的内存地址
int var = 20; /* 实际变量的声明 */ int *ip; /* 指针变量的声明 */ ip = &var; /* 在指针变量中存储 var 的地址 */
*和&都是一元运算符
&var 返回的是变量var在内存中的地址
*ip 返回的是指向的变量var(20)的值
普通指针和数组指针:
#include <stdio.h> // 指针 &a=获取变量地址 *a=指针变量 int main() { int var = 12; int *ip = NULL; ip = &var; printf("var 变量地址:%p \n",&var); printf("ip 变量存储的地址:%p \n",ip); printf("ip 变量地址:%p \n",&ip); printf("*ip 变量的值:%d \n",*ip); system("pause"); // int var = { // 地址:000000000062FE14 // 值:12 // } // &var = var地址 // int *ip = { // 值:NULL // 地址:000000000062FE10 // } // ip = &var // *ip = var值 = 12 // ip = &var = var地址 //指针与数组 const int MAX = 3; int arr[] = {10,100,200}; int i,*ptr[MAX]; for(i=0;i<MAX;i++){ ptr[i] = &arr[i]; } for(i=0;i<MAX;i++){ printf("var[%d] 地址:%p || %p,值:%d \n",i,ptr[i],&arr[i],*ptr[i]); } system("pause"); return 0; }
函数指针:
#include <stdio.h> // 函数指针 int max(int x,int y){ return x>y?x:y; } int main() { int (*p)(int,int) = &max; //&可以忽略 int a,b,c,d; printf("请输入3个数字:"); scanf("%d %d %d",&a,&b,&c); d = p(p(a,b),c); printf("最大数为:%d \n",d); system("pause"); }
字符串
C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。\0 是用于标记字符串的结束
#include <stdio.h> #include <string.h> // 字符串 int main() { char arr[6] = {'h','e','l','l','o','\0'}; printf("%s \n",arr); char str[] = "hello"; printf("%s \n",str); char str1[] = "字符串1",str2[] = "字符串2",str3[5]; // 操作字符串的函数 需要引入<string.h>标准库 strcpy(str3,str1);//将str1复制到str3 printf("str3=%s \n",str3); strcat(str3,str2);//将字符串str2链接到str3末尾 printf("str3=%s \n",str3); int len = strlen(str3);//返回str3的长度 printf("str3的长度为%d \n",len); int cmp = strcmp(str3,str1);//如果 str1,str3相同的,返回 0;str1<str3 返回小于 0;str1>str3 返回大于 0。 printf("cmp=%d \n",cmp); char *chr,ch='1';//ch为一个字符 // str3="字符串1字符串2" chr = strchr(str3,ch);//返回一个指针 字符ch 第一次出现的位置 printf("chr=%s \n",chr);//返回 "1字符串2" char s[2] = "串";//汉字一般占2个字节(系统或编码方式不一样可能占3个字节) char *ss = strstr(str3,s);//返回一个指针,指向字符串 str3字符串 s 的第一次出现的位置 printf("\'串\'出现在str3的位置为:%s \n",ss); system("pause"); }
结构体
用struct定义,自定义数据类型用来存储不同类型的数据成员
struct 结构名 { 数据类型1; 数据类型2; ... } 变量1,变量2 ... ;
结构体变量的初始化,访问结构成员,结构作为函数参数,指向结构的指针 例子:
#include <stdio.h> #include <string.h> // 结构体 struct Books//生成结构体Books { char title[50]; char author[50]; char subject[50]; int book_id; } book;//声明结构变量book void printBook(struct Books book); void setBook(struct Books *b,char title[],char author[],char subject[],int book_id); int main() { //初始化book变量 strcpy(book.title,"百科全书"); strcpy(book.author,"小明"); strcpy(book.subject,"啥东西都有"); book.book_id = 1; struct Books book1;//声明结构变量 book1 setBook(&book1,"十万个为啥","小红","啥东西都有解释",2); printBook(book); printf("\n"); printBook(book1); system("pause"); } //通过指向结构变量的指针 "->"访问并初始化结构变量 void setBook(struct Books *b,char title[],char author[],char subject[],int book_id) { strcpy(b->title,title); strcpy(b->author,author); strcpy(b->subject,subject); b->book_id = book_id; } //结构作为参数 void printBook(struct Books book) { printf("标题:%s \n",book.title);//book.title 访问结构成员 printf("作者:%s \n",book.author); printf("主题:%s \n",book.subject); printf("ID:%d \n",book.book_id); }
共用体
可以在相同的内存位置定义个带有多成员的共用体,只能有一个成员带有值
union 共用体名 {
数据类型1;
数据类型2;
...
} 变量1,变量2 ... ;
使用例子:
#include <stdio.h> #include <string.h> //共用体 int main() { // 声明共用体 union Data{ int i; float f; char c[10]; }; // 定义共用体 变量 union Data data; // 初始化共用体 数据成员 data.i = 123; printf("i:%d \n",data.i);//正常输出 123 data.f = 20.3; printf("f:%f \n",data.f);//正常输出 20.299999 strcpy(data.c,"1234567890"); printf("c[10]:%s \n",data.c);//正常输出1234567890 // 访问共用体数据成员 printf("\ni:%d \n",data.i);//i内存位置被c[10]替换输出数据有问题 875770417 printf("f:%f \n",data.f);//f内存位置被c[10]替换输出数据有问题 0.000000 printf("c[10]:%s \n",data.c);//内存位置被替换为 正常输出1234567890 system("pause"); }
位域
1000 1000 这是8位(bit) = 1字节(byte/B)
有些信息存储时,不需要占一个完整的字节(8位),而只需占几个或1个二进制位(如:0000 0001 只需要最后1位)
所以c语言提供了一种数据结构,称为"位域"或"位段,可以设置变量用多少位
struct 位域结构名 { //type 只能为int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型 type [member_name] : width ; type [member_name] : width ; ... };
这种结构在内存中占8个字节
但是我们a和b可能只存储 0或1 只需要1位,多余的空间就会浪费。 所以就需要用到位域
使用位域后,这个结构体就占用4字节(32位)。每个变量占1位 ,还剩余30位,可以分配给剩余结构体中变量
#include <stdio.h> // 位域 int main() { // unsigned int类型默认是2字节或4字节 struct { unsigned int a:1; unsigned int b:1; } status; struct { unsigned int a; unsigned int b; } status1; printf("status size:%dB \n",sizeof(status));//status size:4B printf("status1 size:%dB \n",sizeof(status1));//status size:8B // status.a = 2;//因为a只分配了1位 2等于二进制0010 所以会报错 status.a = 1;//1等于二进制0001 printf("status.a:%d \n",status.a); system("pause"); }
typedef 和 #define
typedef:为类型取一个新的名字
#define:为各种数据类型定义别名
不同点
- typedef 仅限于为类型定义符号名称,#define 不仅可以为类型定义别名,也能为数值定义别名,比如您可以定义 1 为 ONE。
- typedef 是由编译器执行解释的,#define 语句是由预编译器进行处理的。
#include <stdio.h> // typedef 关键字为类型取一个新的名字 int main() { typedef unsigned int BYTE;//定义unsigned int别名为 BYTE BYTE A; unsigned int a; #define zx int //定义int别名为zx #define PI 3.14 //定义常量 zx b;
A = 2; a = A; b = a;
printf("%d == %d \n",A,a); printf("%d,PI=%f\n",b,PI); system("pause"); }
输入输出
C 语言把所有的设备都当作文件。所以设备(比如显示器)被处理的方式与文件相同。
#include <stdio.h> // 输入输出 int main() { int a; char s[50]; // printf("Enter a value:"); // scanf("%s",&s);//输入 从标准输入流 stdin 读取输入 // printf("value = %s \n",s);//输出 写入到标准输出流 stdout printf("Enter a char:"); a = getchar();//屏幕读取下一个可用的字符,并把它返回为一个整数 printf("you entered char:\n"); putchar(a);//把字符输出到屏幕上,并返回相同的字符。 printf("\n"); // char str[100]; // printf("Enter a string: \n"); // gets(str);//从 stdin 读取一行到 s 所指向的缓冲区,直到一个终止符或 EOF // printf( "\nYou entered: "); // puts(str);//字符串 s 和一个尾随的换行符写入到 stdout system("pause"); }
文件读写
不管是文本文件还是二进制文件,都是代表了一系列的字节。
打开文件 FILE *fopen( const char * filename, const char * mode );
filename 是字符串,文件名, mode 是下列值中的一个
r | 打开一个已有的文本文件,允许读取文件。 |
w | 打开一个文本文件,允许写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会从文件的开头写入内容。如果文件存在,则该会被截断为零长度,重新写入。 |
a | 打开一个文本文件,以追加模式写入文件。如果文件不存在,则会创建一个新文件。在这里,您的程序会在已有的文件内容中追加内容。 |
r+ | 打开一个文本文件,允许读写文件。 |
w+ | 打开一个文本文件,允许读写文件。如果文件已存在,则文件会被截断为零长度,如果文件不存在,则会创建一个新文件。 |
a+ | 打开一个文本文件,允许读写文件。如果文件不存在,则会创建一个新文件。读取会从文件的开头开始,写入则只能是追加模式。 |
"rb", "wb", "ab", "rb+", "r+b", "wb+", "w+b", "ab+", "a+b"
#include <stdio.h> //文件的读写 int main() { FILE *fp = NULL; // 写入文件 fp = fopen("./13.txt","w");// "w" 打开一个文本文件,允许写入文件。如果文件不存在会创建一个新文件 fputc(2,fp); //把一个字符写入到流中 fprintf(fp,"这是 创建的文件\n"); //把一个字符串写入到文件中 fputs("这是 fputs 输入的内容\n",fp); //把字符串 s 写入到 fp 所指向的输出流中 成功返回一个非负值,发生错误,返回 EOF fclose(fp);//关闭文件 char buff[255]; // 读取文件 fp = fopen("./13.txt","r");//打开一个已有的文本文件,允许读取文件 char c = fgetc((FILE *)fp);//从 fp 所指向的输入文件中读取一个字符 printf("0:%d \n",c); fscanf(fp,"%s",buff);//读取字符串,但是在遇到第一个空格和换行符时,它会停止读取 printf("1:%s \n",buff); fgets(buff,255,(FILE *)fp);//从 fp 所指向的输入流中读取 n - 1 个字符 printf("2:%s \n",buff); fgets(buff,255,(FILE *)fp); printf("3:%s \n",buff); // 0:2 // 1:这是 // 2: 创建的文件 // 3:这是 fputs 输入的内容 fclose(fp); return 0; }
预处理器
相当于文本替换工具,会在编译器实际编译之前执行
14.c
#include <stdio.h> // 预处理器 #include "14.h" //包含一个源代码文件 int main() { //定义宏 #define PI 3.14 //把所有 PI 替换成3.14 #define uInt unsigned int //把所有 uInt 替换成 unsigned int uInt a = 222; #undef PI //取消已定义的宏 #define PI 3.1415926 //重新定义PI printf("%f,%d \n",PI,a); myFun(); #ifndef MESSAGE //如果宏没有定义,则返回真 #define MESSAGE "success" #endif printf("MESSAGE = %s \n",MESSAGE); return 0; } void myFun() { printf("This is 14.h a=%d,arr=%s \n",a,arr); }
14.h
int a = 4; char arr[] = "hello word"; void myFun();
头文件
头文件是扩展名为 .h 的文件,包含了函数声明,宏定义,变量。被多个源文件中引用共享。有两种类型的头文件:程序员编写的,编译器自带。
#include <file.h> #include "file.h"
强制类型转换
强制类型转换是把变量从一种类型转换为另一种数据类型
#include <stdio.h> // 强制类型转换 int main() { int a = 6; char b = 'b'; float c = a + b;//a 强制转换为 6.000000 b 强制转换为 98('b'的ASCII=98) printf("a=%d+b=%c,c=%f \n",a,b,c); system("pause"); }
内存管理
C分配和管理函数。这些函数在 <stdlib.h>头文件
void *calloc(int num, int size);//分配了 num*size 个字节长度的内存,每个字节值都初始化为0 void free(void *address);//释放 address 所指动态分配的内存空间 void *malloc(int num);//堆区分配指定大小的内存空间,不会初始化值 void *realloc(void *address, int newsize);//内存扩展到 newsize
使用malloc分配内存
#include <stdio.h> #include <stdlib.h> #include <string.h> int main() { char name[100]; char *desc; strcpy(name,"weilian tom"); desc = (char *)malloc(200 * sizeof(char));//动态分配内存 它的值不会初始化 printf("%p \n",desc); if(desc == NULL){ fprintf(stderr,"Error - unable to allocate required memory\n"); }else{ strcpy(desc,"weilian tom is a famous professional clown actor \n"); } // free(desc);//释放内存 printf("name:%s\ndesc:%s \n",name,desc); system("pause"); }
命令行参数
执行程序时,在命令行传值给程序
传入的参数是由main函数处理的,argc是参数个数 argv是参数列表
#include <stdio.h> // 命令行参数 int main(int argc, char *argv[]) { printf("有%d个参数 \n",argc); int i; for(i=0;i<argc;i++) { printf("第%d个参数是:%s \n",i+1,argv[i]); } }
执行程序后 第一个参数总是程序名
时间格式化和定时器函数
#include <stdio.h> #include <time.h> // 标准库 time.h void delayed(void (*fun)()); void parseTime(); int main() { //time_t这是一个适合存储日历时间类型 time_t curtime;//声明time_t格式变量 printf("%d",curtime); time(&curtime);//计算当前日历时间,并把它编码成 time_t 格式 printf("%s",ctime(&curtime));//ctime 返回表示当地时间的字符串 delayed(&parseTime); return 0; } // 获取当前时间并格式化 void parseTime(){ time_t ct; time(&ct); // 格式化时间 struct tm *nowTime = localtime(&ct);// 返回本地时区的值来填充 tm 结构体 //年tm_year 月tm_mon 日tm_mday 时tm_hour 分tm_min 秒tm_sec printf("%d-%d-%d %d:%d:%d \n",nowTime->tm_year+1900,nowTime->tm_mon+1,nowTime->tm_mday,nowTime->tm_hour,nowTime->tm_min,nowTime->tm_sec); } // 延时函数 void delayed(void (*fun)()){ while (1) { static int t1,t2; if(t2-t1 > 1000){ fun(); t1 = t2; } t2 = clock();//返回程序执行起到现在使用的时间 } }