c语言基础
一、环境搭建
-
编译器(MinGW)
- 下载地址:https://osdn.net/projects/mingw/downloads/68260/mingw-get-setup.exe
- 安装中一个注意细节
需要依次右键标记安装,然后点击Apply Changes
-
IDE(CLion)
- 下载地址:https://www.jetbrains.com/clion/download/other.html
- 建议选择2022.1.3版本
需要atob处理后使用: R1NTWFpFWjU2Ry1leUpzYVdObGJuTmxTV1FpT2lKSFUxTllXa1ZhTlRaSElpd2liR2xqWlc1elpXVk9ZVzFsSWpvaWMybG5iblZ3SUhOamIyOTBaWElpTENKaGMzTnBaMjVsWlU1aGJXVWlPaUlpTENKaGMzTnBaMjVsWlVWdFlXbHNJam9pSWl3aWJHbGpaVzV6WlZKbGMzUnlhV04wYVc5dUlqb2lJaXdpWTJobFkydERiMjVqZFhKeVpXNTBWWE5sSWpwbVlXeHpaU3dpY0hKdlpIVmpkSE1pT2x0N0ltTnZaR1VpT2lKUVUwa2lMQ0ptWVd4c1ltRmphMFJoZEdVaU9pSXlNREkxTFRBNExUQXhJaXdpY0dGcFpGVndWRzhpT2lJeU1ESTFMVEE0TFRBeElpd2laWGgwWlc1a1pXUWlPblJ5ZFdWOUxIc2lZMjlrWlNJNklsQlRWeUlzSW1aaGJHeGlZV05yUkdGMFpTSTZJakl3TWpVdE1EZ3RNREVpTENKd1lXbGtWWEJVYnlJNklqSXdNalV0TURndE1ERWlMQ0psZUhSbGJtUmxaQ0k2ZEhKMVpYMHNleUpqYjJSbElqb2lVRmRUSWl3aVptRnNiR0poWTJ0RVlYUmxJam9pTWpBeU5TMHdPQzB3TVNJc0luQmhhV1JWY0ZSdklqb2lNakF5TlMwd09DMHdNU0lzSW1WNGRHVnVaR1ZrSWpwMGNuVmxmU3g3SW1OdlpHVWlPaUpEVENJc0ltWmhiR3hpWVdOclJHRjBaU0k2SWpJd01qVXRNRGd0TURFaUxDSndZV2xrVlhCVWJ5STZJakl3TWpVdE1EZ3RNREVpTENKbGVIUmxibVJsWkNJNlptRnNjMlY5TEhzaVkyOWtaU0k2SWxCRFYwMVFJaXdpWm1Gc2JHSmhZMnRFWVhSbElqb2lNakF5TlMwd09DMHdNU0lzSW5CaGFXUlZjRlJ2SWpvaU1qQXlOUzB3T0Mwd01TSXNJbVY0ZEdWdVpHVmtJanAwY25WbGZWMHNJbTFsZEdGa1lYUmhJam9pTURFeU1ESXlNRGt3TWxCVFFVNHdNREF3TURVaUxDSm9ZWE5vSWpvaVZGSkpRVXc2TlRNNU9ERTVNREl5SWl3aVozSmhZMlZRWlhKcGIyUkVZWGx6SWpvM0xDSmhkWFJ2VUhKdmJHOXVaMkYwWldRaU9tWmhiSE5sTENKcGMwRjFkRzlRY205c2IyNW5ZWFJsWkNJNlptRnNjMlY5LWlBVUw5Yzg5aEErb1hnRkFhanNWOVF5Q09Wc0VOSkdrWmlHUXZXN1hLYUpJRDVYdFhieFU0aUJkTVBhaUpFMUI4ZFpKeTI3VnQxV0pqaW4rb1F0blNDNDd0ME1DeDYxdlBPQkdrL0tBdkVCb2k2ZDhtZlA1dTcyOWcwR0h2YTNuUFhzWTMrSWJuc1dtN1NJc0Iwc3l2dHIxVlJtcXQ3dEJLbTJGNmdSbjRaOFNudmtvUXV5cnZ0Wk44anVKQkV6M2hZR0hINGgranBNbkFZalhaME05MldhRDRhSXZ0R2F6cDQxZjQvRnVYVzZJY1NIVHd1YWxXVkh1dlM5NWZ1eHhBd3BieXhQeVlMb20xVWgvbGE2a1k5K3ppM1I1WjFhbU9IRU9DeVpuU29GRGNNZUtGM3loUVU2L0FqWWlUL2ZPZklDTmNhSy9CSFlaMFZGTUk4ek9RQT09LU1JSUVURENDQWpTZ0F3SUJBZ0lCRFRBTkJna3Foa2lHOXcwQkFRc0ZBREFZTVJZd0ZBWURWUVFEREExS1pYUlFjbTltYVd4bElFTkJNQjRYRFRJd01UQXhPVEE1TURVMU0xb1hEVEl5TVRBeU1UQTVNRFUxTTFvd0h6RWRNQnNHQTFVRUF3d1VjSEp2WkRKNUxXWnliMjB0TWpBeU1ERXdNVGt3Z2dFaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQkR3QXdnZ0VLQW9JQkFRQ1VsYVVGYzF3ZitDZlk5d3pGV0VMMmV1S1E1bnN3cWI1N1Y4UVpHN2Q3Um9SNnJ3WVVJWHNlVE9BRnEyMTBvTUVlKytMQ2p6S0R1cXdEZnN5aGdETlRnWkJQQWFDNHZVVTJveStYUitGcThuQml4V0lzSDY2OEhlT25SSzZSUmhzcjBySnpSQjk1YVozRUFQekJ1UTJxUGFOR20xN3BBWDBSZDZNUFJnanA3NUlXd0k5ZUE2YU1FZFBRRVZON3V5T3RNNXpTc2pvajc5TGJ1MWZqU2hPblFadUpjc1Y4dHFuYXllRmtOenYyTFRPbG9mVS9UYng1MDJSbzA3M2dHam9lUnpOdnJ5bkFQMDNwTDQ4NlAzS0NBeWlOUGhEczJ6OC9DT01yeFJsWlc1bWZ6bzB4c0swZFFHTkgzVW9HLzlSVndIRzRlUzhMRnBNVFI5b2V0SFpCQWdNQkFBR2pnWmt3Z1pZd0NRWURWUjBUQkFJd0FEQWRCZ05WSFE0RUZnUVVKTm9SSXBiMWhVSEFrMGZvTVNOTTlNQ0VBdjh3U0FZRFZSMGpCRUV3UDRBVW81NjJTR2RDRWpaQnZXM2d1YlNnVW91WDhiT2hIS1FhTUJneEZqQVVCZ05WQkFNTURVcGxkRkJ5YjJacGJHVWdRMEdDQ1FEU2JMR0Rzb041NFRBVEJnTlZIU1VFRERBS0JnZ3JCZ0VGQlFjREFUQUxCZ05WSFE4RUJBTUNCYUF3RFFZSktvWklodmNOQVFFTEJRQURnZ0lCQUJxUm9OR3hBUWN0OWRRVUZLOHhxaGlaYVlQZDMwVGxtQ21TQWFHSjBlQnB2a1ZlcUEyakdZaEFRUnFGaUFsRkM2M0pLdld2UlpPMWlSdVdDRWZVTWtkcVE5VlFQWHppRS9CbHNPSWdyTDZSbEpmdUZjRVo4VEszc3lJZklHUVpOQ3hZaExMVXVldDJIRTZMSllQUTVjMGpINGtEb29ScGNWWjRyQnhOd2RkcGN0VU8ydGU5VVU1L0ZqaGlvWlFzUHZkOTJxT1RzVis4Q3lsMmZ2TmhOS0QxVXU5ZmY1QWtWSVFuNEpVMjNvemRCL1I1b1VsZWJ3YVRFNldaTkJzK1RBL3FQais1L3dlOU5INzFXUkIwaHFVb0xJMkFLS3lpUHcrK0Z0TjRTdTF2c2REbHJBekRqOUlManBqSktBMUltdVZjRzMyOS9XVFlJS3lzWjFDV0szekFUZzlCZUNVUEFWMXBReThUb1hPcStSU1llbjZ3aW5aMk9POTNleUh2Mkl3NWtibjFkcWZCdzFCdVRFMjlWMkZKS2ljSlN1OGlFT3Bmb2Fmd0pJU1htejF3bm5XTDNWLzBOeFR1bGZXc1h1Z09vTGZ2MFpJQlAxeEg5a21mMjJqalEySmlIaFFaUDdaRHNyZVJyT2VJUS9jNHlSOElRdk1MZkMwV0tRcXJIdTVaelhUSDROTzNDd0dXU2xUWTc0a0U5MXpYQjVtd1dBeDFqaWcrVVhZYzJ3NFJrVmh5MC8vbE9tVnlhL1BFZXB1dVRUSTQrVUp3QzdxYlZsaDV6ZmhqOG9UTlVYZ04wQU9jK1EwL1dGUGwxYXc1VlYvVnJPOEZDb0IxNWxGVmxwS2FRMVloK0RWVThrZStydDlUaDBCQ0hYZTB1Wk9FbUgwbk9uSC8wb25E
- 解决中文乱码问题:
- 按住快捷键Ctrl + shift + alt + /
- 然后点开弹出的界面中第一项Regestry...
- 再取消勾选第一个Key(run.process.with.pty)后面的value选项
二、基础语法
-
分类
- 字符串
- 数组
- 整型相关(short、int、long)
- 指针
- 结构体
- 预处理和头文件
-
字符串
- 在C语言中没有字符串。
#include <stdio.h> #include <string.h> int main() { // 字符数组 <=> 字符串 char v1[] = "abc"; // 等价于 char v1[] = {'a', 'b', 'c', '\0'}; char v2[] = {'h', 'e', 'l', 'l', 'o','\0'}; char v3[] = "def"; // sizeof()方法:用于计算各数据类型或变量所占用字节数,针对字符串,包含末尾的空字符'\0' printf("字符串(%s)字节大小:%d\n", v1, sizeof(v1) / sizeof(char)); // 结果:4 // strlen()方法:用于计算一个字符串的长度(不包括字符串末尾的空字符’\0’) printf("字符串(%s)长度:%d\n", v1, strlen(v1)); // 结果:3 // strcpy()函数:将一个字符串数组中的值拷贝到另一个字符串数组中,在C语言中,数组是不能直接赋值给另一个数组的。 char v4[sizeof(v1) / sizeof(char)]; // 定义v4数组,保证大小与v1数组相同 strcpy(v4, v1); // 使用strcpy()函数将v1的值拷贝到v4中 // strcat()方法:字符串拼接,会修改dest字符串的内容,dest和src字符串必须以空字符('\0')结尾,否则连接的结果可能会不正确。 strcat(v1, v3); printf("字符串(%s)拼接(%s)后结果:%s,长度:%d\n", v4, v3, v1, strlen(v1)); // 字符数组 -> 字符串 printf("v2值:%s\n", v2); }
- 在C语言中没有字符串。
-
数组
- 数组中元素固定、类型固定
# include <stdio.h> int main(int argc, char const *argv[]) { // 字符数组 char v1[] = "hello"; // 整型数组(每个整型4字节) int v2[] = {11, 22, 33, 44, 55}; printf("v1的值为: %p \n", v1); printf("v1的值为: %p \n", &v1); printf("v1的值为: %p \n", &v1[0]); printf("v1的值为: %p \n", &v1[1]); printf("----------------------------------------\n"); printf("v2的值为: %p \n", v2); printf("v2的值为: %p \n", &v2); printf("v2的值为: %p \n", &v2[0]); printf("v2的值为: %p \n", &v2[1]); return 0; } /*输出结果 v1的值为: 000000c336bff8ba v1的值为: 000000c336bff8ba v1的值为: 000000c336bff8ba v1的值为: 000000c336bff8bb ---------------------------------------- v2的值为: 000000c336bff8a0 v2的值为: 000000c336bff8a0 v2的值为: 000000c336bff8a0 v2的值为: 000000c336bff8a4 */
-
整型相关
- int :4字节
- long:(通常为4字节或8字节,与平台相关,因系统而异)
# include <stdio.h> int main(int argc, char const *argv[]) { // 字符数组 // signed short v1[] = {11, 22, 33}; //默认是有符号类型 // unsigned short v1[] = {11, 22, 33}; short v1[] = {-11, 22, 33}; int v2[] = {11, 22, 33}; long v3[] = {111,222,333}; printf("short值:%d,内存地址:%p\n", v1[0], &v1[0]); printf("short值:%d,内存地址:%p\n", v1[1], &v1[1]); printf("----------------------\n"); printf("int值:%d,内存地址:%p\n", v2[0], &v2[0]); printf("int值:%d,内存地址:%p\n", v2[1], &v2[1]); printf("----------------------\n"); printf("long值:%d,内存地址:%p\n", v3[0], &v3[0]); printf("long值:%d,内存地址:%p\n", v3[1], &v3[1]); // 相邻地址差值是4, return 0; } /* short值:-11,内存地址:0000009e389ffcfa short值:22,内存地址:0000009e389ffcfc ---------------------- int值:11,内存地址:0000009e389ffcec int值:22,内存地址:0000009e389ffcf0 ---------------------- long值:111,内存地址:0000009e389ffce0 long值:222,内存地址:0000009e389ffce4 */
-
指针
- 本质上是一种数据类型,用于表示某种数据的内存地址,可以实现让多个变量指向同一个值
- 两个关键字符:
- *
- 放在定义变量前:表示指向这个类型的指针【比如:int *v2 = &v1;】
- 放在指针类型变量前:表示解引用,即获取内存地址中存储的值
- 一个指针占8字节
# include <stdio.h> int main(int argc, char const *argv[]) { int v1[] = {11, 22, 33}; int *p1 = v1; // 等价于&v1、&v1[0] printf("指针类型占据字节大小:%d\n", sizeof(p1)); printf("指针指向的内存地址:%p,存储的数据:%d,指针自身内存地址:%p \n", p1, *p1, &p1); p1 += 1; printf("指针指向的内存地址:%p,存储的数据:%d,指针自身内存地址:%p \n", p1, *p1, &p1); char v2[20]; char *p2 = v2; /* sprintf函数是C语言中的一个字符串格式化输出函数,用于将数据按照指定的格式输出到字符串中 参数1:是存放格式化输出结果的字符串的指针 参数2:格式化字符串 后面是可变参数 */ sprintf(p2, "%c", 'h'); p2 += 1; sprintf(p2, "%c", 'e'); p2 += 1; sprintf(p2, "%c", 'l'); p2 += 1; sprintf(p2, "%c", 'l'); p2 += 1; sprintf(p2, "%c", 'o'); p2 += 1; sprintf(p2, "%c", '\0'); printf("依次添加单个字符后,格式化输出结果:%s \n", v2); char v3[50]; int num = 123; float f = 3.14; sprintf(v3, "数字:%d,浮点数:%f\n", num, f); printf("一次性格式化输出结果:%s\n", v3); return 0; } /* 指针类型占据字节大小:8 指针指向的内存地址:00000001001ffd24,存储的数据:11,指针自身内存地址:00000001001ffd18 指针指向的内存地址:00000001001ffd28,存储的数据:22,指针自身内存地址:00000001001ffd18 依次添加单个字符后,格式化输出结果:hello 一次性格式化输出结果:数字:123,浮点数:3.140000 */
- 示例:通过指针修改字符串的值
#include <stdio.h> int main() { char v[] = "hello"; char *pp = v; // 修改字符串的值,等价于后面的操作 // pp[0] = 'a'; // pp[3] = 'b'; *pp = 'a'; pp += 3; *pp = 'b'; printf("%s", v); // aelbo return 0; }
-
指针的指针
- 一个指针,内部存储了另一个指针的内存地址
# include <stdio.h> int main(int argc, char const *argv[]) { int v1 = 100; int *p1 = &v1; int **p2 = &p1; int ***p3 = &p2; printf("p1:%d,p2:%d,p3:%d \n", *p1, **p2, ***p3); // p1:100,p2:100,p3:100 // 修改v1变量的值,其它变量取值均随之改变 v1 = 200; printf("p1:%d,p2:%d,p3:%d \n", *p1, **p2, ***p3); // p1:200,p2:200,p3:200 return 0; }
定义指针的指针时,在前面添加*,同理依次添加*;取指针指向的内存地址中存取的数据时,根据变量前面几个*,采用相同数量的*即可
- 一个指针,内部存储了另一个指针的内存地址
-
结构体
- 结构体用于将不同的数据类型组合在一起形成一个新的数据类型。
- 定义:
结构体的定义可以按照以下格式进行: struct 结构体名 { 数据类型 成员变量1; 数据类型 成员变量2; // 可以继续添加其他成员变量 }; 在这个定义中,struct关键字用于声明一个结构体,后面跟着结构体的名称。花括号内是结构体的成员变量,每个成员变量都由数据类型和变量名组成,用分号分隔。 以下是一个结构体的示例定义: struct Person { char name[50]; int age; float height; }; 在这个示例中,Person是结构体的名称,它包含了一个字符数组成员变量name,一个整型成员变量age,以及一个浮点型成员变量height。
-
示例
#include <stdio.h> // 定义结构体 struct Person { char name[30]; int age; float height; }; int main() { struct Person v1 = {"eliwang", 28, 1.80}; printf("%s身高:%.2f \n", v1.name, v1.height); struct Person *pp = &v1; // pp是一个变量,他是一个指针类型。 *pp printf("%s年龄:%d岁 \n", (*pp).name, (*pp).age); printf("%s年龄:%d岁 \n", pp->name, pp->age); // 上述的一种简写方式,对于指向结构体的指针,使用箭头运算符->可以直接访问结构体成员 return 0; } /* eliwang身高:1.80 eliwang年龄:28岁 eliwang年龄:28岁 */
- 单向链表实现
struct Node{ int data; struct Node *next; } struct Node v3 = {33}; struct Node v2 = {22, &v3}; struct Node v1 = {11, &v2}; v1.data; // 11 v1.next->data; // 22 v1.next->next->data; // 33
-
预处理和头文件
- 预处理
- 在程序运行之前会先运行
-
# include <stdio.h> # define ME 100 # define SIZE 18 # define ADD(x1, x2)(x1+x2+100) # define DB(x1, x2)(x1*x2) int main() { int data = 19; printf("%d-%d-%d \n", ME, SIZE, data); // 100-18-19 printf("ADD执行结果:%d \n", ADD(20, 40)); // 20 + 40 + 100 = 160 printf("DB执行结果:%d \n", DB(2 + 3, 40)); // 2 + 3 * 40 = 122,并不会先计算传入的参数结果,而是整体进行替换操作 return 0; }
-
- 在程序运行之前会先运行
- 头文件
- 头文件的作用是声明函数、变量和数据类型的原型,以便在其他源文件中使用。
- 头文件通常包含函数和变量的声明,而不包含实际的函数定义。
- 使用#include<xxx.h>方式引用的是标准库的头文件,而使用#include “xxx.h”方式引用的是自定义的头文件或者项目内的头文件。
// 项目目录 ├── main.c ├── utils.c └── utils.h // utils.h int plus(int v1); // utils.c int plus(int v1){ return v1 + 100; } // main.c # include <stdio.h> # include "utils.h" int main() { int data = plus(100); printf("结果:%d \n", data); return 0; }
- 预处理