抽丝剥茧C语言(初阶 下)

2024-02-23 07:20
文章标签 语言 初阶 抽丝剥茧

本文主要是介绍抽丝剥茧C语言(初阶 下),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C语言初阶 下

  • 导语
  • 操作符
    • 算术操作符
    • 位移操作符
    • 位操作符
    • 赋值操作符
    • 单目操作符
    • 关系操作符
    • 逻辑操作符
    • 条件操作符
    • 逗号表达式
    • 下标引用、函数调用和结构成员
  • 常见关键字
    • 关键字 typedef
    • 关键字static
  • define 定义常量和宏
  • 指针
    • 内存
    • 指针变量的大小
  • 结构体
  • 结论
  • 给家人们的留言!!!

导语

如果到现在你也感觉有很多地方听不懂,没关系,我的这三篇博客只是让我们一起了解C语言大概是什么样子,对它有一个最初的了解就可以了,以后会有更详细的博客。

操作符

操作符是很重要的东西,这里我们讲解一下简单的,我没讲的家人们先记住就可以了,因为涉及到一些其他知识,我们慢慢来。

算术操作符

+     -     *      /      %

这是我们目前最常见运算操作符,字面意思,加减乘除什么的。

#include <stdio.h>
int main()
{int a = 3;int b = 2;int c, d, e, f, g;c = a + b;//加法运算d = a - b;//减法运算e = a * b;//乘法运算f = a / b;//除法运算g = a % b;//这个是取模,也就是取余数printf("%d %d %d %d %d", c, d, e, f, g);return 0;
}

我们来看一下运算结果(结果我就不放图片了)

5 1 6 1 1

我们除了变量 f 其他结果看起来是正常的对吗,这个符号确实是除号,这里我要说明一下,并不是你输入法打出来一个➗才是除号,而是要取键盘上的键打出来的符号,C语言定义中,/ 这个符号就是除号,其他的符号也是一样的,虽然看起来和你手写不一样,但他的定义就是这个意思。

那么为什么结果不正确呢?是因为在C语言规定中,两个数据相除,如果想得到浮点数(也就是小数,之所以叫浮点数,是因为小数点可以移动,所以叫做浮点数)那么 / 两边必须有一个浮点数才行!

例如:

#include <stdio.h>
int main()
{float a = 3.0 / 2;//float b = 3 / 2.0;//printf("%f %f", a, b);//打印浮点类型的数据就要用%freturn 0;
}

我们这段代码的结果就是:

1.50000 1.500000

至于为什么后面有这么多零,大家先忽略,我们先不进行深入了解。
其实不仅仅是除法运算,其他运算也是一样的,只要两边有一个浮点数就可以算出来浮点数的结果。

位移操作符

>>     //右位移操作符
<<     //左位移操作符

位操作符

&     //按位与
^     //按位异或
|     //按位或

赋值操作符

=    +=    -=    *=    /=    &=   ^=   |=    >>=   <<=

这些就比较容易理解了,第一个是赋值,顾名思义,是把一个数值赋予给一个变量,如果被赋值是一个常量,那么编译器就会报错,因为常量不可以被修改的;第二个是加等,a+=1 等同于 a=a+1 ;第三个是减等,请参考第二个;第三个是乘等;第四个是除等。剩下的我就不说名字了,因为大家都已经知道了。
看代码

#include <stdio.h>
int main()
{int a = 2;printf("%d ", a);int b = 2;printf("%d ", b += 1);int c = 2;printf("%d ", c -= 2);int d = 2;printf("%d ", d *= 2);int e = 2;printf("%d ", e /= 2);return 0;
}

输出结果:

2 3 0 4 1

剩下的赋值操作符我就不说了,后面的博客会讲。

单目操作符

!           //逻辑反操作
-           //负值
+           //正值
&           //取地址
sizeof      //操作数的类型长度(以字节为单位)
~           //对一个数的二进制按位取反
--          //前置、后置--
++          //前置、后置++
*           //间接访问操作符(解引用操作符) 
(类型)      //强制类型转换

