我C了,这个C语言怎么这么难学?
个人的C语言学习笔记等以后忘了内容就回来看看。Hello World!依旧每个语言第一步但是有前提安装C语言环境这里我用的是windows系统liunx等到时候再说环境搭建首先需要用到gcc是个编译器很多语言都可以用这个先下载cygwin这是一个在Windows上模拟UNIX环境的软件cygwin官网连接下载完成后运行.exe文件就会进入这个界面点击下一页然后按照一下步骤操作这里我们可以添加阿里云镜像 https://mirrors.aliyun.com/cygwin/点击下一页之后就会开始下载等下载结束之后会自动打开软件包选择界面视图可以切换成”完整“就会变成下面的样子接下来开始搜索我们需要的软件包并下载ggc环境需要的包有gcc-core、gcc-g 、libgccpp1、gdb、make、cmake。像这样全部选择好之后下一页会有个审查列表忘记截图了确认完之后继续下一页就开始下载了。安装完成之后的样子是这样的要命居然让我等了挺长时间为了能让系统识别到gcc还需要把cygwin的bin目录添加到系统的PATH环境变量中在里面点击新建随后把路径输入进去即可。之后在终端中输入gcc --version就可以查看gcc的版本信息了gcc --version如果你得到的结果和上图内容差不多那恭喜你安装成功了也恭喜我。运行Hello World环境搭建好之后新建一个文本文件在里面添加这样的内容并将后缀名该为.C#include stdio.h int main() { printf(Hello, World! \n); }用命令行打开文件所在的位置然后执行gcc 文件名.c,之后会在当前目录生成一份.exe文件(在没有预设名称的情况会默认为a.exe)之后直接运行这个.exe文件就完成了gcc Hello.c a.exe这是结果基础语法C语言编写的程序是由令牌(Token)组成的编译器通过分析源代码把代码拆解成许多令牌令牌是编译器能识别的最小单元整个C程序由这些令牌构成。令牌主要有以下几个类型关键词代表有特殊含义的单词int、if等。标识符由程序员自定义的名称用于变量、函数等。常量一种固定不变的值整数、浮点数小数等。字符串字面量由双引号包裹的字符序列像是”hello“这样的。运算符就是执行运算的符号加减乘除与或非等。分割符用于分割代码块的符号;、{}。基本结构首先需要一串C程序的源码如下#include stdio.h int main() { printf(Hello, World! \n); return 0; }这是之前写的hello.c结构有这些预加载指令#include 在编译前由预处理器处理。主函数每个C程序由有且只有一个main()函数是程序的入口。函数定义定义程序中使用的函数描述函数的具体实现逻辑。变量声明声明程序中使用的变量C89 要求声明必须在代码块最开头。除此之外还有更加复杂的结构之后会对详细讲解分隔符用于分割语句、表达式和代码块帮助编译器快速理解结构常见的分隔符有逗号,用于变量声明、函数参数或初始化列表中的多个元素。分号;语句的结尾是常见的分隔符。圆括号( )用于函数调用时的参数列表循环语句的控制强制表达式求值顺序。方括号[ ]定义数组的声明和元素访问。大括号(花括号){ }用于定义函数体、控制代码块以及复合字面量与数组/结构体的初始化。一个单独的分号也可以作为一个空语句表示什么都不做while (condition); //空循环等条件发送变化注释C语言有两种注释方式// 这是单行注释 /* 多行注释 多行注释 多行注释 多行注释 */单行注释可以独占一行编译器不会”看到“也可以放在代码的末尾可以用来说明某个变量。多行注释适合用于函数说明模拟头部等较长的注释场景。在使用注释时需要注意一下几点不能出现内嵌套注释/* /* */ */是非法的。注释不能出现在字符串字面量或字符串常量内部。注释是人看到目的不是为了解释”做了什么“而是为了说明”为什么要这么做“应为代码本身就可以表达”做了什么“。养成良好的注释习惯是很重要的有利于团队之间的维护和开发。标识符标识符是程序中变量、函数、数组、类型等的名字由程序员自行定义。标识符由字母大写或小写、数字和下划线组成但第一个字符必须是字母或下划线不能是数字。标识符也有命名规则以A-Z或a-z或下划线_开始后跟零个或多个字母、下划线和数字(0-9)。C标识符内不允许出现标点字符比如、$和%。C是区分大小写的。命名建议标识符应具有描述性能清晰表达其用途。常见的命名风格有下划线命名法my_variable和小驼峰命名法myVariable在同一项目中保持一致即可。以双下划线__开头的标识符通常由编译器或标准库保留应避免在用户代码中使用。常量常量的意思就是固定值在执行程序期间不会改变C语言中定义常量主要有两种方式使用const关键字或者用#define宏定义。常量可以是整数常量、浮点数常量、字符常量、枚举常量等const int MAX 350234; // 整型常量 const float PI 0.618; // 浮点型常量 const char NEWLINE \n; // 字符常量转义字符字符常量中常用的转义字符包括\n换行\t水平制表符\\反斜杠\单引号\0空字符字符串结束标志使用const定义的常量有类型信息编译器可以进行类型检查相比#define宏更安全是 C99 及之后推荐的方式。字符串字面量简单直白说就是由双引号括起来的字符序列用于表示文本数据。字符串字面量在内存是以字符数组的形式存储的末尾会自动添加空字符标志字符串结束。char greeting[] see you again; // 编译器自动计算长度包含 \0值得注意的是”see“在内存中实际占用4个字节s、e、e、\0。字符串字面量存储在只读内存区域不可修改如需修改应将其复制到字符数组中。两个相邻的字符串字面量会被编译器自动拼接home, work会自动拼接为homework。字符A(字符常量)和字符串A(字符串字面量)是不同的后者包含A和\0这两个字符。运算符运算符用于执行各种操作如算术运算、逻辑运算、比较运算等。运算符作用于操作数形成表达式并产生结果C语言中常见的运算符如下算术运算符,-,*,/,%其中/对整数做整除%取余数关系运算符,!,,,,返回 1 表示真0 表示假逻辑运算符与,||或,!非支持短路求值位运算符按位与,|按位或,^按位异或,~按位取反,左移,右移赋值运算符,,-,*,/,%复合赋值如a b等价于a a b自增/自减运算符自增,--自减分前缀先运算再取值和后缀先取值再运算两种形式其他运算符sizeof计算类型或变量字节大小,?:三目运算符,取地址,*解引用,-指针访问成员,.直接访问成员运算符具有优先级和结合性优先级决定运算顺序结合性决定同级运算符的运算方向。当不确定优先级时建议使用圆括号明确分组以提高代码可读性。关键字关键字说明auto声明自动变量局部变量默认存储类型现代 C 中很少显式使用break跳出当前循环或 switch 语句caseswitch 语句中的分支标签char声明字符型变量或函数返回值类型通常占 1 字节const定义只读常量被 const 修饰的变量其值不能再被改变continue结束本次循环迭代直接开始下一轮循环defaultswitch 语句中的默认分支所有 case 均不匹配时执行dodo-while 循环的循环体循环体至少执行一次double声明双精度浮点型变量或函数返回值类型通常占 8 字节else条件语句的否定分支与 if 连用enum声明枚举类型用于定义一组命名的整型常量extern声明变量或函数在其它文件或本文件的其他位置定义float声明单精度浮点型变量或函数返回值类型通常占 4 字节forfor 循环语句适合已知循环次数的场景goto无条件跳转语句应谨慎使用可能降低代码可读性if条件判断语句int声明整型变量或函数返回值类型通常占 4 字节long声明长整型变量或函数返回值类型register建议编译器将变量存入寄存器以提高访问速度现代编译器通常自动优化return函数返回语句可带返回值也可不带void 函数short声明短整型变量或函数返回值类型通常占 2 字节signed声明有符号类型变量可表示负数整型默认有符号sizeof计算数据类型或变量所占的字节数编译期运算符static声明静态变量局部静态变量生命周期延续到程序结束全局静态变量限制为文件作用域struct声明结构体类型将不同类型的数据组合成一个整体switch多分支选择语句根据表达式的值跳转到对应 casetypedef为已有数据类型定义新的别名提高代码可读性unsigned声明无符号类型变量只能表示非负数范围更大union声明共用体类型所有成员共享同一块内存空间void声明函数无返回值或无参数也用于声明无类型指针void*volatile声明变量可能被程序外部因素如硬件或中断改变禁止编译器优化该变量的读写whilewhile 循环语句适合循环次数未知但终止条件明确的场景不需要全部记以后写程序的时候写着写着就记住了C中的空格只包含空格的行被称为空白行可能带有注释编译器会完全忽略它。在C中空格只是为了让编译器能够识别语句中的关键词或者变量等。例如int total;这里 int 和 total之间的空格作用就是让编译器可以识别 int 和 total这两个语句。这一节总算是结束了基本都是理论知识而且后续写代码的过程中都会自然而然的形成一种习惯的。数据类型数据类型是用于声明不同变量或函数的变量的·类型确定了变量的内存占用空间以及如何解释储存的位模式。C中的类型可以分为以下几种基本数据类型是算数类型包括整数类型(int)、字符型(char)、浮点型(float)和双精度浮点型(double)枚举类型这个也是算数类型被用来定义在程序中只能赋予其一定的离散整数的变量。void类型类型说明符void说明没有值的数据类型通常用于函数返回值。派生类型包括数组类型指针和结构体类型。数组和结构体统称为聚合类型。函数的类型是指函数的返回值类型。整数类型类型存储大小值范围char1 字节-128 到 127 或 0 到 255unsigned char1 字节0 到 255signed char1 字节-128 到 127int2 或 4 字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647unsigned int2 或 4 字节0 到 65,535 或 0 到 4,294,967,295short2 字节-32,768 到 32,767unsigned short2 字节0 到 65,535long4 字节-2,147,483,648 到 2,147,483,647unsigned long4 字节0 到 4,294,967,295为了得到某个类型或某个变量在特定平台上的准确大小可以使用sizeof(type)获取对象或类型的储存字节大小示例#include stdio.h #include limits.h int main() { printf(float 储存大小 %lu \n, sizeof(unsigned long)); }这里的“%lu”可以视为可被替换的占位符这个之后会讲上面代码的执行结果float 储存大小 8浮点类型下表列出了关于标准浮点类型的存储大小、值范围和精度类型存储大小值范围精度float4 字节1.2E-38 到 3.4E386 位有效位double8 字节2.3E-308 到 1.7E30815 位有效位long double16 字节3.4E-4932 到 1.1E493219 位有效位下面的实例将输出浮点类型占用的存储空间以及它的范围值#include stdio.h #include float.h int main() { printf(float 存储最大字节数 : %lu \n, sizeof(float)); printf(float 最小值: %E\n, FLT_MIN ); printf(float 最大值: %E\n, FLT_MAX ); printf(精度值: %d\n, FLT_DIG ); return 0; }执行后的结果float 存储最大字节数 : 4 float 最小值: 1.175494E-38 float 最大值: 3.402823E38 精度值: 6void类型void类型没有可用的值通常用于这三种情况序号类型与描述1函数返回为空C 中有各种函数都不返回值或者您可以说它们返回空。不返回值的函数的返回类型为空。例如void exit (int status);2函数参数为空C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如int rand(void);3指针指向 void类型为 void * 的指针代表对象的地址而不是类型。例如内存分配函数void *malloc( size_t size );返回指向 void 的指针可以转换为任何数据类型。类型转换一个类型是可以转换成另一个类型的C语言中由两种转换方式隐式类型转换这种转换方式是在表达式中自动发生的无需进行任何函数调用或是指令通常是较小的类型转换成较大的类型比如int类型转换成long类型float类型转换成double类型。显式类型转换显示转换需要使用强制类型转换运算符可以将一种数据类型的值强行转换成另一种可能会导致数据丢失或截断。隐式类型转换#include float.h #include stdio.h int main() { int i 10; float f 2.5; double d i f; printf(得到的结果为%f,d); // 得到的结果为12.500000 }显式类型转换#include stdio.h #include float.h int main() { float f 2.5; int i (int)f; printf(结果为%u, i); // 结果为2 }C语言的数据类型到此结束说下感想数据在计算机语言中极为重要我认为一台计算机之所以能做到如此智能就是进行数据交互的结果无数的数据在电路板之间传输慢慢构建起一个庞大的互联网变量变量其实是可操控存储区的名称每个变量都有特定的类型类型决定了这个变量的储存大小和布局运算符可以应用与变量上。C中的变量定义变量定义就是让编译器知道在何时何处创建变量的储存以及怎么创建。变量定义的方法是指定一个数据类型并包含了该类型的一个或多个变量格式如下//数据类型 变量名或变量名列表; int i; float score; //定义多个变量 int num1, num2, num3; float fnum1, fnum2, fnum3;上半部分定义了int类型变量ifloat类型score下半部分定义了int类型的三个变量num1、num2、num3float类型的三个变量fnum1、fnum2、fnum3初始化变量所谓初始化变量就是在定义变量的同时给这个变量赋值初始化不仅可以在定义时进行也可以在后续代码中进行。定义变量时初始化初始化变量的格式如下// 类型 变量名 值 int age 18;再举几个详细的例子int x 10; // 整型变量 x 初始化为 10 float pi 3.14; // 浮点型变量 pi 初始化为 3.14 char ch A; // 字符型变量 ch 初始化为字符 A int d 3, f 5; // 定义并初始化 d 和 f byte z 22; // 定义并初始化 z // 声明外部变量 extern int d; extern int f;定义变量后初始化后续初始化变量的格式// 变量名 值; int i; i 325;更多例子int x; // 整型变量x定义 x 20; // 变量x初始化为20 float pi; // 浮点型变量pi定义 pi 3.14159; // 变量pi初始化为3.14159 char ch; // 字符型变量ch定义 ch B; // 变量ch初始化为字符B需要注意的是变量在使用之前应该被初始化。未初始化的变量的值是未定义的可能包含任意的垃圾值。因此为了避免不确定的行为和错误建议在使用变量之前进行初始化。默认值如果你定义了一个变量但是没有显式初始化第一次赋值那么这个变量将会以该数据类型的默认值为变量的值。以下是部分数据类型的默认值整型变量int、short、long等默认值为0。浮点型变量float、double等默认值为0.0。字符型变量char默认值为\0即空字符。指针变量默认值为NULL表示指针不指向任何有效的内存地址。数组、结构体、联合等复合类型的变量它们的元素或成员将按照相应的规则进行默认初始化这可能包括对元素递归应用默认规则。需要注意的是局部变量在函数内部定义的非静态变量不会自动初始化为默认值它们的初始值是未定义的包含垃圾值。因此在使用局部变量之前应该显式地为其赋予一个初始值。总结起来C 语言中变量的默认值取决于其类型和作用域。全局变量和静态变量的默认值为0字符型变量的默认值为 \0指针变量的默认值为 NULL而局部变量没有默认值其初始值是未定义的。变量的声明变量声明向编译器保证变量以指定的类型和名称存在这样编译器在不需要知道变量完整细节的情况下也能继续进一步的编译。变量声明只在编译时有它的意义在程序连接时编译器需要实际的变量声明。变量的声明有两种情况1、一种是需要建立存储空间的。例如int a 在声明的时候就已经建立了存储空间。2、另一种是不需要建立存储空间的通过使用extern关键字声明变量名而不定义它。 例如extern int a 其中变量 a 可以在别的文件中定义的。示例#include stdio.h // 函数外定义变量 x 和 y int x; int y; int addtwonum() { // 函数内声明变量 x 和 y 为外部变量 extern int x; extern int y; // 给外部变量全局变量x 和 y 赋值 x 1; y 2; return xy; } int main() { int result; // 调用函数 addtwonum result addtwonum(); printf(result 为: %d,result); return 0; }当上面代码编译并执行之后的结果result 为: 3如果需要在一个源文件中引用另外一个源文件中定义的变量我们只需在引用的文件中将变量加上 extern 关键字的声明即可。这是第一个文件代码(addtwonum.c)#include stdio.h /*外部变量声明*/ extern int x ; extern int y ; int addtwonum() { return xy; }这是另一个(test.c)#include stdio.h /*定义两个全局变量*/ int x1; int y2; int addtwonum(); int main(void) { int result; result addtwonum(); printf(result 为: %d\n,result); return 0; }两个代码一起编译并执行$ gcc addtwonum.c test.c -o main $ main.exe输出结果左值和右值左值lvalue指向内存位置的表达式被称为左值lvalue表达式。左值可以出现在赋值号的左边或右边。右值rvalue术语右值rvalue指的是存储在内存中某些地址的数值。右值是不能对其进行赋值的表达式也就是说右值可以出现在赋值号的右边但不能出现在赋值号的左边。变量是左值因此可以出现在赋值号的左边。数值型的字面值是右值因此不能被赋值不能出现在赋值号的左边常量既然有变量那就有常量常量在程序执行期间不会变更这些固定的值又叫做字面量。常量可以是任何基本数据类型例如整数常量浮点常量字符常量字符串常量也有枚举常量。整数常量整数常量可以是十进制、八进制或十六进制的常量。前缀指定基数0x 或 0X 表示十六进制0 表示八进制不带前缀则默认表示十进制。整数常量也可以带一个后缀后缀是 U 和 L 的组合U 表示无符号整数unsignedL 表示长整数long。后缀可以是大写也可以是小写U 和 L 的顺序任意。举几个常量实例212 /* 没问题正常整数 */ 215u /* 没问题这个“u”表示无符号整数 */ 0xFeeL /* 没问题16进制 */ 078 /* 有问题8 不是八进制的数字 */ 032UU /* 有问题不能重复后缀 */还有各种类型的常量实例85 /* 十进制 */ 0213 /* 八进制 */ 0x4b /* 十六进制 */ 30 /* 整数 */ 30u /* 无符号整数 */ 30l /* 长整数 */ 30ul /* 无符号长整数 */浮点常量浮点常量由整数部分、小数点、小数部分和指数部分组成。您可以使用小数形式或者指数形式来表示浮点常量。当使用小数形式表示时必须包含整数部分、小数部分或同时包含两者。当使用指数形式表示时 必须包含小数点、指数或同时包含两者。带符号的指数是用 e 或 E 引入的。举几个浮点常量实例3.14159 /* 没问题标准的浮点数 */ 314159E-5L /* 没问题指数形式浮点数 */ 510E /* 有问题不完整的指数 */ 210f /* 有问题没有小数或指数 */ .e55 /* 有问题缺少整数或分数 */字符常量字符常量是括在单引号中例如x 可以存储在char类型的简单变量中。字符常量可以是一个普通的字符例如 x、一个转义序列例如 \t或一个通用的字符例如 \u02C0。在 C 中有一些特定的字符当它们前面有反斜杠时它们就具有特殊的含义被用来表示如换行符\n或制表符\t等。这里列一个转义序列码转义序列含义\\\ 字符\ 字符\ 字符\?? 字符\a警报铃声\b退格键\f换页符\n换行符\r回车\t水平制表符\v垂直制表符\ooo一到三位的八进制数\xhh . . .一个或多个数字的十六进制数有好多转义字符我都没用过等写完这篇博客再去试试。字符常量的 ASCII 值可以通过强制类型转换转换为整数值。char myChar a; int myAsciiValue (int) myChar; // 将 myChar 转换为 ASCII 值 97字符串常量字符串字面值或常量是括在双引号 中的。一个字符串包含类似于字符常量的字符普通的字符、转义序列和通用的字符。您可以使用空格做分隔符把一个很长的字符串常量进行分行。下面的实例显示了一些字符串常量。下面这三种形式所显示的字符串是相同的。hello, dearhello, \dearhello, d ear定义常量在 C 中有两种简单的定义常量的方式使用#define预处理器 #define 可以在程序中定义一个常量它在编译时会被替换为其对应的值。使用const关键字const 关键字用于声明一个只读变量即该变量的值不能在程序运行时修改。#defind 预处理#defind定义常量的格式是这样的#defind 常量名 常量值来个示例#define character Rem #include stdio.h int main() { printf(who is my favorite character? \n); printf(its: %s, character); }Rem是动漫《re:0》中的雷姆编译并执行后的结果who is my wife?its: Remconst 关键字用const定义常量格式const 数据类型 常量名 常量值;在用const定义常量时必须在一个语句内完成示例#include stdio.h int main() { const char unchanging[] My belief; printf(whatt is unchanging? \n); printf(ist: %s,unchanging); return 0; }编译并执行后的结果whatt is unchanging?ist: My belief两种常量定义方式的区别#define 与 const 这两种方式都可以用来定义常量选择哪种方式取决于具体的需求和编程习惯。通常情况下建议使用 const 关键字来定义常量因为它具有类型检查和作用域的优势而 #define 仅进行简单的文本替换可能会导致一些意外的问题。#define 预处理指令和 const 关键字在定义常量时有一些区别替换机制#define是进行简单的文本替换而const是声明一个具有类型的常量。#define定义的常量在编译时会被直接替换为其对应的值而const定义的常量在程序运行时会分配内存并且具有类型信息。类型检查#define不进行类型检查因为它只是进行简单的文本替换。而const定义的常量具有类型信息编译器可以对其进行类型检查。这可以帮助捕获一些潜在的类型错误。作用域#define定义的常量没有作用域限制它在定义之后的整个代码中都有效。而const定义的常量具有块级作用域只在其定义所在的作用域内有效。调试和符号表使用#define定义的常量在符号表中不会有相应的条目因为它只是进行文本替换。而使用const定义的常量会在符号表中有相应的条目有助于调试和可读性。从别的地方抄过来的想让我自己重新写的话直接跟我说。储存类为啥叫这个名字储存类定义了C语言中变量或者函数的储存位置作用域和生命周期。可用的储存类一共有这些autoregisterstaticexternautoauto储存类时所有局部变量的默认储存类。定义在函数中的变量默认auto储存类在函数开始时创建结束时销毁int main() { int count; auto int age; }上面定义了两个auto储存类的变量auto只能在函数内使用也就是只能修饰局部变量registerregister存储类用于定义存储在寄存器中而不是 RAM 中的局部变量。这意味着变量的最大尺寸等于寄存器的大小通常是一个字且不能对它应用一元的 运算符因为它没有内存位置。register 存储类定义存储在寄存器所以变量的访问速度更快但是它不能直接取地址因为它不是存储在 RAM 中的。在需要频繁访问的变量上使用 register 存储类可以提高程序的运行速度。{register int num;}寄存器只用于需要快速访问的变量比如计数器。还应注意的是定义 register 并不意味着变量将被存储在寄存器中它意味着变量可能存储在寄存器中这取决于硬件和实现的限制。staticstatic储存类指示编译器在程序生命周期内保持局部变量的存在不需要每次都创建和销毁使用static可以在调用函数之间保持局部的变量值。static也可以用于全局变量当用来修饰全局变量时会使变量只能在声明它的文件中。static修饰全局变量和局部变量的演示#include stdio.h /* 函数声明 */ void func1(void); static int count10; /* 全局变量 - static 是默认的 */ int main() { while (count--) { func1(); } return 0; } void func1(void) { /* thingy 是 func1 的局部变量 - 只初始化一次 * 每次调用函数 func1 thingy 值不会被重置。 */ static int thingy5; thingy; printf( thingy 为 %d count 为 %d\n, thingy, count); }编译并执行后的结果thingy 为 6 count 为 9thingy 为 7 count 为 8thingy 为 8 count 为 7thingy 为 9 count 为 6thingy 为 10 count 为 5thingy 为 11 count 为 4thingy 为 12 count 为 3thingy 为 13 count 为 2thingy 为 14 count 为 1thingy 为 15 count 为 0externextern存储类用于定义在其他文件中声明的全局变量或函数。当使用 extern 关键字时不会为变量分配任何存储空间而只是指示编译器该变量在其他文件中定义。extern存储类用于提供一个全局变量的引用全局变量对所有的程序文件都是可见的。当您使用extern时对于无法初始化的变量会把变量名指向一个之前定义过的存储位置。第一个文件main.c#include stdio.h int count ; extern void write_extern(); int main() { count 5; write_extern(); }第二个文件support.c#include stdio.h extern int count; void write_extern(void) { printf(count is %d\n, count); }同时编译两个文件$ gcc main.c support.c执行结果count is 5