在本文中,对每个知识点做简单认识,不做详细讲解,先基本了解c语言的基础知识,对c语言有一个大概的认识。
#include
int main()
{
printf("hello bit\n");
printf("he he\n");
return 0;
}
在此,我简单解释一下这段代码的意思
c语言中数据类型有以下7种
char | 字符型 |
---|---|
short | 短整型 |
int | 整型 |
long | 长整型 |
long long | 更长的整型 |
float | 单精度浮点数 |
double | 双精度浮点数 |
那什么又是字符,整型,浮点型呢
那么为什么我们对整型和浮点型的描述需要这么多类型呢?
主要原因是因为我们需要对内存空间进行合理的分配,short long int long long虽然都是描述整型的,但是他们的取值范围不一样,在内存中所占的空间大小也就不一样
选择使用哪种整型类型取决于你对内存空间和数值范围的需求。如果你需要处理的数值较小,可以选择使用 short 或者 int 类型,而如果需要处理的数值范围较大,你可以选择使用 long或者long long 类型。
同理,浮点型也一样,double的精度更高,但是占用的内存空间会更大,而float的精度低,但是占用的内存空间比较小,需要说明的是,浮点型在内存中是无法精确存储的,这与浮点型在内存中的存储方式有关,只能对浮点型的精度进行控制,控制误差,无法准确的存储浮点型。
数据类型 | 类型名字 | 内存中占用的空间大小 |
---|---|---|
char | 字符型 | 1个字节 |
short | 短整型 | 2个字节 |
int | 整型 | 4个字节 |
long | 长整型 | 4个或者8个字节 |
long long | 更长的整型 | 8个字节 |
float | 单精度浮点数 | 4个字节 |
double | 双精度浮点数 | 8个字节 |
在此我们需要了解一下内存存储的单位
1个字节等于8个二进制位,而一个千字节又等于1024个字节,一兆等于1024个千字节………以此类推
变量即稳定不变的量,而常量就是会发生改变的量,常量和变量的概念都源于生活,生活中有些东西是不会改变的,比如说圆周率,血型等,当然生活中也存在一些会改变的东西,比如说年龄,体重等……
int age = 150;
float weight = 45.5;
char ch = 'w';
在此我解释一下 int age = 150;这段代码的意思:
变量的命名字符不能超过63个字符是因为在大多数编程语言中,标识符的长度都有一定的限制。这是由于编程语言的解析器和编译器在处理代码时需要预留一定的内存空间来存储变量名。超过限制的字符数可能导致内存溢出或其他运行时错误。此外,过长的变量名也会降低代码的可读性和可维护性。因此,建议在命名变量时坚持简洁、清晰和有意义的原则,同时遵循编程语言的命名约定。
变量名区分大小写的意思就是说Abandon和abandon是两个不同的变量名,ABandon和abandon也同样是两个不同的变量名
C语言中的关键字是具有特殊含义的标识符,它们被用于特定的语法结构和功能。比如说int short long等都是关键字,不能将关键字拿来命名
变量分类一般有两种
在了解局部变量和全局变量前,我们先了解一下什么是变量的作用域和生命周期
那么我们如何知道一个变量是局部变量还是全局变量呢?
#include
int global = 2019;
int main()
{
int local = 2018;
printf("global = %d\n", global);
return 0;
}
一个变量的作用域就是最靠近这个变量的左右括号所括起来的范围,如果有括号将变量括起来了,那么这个变量就是局部变量,反之则是全局变量
例如:
上述代码中的local就是一个局部变量,它的作用域是主函数,而global则是一个全局变量,它的作用域是整个项目
变量创建的本质:先在内存中开辟一块空间,存放数据,出了作用域变量就无法使用了,当变量的生命周期结束了,就会被销毁(即被操作系统回收,不是将内存给销毁了,只是将内存还给操作系统了)
注意不要在一个项目中定义两个重名的全局变量,他们的作用域都是整个项目,如果重名了,则会报错
但是有时候我们可能会很粗心,不小心将全局变量和局部变量的名字重名了,c语言允许这种情况发生吗?
#include
int global = 2019;
int main()
{
int local = 2018;
int global = 2020;
printf("global = %d\n", global);
return 0;
}
很明显,两个global的作用域在main函数中都存在,那printf()打印的结果是什么呢?是否会报错呢?
让我们看看这段代码的运行结果
我们发现程序运行良好,没有报错,并且global打印的值是2020,那么我们可以得出一个很重要的结论
当局部变量和全局变量同名的时候,局部变量优先使用
printf()用于格式化输出,是一个C语言中的标准库函数,它被定义在stdio.h头文件中。它的全名是"print formatted"(格式化打印),当我们用printf()函数时,需要用#include
#include
int main()
{
int global = 2020;
printf("global = %d\n", global);
return 0;
}
字符串就是一个个字符组成的集合,为了和其他数据区分开来,需要用双引号" “和别的数据类型区分开来
比如"1456346346” “asfih325” "sfafkjh"等都是字符串
接着我们来了解一下printf()函数中的占位符
%c | 字符的占位符 |
---|---|
%s | 字符串的占位符 |
%d | 整型的占位符 |
%f | float的占位符 |
%lf | double的占位符 |
%p | 地址的占位符 |
下面给出部分使用用例:
第一个:%c
#include
int main()
{
char ch = 'A';
printf("Character: %c\n", ch);
return 0;
}
输出结果:Character: A
第二个:%s
#include
int main()
{
char str[] = "Hello";
printf("String: %s\n", str);
return 0;
}
输出结果:String: Hello
第三个:%d
#include
int main()
{
int num = 42;
printf("Integer: %d\n", num);
return 0;
}
输出结果:Integer: 42
第四个:%f
#include
int main()
{
float f = 3.14;
printf("Float: %f\n", f);
return 0;
}
输出结果:Float: 3.140000
在使用C++中的printf函数进行格式化输出时,可以使用格式控制符来指定宽度、精度和填充字符。
下面是一个示例:
#include
int main()
{
int num = 42;
float pi = 3.14159;
printf("%5d\n", num);
printf("%-5d\n", num);
printf("%.2f\n", pi);
printf("%06d\n", num);
return 0;
}
scanf()是一个C语言中的标准库函数,用于输入数据,它的主要作用是将用户输入的数据存储到变量中,以供程序进一步处理或使用。它被定义在stdio.h头文件中,当我们用scanf()函数时,需要用#include
用法:scanf(format, variable);
下面通过一个例子来演示scanf()函数的用法
#include
int main()
{
int num = 0;
printf("请输入一个数字赋值给num:");
scanf("%d", &num);
printf("num = %d\n", num);
return 0;
}
接下来我来解释一下scanf(“%d”, &num);这段代码的意思,
这段代码的意思是先读取用户输入的数据,并将这个数据赋给num这个变量,
1:输入数据时候的分隔符要和scanf()函数中的分隔符一样
比如:
scanf(“%d,%d”&num1,&num2);
&num1与&num2之间有一个逗号,那么我们在输入两个数据的时候也应该加上逗号,比如:100,200(切记不要加空格)
scanf(“%d,%d”&num1 &num2);
&num1与&num2之间有一个空格,那么我们在输入两个数据的时候也应该加上比如:100 200(切记不要加逗号)
逗号在这里作为一个分隔符,用于区分两个不同的整数输入。如果你的格式化字符串中指定了逗号,那么在输入数据时必须按照这个格式输入,即在两个整数之间使用逗号进行分隔。
这样做的目的主要有两个原因:
增加输入的可读性和可预测性:通过使用逗号来分隔输入数据,可以在视觉上清晰地区分不同的整数,并确保输入数据的顺序和格式符合预期。这有助于提高输入数据的可读性和可预测性,减少输入错误的可能性。
确保输入数据的正确解析:格式化字符串中的逗号告诉scanf函数,在读取输入数据时应该遇到逗号作为分隔点,将输入数据分别存储到对应的变量中。如果输入数据不按照指定的格式进行分隔,例如输入的两个整数之间没有逗号或者有额外的空格,scanf函数可能无法正确解析数据,导致读取错误的整数或发生错误。
常量一般有三种
const int n = 100;
这个代码中,n被const修饰了,所以具有了常属性,n不能被修改,如果后面有对n的值进行修改的代码,那么这个代码会报错但是n还是一个变量,只不过有了常属性了而已,故又称作常变量
const修饰的常变量只是在语法层面限制了变量n不能被直接改变,但是n的本质上还是一个变量,只是具有了常属性
#include
#define PI 3.14159
int main() {
printf("The value of PI is: %f\n", PI);
return 0;
}
下面我来解释一下这段代码的意思:
通过#define定义常量的意义:
enum Sex
{
MALE,
FEMALE,
SECRET
};
这段代码定义了一个枚举类型Sex,它包含三个枚举成员:MALE、FEMALE和SECRET。每个枚举成员都表示一个特定的性别选项。枚举常量如果不赋值,那么第一个枚举成员默认为0,接着第二个枚举成员默认为1,接着第三个枚举成员默认为2
在这个情况中MALE的值为0,FEMALE的值为1,SECRET的值为2(程序员是从0开始数数的)
enum Sex
{
MALE = 6,
FEMALE,
SECRET
};
而在这种情况下MALE的值为6,FEMALE的值为7,SECRET的值为8
enum Sex
{
MALE = 6,
FEMALE = 9,
SECRET= 13
};
当然也可以分别对MALE,FEMALE,SECRET分别赋值,
在这种情况下MALE的值为6,FEMALE的值为9,SECRET的值为13
与#define定义的标识符常量类似,枚举常量也让数字具有了意义,而不只是单纯的一个数字,例如
enum Sex
{
MALE = 6,
FEMALE = 9,
SECRET= 13
};
在这个例子中,我们可以用MALE来代替6来使用,
printf(“%d”,MALE);很明显打印结果是6,使用枚举能够让代码更具可读性,因为每个枚举成员都有一个有意义的名字。在代码中使用枚举成员,可以更清晰地表达其含义,而不是使用数字或其他形式的常量
字符串顾名思义结束一串字符,字符串被双引号" "引住
需要注意的是:
'w’与"w"不同,一个是字符w,另一个则是字符串w
并且字符串是以\0为结束标志的,这个标志我们无法肉眼看见,需要通过调试才能发现,当然\0在计算字符串长度的时候是不被计算的,而在计算字符串的内存大小时则会被计算进去
char只能存储单个字符,字符串的存储需要通过字符数组来实现(有个印象即可)
转义字符是由反斜杠(\)后面跟着一个特定字符组成的字符序列。它们的存在是为了在字符串中表示一些特殊字符、字符序列或者不可打印的字符。转义字符的目的是用来改变字符原本的含义,让这些字符具有特殊的功能。
因为键盘上的按键都是我们使用最频繁的按键,但是我们的键盘上能打印的字符有限,但是又有一些符号在某些情况下我们可能需要用到比如说29中的9,所以有了转义字符,这就是转义字符的意义
以下是常用的转义字符
\? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
---|---|
\’ | 用于表示字符常量’ |
\" | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为一个转义序列符 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字。 如: \130 X |
\xdd | dd表示2个十六进制数字。 如: \x30 0 |
问题1:写个程序在屏幕上打印一个单引号’,怎么做?
问题2:写个程序在屏幕上打印一个字符串,字符串的内容是一个双引号“,怎么做?
可能有人会觉得这题很简单,不假思索就说出了
然而这种写法是错误的:
在C语言中,单引号和双引号都具有特殊的含义。单引号用于表示一个字符字面量,而双引号用于表示一个字符串字面量。
如果你直接将一个单引号或双引号放在 printf 函数的格式字符串中进行打印,编译器会将其解释为字符串的起始或结束,并且期望在引号之后提供相应的内容。这样会导致编译错误,因为你提供的内容是不完整的。
正确的做法:
include <stdio.h>
int main()
{
printf("%c\n", '\'');
printf("%s\n", "\"");
return 0;
}
正确的做法是通过转义字符,通过转义字符来防止程序将单引号解析成一个字符字面量,同理双引号也一样
注释是程序代码中用于给人阅读和理解的文字说明。注释可以被编译器或解释器忽略,不会影响代码的执行。注释的目的是提供代码解释、补充说明、指导团队合作和代码维护等方面的信息
以下的代码的意义:
解释代码:注释可以用来解释代码的逻辑、意图、设计思路等,帮助其他开发人员理解代码。
提供文档:注释可以作为代码的文档,描述代码的用途、参数、返回值等信息,方便其他人使用和维护。
团队合作:注释可以帮助团队成员在合作开发过程中更好地理解、评审和改进代码。
注释对于代码的可读性、可维护性和可理解性都非常重要,是编写高质量代码的重要组成部分
c语言的注释有两种
对于单行注释来说, // 只能注释掉 // 所在行的代码,注释掉的是一行的代码
而对于多行注释来说, / * */ 可以注释掉两个/ /之间的所有内容,但是要注意多行注释不能够嵌套,即
/ * / * */ */,
因为/*会先和与它最近的 */ 进行匹配所以/ * / * */ */,就相当于只有 */了
if的基本结构:
if (condition)
{
// 当条件为真时执行的代码块
}
else
{
//当上述条件不满足时执行这个代码
}
condition是一个表达式
如果condition的值为真,那么则执行 if 中的代码
如果condition的值为假,那么则不执行 if 中的代码,此时如果有else,则执行else中的代码
下面是if的基本使用方法:
#include
int main()
{
int coding = 0;
printf("你会去敲代码吗?(选择1 or 0):>");
scanf("%d", &coding);
if(coding == 1)
{
prinf("坚持,你会有好offer\n");
}
else
{
printf("放弃,回家卖红薯\n");
}
return 0;
}
while 循环是 C 语言中的一种迭代控制结构,用于反复执行一段代码,直到给定条件变为假。
while 循环的基本语法如下:
while (条件)
{
// 循环体,会重复执行的代码块
}
首先,计算条件的值。如果条件为真,则执行循环体中的代码块;然后再次计算条件的布尔值。如果条件仍然为真,则再次执行循环体中的代码块。这个过程会一直重复,直到条件变为假为止。如果初始时条件的值就为假,那么循环体中的代码块将不会被执行。则会教学执行接下来的其他代码。
以下是一个使用 while 循环的示例,它打印数字 1 到 5:
#include
int main() {
int i = 1;
while (i <= 5) {
printf("%d\n", i);
i = i + 1;
}
return 0;
}
while 循环可以用在满足某个条件时重复执行某块代码。在编写 while 循环时,务必要确保循环的条件最终会变为假,否则会导致无限循环。在上述代码中,我们通过控制 i 的值来控制条件,没有造成死循环
死循环是指在程序中一个循环条件始终为真,导致循环永远不会终止的情况。也就是说,循环中的代码会一直执行下去,直到程序被手动中断或发生其他异常。循环才能够结束
一个函数完成某一个特定的功能,此前我们学习的printf()函数的功能就是将字符串打印在屏幕上,scanf()函数的功能就是读取用户输入的数据,并赋值给某个特定的变量,一个函数完成一个特定的功能,这就是函数
在此我们简单了解函数即可,后续会对函数及其他各个知识点进行深度展开
除了库中已经写好的许多函数,我们还可以自己自定义一个函数,因为库中的函数不可能无穷无尽,将功能覆盖到各个方面,所以这时就需要我们自定义一个函数使用,这大大的加大了程序的灵活性和可扩展性,让我们能够在特定的需求下完成特定的功能
函数名:函数名是函数的标识符,用于唯一标识函数并在其他地方调用它。函数名应当具有描述性,能够清晰地表达函数的功能和用途。
参数列表:函数可以接受零个或多个参数,参数用于传递数据给函数。参数列表定义了函数可以接受的参数的类型、顺序和数量。
返回值类型:函数可以返回一个值作为函数的输出结果。返回值类型定义了函数返回值的数据类型。函数体中return返回值的类型( return中值的类型 )必须要和返回值类型( 函数名前的类型 )一样。
函数体:函数体是函数的具体实现。它包含了函数的操作和逻辑,通过一系列的语句来完成特定的任务。
这四个要素共同定义了函数的结构和行为。函数名用于标识函数,参数列表用于传递数据给函数,返回值类型定义了函数返回值的数据类型,函数体用于实现函数的具体逻辑。通过调用函数名并传递相应的参数,可以执行函数体中的代码,并返回一个特定的值作为结果。
接着我们来简单实现一个函数,这个函数的功能用于相加两个整型
所以现在函数的大致样子应该是
int Add(int x,int y)
{
}
最终函数如下
int Add(int x, int y)
{
int z = x + y;
return z;
}
以上便是我们通过分析自定义的一个对两个整型相加的简单函数,因为我们此时没有调用printf()函数,不用引用头文件 #include
接着我们通过主函数调用一下这个函数,代码如下:
#include
int Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 22;
int c = Add(a, b);
printf("c = %d", c);
return 0;
}
打印结果为
可能有同学对int c = Add(a, b);这一行代码不是很理解,我在这简单解释一下不做过多的展开,以后会深入讲解,
在c语言中,我们调用自定义函数的方式是通过 函数名+(参数列表)
举个简单的例子,我们调用Add函数是通过 Add(参数列表)
因为我们在自定义的时候为Add函数设置了两个整型变量,所以当我们用这个函数的时候,也需要用两个整型的变量当作参数进行传递,并且变量顺序和类型都要一致
所以我们调用Add函数应该写 Add(a,b);a和b就是我们传给Add函数的两个参数,Add函数将a的值赋给了x,将b的值赋给了y,然后进行运算,
接着将z作为返回值返回,
此时Add(a,b)的值也就是Add这个函数的返回值了,也就是z,所以此时c就是a和b的和
到此Add函数完成了一个简单的两个整数相加的功能
如果此时我们还需要进行另外两个整数的相加,只需要再调用一次Add即可,这也就是函数很重要的作用之一:
代码复用
在生活中,我们有一张钞票,我们可以用手拿着,但是如果钞票多了,变成几十万了,我们该怎么存储呢?我们可以用钱包存储钱,此时钱包里存储就是钱的集合,同理,在c语言中,我们可以用一个int 存储一个整型,那么我们该用什么类型来存储很多个整型呢?
答案是整型数组
那么什么是数组呢?
c语言给出规定:数组是一组相同类型元素的集合
这一句话中强调了两个方面的因素
必须是一组类型相同的元素并且是集合,才能够叫做数组
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
这是一个数组的简单定义方式,接下来我来解释一下这行代码的意思
c语言规定:数组的每个元素在数组中都有一个下标,并且下标是从0开始的(程序员从0开始数数而不是1),并且数组中的元素可以通过下标来访问
int arr[10] = {0,0,0,0,0,0,0,0,0,0};
数组中有10个元素,所以下标的范围是0-9从0开始数(0,1,2,3,4,5,6,7,8,9一共有10个)
我们访问数组中的元素是通过 [ ]来访问的,下面通过一个例子来说明 [ ]的使用
例如:arr[3]
数组的元素如果不初始化的话,默认值是0,并且在部分编译器中int arr[ ]
括号中只能放常量,而不能放变量,在c99标准中引入了变长数组的概念,这时候允许数组的大小是变量,但是这种数组不能直接初始化,需要后面再对这个数组赋值
加减与我们平常用的加减乘没什么区别,在这就不过多讲解了
乘号的用法与我们平时所用基本上没区别,只需要注意在c语言中乘号是*而不是x,主要原因是要将乘号和字符x区别开来
/ 表示进行除法运算,但是不同于我们平时的除法,c语言中的除法运算有两种,一是整数除法,二是浮点数除法
整数除法:
7 / 2 = 3
8 / 2 = 4
9 / 2 = 4
小数除法:
7.0 / 2 = 3.5
7 / 2.0 = 3.5
7.0 / 2.0 = 3,5
8.0 / 2 = 4.0
(4和4.0的意义不一样,不要混淆)
取模操作符又叫做取余操作符,只能适用于整数(包括正整数和负整数),如果又小数则会报错, %就是取出余数作为结果,
10 % 3 = 1
10 % 2 = 2
10 % 1 = 0
需要注意的是,如果对n进行取模,那么n的取值范围在0 ~ (n-1),多利用好这个性质,对后面编程很有帮助
在C语言中,= 操作符用于将一个值赋给一个变量。它将右侧的表达式的结果赋值给左侧的变量。比如:
int num = 10; // 定义一个整数变量num,并将值10赋给它
int ret = num //将num的值赋给ret
对于最后一点可能有同学很难理解,这里举一个简单的代码例子示例说明
#define _CRT_SECURE_NO_WARNINGS 1
#include
int main()
{
int a = 5;
int b = (a = 10); // 将 10 赋值给 a,赋值表达式的值是 10,然后将 10 赋值给 b
printf("%d\n", a); // 输出 10
printf("%d\n", b); // 输出 10
return 0;
}
int a = 10;
a = a +18;
这段代码很好理解,首先是定义了一个int 类型的变量a 并将10赋值给了a,接着第二行代码表示先将a加上10,再将a +10这个值赋给 a,也等价于 a = 10+18 即 将28赋给a,所以a 等于28
a = a +18有一种缩写形式,即 a += 18;
在编程中,a = a + 18和a += 18是等价的,两者都会将变量a的值加上18,并将结果赋值给a。这两种写法的意义在于简化代码和提高可读性。
使用+=操作符可以使代码更加简洁和易于理解。它是一种常见的缩写形式,可以将加法运算和赋值操作合并到一起。比如,如果你需要多次对一个变量进行累加操作,使用+=可以让代码变得更简洁,方便开发人员编写更简洁的代码。
所以你可以将 a += 18理解为,在a的基础上,再加上18,这样会更方便理解
同上,a = a -10 可以缩写成 a -= 10,可以理解为在a的基础上再减10
同理,a = a * 9 可以缩写成 a *= 9,可以理解为在a的基础上,再乘上9
同理, a = a / 6 可以缩写成 a /= 6,可以理解为在a的基础上 /6
在c语言中,0表示假,而非0的值表示真,举个例子:
0是假
1是真
2是真
-1是真
1.2是真
-1.2是真
而 ! 操作符表示逻辑反操作,就是真变成假,假变成真,
! 0 是真
!1是假
!2是假
!(-1)是假
!1.2是假
!(-1.2)是假
这就是 !操作符的作用
&它用于获取一个变量的内存地址,这个地址表示了变量在计算机内存中存储的位置,而在C语言中,地址是用于标识内存中某个特定位置的值的位置。每个变量和每个数据在内存中都有唯一的地址。你可以将地址视为内存中的房间号,而变量和数据就是存放在这些房间中的内容。
比如:
int a = 5;
double b = 1.25;
char c = ‘w’;
对于每个变量来说,变量不仅有对应的值,还要唯一对应的空间,值属性和址属性是一个变量最基本的两个属性,可以将地址视为内存中的房间号,而变量和数据就是存放在这些房间中的内容。在此不过多深入
需要注意的是sizeof是一个操作符,而不是函数,许多人误以为sizeof是一个函数,sizeof操作符用于获取变量或数据类型的大小(以字节为单位),下面举一个简单的例子
#include
int main()
{
int a = 10;
int c = sizeof (a);
printf("a 占用的内存大小是(单位:字节) %d", c);
return 0;
}
程序的运行结果如下:
这段代码中,首先定义了一个整型变量,接着通过sizeof操作符求出a的大小(以字节为单位),赋给了c,接着打印c的值便可知道 a 所占的内存大小
sizeof的基本用法就是将要求所占空间大小的变量或者类型放在括号内即可
int a = 18;
要求 a 所占空间大小可以用sizeof
下面是sizeof的多种写法
注意! sizeof int 是错误的写法,要避免,著有当括号内是变量时,括号才能够省略
在C语言中,++操作符用于递增变量的值。每次递增 1 ,它有两种形式:前缀形式和后缀形式
#include
int main()
{
int x = 5;
int y = ++x;// 现在x的值为6,y的值也为6
printf("x = %d y = %d", x, y);
return 0;
}
首先我们定义了一个整型变量x,x的值为5
在第二行代码中,++在x的前面,故称作前置++,前置++会先自增 1 ,再进行运算
所以第行代码就等价与 int y = 6;所以x和y的值都为6
注意int y = ++x;与int y = x +1并不等价,int y = ++x;不仅改变了y的值,还改变了x的值,让x自增了 1 ,而int y = x +1只是改变了y的值,x的值并未发生改变,请注意区分
#include
int main()
{
int x = 5;
int y = x++;// 现在x的值为6,y的值为5
printf("x = %d y = %d", x, y);
return 0;
}
与++操作符类似,- -操作符也有两种前置 - -和后置 - -两种形式
#include
int main()
{
int x = 5;
int y = --x;// 现在x的值为4,y的值也为4
printf("x = %d y = %d", x, y);
return 0;
}
运行结果如图所示
和++操作符类似,因为 - -在x的前面,是前置 - -,我们先自减 1 ,在进行运算
2.后缀形式:var–
后缀形式会先使用变量的当前值,然后再将其减1。例如:
#include
int main()
{
int x = 5;
int y = x--;// 现在x的值为4,y的值为5
printf("x = %d y = %d", x, y);
return 0;
}
C语言中的强制类型转换操作符用于将一个数据类型转换为另一个数据类型。它的语法形式是:
(type) expression
请注意,强制类型转换可能会丢失精度或导致未定义的行为,因此在使用时需要谨慎。应该确保转换的结果是合理和可预期的。
( 类型)操作符是c语言中的强制类型转换操作符,请不要将强制类型转换操作符与函数中的()混淆,比如Add()
下面举一个简单的代码例子示例强制类型转换操作符的作用:
#include
int main() {
float num1 = 3.14;
int num2 = 2;
int result;
result = (int)num1 + num2; // 强制将浮点数 num1 转换为整数
printf("Result: %d\n", result);
return 0;
}
在上面的代码中,我们定义了一个浮点数变量 num1,一个整数变量 num2,以及一个整数变量 result。我们将 num1 使用强制类型转换操作符 (int) 转换为整数,然后与 num2 相加赋值给 result。最后,我们打印出 result 的值。
输出结果将为 Result: 5,因为 3.14 被转换为整数 3,然后与整数 2 相加得到结果 5。
C语言中的关系操作符用于比较两个值的大小关系,并返回一个布尔值(0或1)。以下是C语言中常用的关系操作符:
注意,在c语言中相等符号是 = =,而 = 是赋值符号,请不要混淆赋值符号和相等符号
下面演示一下关系操作符的用处:
#include
int main() {
int a = 5;
int b = 10;
if (a == b)
{
printf("a equals b\n");
}
else
{
printf("a is not equal to b\n");
}
if (a > b)
{
printf("a is greater than b\n");
}
else
{
printf("a is less than b\n");
}
return 0;
}
输出结果是:
a is not equal to b
a is less than b
C语言中的逻辑操作符用于对布尔表达式进行运算,并返回一个布尔值(0或1)。以下是C语言中常用的逻辑操作符:
逻辑操作符常用于条件判断和控制流程中。例如,在if语句中,可以使用逻辑与和逻辑非操作符组合对多个条件进行判断。
以下是一个示例代码,演示了逻辑操作符的使用:
#include
int main() {
int x = 10;
int y = 18;
int z = -26;
if (x > 0 && y > 0)
{
printf("x和y都是正数\n");//这句话被执行了
}
if (x > 0 && z > 0)
{
printf("x和z都是正数\n");
}
if (x > 0 || z > 0)//这句话被执行了
{
printf("x和z中存在有一个是正数\n");
}
if (x > 20 || z > 20)
{
printf("x和z中存在有一个数大于20\n");
}
if (x == y)
{
printf("x和y相等\n");
}
if (!(x == y))//这句话被执行了
{
printf("x和y不相等\n");
}
return 0;
}
输出结果为:
我们可以将逻辑与&&理解成我们生活中的并且
将逻辑或 | | 理解成我们生活中的或者
在C语言中,条件操作符(也被称为三元运算符)被用于根据一个条件的结果来选择两个值中的一个。它的语法形式为:
condition ? value1 : value2
我们可以通过条件操作符来实现一个简单的双分支,下面通过代码和画图的形式帮助读者理解:
#include
int main() {
int a = 88;
int b = 100;
int c = (a > b) ? a : b;
printf("a和b中的较大值是%d",c);
return 0;
}
运行结果如图所示
流程图如图所示:
我们可以通过条件操作符实现一个双分支,通过这个特性我们可以很快求出a和b中的最大值,并赋给c,最后打印出来
总的来说,条件操作符在C语言中的意义在于简化代码、提高可读性和灵活性。它是一种轻量级的条件选择和赋值工具,可以用于简单的条件判断和赋值操作。
在C语言中,逗号表达式是一种特殊的表达式,它允许在一个语句中同时使用多个表达式,并以逗号分隔。逗号表达式的结果是最后一个表达式的值。
逗号表达式的语法如下:
(expr1, expr2, expr3, …, exprn)
逗号表达式的执行过程是从左到右,依次计算每个表达式
并且整个逗号表达式的结果为最后一个表达式的值。
下面举个简单的代码例子演示逗号表达式的使用:
#include
int main() {
int a = 10;
int b = 18;
int c = 22;
int d = (a + b, b - c, c + a);
printf("d的值是:%d", d);
return 0;
}
int d = (a + b, b - c, c + a);在这段代码中,
虽然这段代码中直接计算c + a也等于32,那我们是不是遇见逗号表达式就直接计算最后一个式子就可以了呢?答案是否定的,接下来再看一个代码例子:
#include
int main() {
int x = 1, y = 2, z = 3;
int result = (x++, y++, z++, x + y + z);
printf("result的值是:%d\n", result);
printf("x的值是:%d\n", x);
printf("y的值是:%d\n", y);
printf("z的值是:%d\n", z);
return 0;
}
该代码会输出:
result的值是:9
x的值是:2
y的值是:3
z的值是:4
如果直接计算最后一个表达式,逗号表达式的结果应该是6,这说明了逗号表达式的每个操作都被顺序执行,并且返回最后一个表达式的结果。所以在实际编程中,不能直接计算逗号表达式的最后一个结果作为逗号表达式的结果
在C语言中,typedef关键字用于创建类型别名。它可以帮助我们简化复杂的类型声明,并增加代码的可读性。
typedef的基本语法如下:
typedef 原类型 新类型名;
下面是一些typedef的使用示例:
#include
int main() {
typedef int Age;//创建自定义类型别名
Age myAge = 25;
printf("%d", sizeof(Age));
return 0;
}
这段程序的输出结果是 4
typedef 在 C 语言中具有重要的意义,它允许我们为已有数据类型创建新的别名。这在以下几个方面非常有用:
总的来说,typedef 具有提高代码可读性、可移植性和模块化的优势,可以使代码更易于理解和维护
在c语言中,static关键字有三种用法
在C语言中,使用static关键字修饰局部变量具有下面两个作用:
下面是一个简单的示例代码,演示了static关键字修饰局部变量的作用:
#include
void example() {
static int count = 0; // 使用static关键字修饰局部变量
int normalCount = 0; // 普通的局部变量
count++;
normalCount++;
printf("Static Count: %d\n", count);
printf("Normal Count: %d\n", normalCount);
}
int main() {
example(); // 调用example函数
example(); // 再次调用example函数
example(); // 再次调用example函数
return 0;
}
void表示这个函数没有返回值
在上面的代码中,我们定义了一个名为example的函数。在函数内部,我们使用了static关键字修饰了一个名为count的局部变量,同时也定义了一个普通的局部变量normalCount。
每次调用example函数时,count变量都会自增,并且保留上一次的值。而normalCount变量则会在每次函数调用时重新初始化为0。
当我们运行上述代码时,输出将会是:
可以看到,使用static关键字修饰的count变量在多次函数调用之间保持了其值,而普通局部变量normalCount则每次函数调用时都重新初始化为0。
static修饰局部变量,本质上是将变量的存储空间由栈区转移到静态区
注意,static修饰局部变量只是改变了变量的生命周期,并没有改变变量的作用域,static只是让变量出了作用域仍然存在,直到程序结束,生命周期才结束
基本格式是:
static int x;
当在C语言中使用static修饰全局变量时,它会改变该全局变量的链接属性,让全局变量的外部连接属性改成了内部连接属性,限制其作用域只在声明它的源文件内部可见,无法被其他源文件访问。此时,该全局变量的作用类似于一个私有变量,只能在声明它的源文件内部使用。
通俗一点说,就是我们在文件 1 中定义的全局变量被static修饰后,那么在文件 2 中就无法使用这个全局变量,无法使用也就修改不了全局变量的值了
使用static修饰全局变量主要有以下几个作用:
总体来说,使用static修饰全局变量可以隐藏、保护全局变量的作用范围,使其更加安全且适用于特定的源文件内部使用。
基本格式是:
static void func() {
// 函数体
}
和修饰全局变量类似,在C语言中,使用static修饰函数可以将函数的作用域限制在当前源文件中,从而隐藏函数,使其无法在其他源文件中访问。
通俗一点说,就是我们在文件 1 中定义的函数被static修饰后,那么在文件 2 中就无法使用这个函数了
和全局变量类似,函数也具有外部链接属性,但是函数被static修饰之后,外部链接属性就变成了内部连接属性,使得函数只能在本文件中使用
当static修饰函数时,它的作用是隐藏函数。这可以有效地避免函数与其他源文件中的同名函数发生冲突,提高代码的可维护性。
extern的基本格式
在C语言中,extern关键字有以下两个主要用途:
extern关键字可以用于在一个源文件中声明在其他源文件中定义的全局变量。这样,在当前源文件中就能够使用该全局变量,而不需要重新定义它。
举个例子,假设我们有两个源文件 file1.c 和 file2.c,其中 file1.c 定义了一个全局变量 int globalVar = 10;。在 file2.c 中可以使用 extern int globalVar; 声明,来引用 file1.c 中定义的全局变量 globalVar。
// file1.c
int globalVar = 10;
// file2.c
extern int globalVar;
int main() {
printf("%d\n", globalVar); // 输出: 10
return 0;
}
extern关键字可以用于声明在其他源文件中定义的函数。这样,在当前源文件中就能够使用该函数,而不需要重新定义它。
举个例子,假设我们有两个源文件 file1.c 和 file2.c,其中 file1.c 定义了一个函数 void foo() { … }。在 file2.c 中可以使用 extern void foo(); 声明,来引用 file1.c 中定义的函数 foo()。
// file1.c
void foo() {
printf("Hello from foo!\n");
}
// file2.c
extern void foo();
int main() {
foo(); // 输出: Hello from foo!
return 0;
}
需要注意的是,extern关键字只能用于变量或函数的声明,而不能用于定义。变量或函数的定义应该在其他地方,例如其他源文件中。extern关键字的目的是告诉编译器该变量或函数是在其他地方定义的,并在当前文件中引用它。
总的来说,extern关键字用于引用在其他源文件中定义的全局变量和函数,使得在当前源文件中可以使用它们而无需重新定义。这样可以方便地实现源文件之间的变量和函数的共享和通信。
4: 内存空间分布
在C语言中,程序运行时会使用三种不同的内存空间:栈区、堆区和静态区(全局区)。
栈区(Stack):
堆区:
静态区(Static):
了解即可,后面会详细展开
在C语言中,#define是一个预处理指令,用于定义常量和宏。
define定义标识符常量:
#define MAX 1000
define定义宏
#define ADD(x, y) ((x)+(y))
和标识符常量类似,宏也会被替换,比如
ADD(x,y)就会被替换为((x)+(y)),
注意ADD(x, y) 是由#define定义的宏,而不是函数,请注意区分
下面通过一个简单代码例子来说明宏的作用:
#define ADD(x, y) (x)+(y)
#include
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);
printf("sum = %d\n", sum);
return 0;
}
宏定义是使用#define关键字指定的,宏的名称是ADD,宏的替换体是(x)+(y)。这个宏定义表示在代码中使用ADD(x, y)时,会将其替换为(x)+(y)。
在main函数中,有两个使用了宏的行。
表示将宏ADD中的x替换为2,y替换为3,得到的替换结果是(2)+(3),这个结果为5。然后将计算结果赋值给变量sum。
表示将宏ADD中的x替换为2,y替换为3,得到的替换结果是(2)+(3)。
请注意! sum = 10*ADD(2, 3); 中的ADD(2,3)被替换后 式子变成了10 * (2)+(3)这个结果等于23,请不要误以为是2和3相加后再乘10,宏只是简单的进行替换,并不运算
关于宏的替换规则,当使用ADD(x, y)时,预处理器会将其替换为(x)+(y)。在替换过程中,预处理器会将参数x和y直接替换到宏定义中的对应位置。这种替换是简单的文本替换,没有类型检查或计算。因此,在使用宏时要确保参数和替换体的正确匹配,并注意避免出现意料之外的错误。
一一个小格为一个单位,一个字节就是8个小格
因为一个二进制位可以存放0和1,有两种组合方式,一个字节有8位,所以一个字节可以有28= 256 中组合方式
在每一个字节的起始位置处,都有一个地址,我们通过这个地址就可以找到这个空间在内存中的位置
注意,每个地址都是唯一的
目前计算机主要分为32位计算机和64位计算机两种,
指针是用于存放地址的变量
在计算机上,有地址线电线产生的高低电平信号,这些信号会转换成数字信号0 和 1,32位机器顾名思义就是有32根地址线,而64位机器就是有64根地址线
对于一根地址线,它有两种组合,即0和1
对于两根地址线,它有4种组合,即00 01 10 11
对于三根地址线,它有8种组合,即 000 001 010 011 100 101 110 111
……
以此类推
那么32根地址线,就有232种组合,它能够管理232个地址
64根地址线,就有264种组合,它能够管理264个地址
所以我们要想储存一个变量的地址,
而一个字节又等于8个比特位,所以
注意,指针和地址是两个不同的概念,指针是用于存放地址的变量,而地址是变量中的很重要的一个属性
那么我们该如何通过指针来存放一个变量的地址呢?
#include
int main()
{
int num = 10;
int* p = #
*p = 20;
printf("%d", num);
return 0;
}
接下来我将通过画图的形式讲解
10的二进制是1010,如果补齐64位的话,就是
00000000 00000000 00000000 00000000
00000000 00000000 00000000 00001010
这就是10在内存中存储的数据
如图所示,我们定义了一个数值为10的空间,接着我们通过取地址符&,将num的地址取出来,注意,取地址取出来的是开头处的地址,然后把地址赋给了专门存放地址的变量->指针int *p,此时p就存放了num开头的地址了
那么此时我们就有了num的地址,并将地址存放在指针变量p中,我们该如何通过p来找到num呢?
这时又另外一种操作符
解引用操作符 *
注意,这个解引用操作符和p上的*意义完全不同,不要混淆
解引用操作符就是取地址操作符的逆运算
&num 就是通过num找到num的地址
而*p 则是通过num的地址,找到num(此时p存放的是num的地址)
我们说过,一个变量它有值属性和址属性两种属性
那么,这两个属性有什么关联吗?
答案是有的,我们可以通过值属性找到址属性,也可以通过址属性找到值属性
那么我们再来解释一下上面的代码
首先我们定义了一个int 类型的num变量,并赋值为10,接着将num的地址取出来,并将地址赋给了指针p,接着我们通过解引用操作符 * 来找到num的值,并将20赋给它,所以num的值也就被改成了20
在生活中,基本数据类型可以描述绝大多数的物体,比如说名字,身高,体重,但是还有一部分物体还不足够被描述,比如说我们该如何完整的描述一本书呢?包括书的名字,价格,作者。页数,等等,由此便有了结构体这个概念
C语言中的结构体是一种自定义的数据类型,它允许我们将不同类型的数据组合在一起,形成一个逻辑上相关的数据单元。结构体可以包含不同的数据类型,如整型、字符型、浮点型、指针等,并且可以根据需要添加多个成员变量。
结构体的定义使用关键字struct,后面跟着结构体的名称和花括号。在花括号中定义结构体的成员变量,每个成员变量由数据类型和名称组成,中间用分号分隔。
例如,下面是一个定义了一个简单的学生结构体的例子:
struct Student {
int id;
char name[20];
float score;
};
其中Student是结构体的名字,int id; char name[20]; float score;是结构体的成员
那么我们该怎么对结构体赋值呢?
struct Stu s = {"张三", 20, "男", "20180101"};
这样顺序赋值即可,后面会深入讲解结构体,目前了解这种方式即可
结构体成员的访问有两种方式
下面是代码示例:
#include
struct Stu
{
char name[20];
int age;
char sex[5];
char id[15];
};
int main()
{
struct Stu s = { "张三", 20, "男", "20180101" };
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
struct Stu* ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps -> id);
return 0;
}
本章完,后续会陆续更新c语言的知识,如果本文中出现错误,欢迎指出!!