第一个是逻辑反操作,比如说,你的 if 语句的判断条件是非零的数字,但是你在前面加上了!就会让判断条件两极反转;加号减号我就不说了,正负值而已;sizeof这是个操作符,不是函数,我们之前和它见过面了;- - 和++分前后,在前面就是先运算,后使用,在后面就是先使用,后运算,运算 - - 是减 1,++是加1(这里就不进行展示,后面会了解);(类型)里面是什么整形,浮点型之类的,作用是强制转换一个已经定义的类型。

#include <stdio.h>
int main()
{int q = 0;if (!q == 0)printf("w\n");int e = 0;printf("%d\n", sizeof (int));printf("%d\n", sizeof e );int a = (int)'a';printf("%d\n", a);return 0;
}

输出结果:

4
4
97

我们的第一个if语句并没有进去,因为!的功劳。sizeof后面加括号打印 int 类型的大小是因为C语言语法规定,而下面的 e 就不用。最后打印的97是字符 a 的ASCII值。

关系操作符

>    //大于
>=   //大于等于
<    //小于
<=   //小于等于
!=   //用于测试“不相等”
==   //用于测试“相等”

这些大多数用来判断两边的值是什么关系。

逻辑操作符

&&     //逻辑与
||     //逻辑或

例如:

#include <stdio.h>
int main()
{int a, b;scanf("%d %d", &a, &b);if (a && b)printf("q\n");elseprintf("w\n");int z, x;scanf("%d %d", &z, &x);if (z || x)printf("p\n");elseprintf("o\n");return 0;
}

实验1:
输入1 2
结果 q
输入1 0(或者是1 2)
结果 p
实验2:
输入1 0(或者是0 0)
结果 w
输入0 0
结果 o

以上实验说明什么?
逻辑与这个符号,两边需要都为真(非零)才能通过,一个真一个假(为零)或者是都为假则不能通过。
逻辑或这个符号,两边只要有一个为真就能通过,两个真也可以,两个假就不行了。

条件操作符

exp1 ? exp2 : exp3

这里的exp是一条语句,作用是如果exp1成立,那么就选择exp2;反之选择exp3.
例如:

#include <stdio.h>
int main()
{int a = 3;int b = 2;int c = (a < b ? a : b);printf("%d", c);return 0;
}

因为条件操作符需要返回一个值,这里返回一个整形,所以用整形来接收。
输出结果是:

2

逗号表达式

exp1, exp2, exp3, …expN

这个是取最后一个表达式的值。

#include <stdio.h>
int main()
{int a = 1;int b = 2;int c = 3;int d = 4;int e = 5;int m, n, v;int z = (m = a + b, n = c + m, v = e + n);printf("%d", z);return 0;
}

输出结果如下:

11

下标引用、函数调用和结构成员

[]     ()      .      ->

第一个我们之前说过了对吧,除了数组的初始化以为,剩下使用就表示数组的下标了,用法是数组名后面加一个 [ ] 里面是变量或者是常量都可以;圆括号是不仅仅是强制类型转换也是函数的调用,也就是我们之前说的参数,也就是传参,你把你想传过去的值传过去就好了(具体的后面会说),用法是前面加上函数名。剩下的以后会了解的。
至于花括号 { } 这个是是什么?当然是作用域了,你们慢慢体会。
这两个我就不举例子了。

常见关键字

C语言提供了丰富的关键字,这些关键字都是语言本身预先设定好的,用户自己是不能创造关键字的。

auto :声明自动变量

break:跳出当前循环

case:开关语句分支

char :声明字符型变量或函数返回值类型

const :声明只读变量

continue:结束当前循环,开始下一轮循环

default:开关语句中的“默认”分支

do :循环语句的循环体

double :声明双精度浮点型变量或函数返回值类型

else :条件语句否定分支(与 if 连用)

enum :声明枚举类型

