准备
目标:快速学习c语言
编程工具:腾讯在线编译器 Cloud Studio 官网
条件:具备其他编程语言的基础
Clould Studio的使用
① 进入上面的网址
② 注册绑定完完账号,点击产品,点击Cloud Studio
③ 新建一个文件夹(可省略此步骤),新建一个文件,必须以.c结尾
④ 完成语言的编写后,在控制台输入 gcc xxx.c 回车,再次输入./a.out 回车,即可运行你的c语言
C语言的应用
① 操作系统
② 语言编译器
③ 汇编器
④ 文本编辑器
⑤ 打印机
⑥ 网络驱动器
⑦ 现代程序
⑧ 数据库(MySQL)
⑨ 语言解释器
⑩ 实体工具
你好!世界!
#include <stdio.h>
int main() {
printf("123"); /* 格式化输出 */
return 0; /*退出程序
}
- 程序的第一行是预处理命令,告诉程序编译之前要包含stdio.h文件
- 下一行是主函数main,函数从这里开始执行
- 注释使用 /* xxxx */
C的令牌和规则
printf("123");
1. c的令牌可以是关键字,标识符,常量,字符串值,或者是一个符号
2. 这5个令牌分别是:
printf
(
"123"
)
;
3.c的注释
//单行注释
/* 单行注释 */
/*
多行注释
多行注释
多行注释
*/
4.c的标识符
不能带标点字符,如@,%,#这些,而且c是区分大小写的
C的关键字
//关键字不能作为常量名,方法名或者其他标识符名称
auto //声明自动变量
break //跳出当前循环
case //开关语句分支
char //声明字符变量或者返回值类型
const //声明只读变量
continue //结束当前循环进行下一轮循环
default //开关语句分支的默认分支
do //循环语句的循环体
double //双精度浮点数,和char都是小数类型,但它的准确度更高
else //否定分支,与if连用
enum //声明枚举类型
extern //声明变量或者函数是在是它文本的位置定义
float //单精度浮点数
for //一种循环语句
goto //无条件跳转语句
if //条件语句
int //整型
long ///长整型
register //声明寄存器变量
return //返回语句,可以带参数,也可以不带
short //短整型
signed //有符号变量
sizeof //计算数据类型或者变量长度(即所占字节数)
static //声明静态变量
struct //声明结构体类型
switch //switch...case循环语句
typedef //用于给数据类型起别名
unsigned //声明无符号类型
union //声明共用体类型
void //声明无返回值,无指针类型
volatile //说明变量在程序执行中被隐含的改变
while //while循环关键字
//为什么我这里罗列出来是因为可以对c的语法有一个大概的印象
C的数据类型
① 在 C 语言中,数据类型指的是用于声明不同类型的变量或函数的一个广泛的系统。变量的类型决定了变量存储占用的空间,以及如何解释存储的位模式。
C中的数据类型可以分为以下几类:
① 基本类型:整数类型 + 浮点型
② 枚举类型
③ void类型:表示无可用值
④ 派生类型:指针类型、数据类型、结构类型、共用体类型、函数类型
⑤ 数组类型和函数类型叫做集合类型,函数类型指的是函数的返回值类型
整数类型:
//使用sizeof(xxx)关键字取得数据类型的字节数
#include <stdio.h>
#include <limits.h>
int main() {
printf("int类型的字节数为:%lu \n",sizeof(int)); //取得int类型的字节数 ,输出结果为4
return 0;
}
浮点类型:
#include <stdio.h> /*头文件*/
#include <limits.h>
#include <float.h>
int main() {
printf("float最大值:%E \n",FLT_MAX); //%E是指以指数形式输出单双精度浮点数
printf("float最小值:%E \n",FLT_MIN);
printf("精度值 %d \n",FLT_DIG);
return 0;
}
//输出结果为:
//float最大值:3.402823E+38
//float最小值:1.175494E-38
//精度值 6
void类型
① 函数的返回类型为空,如void exit(int status);
② 函数参数为空,如 int rand(void);
③ 指针指向void,代表对象的地址,如void *malloc(size_t size); 返回的是指向void的指针
c的变量
变量及其描述:
//c中变量的定义和解释
#include <stdio.h>
int main() {
char c = 'a'; //变量的声明
float f = 123;
double db = 666;
extern int a; //二者的区别在下方解释
int b;
return 0;
}
//声明变量带extren和不带extern的却别
//extern不带是立即声明和建立存储空间,是声明和定以
//带的就是只声明不定义,没有建立相应的存储空间
//变量a又可以在别的文件中定义
//extern关键字的案例如下
#include <stdio.h>
int x; //函数外定义变量x和y
int y;
int plus() {
extern int x; //声明为外部变量
extern int y;
x = 1; //给外部变量赋值
y = 4;
return x+y; //返回俩个变量之和
}
int main() {
int res;
res = plus(); //调用plus方法
printf("res的结果为:%d",res); //输出结果为5,正确。
return 0;
}
//如果我们要在一个文件中引用另一个文件中的变量,要需要在这个变量前面加上***extern***即可
C的左值(LValues)和右值(RValues)
① 左值:指向内存位置,可出现在赋值号的左边和右边
② 右值:指的是存储到内存地址下的具体的数值,只能在赋值号右边存储
③ 如 10 = 20;,就是错误的语句,右值不能在左边
C常量
c的常量是固定值,在程序执行期间不会改变,又叫做字面量,常量定义后不可修改
① 以下是各种类型的整数常量的示例:
② 浮点类型的常量:
③转义字符
#include <stdio.h>
#define indentifier value //使用#define预处理器
#define LENGTH 20 //定义常量方式一,使用#define XXXX value 的形式
#define WIDTH 5
#define NEWLINE '\n'
int main() {
#define Ax 996
printf("Hello\?\nWorld!");
const int length = 30; //定义常量方式二,使用const type xxxx = value 的形式,二者都行
const int width = 100;
const char c = '\n';
int result;
result = length + width;
int area;
area = LENGTH * WIDTH;
printf("value of area:%c %d",NEWLINE,area);
printf("result is:%c %d",c,result);
printf("Ax value is: %d",Ax);
return 0;
}
//方法一 #define xxx value
//方法二 const type xxx = value;
//以上输出结果为:
//Hello?
//World!value of area:
//100result is:
//130Ax value is: 996
//%d是指以十进制有符号形式输出整数,正整数符号不显示
C的存储类
c的存储类的关键字是控制变量或者函数的可见性和生命周期
① auto
② register
③ static
④ extern
#include <stdio.h>
void func1(void); //定义一个方法
static int count = 10; //全局静态变量
int main() {
{
int mount; //auto是所有局部变量默认的存储类
auto int month; //auto只能用在函数内部,修饰局部变量
}
{
register int miles; //register存储类一般用于计数器,自身不能运用&运算符,因为无内存位置
}
while(count--) {
func1();
}
return 0;
}
void func1(void) { //初始化方法
static int thingy = 5; //局部静态变量
thingy++;
printf("count is %d,thingy is %d \n",count,thingy);
}
//thingy使用static修饰后,不会在每次调用时重置
//auto是所有局部变量的存储类
//register一般用于计数器,无内存地址
//extern,当你想要取得一个已定义的变量或者函数的内存地址的时候,使用它。
//extern,相当于在另一个文件中声明全局变量或函数
C的运算符
算数运算符:
#include <stdio.h>
int main() {
int a = 10;
int b = 3;
int c;
c = a + b;
printf("a+b=: %d \n",a+b);
c = a - b;
printf("a-b=: %d \n",a-b);
c = a * b;
printf("a*b=: %d \n",a*b);
c = a % b;
printf("a求余b=: %d \n",a%b);
c = a++;
printf("a++ =: %d \n",c);
c = a--;
printf("a-- =: %d \n",c);
return 0;
}
//输出结果为:
//a+b=: 13
//a-b=: 7
//a*b=: 30
//a求余b=: 1
//a++ =: 10 赋值后+1 c = 10 a = 11
//a-- =: 11 赋值后-1 c = 11,a = 10
//++和--就是先赋值还是先计算自增自减的问题,在前先计算再赋值,在后,先赋值再计算
逻辑运算符:
#include <stdio.h>
int main() {
int a = 1;
int b = 2;
if(a && b) {
printf("true\n");
}
if(a || b) {
printf("true\n");
}
a = 0;
b = 3;
if(a && b) {
printf("true\n");
}else{
printf("false\n");
}
if(a || b) {
printf("true\n");
}else{
printf("false\n");
}
if(!a) {
printf("a为0\n");
}
return 0;
}
//a && b =a和b都是不为0返回真
//a || b =a和b有一个不为0就返回真
//!取反操作
//输出的结果为:
//true
//true
//false
//true
//a为0
位运算符
#include <stdio.h>
int main()
{
unsigned int a = 60; /* 60 = 0011 1100 */
unsigned int b = 13; /* 13 = 0000 1101 */
int c = 0;
c = a & b; /* 12 = 0000 1100 */
printf("Line 1 - c 的值是 %d\n", c );
c = a | b; /* 61 = 0011 1101 */
printf("Line 2 - c 的值是 %d\n", c );
c = a ^ b; /* 49 = 0011 0001 */
printf("Line 3 - c 的值是 %d\n", c );
c = ~a; /*-61 = 1100 0011 */
printf("Line 4 - c 的值是 %d\n", c );
c = a << 2; /* 240 = 1111 0000 */
printf("Line 5 - c 的值是 %d\n", c );
c = a >> 2; /* 15 = 0000 1111 */
printf("Line 6 - c 的值是 %d\n", c );
}
//&& 操作有一个0就为0
//|| 操作有一个1就为1
//^ 相同即为0,其他都为1
//最后都是以十进制进行输出
赋值运算符
#include <stdio.h>
int main() {
int a = 20;
int c;
c = a;
printf("Line1 - =:%d\n",c);
c += a;
printf("Line2 - +=:%d\n",c);
c -= a;
printf("Line3 - -=:%d\n",c);
c *= a;
printf("Line4 - *=:%d\n",c);
c /= a;
printf("Line5 - /=:%d\n",c);
c %= a;
printf("Line6 - 求余=:%d\n",c);
c <<= a;
printf("Line7 - <<=:%d\n",c);
c >>= a;
printf("Line8 - >>=:%d\n",c);
c &= a;
printf("Line9 - &=:%d\n",c);
c |= a;
printf("Line10 - |=:%d\n",c);
c ^= a;
printf("Line11 - ^=:%d\n",c);
return 0;
}
//输出结果为:
//Line1 - =:20
//Line2 - +=:40
//Line3 - -=:20
//Line4 - *=:400
//Line5 - /=:20
//Line6 - 求余=:0
//Line7 - <<=:0
//Line8 - >>=:0
//Line9 - &=:0
//Line10 - |=:20
//Line11 - ^=:0
其他运算符
#include <stdio.h>
int main() {
int a = 5;
short b;
double c;
int* ptr; //prt用于指向一个变量
//sizeof 获得该数据类型的字节数
printf("Line1 - int:%lu\n",sizeof(a));
printf("Line2 - short:%lu\n",sizeof(b));
printf("Line3 - double:%lu\n",sizeof(c));
ptr = &a; //&a 返回变量a的内存地址
printf("a - = %d\n",a);
printf("ptr - = %d\n",*ptr); //ptr指向了一个内存地址,必须带上*号
b = (a == 100)?123:125; //三元运算符,用法和其他面向对象编程的方法一样
printf("b的值为:%d",b);
return 0;
}
//sizeof(type) sizeof(xxx)是取得某个数据类型或者变量的数据类型的字节数
//type* 使该变量成为一个指向内存地址的变量,使用是必须加上* xxx
//&xxx 取得某个变量的内存地址
//输出的结果为:
//Line1 - int:4
//Line2 - short:2
//Line3 - double:8
//a - = 5
//ptr - = 5
//b的值为:125
c的运算符的优先级:
一般我们只需要知道先乘除后加减即可,特殊需求可加()解决
C判断
#include <stdio.h>
int main() {
int a = 25;
int b = 30;
int c = 35;
int grade = 'a';
if(a > 10) { /* c把非零和非空假定为true,为0或者为null假定为false*/
printf("a大于10\n"); /* if语句 */
}
printf("a的值为:%d\n",a);
if(b > 20) { /* if-else语句 */
printf("b的值大于20\n");
}else{
printf("b的值小于20\n");
}
printf("b的值为:%d\n",b);
if(c < 5) { /*if-else if-else语句*/
printf("c小于5\n");
}else if(c>5 && c<=10) {
printf("c在5~10之间\n");
}else if(c>10 && c<=15) {
printf("c在10~15之间\n");
}else{
printf("c大于15\n");
}
if(c>30) { /* if嵌套语句 */
if(c == 35) {
printf("c等于35\n");
}
}
switch(grade) { /* switch-case循环语句 */
case 'a': /* 条件分支的语法和c# java完全一样 */
printf("优秀\n");
break;
case 'b':
printf("一般\n");
break;
case 'c':
printf("差\n");
break;
default:
printf("未知成绩!\n");
break;
}
return 0;
}
//基本语法和c# java类似,不再赘述
//输出结果为:
//a大于10
//a的值为:25
//b的值大于20
//b的值为:30
//c大于15
//c等于35
//优秀
C循环
C的循环基本和其他语言一样,它有一个无限循环是这样 for(;;) { 表达式 }
还有就是循环的控制语句:
break 跳出循环
continue 继续循环
goto 语句
#include <stdio.h>
int main() {
int a = 4;
while(a < 20) { /* While循环 */
printf("a = %d \n",a);
a++;
}
printf("while循环完成\n");
for(int i = 0;i<5;i++) { /* for循环 */
printf("ABC\n");
}
printf("for循环结束\n");
int d = 1; /* do while循环 */
do {
printf("one do-while loop~\n");
d = d + 1;
}while(d < 5);
for( ; ; ){ /* 无限循环,按下ctrl + c可以终止循环,注意分号之间的空格必须有 */
printf("无限循环\n");
}
return 0;
}
//基本循环的语法和面向对象的语言基本一样,不再赘述。
C函数
C 标准库提供了大量的程序可以调用的内置函数。例如,函数 strcat() 用来连接两个字符串,函数 memcpy() 用来复制内存到另一个位置。
函数的四个主体:
return_type function_name( parameter list )
{
body of the function
}
① return_type 返回值类型,如void
② function_name 方法名
③ parameter 参数
④ body of the function 方法体
#include <stdio.h>
int max(int,int); //声明一个方法
int main() {
int a = 99;
int b = 120;
int c = max(a,b); //调用
printf("结果为:%d\n",c);
return 0;
}
int max(int num1,int num2) { //定义max方法
int result;
if(num1 > num2) {
result = num1;
}else {
result = num2;
}
return result;
}
//c与其他语言的不同就是他可以定义方法
//有点类似C#中的委托类型
//关于c的函数的参数
//其他编程语言,如c#,我们会知道值类型参数和引用参数
//c语言管他们叫做传值参数和引用参数
//传值相当于复制新值给函数的形式参数,不会影响原先的值
//而引用参数则是给的是参数的内存地址的指针,会改变原来的值
//输出结果为:
//结果为:120
C的作用域规则
全局变量:存储在内存的全局存储区中,占用静态的存储单元
局部变量:存储在栈中,只有函数被调用是才会动态的分配存储单元
形式参数:如果全局和局部变量的标识符一样,会采用局部变量(就近原则)
全局变量会自动初始化,局部变量则不会
C的数组
#include <stdio.h>
const int MAX = 4;
int main() {
//数组的声明定义和遍历
int nums[4];
int i,j;
for(i=0;i<MAX;i++) {
nums[i] = i + 100;
}
for(j=0;j<MAX;j++) {
printf("Line%d - %d\n",j,nums[j]);
}
printf("数组定义声明遍历End\n");
//多维数组
//先行后列,访问多维数组时行和列的索引
int numsArray[3][3] = {
{3,6,9},
{2,4,8},
{6,6,6}
};
printf("2行2列的值为:%d\n",numsArray[1][1]);
printf("多维数组End\n");
return 0;
}
//这个和c#不同,c#是在数据类型后加[],而c语言是在变量名之后加[]
//c的一维和多维数组都是这样
C的枚举
#include <stdio.h>
#include <stdlib.h>
#define MON_DEF 1 /* 以#define形式声明变量 */
#define TUE_DEF 2
#define WED_DEF 3
#define THU_DEF 4
#define FRI_DEF 5
#define SAT_DEF 6
#define SUN_DEF 7
enum DAY { /* 直接声明枚举 */
MON=1,TUE,WED,THU,FRI,SAT,SUN
};
enum Color { /* 声明枚举和变量 */
RED = 1,
GREEN,
BLUE
}color;
enum Fruit {
APPLE = 1,
ORANGE,
BANANA
};
int main() {
enum DAY day; /* 声明枚举变量 */
day = SAT;
printf("%d\n",day);
color = GREEN;
printf("%d\n",color);
return 0;
}
//c的枚举的变量给在声明枚举类型是给出
//如果不指定枚举值,默认为0,下一个自动+1
//值类型转换为枚举类型需要加 (enum enumType) xxx;
//输出的结果为:
//6
//2
C指针
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
const int MAX = 3;
const int LEN = 10;
int main() {
int a = 10;
char c[10];
//使用 &xxx 取得一个变量的内存地址 %p表示输出内存地址
printf("a的内存地址为 %p\n",&a); //输出 a的内存地址为 0x7ffd9484e96c
printf("b的内存地址为 %p\n",&c); //输出 b的内存地址为 0x7ffd9484e970
//声明指针 type *var_name;
//每一个内存地址都是十六进制
int *azz;
float *fzz;
double *dzz;
char *ch;
//使用指针输出内存地址和对应的值
int var = 10;
int *izz;
izz = &var;
printf("var的指针内存地址为: %p\n",izz); //输出 var的指针内存地址为: 0x7ffc6c88fb04
printf("var的指针的值为: %d\n",*izz); //输出 var的指针的值为: 10
//空指针赋值
int *nzz = NULL;
printf("nzz的指针内存地址为: %p\n",nzz); //输出 nzz的指针内存地址为: (nil)
//检测指针为空或者不为空
if(nzz) {
printf("nzz不为空\n");
}
if(!nzz) {
printf("nzz为空\n"); //nzz为空
}
//指针的算数运算
//递增一个指针
int arr[] = {11,33,99};
int *ptr;
ptr = arr; //默认会赋值第一个数组的元素,可以不加&符号
for(int i=0;i<3;i++) {
printf("地址为: %p\n",ptr); //会分别输出他们的指针地址和值
printf("值为: %d\n",*ptr);
ptr++;
}
printf("递增指针End!\n");
//递减一个指针
int otherArr[] = {777,666,777}; //取最后一个数组元素的指针,必须加上&符号
int *oazz;
oazz = &otherArr[MAX-1];
for(int i=0;i<MAX;i++) {
printf("地址为: %p\n",oazz); //会分别输出他们的指针地址和值
printf("值为: %d\n",*oazz);
oazz--;
}
printf("递减去指针End!\n");
//指针的比较
int myArr[] = {10,20,200};
int *head;
int *end;
head = myArr;
end = &myArr[MAX-1];
while(head < end) {
printf("myArr元素的指针的内存地址为: %p\n",head);
printf("myArr元素的值为: %d\n",*head);
head++;
}
printf("指针比较End!\n");
//指针数组
int mineArr[] = {5,2,0};
int *zzArr[MAX];
for(int i=0;i<MAX;i++) {
zzArr[i] = &mineArr[i]; //指针也可以有数组,指针数组需要对每一个指针进行赋值
}
for(int i=0;i<MAX;i++) {
printf("指针数组里的值为: %d\n",*zzArr[i]);
}
printf("指针数组End!\n");
//指向指针的指针
int simpleValue = 998;
int *szz;
int **szzzz;
szz = &simpleValue;
szzzz = &szz; //定义指向指针的指针需要再加一个*号,例如 **xxxx,使用时也要加上俩个*号
printf("simpleValue的值为: %d\n",simpleValue);
printf("指针szz所指向的值为: %d\n",*szz);
printf("指针szzzz所指向的值为: %d\n",**szzzz);
printf("指针的指针End\n");
//从函数返回指针
//定义返回指针的函数 int * xxxx() { ... }
int * getRandom() {
static int r[10];
for(int i=0;i<LEN;i++) {
r[i] = rand();
}
return r;
}
int *g;
g = getRandom();
for(int i=0;i<LEN;i++) {
printf("随机的数为: %d\n",*(g+i)); //遍历数组的指针需要 +i 例如*(g+i)
}
printf("从函数返回指针End\n");
return 0;
}
//难点和易忘的点是,遍历数组的指针时要记得更新指针,如上边的*(g+i)
//还有就是返回指针的函数的定义,int * xxx() {。。。}
C函数指针和回调函数
关于回调函数的一段话:
以下是自知乎作者常溪玲的解说:
你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
//c的函数指针
int max(int x,int y) {
return x>y?x:y;
}
int a,b,c,d;
int (* m)(int,int) = &max; //函数的指针的声明方式为 返回值 (* xxx)(参数类型,参数类型,参数类型,...)
//使用时就是用这个你定义的标识符即可
printf("请输入三个数~\n");
scanf("%d %d %d",&a,&b,&c);
d = m(m(a,b),c);
printf("d的值为: %d\n",d);
printf("函数指针End\n");
//c回调函数
int getRandomValue(void) { //没有参数可以使用void表示,也可以省略
return rand();
}
void MyMethod(int *array,int size,int (* getRandom)(void)) { //参数需要传入一个函数指针,作为回调函数
for(int i=0;i<size;i++) {
array[i] = getRandom();
}
}
int myArr[3];
int size = 3;
MyMethod(myArr,size,getRandomValue);
for(int i=0;i<size;i++) {
printf("myArr Result: %d\n",myArr[i]);
}
printf("回调函数End\n");
return 0;
}
//这个功能就相当于c#的委托 delegate
C字符串
#include <stdio.h>
#include <string.h>
int main() {
//声明字符串
char h1[6] = {'h','e','l','l','o','\0'}; //声明时必须致指定size的大小
char h2[] = "hello2";
printf("h1: %s\n",h1);
printf("h2: %s\n",h2);
//字符串的一些方法
char newStr[5];
strcpy(newStr,h2); //copy方法 strcopy(s1,s2); 将s2复制到s1
printf("newStr的值为: %s\n",h2);
char s1[] = "hello,";
char s2[] = "world!";
strcat(s1,s2);
printf("s1的值为:%s\n",s1); //concat方法 strcat(s1,s2); 将s2连接到s1
int length = strlen(s1);
printf("连接后的字符串的长度为: %d\n",length); //strlen方法 strlen(s1); 求s1字符串的长度
char *zz_1;
zz_1 = strchr(s1,'e'); //strchr(s1,'x'); 返回x字符在s1字符串第一次出现的指针地址
printf("e在s1字符串首次出现的内存地址为: %p\n",zz_1);
char *zz_2;
zz_2 = strstr(s1,"ell"); //strstr(s1,s2); 返回s2字符串在s1字符串中第一次出现的位置
printf("字符串ell在s1字符串中首次出现的内存地址为: %p\n",zz_2);
return 0;
}
//使用字符串相关的方法必须引入 <string.h>的头文件
C结构体
需要注意一下位域的定义和位域变量还有位域成员的运算
#include <stdio.h>
#include <string.h>
//声明枚举方法1
struct Book{
char title[50];
char author[50];
char subject[50];
int book_id;
}book;
//声明枚举方法2
struct Simple1 {
int a;
short b;
float c;
};
//声明枚举方法3,带有多个变量
struct Simple2 {
int a;
short b;
float c;
} s1,s2,s3;
//声明枚举类型方法4,使用typedef + struct关键字
typedef struct {
int a;
short b;
float c;
} Simple3;
//使用枚举变量声明
struct Simple2 t1,t2[20],*t3; //使用struct xxxx{} 构造的结构体,声明变量时必须带上struct关键字
Simple3 t4,t5[20],*t6; //使用typedef struct {}xxxx; 构造的结构体,声明变量不带struct关键字
//结构体可以包含其他结构体
struct Example {
int a;
int b;
struct Simple2 c;
};
//结构体可以包含自身的指针地址
struct Example1 {
int a;
int b;
struct Example1 *next_node;
};
//如果俩个结构体互相包含,那么对其中的一个结构体进行不完整声明
struct B b;
struct B {
struct A *a; //结构体B包含结构体A的指针
};
struct A{
struct B *b; //结构体A包含结构体B的指针
};
//结构体变量的初始化
struct MyBook {
char title[50];
char author[50];
char subject[50];
int book_id;
} myBook = {"C语言","C语言之父","编程语言",12313};
void MyPrint(struct MyBook mk);
void MyPrint2(struct MyBook *mk);
int main() {
printf("myBook的作者是%s,属于%s科目\n",myBook.author,myBook.subject);
//访问结构体成员并赋值
struct MyBook book1;
strcpy(book1.title,"c语言入门到精通");
strcpy(book1.author,"知名大佬");
printf("%s写了一本%s\n",book1.author,book1.title);
//结构体还可用作参数
MyPrint(book1);
//结构体指针参数
MyPrint2(&book1);
return 0;
}
//位域的定义和位域变量
//8bit = 1byte
struct bs {
int a:8;
int b:2;
int c:6;
}data;
struct bs2 {
int a:4;
int :4; //空域,不能使用,如果存储的空间不够,那就从c开始存储,不能大于8位
int c:4; //再存储只能从第二个字节开始
int d:8;
}data2;
//位与算符 &= |= 例如一个结构体的指针 ptr->a&=3; ptr->a&=1; 都是位于运算
//结构体作为参数
void MyPrint(struct MyBook mk) {
printf("这本书叫%s\n",mk.title);
}
//结构体指针作为参数,访问结构体的成员需要加上 -> 符号
void MyPrint2(struct MyBook *mk) {
printf("MyPrint2方法的书的作者是:%s\n",mk->author);
}
C共用体
它允许你在同一个内存地址上有不同类型的数据成员,但是只能给其中一个成员初始化,否则会出现数据损坏的情况
#include <stdio.h>
#include <string.h>
//共用体1
union Data {
int i;
float f;
char c[20];
};
//共用体2
union Exmple {
int i;
int j;
char ch[30];
};
int main() {
union Data data;
data.i = 20;
data.f = 128.8;
strcpy(data.c,"C语言学习中...");
printf("i是: %d\n",data.i); //会出现数据错误的情况,输出 i是: -1380980669
printf("f是: %f\n",data.f); //会出现数据错误的情况,输出 f是: -0.000000
printf("最后的字符串的数据是: %s\n",data.c); //数据输出正确, 输出 最后的字符串的数据是: C语言学习中...
//如果想使数据正确输出的话,那么必须在初始化后立即使用,这也应了我们之前那句话只能对其中一个成员赋值
union Exmple e;
e.i = 55;
printf("完整的i是:%d\n",e.i); //输出 55
e.j = 66;
printf("完整的j是:%d\n",e.j); //输出 66
strcpy(e.ch,"this is my test~"); //输出 this is my test~
printf("完整的ch是:%s\n",e.ch);
return 0;
}
C的位域
位域是一种更好利用内存空间的结构体
#include <stdio.h>
#include <string.h>
int main() {
//声明位域的格式
/* struct {
type var_name:width; //type是数据类型 width是位数宽度必须小于等于该类型的位数
}
*/
//这个结构占8字节
struct {
unsigned int i1;
unsigned int i2;
}e1;
//这个结构占4字节
struct {
unsigned int i1:1;
unsigned int i2:1;
}e2;
printf("e1的字节数为:%lu\n",sizeof(e1)); //%lu 输出的是32位无符号整数,输出8
printf("e2的字节数为:%lu\n",sizeof(e2)); //%lu 输出的是32位无符号整数,输出4
//尝试超过位域的限制位数
struct {
unsigned int num:3;
}bitvar;
bitvar.num = 5;
printf("num的值为%d\n",bitvar.num);
bitvar.num = 7;
printf("num的值为%d\n",bitvar.num);
bitvar.num = 8; //这里说不能超过8,我们num的位的数量进行了限制,不能超过7
printf("num的值为%d\n",bitvar.num); //去掉上面的限制 :3 我们就可以输出8了
return 0;
}
C的typedef和#define
typedef #define都相当于给我们一个自定义数据类型的功能
#include <stdio.h>
#include <string.h>
#define RIGHT_IDEA 1
#define ERROR_IDEA 0
int main() {
// typedef 和 #define 关键字都相当于起别名的功能
//typedef 是由编译器执行
//#define 则是预处理,后边不加分号
typedef struct Books {
char title[50];
char author[50];
char subject[50];
int book_id;
}books;
books b1;
strcpy(b1.title,"代码之美");
strcpy(b1.author,"美国某个大佬");
strcpy(b1.subject,"计算机语言");
b1.book_id = 12312;
printf("%s发明了一本%s,属于%s类书籍\n",b1.author,b1.title,b1.subject); //正确输出了一句话,美国某个大佬发明了一本代码之美,属于计算机语言类书籍
printf("正确的想法是:%d\n",RIGHT_IDEA); //输出 1
printf("错误的想法是:%d\n",ERROR_IDEA); //输出 0
return 0;
}
//typedef是由编译器执行解释的
//#define是预处理命令,是由预编译器执行解释的
C的输入和输出
printf格式化输出符号表
① %d 一般用于输出数字(十进制有符号整数)
② %u 十进制无符号整数
③ %f 输出float浮点数
④ %s 输出字符串
⑤ %c 输出单个字符
⑥ %p 输出指针的值
⑦ %lu 32位无符号整数
⑧ %llu 64位无符号整数
#include <stdio.h>
int main() {
//getchar(); putchar(); 输入和输入的方法,只能限制在一个字符
int c;
printf("请输入一个字符:");
c = getchar();
printf("你的字符为:");
putchar(c);
printf("\n");
//gets(char str[])); puts(char str[]); 方法是输出输入一个字符串
//这里编译器会提示错误,应该是不支持
//scanf函数
int nums;
char strs[50];
printf("请输入一个数和字符串:");
scanf("%d %s",&nums,strs); //期间的输入的间隔可以使用
printf("\n");
printf("您输入的数是%d字符串为%s\n",nums,strs);
return 0;
}
//主要是学习了scanf()输入函数,他需要传递的是变量的指针
//getchar();
//putchar(char c);
//gets(char str[]);
//puts(char str[]);
C文件读写
C文件读写关键字
#include <stdio.h>
int main() {
FILE *fp = NULL;
fp = fopen("m_test.txt","w+"); //在Cloud Studio的文件目录下查找你的文件,可以发现成功的打印了
fprintf(fp,"this is test1~\n");
fputs("this is test2~\n",fp);
fclose(fp);
printf("文件写入完成!\n");
char buff[255];
fp = fopen("m_test.txt","r"); //使用 fscanf(File *fp,formatChar,char ch); 读取数据,遇到第一个空格就会停止
fscanf(fp,"%s",buff);
printf("1: %s\n",buff);
fgets(buff,255,fp); //第一次使用 fgets(char ch,int length,File *fp); 输出下一半 2: is test1~
printf("2: %s",buff);
fgets(buff,255,fp); //第儿次使用 fgets(char ch,int length,File *fp); 完整输出了 3: this is test2~
printf("3: %s",buff);
fclose(fp);
//二进制I/O函数
//这俩个函数用于存储块的读写,通常是数组或者结构体
/*
size_t fread(void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
size_t fwrite(const void *ptr, size_t size_of_elements,
size_t number_of_elements, FILE *a_file);
*/
return 0;
}
//如果你使用的是cloud stuido编译器,那么生成的xxx.txt文件请在你的编译器的文件目录下查找
C预处理器
c预处理器就相当于一个文本处理器
它会在编译器实际编译之前完成所需处理
我们把c的预处理器叫做CPP(C Preprocessor)
#include <stdio.h>
#define MAX_ARRAY_LENGTH 20 //告诉c预处理器将 MAX_ARRAY_LENGTH 替换为 20
// #include "mystdio.h" //从本地加载mystdio.h,吗,没有此文件编译器会报错
#undef FILE_SIZE //取消 FILE_SIZE的定义
#define FILE_SIZE 30 //将FILE_SIZE 重新定义为 30
#ifndef MESSAGE //这个指令告诉CPP,如果MESSAGE没有定于则定义为 you win
#define MESSAGE "you win!"
#endif
#ifdef DEBUG //这个指令告诉编译器,如果定于了DEBUG,则要执行处理语句
/*执行处理语句*/
#endif
//预处理运算符
// \是延续命令符,如预定义命令太长的话可以使用
#define Msg_for(a,b) \
printf(#a "and" #b",we love you!\n");
//标记粘贴运算符
// ##是粘贴运算符
#define tokenpaster(n) printf("token" #n "=%d",token##n)
//参数化的宏,用宏模拟函数
#define MAX(x,y) ((x)>(y)?x:y)
#define PF(X) (X*X)
int main() {
printf("File :%s\n", __FILE__ ); //**注意** 系统预定义的命令前后是 2个_
printf("Date :%s\n", __DATE__ );
printf("Time :%s\n", __TIME__ );
printf("Line :%d\n", __LINE__ );
printf("ANSI :%d\n", __STDC__ );
Msg_for("LAOWANG","LAOLI"); //输出 "LAOWANG"and"LAOLI",we love you! ,#xxx相当于使用这个变量
int token32 = 36; //相当于 token32 = token32 = 36,因为token和32粘贴在一起就成了一个变量
tokenpaster(32);
printf("MAX的结果为:%d",MAX(154,6)); //求最大值宏
printf("参数的平方是:%d",PF(7)); //求平方的宏
return 0;
}
C头文件
#include <xxxx.x> //是引用系统的头文件
#include "xxxx.x" //是引用我们自定义的头文件
#ifndef HEADER_FILE //只引用一次头文件
#define HEADER_FILE
the entire header file file
#endif
#if SYSTEM_1 //有条件的引用头文件
# include "system_1.h"
#elif SYSTEM_2
# include "system_2.h"
#elif SYSTEM_3
...
#endif
#define SYSTEM_H "system_1.h" //使用c的自定义宏代我们自己定义的文件名
....
#include SYSTEM_H
C强制类型转换
#include <stdio.h>
int main() {
//类型转换
int x=15,y=7;
double res;
res = (double)x/y; //强制类型转换和c#是一样的,直接就是 (type_name)xxx
printf("res is: %f",res);
//整数提升
int a = 17,sum;
char c = 'c';
sum = a+c;
printf("结果为:%d\n",sum);
printf("c的结果为:%d",c);
return 0;
}
//一般小到大的数据类型编译器必须使用强制转换(type_name)xxx
//大到小就是编译器自动隐式转换
C错误处理
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
extern int errno ;
int main ()
{
FILE * pf;
int errnum;
pf = fopen ("unexist.txt", "rb");
if (pf == NULL)
{
errnum = errno;
fprintf(stderr, "错误号: %d\n", errno);
perror("通过 perror 输出错误");
fprintf(stderr, "打开文件错误: %s", strerror( errnum ));
}
else
{
fclose (pf);
}
//被零除的错误的检验
int a = 20;
int b = 0;
int res;
if(b == 0) {
fprintf(stderr,"除数不能为0退出运行!\n");
exit(EXIT_FAILURE); //exit(-1)也可以
}
res = a/b;
exit(EXIT_SUCCESS); //exit(0)也可以
printf("验证完的结果为:%d\n",res);
return 0;
}
// perror("你的提示");
// fprintf(stderr, "打开文件错误: %s", strerror( errnum ));
//程序执行一般会返回exit(EXIT_SUCCESS); 即exit(0);
//退出程序就是 exit(EXIT_FAILURE);
C递归
阶乘:例如求3的阶乘,就是1x2x3的值为6
斐波那契数列就是 1,1,2,3,5,8,13 这样前俩个数相加得到够一个的值,下面还有一个视频讲解斐波那契数列。
视频链接
#include <stdio.h>
//求一个数的阶乘
double factorial(unsigned int i) {
if(i <= 1) {
return 1;
}
return i * factorial(i-1);
}
//打印斐波那契数列
int fibonaci(int i) {
if(i==0) {
return 1;
}
if(i==1) {
return 1;
}
return fibonaci(i-1) + fibonaci(i-2);
}
int main() {
int i = 3; //3的阶乘为 3*2*1=6
printf("i的阶乘为:%f\n",factorial(i));
for(int i=0;i<10;i++) { //打印了斐波那契数列 1,1,2,3,5,8,13,21,34,55,
printf("%d\n",fibonaci(i));
}
return 0;
}
C可选参数
#include <stdio.h>
#include <stdarg.h>
double average(int num,...) {
//va_list 是一个类型
va_list valist;
double sum = 0.0;
int i;
//初始化num和valist
va_start(valist,num);
//访问所有赋给valist的值
for(i=0;i<num;i++) {
sum += va_arg(valist,int);
}
//清理valist保留的内存
va_end(valist);
return sum/num;
}
int main() {
printf("test1 result: %f\n",average(4,2,5,3,6));
printf("test2 result: %f\n",average(2,10,10));
return 0;
}
//1.定义一个va_list 类型的集合存储所有输入的参数
//2.保证方法的第一个参数传递的是所有参数的个数,有几个参数就传入几
//3. va_start(valist,num); 进行初始化
//4. for循环+num,va_arg(valist,int); 遍历每一个参数
//5. va_end(valist); 清理valist的内存区域
//输出的结果为:
//test1 result: 4.000000
//test2 result: 10.000000
C的内存管理
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char name[100];
char *des;
strcpy(name,"WangDaNa");
/* 动态分配内存 */
//malloc也可以使用calloc替代,只不过每个空间的初始化的值是未知的
des = (char *)malloc(30 * sizeof(char));
// des = (char *)calloc(200,sizeof(char));
if(des == NULL) {
fprintf(stderr,"Error - unable to allocate required memory\n");
}else{
strcpy(des,"This is my descryption,this is my test!");
}
/* 输出结果 */
printf("Name:%s\n",name);
printf("Description:%s\n",des);
/*
1.如果是定义数组的形式,我们一但定义,内存的大小就是固定的我们无法改变
2.使用malloc或者calloc动态分配内存则不一样,可以使用realloc重新分配内存
*/
/* 假如我想要更大的内存 */
des = (char *)realloc(des,100 * sizeof(char));
if(des == NULL) {
fprintf(stderr,"Error - unable to allocate required memory\n");
}else{
strcat(des,"This is my descryption2,this is my test!2");
}
printf("NewDescription:%s\n",des);
/* 释放内存 */
free(des);
return 0;
}
//分配内存使用malloc或者calloc函数
//重新分配内存使用realloc函数
//释放内存使用free函数
C的命令行参数
#include <stdio.h>
/* argc是参数个数,argv是指针数组 */
int main(int argc,char *argv[]) {
if(argc == 2) {
printf("argv[1]:%s\n",argv[1]);
}else if(argc > 2) {
printf("Too many arguments supplied.\n");
}else {
printf("One argument supplied.\n");
}
return 0;
}
//传递参数时可以不加引号,每个参数空格分隔开
//也可以加上引号,这样就成了一个参数
//argv[0]一直会存储程序的名称,第一个参数从索引1开始存储