extern:声明变量或函数是在其它文件或本文件的其他位置定义

float:声明浮点型变量或函数返回值类型

for:一种循环语句

goto:无条件跳转语句

if:条件语句

int: 声明整型变量或函数

long :声明长整型变量或函数返回值类型

register:声明寄存器变量

return :子程序返回语句(可以带参数,也可不带参数)

short :声明短整型变量或函数

signed:声明有符号类型变量或函数

sizeof:计算数据类型或变量长度(即所占字节数)

static :声明静态变量

struct:声明结构体类型

switch :用于开关语句

typedef:用以给数据类型取别名

unsigned:声明无符号类型变量或函数

union:声明共用体类型

void :声明函数无返回值或无参数,声明无类型指针

volatile:说明变量在程序执行中可被隐含地改变

while :循环语句的循环条件

关键字 typedef

typedef 顾名思义是类型定义,这里应该理解为类型重命名。
比如:

//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{//观察num1和num2,这两个变量的类型是一样的unsigned int num1 = 0;uint_32 num2 = 0;return 0; 
}

这个关键字的实际作用就是你觉得某个关键字太长或者是陌生你不想一遍一遍的去打,那就自己定义一个简单又熟悉的。

关键字static

**在C语言中:
static是用来修饰变量和函数的

  1. 修饰局部变量-称为静态局部变量
  2. 修饰全局变量-称为静态全局变量
  3. 修饰函数-称为静态函数**

在这里插入图片描述

看到没,我们这次多了一个源文件,这是为了方便演示。
在这里插入图片描述
如图,在test.c的源文件已经有main函数了,所以add.c的源文件就不用main函数了,因为它们是一个工程。
这里我们的编译器报错了,为什么呢?
因为你在add.c这个文件里声明了a但是test.c文件不知道,你需要去声明一下,利用关键字extern如图
在这里插入图片描述
这里就成功的打印出来了我们变量a 的值,你只需要声明它是什么类型,变量名称就可以了,这也说明,变量是有外部链接属性的,但我们的主题是关键字static,那么我们来看一下static修饰变量a会怎么样。(无论什么修饰什么记得需要中间加一个空格)
在这里插入图片描述
这里我们又报错了,着说明我们关键字static变量a的外部链接属性变成了内部的链接属性,其实我们本质是改变了变量a的储存方式给改变了,我们储存的大概方式一般是这个样子的。

这里科普一下:我们的电脑里有硬盘,比如说我目前的电脑,512G的硬盘,16G的内存,内存是什么?是传给你处理器数据的储存空间,但是内存这个东西造价昂贵,成本高,技术难突破,而处理器不一样,所以处理器的处理速度越来越快,而内存跟不上,处理器也不能之在那里等着对吧,所以,内存之上还有一块区域叫做高速缓存,在高速缓存之上还有一个叫做寄存器,越往上读写速度越快,但是成本和造价也越贵,空间也越小,高速缓存的单位是mb,寄存器的单位就是kb了。
处理器会先从寄存器里面拿,如果发现没有就去高速缓存里找,高速缓存没有就去内存里拿,这样就大大的提高了效率。

C语言的一大特点就是与内存强相关,C语言拥有三种不同的内存池。
1.静态区(static):全局变量,静态变量储存(生命周期是整个工程)
2.栈区(stack):局部变量存储(自动,连续的内存)
3.堆区(heap):动态存储(非常大的内存池,非连续分配)
在这里插入图片描述
因为static修饰的局部变量是存储在静态区的,static修饰全局变量时,实际改变的是变量的存储位置。 局部变量放在栈区的,被static修饰后放在了静态区。从而导致除了作用域依然存在,生命周期并没有结束。
比如说我再写一个代码:

void add()//我们不需要add函数返回,所以返回类型就是void
{int a = 5;//每一次调用都要重新创建临时变量a,初始化的值是5a++;printf("%d ", a);//这里打印add函数里面临时变量a的值
}#include <stdio.h>
int main()
{int i = 0;while (i < 10)//这里循环里面内容十次{add();//这里是调用add函数i++;}return 0;
}

我们的输出结果是:

6 6 6 6 6 6 6 6 6 6

这次我们用static来修饰一下局部变量a

void add()//我们不需要add函数返回,所以返回类型就是void
{static int a = 5;//这次我们用static修饰了局部变量aa++;printf("%d ", a);//这里打印add函数里面临时变量a的值
}#include <stdio.h>
int main()
{int i = 0;while (i < 10)//这里循环里面内容十次{add();//这里是调用add函数i++;}return 0;
}

输出结果如下:

6 7 8 9 10 11 12 13 14 15

这也能说明static修饰的局部变量a的储存位置被改变了,它的生命周期会一直到工程结束为止。
static最后的一个作用就是修饰函数
这里我就不用我的编译器份文件说了,直接注释标明

//代码1
//add.c
int Add(int x, int y)
{return c+y;
}
//test.c
int main()
{printf("%d\n", Add(2, 3));return 0;
}
//代码2
//add.c
static int Add(int x, int y)
{return c+y;
}
//test.c
int main()
{printf("%d\n", Add(2, 3));return 0;
}

代码1正常,代码2在编译的时候会出现连接性错误。
其他关键字以后用到我会讲解。

define 定义常量和宏

这里先说一下,define是预处理指令,也就是说在编译的初阶段时,对于某些东西进行文本上的替换。
例如:

//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
#include <stdio.h>
int main()
{int sum = ADD(2, 3);printf("sum = %d\n", sum);sum = 10*ADD(2, 3);printf("sum = %d\n", sum);printf("%d\n",MAX);return 0; 
}

运行结果如下:

sum = 5
sum = 50
1000

对于ADD来说,x和y被替换成了2和3假如说没有那个括号的话,我们第二个结果就不一样了。对于MAX来说,它以后就代表1000这个常量了。
看这里!靓仔!标识符.

指针

它来了,它来了!
嗯,我们C语言最灵魂的地方——指针来了。

内存

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的 。
所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节
为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址
就相当于某人在广东省深圳市某区某街某小区某号楼某单元某层楼几号房一样!也就是说指向了你的这个位置。
所以说,地址就是指针。
在这里插入图片描述
变量是创建内存中的(在内存中分配空间的),每个内存单元都有地址,所以变量也是有地址的。
我们这里来说一下单位转换:
8bit=1byte(字节)
1kb=1024byte
1mb=1024kb
1gb=1024mb
1tb=1024gb

再说一下我们最常用的进制,二进制八进制十六进制,至于怎么互相转换的,小白看一下视频吧。
链接在这里进制转换
我们来看,一个字节等于8个bit位,一个bit位里面只能储存一个数字,因为我们计算机储存的本质正负电信号,也就是二进制0 1
至于后面为什么是0x什么什么的,这是因为0x表示十六进制

00000000000000000000000000000001
这个结果就等于1
二进制转换为十六进制
0x00000001
上面的视频最好一定要看哦。

二进制储存没有十六进制储存简洁明了,所以地址编号用十六进制表示!
我们了解了这么多,来取出一个地址打印看看!

#include <stdio.h>
int main()
{int num = 10;&num;//前面加一个&符号,这里是取出num的地址,不能是常量//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)printf("%p\n", &num);//打印地址,%p是以地址的形式打印return 0;
}

打印结果(32位系统)

0012FF47
打印的结果每次都不一样,这是因为程序在运行的时候创建局部变量num当整个程序运行结束之后这个局部变量的生命周期也走到尽头了,每次创建变量都需要在栈区里随机的一个位置创建,所以每次的结果都不一样。

在这里插入图片描述

看到了吗,取出来的只是首个字节的地址,因为能通过首个地址找到后面三个字节的地址。

常量可以储存到变量里面,那地址如何存储?这需要定义指针变量。

int num = 10;
int *p;//p为一个整形指针变量,如果int换成char就是字符类型,也就是说前面这个地方是决定指针类型的地方
p = &num;//将变量num储存到p这个指针里面

当 * 与一个变量结合时,说明这个变量是指针变量。
那么指针该如何使用呢?

#include <stdio.h>
int main()
{int num = 10;int *p = &num;*p = 20;//*这个操作符叫做解引用操作符,不仅仅是初始化可以用到,也是释放储存到指针变量的钥匙return 0;
}

解引用这个操作符你不要把他和初始化弄混, C语言定义指针的初始化就是这个样子,初始化前面是有类型定义的,而解引用并没有。
解引用是咋回事呢,指针的储存就相当于你把地址num这个东西放进了包裹p里,你想打开这个包裹,就需要解引用这个操作才能打开这个包裹。
然后通过num地址找到了里面的10这个元素。
至于为什么是指针变量,就拿p来说,它可以指向num的地址,也可以指向其他地址。

int a=30;
p = &a;//这里不再储存变量num的地址而是变量a的地址

只要是数据储存在内存里就会有地址
在这里插入图片描述

就算是指针变量也一样。(这里先不套娃了,后期再说)
以整形指针举例,可以推广到其他类型,如:

#include <stdio.h>
int main()
{char ch = 'w';char* pc = &ch;*pc = 'q';printf("%c\n", ch);return 0; 
}

输出结果为

q

指针变量的大小

//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{printf("%d\n", sizeof(char *));printf("%d\n", sizeof(short *));printf("%d\n", sizeof(int *));printf("%d\n", sizeof(double *));return 0; 
}

输出结果为

4
4
4
4

为什么呢?不是说char类型是两个字节,short类型是两个字节吗?
其实这是指针的大小,我们上面说过了,所以不要在意指针变量前面的是什么类型,这个以后会说用处的,不要急。

结构体

结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型。
比如描述学生,学生包含: 名字+年龄+性别+学号 这几项信息。
这里只能使用结构体来描述了。

struct Stu//前面是定义结构体的声明关键字,后面是自定义标识符
{char name[20];//名字int age;      //年龄char sex[5];  //性别char id[15]//学号
};

这就是就结构体了。下面我们来看看结构体的初始化和使用方法:

//打印结构体信息
#include <stdio.h>
struct Stu//前面是定义结构体的声明关键字,后面是自定义标识符
{char name[20];//名字int age;      //年龄char sex[5];  //性别char id[15]; //学号
};
int main()
{struct Stu s = { "张三", 20, "男", "20180101" };//.为结构成员访问操作符,利用这个操作符访问s这个学生的个人信息printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);//->操作符,相当于指针变量ps解引用之后访问s这个学生的信息一样struct Stu* ps = &s;printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps -> id);
}

输出结果如下:
在这里插入图片描述
结构体以后会详细讲的,先了解这么多吧。

结论

操作符主要是介绍了一些简单的,注意平时积累,慢慢就会了,以后遇到没讲的我会详细讲解的。
用户自己是不能创造关键字
关键字static
static修饰局部变量改变了变量的生命周期让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
预处理指令是在编译初阶更改文本!
注意自定义标识符的规则!
指针大小在32位平台是4个字节,64位平台是8个字节。
结构体是像数据类型的那种东西,我的理解是自定义的数据类型。

给家人们的留言!!!

家人们,抽丝剥茧C语言的初阶——初识C语言,到此就完结了, 目前我们已经了解了C语言大概是什么样子的,对于以后学习C语言更加方便。
我并不是C语言只讲了这么些,而是让大家熟悉下C语言,不过很重要!!!
请路过的同志们点点赞,互关一波!!!

这篇关于抽丝剥茧C语言(初阶 下)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/737918

相关文章

C语言线程池的常见实现方式详解

《C语言线程池的常见实现方式详解》本文介绍了如何使用C语言实现一个基本的线程池,线程池的实现包括工作线程、任务队列、任务调度、线程池的初始化、任务添加、销毁等步骤,感兴趣的朋友跟随小编一起看看吧... 目录1. 线程池的基本结构2. 线程池的实现步骤3. 线程池的核心数据结构4. 线程池的详细实现4.1 初

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

透彻!驯服大型语言模型(LLMs)的五种方法,及具体方法选择思路

引言 随着时间的发展,大型语言模型不再停留在演示阶段而是逐步面向生产系统的应用,随着人们期望的不断增加,目标也发生了巨大的变化。在短短的几个月的时间里,人们对大模型的认识已经从对其zero-shot能力感到惊讶,转变为考虑改进模型质量、提高模型可用性。 「大语言模型(LLMs)其实就是利用高容量的模型架构(例如Transformer)对海量的、多种多样的数据分布进行建模得到,它包含了大量的先验

C语言 | Leetcode C语言题解之第393题UTF-8编码验证

题目: 题解: static const int MASK1 = 1 << 7;static const int MASK2 = (1 << 7) + (1 << 6);bool isValid(int num) {return (num & MASK2) == MASK1;}int getBytes(int num) {if ((num & MASK1) == 0) {return

MiniGPT-3D, 首个高效的3D点云大语言模型,仅需一张RTX3090显卡,训练一天时间,已开源

项目主页:https://tangyuan96.github.io/minigpt_3d_project_page/ 代码:https://github.com/TangYuan96/MiniGPT-3D 论文:https://arxiv.org/pdf/2405.01413 MiniGPT-3D在多个任务上取得了SoTA,被ACM MM2024接收,只拥有47.8M的可训练参数,在一张RTX

如何确定 Go 语言中 HTTP 连接池的最佳参数?

确定 Go 语言中 HTTP 连接池的最佳参数可以通过以下几种方式: 一、分析应用场景和需求 并发请求量: 确定应用程序在特定时间段内可能同时发起的 HTTP 请求数量。如果并发请求量很高,需要设置较大的连接池参数以满足需求。例如,对于一个高并发的 Web 服务,可能同时有数百个请求在处理,此时需要较大的连接池大小。可以通过压力测试工具模拟高并发场景,观察系统在不同并发请求下的性能表现,从而

C语言:柔性数组

数组定义 柔性数组 err int arr[0] = {0}; // ERROR 柔性数组 // 常见struct Test{int len;char arr[1024];} // 柔性数组struct Test{int len;char arr[0];}struct Test *t;t = malloc(sizeof(Test) + 11);strcpy(t->arr,

C语言指针入门 《C语言非常道》

C语言指针入门 《C语言非常道》 作为一个程序员,我接触 C 语言有十年了。有的朋友让我推荐 C 语言的参考书,我不敢乱推荐,尤其是国内作者写的书,往往七拼八凑,漏洞百出。 但是,李忠老师的《C语言非常道》值得一读。对了,李老师有个官网,网址是: 李忠老师官网 最棒的是,有配套的教学视频,可以试看。 试看点这里 接下来言归正传,讲解指针。以下内容很多都参考了李忠老师的《C语言非

C 语言基础之数组

文章目录 什么是数组数组变量的声明多维数组 什么是数组 数组,顾名思义,就是一组数。 假如班上有 30 个同学,让你编程统计每个人的分数,求最高分、最低分、平均分等。如果不知道数组,你只能这样写代码: int ZhangSan_score = 95;int LiSi_score = 90;......int LiuDong_score = 100;int Zhou

C 语言的基本数据类型

C 语言的基本数据类型 注:本文面向 C 语言初学者,如果你是熟手,那就不用看了。 有人问我,char、short、int、long、float、double 等这些关键字到底是什么意思,如果说他们是数据类型的话,那么为啥有这么多数据类型呢? 如果写了一句: int a; 那么执行的时候在内存中会有什么变化呢? 橡皮泥大家都玩过吧,一般你买橡皮泥的时候,店家会赠送一些模板。 上