C语言入门知识【笔记总结】
By:weixiao
——大一小白对半年学习c语言的笔记(有错误希望能够帮忙在评论区指正出来!!)
1.C语言の程序设计
C语言是一种面向过程的语言,C程序的运行流程为:编辑源程序->编译源程序->连接目标程序->运行可执行程序。
C语言源程序文件的后缀 .c 编译后生成文件的后缀 .obj 连接后生成的文件后缀 .exe
1.1关于C语言の结构
(1)C程序的基本单位——函数
(2)一个C语言程序是由一个或多个函数组成的,其中必须包含一个 main 函数,而且有且仅有一个 main 函数
(3)数据声明和语句结束最后必须要有一个分号‘ ;’,正如汉语中说完话需要一个句号一样。
(4)程序永远从main执行
(5)要用英文输入法 ※※※※※
1.2关于编译预处理
(1)编译预处理包括文件包含,宏定义和条件编译。
(2)引用头文件
#include<stdio.h>
#include.......
......
(3)宏定义: #define 名称 内容
小小的例题:有如下的宏定义,则下列语句的结果是(910)。
#define MAX 30
#define NUM MAX+10
X=MAX*NUM
题解:3030+10=910
易错:30(30+10)=1200
1.3关于算法
※算法的特性
①有穷性。
②确定性。
③有零个或多个输入。
④有一个或多个输出。
⑤有效性。
程序=数据结构+算法
2.顺序结构程序设计
2.1关于常量和变量
(1)常量分为
整型常量 实型常量 字符常量 字符串常量
(2)字符常量又分为
普通字符 转义字符
单引号单独括起来的一个字符叫普通字符,比如’a’,需要注意的是,字符常量在计算机中所存储的并不是一个字符,而是以ASCII 代码的形式来存储的,像’a’的ASCII 码值为97,因此在计算机存储单元的就是97.
问题来了,ASCII 码值我怎么知道?
解决方法:点击浏览器搜索ASCII 码值即可。
(3)转义字符:顾名思义,把字符转换成其他意义
转义字符 字符值 \’ 一个单引号’ \” 一个双引号” \\ 一个反斜线 \n 换行 \r 回车 \t 空格
(4)字符串常量:用双引号把若干个字符括起来,字符串常量是双引号中的全部字符,比如 “weixiao”
(5)变量:一个有名字的,具有特定属性的存储单元。用来存放数据,变量的值是可以改变的。需注意的是,变量必须先定义,再使用。
(6)char(字符型数据)类型在内存中的存储形式是ASCII 码 。
2.2标识符
(1)定义:我们编程时使用的“名字",像各种变量名、函数名等等。
(2)规则:① 标识符只能由数字、字母、下划线组成。 ② 标识符区分大小写。 ③第一个字符必须是字母或者下划线。
2.3数据类型
(1)数据类型分为:整型类型、浮点类型、字符型
整型类型 基本整型(int)2或4字节,短整型(short)2字节,长整型(long)4字节 浮点类型 单精度浮点型(float)4字节,双精度浮点型(double)8字节 字符型 char
整型的存储方式是:用整数的补码形式存放。
2.4运算符和表达式及优先级
(1)基本的算术运算符:+ - * / %
(2)※※※
自增、自减运算符 作用是使变量的值加 1 或减 1。 ++i,–i 在使用 i 之前,先使 i 的值加(减)1 i++,i– 在使用 i 之后,再使 i 的值加(减)1
(3)关于表达式
用算术运算符和括号将运算对象连接起来的、符合 C 语法规则的式子
(4)关于优先级
在了解优先级之前我们需要掌握一项基本知识——C运算符
算数运算符 + - * / % ++ – 关系运算符 > < == >= <= != 逻辑运算符 ! && || 赋值运算符 = 条件运算符 ?: 逗号运算符 , 指针运算符 * & 求字节数运算符 sizeof 强制类型转换运算符 (类型)
优先级(简化版)(应对简单考试版)! > 算术运算符 > 关系运算符 > && > || > 赋值运算符
如更加详细了解优先级,请进入csdn搜索:C语言运算符优先级
经典的小小例题:
设有声明”char w;int x;double y;",则表达式w-x+y值的数据类型为(double)
题解:char类型看作ASCII 码值,因此w-x返回值是int类型,int+y(double)根据优先级,所以答案是double。
3.C语句
3.1C语句の作用和分类
(1)控制语句
if()…else… 条件语句 for()… > < == >= <= != 逻辑运算符 ! && || 赋值运算符 = 条件运算符 ?: 逗号运算符 , 指针运算符 * & 求字节数运算符 sizeof 强制类型转换运算符 (类型)
(2)输入输出语句
把内容输入到屏幕上,这里我们需要用到printf函数,下面让我们打印一个hello world吧!
#include <stdio.h>
int main()
{
printf("Hello World!");
return 0;
} //注意代码规范性
进阶一下:把变量值输入进去
printf("%x",变量);
%d 输出int变量的值 %c 输出char类型变量的值 %s 输出一个字符串 %f 输出float类型变量的值 %lf 输出double类型变量的值
写一个程序试试吧
#include <stdio.h>
int main()
{
int a = 5, b = 7;
float x = 67.8564, y = -789.124;
char c = 'a'; long n = 1234567;
unsigned u = 65536; printf("%d%d\n", a, b);
printf("%3d%3d\n", a, b);
printf("%f,%f\n", x, y);
printf("%-10f,%-10f/n", x, y);
printf("%8.2f,%8.2f,%4f,%4f,%3f,%3f\n", x, y, x, y, x, y);
printf("%c,%d,%o,%x\n", c, c, c, c);
printf("%ld,%lo,%x\n", n, n, n);
printf("%u,&o,%x,%d\n", u, u, u, u);
printf("%s,%5.3s\n", "computer", "computer");
}
自己试着在编译器上写写看看输出结果是什么吧!
在上面的程序中我们注意到了两种形式“%-10f”和“%8.2f”,其中-10和8.2意思分别是,以左对齐形式后空格十位和以右对齐的方式先空格8位且保留小数点后两位。
关于输入语句
一般形式:
scanf("%x",&变量);
,后面是”&变量“而不是“变量”,scanf函数用到的是变量地址。因此&号自然就是取地址符。
写个程序试试
#include<stdio.h>
int main()
{
int a;
scanf(“%d”,&a);
printf(“a=%d”,a);
return 0;
}
在我学习过程中我发现了一个编译器问题,在使用vs2022过程中直接使用scanf是会报错的,这里存在一个代码安全性的问题,想要正常运行需要在scanf后面加上一个_s,这是第一种解决办法,第二种解决办法就是在所有头文件之前,也就是第一行我们宏定义一个函数
#define _CRT_SECURE_NO_DEPRECATE
当然如果编译器不是vs2022,用的是dev c++或cLion等编译器便不会遇到此问题,可以正常使用scanf函数。
来个例题玩玩
假设公民的个人所得税为工资总额的 5%,编程输入一个公民的工资总额,计算其应缴纳的个人所得税和其扣除所得税后的实际工资,并输出。
#include <stdio.h>
void main()
{
double wage, wax, laterwage;
printf("请输入您的工资:");
scanf_s("%lf", &wage);
wax = wage * 0.05;
laterwage = wage - wax;
printf("您应缴税:%lf,剩余工资:%lf\n",wax,laterwage);
}
4.选择结构程序设计
4.1关于if语句
if语句的常见形式
(1)
if(表达式)
{
语句;
}
(2)
if(表达式)
{
语句;
}
else 语句;
(3)
if(表达式)
{
语句;
}
else if 语句;
需要注意的是else不能单独使用,需要和if配套使用。
4.2关于逻辑运算符
(1)
运算符 含义 举例 解释 && 逻辑与 a&&b a和b都为真,结果就为真,否则为假 || 逻辑或 a||b a和b至少一个为真,结果为真,否则为假 ! 逻辑非 !a 如果a为假,!a为真,反之a为真,!a为假
4.3条件运算符和条件表达式
条件运算符由两个符号?和:组成,必须一起使用
条件表达式的一般形式:
表达式 1 ?表达式 2 :表达式 3
执行顺序:先求解表达式 1,若为非 0(真)则求解表达式 2,此时表达式 2的值就作为整个条件表达式的值。若表达式 1 的值为 0(假),则求解表达式3,表达式 3 的值就是整个条件表达式的值。
4.4关于switch语句
switch语句的一般形式:
switch(表达式)
{
case 常量 1:
语句 1;
break;
case 常量 2:
语句 2;
break;
case 常量 n:
语句 n;
break;
default:
语句 n+1;
break
}
注意:表达式的值需要为整数类型,default可以不存在,用处是在进入switch循环后但都没达到case要求,则执行default。多个case标号也可以共用一组执行语句一般情况下在执行完case需要用break跳出循环,写到这里我便想到一个知识点,就是关于coutinue和break的区别,下面我将引申一下:
在编写程序时,我们有时会用到continue,他与break最大的区别就是,continue跳出本次循环体中剩下尚未执行的语句,但还会进行下一次的循环条件,break将会完全跳出不进行。更加详细和官方的解释请移步csdn搜索:break和continue的作用和区别是什么?
5.循环程序结构设计
5.1 while循环
一般形式:
while(循环条件)
{
语句;
}
使用方法:当…(循环条件)时,执行…(语句)
5.2 do-while循环
一般形式:
do
{
语句;
}
while(循环条件);
使用方法:执行(do)…(语句),判断…(循环条件)正确还是不正确再开始循环。
值得注意的是,do-while语句先执行循环中语句,再判断真假是否进行循环。
5.3 for循环
一般形式:
for(表达式1;表达式2;表达式3;)
{
语句;
}
表达式1:设置初始条件,可以为零个
表达式2:循环条件判定式,判断是否进行循环
表达式3:循环变量的增值
打个九九乘法表试试?
#include<stdio.h>
int main() {
int i,j;
for(i=1; i<=9; i++) {
for(j=1; j<=i; j++)
printf("%d*%d=%2d ",i,j,i*j);
printf("\n");
}
return 0;
}
6. 数组
6.1一维数组
定义:一组同属性的数据构成的集合就成为一个数组,数组中每一个元素都为一个数据类型
一般形式:
数据类型 数组名[常量];
(1)数组名同样需要遵循标识符的命名规则
(2)[常量]代表着元素的个数,也就是数组长度
(3)写法:
如果想让一个数组中全部元素值全部为0,可以写成
int a[5]={0,0,0,0,0};
或者 int a[5]={0};
如果确定元素个数,我们便可以不指定数组长度
int a[]={1,2,3,4,5};
如果元素个数和数组长度不相同呢?
比如:int a[10]={1,2,3,4,5};
他就只能初始化前五个元素,而后五个元素便会默认为0
6.2二维数组
一般形式:
数据类型 数组名[常量][常量];
(1)二维数组的初始化
(1)
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
创建了一个三行四列的数组,即把第一个花括号的值赋给了第一行,依此类推
那什么又是三行四列呢?如下图
a[0][0] a[0][1] a[0][2] a[0][3] a[1][0] a[1][1] a[1][2] a[1][3] a[2][0] a[2][1] a[2][2] a[2][3]
(2)
也可以将所有数据都写在一起,按照数组元素在内存中排列的顺序分配
int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
也可以部分元素赋值
int a[3][4]={{1},{3},{5}};
这样只会在第一列的元素有值,其余都为0
值得注意的是,在定义二维数组的过程中,第一维数组可以不指定,但第二维一定不能省略!!!
int a[3][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
等价于
int a[][4]={{1,2,3,4},{5,6,7,8},{9,10,11,12}};
6.3动态数组的注意事项
关于C99引入VLA的问题,Variable-Length Array (VLA),即运行期确定大小的数组。
也就是在一维数组中的常量表达式替换为了变量,如下
int main()
{
int n;
scanf("%d",&n);
int a[n];
}
但是C11中也将VLA变成了可支持和不支持(由编译器决定),因此在使用动态的时候我们通常不采用VLA,而是malloc的形式去使用,在后面会专门介绍malloc的。
6.4字符数组
(1)注意事项
字符型数据是以字符的 ASCII 代码存储在存储单元中的,而用来存放字符数据的数组就是字符数组,字符数组中每一个元素存放一个字节。
在对字符数组进行初始化时,需要注意花括号中初值个数不能大于数组长度,会出现语法错误,反之,如果数组长度大于花括号初值跟个数,多余长度会使用 ‘\0’ 补齐。
同时,数组长度也可以不规定,通过初值个数自动判定。
(2)初始化的多样形式
char c[]={"hello world"};
char c[]="hello world";
也可以表示成
char c[]={'h','e','l','l','o',' ','w','o','r','l','d','\0'};
上面的是等价关系,也可以表示下面的形式成但不等价
char c[]={'h','e','l','l','o',' ','w','o','r','l','d'};
是合法的。
(3)字符的输入与输出
逐个字符输入输出。用格式符“%c”输入或输出一个字符。
如输入输出字符串,使用格式符“%s”输入或输出字符串。
输出字符串时是通过搜索\0来执行的,通过找到数组的起始地址后逐个输出字符,直到找到\0后,停止。
这是通过学会scanf和printf后我们知道的输入输出,下面会介绍对待字符串输入输出时的函数————gets(fgets)和puts(fputs)函数
(4)gets
函数原型(不用特意记)
# include <stdio.h>
char *gets(char *str);
示例
# include <stdio.h>
int main(void)
{
char str[20] = "\0"; //字符数组初始化\0
printf("请输入字符串:");
gets(str);
printf("%s\n", str);
return 0;
}
由此可见,gets函数对于字符串输入方面是极具便捷性和可读性,甚至字符串中有空格也可以输入。
(5)puts
举一反三,puts也很好理解了,直接上示例
#include <stdio.h>
int main(void)
{
char str[]={"Hello"};
puts(str);
return 0;
}
输出结果:Hello
举一反三不了没关系,如果是学妹,可以找我我亲自教你,如果是学弟,你天生比别人笨啊,这都学不明白!(嘿嘿,滑稽脸!)
介绍一下fgets和fputs
实际上就是在gets和puts上更新,更加安全的函数,gets在控制台上一直输入字符 ,会超出数组的容量,系统虽然不会报错,但是会造成缓冲区溢出,C11取消了gets与puts函数,但不是不能使用,因为正确使用gets和puts是相对方便的一种函数。
1.fgets可以指定输入字符容量大小
2.fgets()和fputs()需要用到stdin流和stdout流,即标准输入和标准输出,下文会说明,可以了解一下,主要记住用法就好
3.fgets的使用形式 fgets(str,3,stdin); //str字符名称 3字符长度 stdin标准输入
4.fputs的使用形式 fputs(str,3,stdout); //str字符名称 3字符长度 stdot标准输出
stdin与stdout流,本人能力有限,知道他是数据流传输,具体详解请搜索baidu
(6)getchar和putchar
上文介绍了输入输出字符串不使用scanf和printf的方法,下面介绍对单一字符输入输出的新方法——getchar和putchar
直接上示例
#include <stdio.h>
int main() {
int ch;
printf("请输入一个字符:");
ch = getchar(); // 从标准输入读取一个字符ASCII码值放到实际变量ch
printf("您输入的字符是:");
putchar(ch); //putchar接收到一个参数(ASCII码值),将字符输出到标准输出
return 0;
}
自己试试?
6.5 关于string.h的常用函数
string.h头文件里含有多种函数,后续会专门写一篇给string.h,先介绍点常用的(简洁展示)
函数 写法 作用 strcat strcat(字符数组1,字符数组2); 字符串连接,将字符串2接到字符串1后面并得到字符数组1的地址 strcpy strcpy(字符数组1,字符串2); 字符串复制,将2复制到1 strcmp strcmp(字符串1,字符串2); 字符串比较函数 strlen strlen(字符数组) 测量字符串长度
注意strcmp比较形式
如果两者字符串相同,则返回值是0
如果字符串1>2,那么返回值>0
如果字符串1<2,那么返回值<0
正确写法:
if(strcmp(str1,str2)>0){
\\写结果
}
7. 函数
7.1关于函数的定义
函数可以分为库函数和自定义函数,我们不对C语言库函数进行讲解(不搞开发),主要介绍一下自定义函数。
由程序员自己设计的函数,并使其达到其功能的目的,就是自定义函数,必须遵从先定义,再调用的原则。
自定义函数又可以分为“定义无参函数”和”定义有参函数“。
7.2 定义无参函数
关于无参函数初始化
类型名 函数名() 类型名 函数名(void)
{ {
函数体 或 函数体
} }
使用void代表空,函数中没有参数
7.3 定义有参函数
关于有参函数初始化
类型名 函数名(初始化参数1,初始化参数2.....)
{
函数体
}
我们将初始化函数过程中的括号内所有的初始化参数统一称之为形式参数,简称形参。
用一个比较大小程序体现一下
#include <stdio.h>
int CompareNumber(int a,int b)
{
if(a>b){
return a;
}
if(a<b){
return b;
}
}
int main()
{
int a=0;
int b=0;
printf("请输入两个数字来进行比较");
scanf("%d %d",&a,&b);
int c=CompareNumber(a,b);
printf("最大的数字为%d",c);
}
运行结果
输入:10 20
输出:20
调用顺序:先进行main再进行调用自定义函数
在上面程序中int c=CompareNumber(a,b); 的“a”“b“,我们称之为实际参数,简称实参。
7.4 函数调用时的数据传递
在调用函数过程中,系统会把实参的值传递给被调用的函数的形参,也就是形参从实参中得到一个值,从而参加函数的运算。
需要注意的是实参和形参应该类型相同。
定义函数的形参,在未发生调用该函数的时候,这时并不会占用存储单元,发生函数调用的时候,函数的形参被临时分配存储单元,将实参的值传入给形参从而进行函数体的命令。在上方的比较大小程序中我们需要返回值并将返回值打印出来,因此定义函数的函数名形式是int类型,如果我们并不需要返回值时,这时函数名就应该是void类型,代表空。
需要注意的是,实参向形参传递是值传递,只能由实参传递给形参而不能形参传递给实参,并且形参和实参在内存中是占用不同的存储单元。
在后续指针的学习,我将会介绍一下传值调用。
7.5 函数的返回值
使用return语句就可以获得函数的返回值,并且在函数中不带回值,函数类型应该定义为void类型,同时函数体内不能出现return语句。
8. 指针
8.1 基本指针概念
变量在内存区中的每一个字节都有一个编号,我们称他为地址,形象化的称地址为“指针”。
8.2 指针变量
(1)指针变量的定义:
类型名*指针变量名
类型名在定义指针变量时必须指定的“基类型”,他也是指针所指向的类型,比如:
int *p; //指针p所指向的类型为整型
(2)引用指针变量
第一种
int a; //定义一个普通变量a
int *p; //定义一个指针变量p
p=&a; //取a的地址,并赋值给p
第二种
*p=1; //将整数1赋值给变量p
前两种结合一下
int a;
int *p;
p=&a;
*p=1;
//加个printf
printf("%d",*p); //获取指针指向的内容
*与&互为逆运算,通俗一点表示x=*&x。
printf("%o",p); //以八进制输出指针变量,由于p=&a也就是输出a的地址
8.3 引用数组元素时的指针运算
(1)如果指针变量 p 已指向数组的一个元素,则 p+1 指向同一数组中的下一个元素,p-1 指向同一数组中的上一个元素。注意:执行 p+1 时并不是将 p 的值(地址)简单地加 1,而是加上一个数组元素所占用的字节数。
(2)如果 p 的数值是&a[0],则 p+i 和 a+i 就是数组元素 a[i]的地址,或者说,它们指向 a 数组序号为 i 的元素。
(3)两个地址间不可以相加
8.4 指针引用数组
数组通常使用下标法来表示,即a[i]
这里介绍一下指针法表示
*(a+i) =a[i]
8.5指针常量与指针变量的混淆
8.6 malloc函数
我们在做一个成绩管理系统时,人数处于一个不确定的状态时,我们就需要用到malloc函数
malloc是动态内存分配函数,用于申请一块连续的指定大小的内存块区域以void*类型返回分配的内存区域地址
使用malloc时,我们需要引入一个头文件
#include <stdlib.h>
malloc函数使用注意事项
在使用malloc开辟空间时,使用完成一定要释放空间,如果不释放会造内存泄漏。
示例:想分配100个字节int类型的空间
int* p = (int *) malloc ( sizeof(int) * 100);
//第二个*是乘的意思不是指针!!!
free函数
作用:释放malloc函数给指针变量分配的内存空间。
注意:使用后该指针变量一定要重新指向NULL,防止悬空指针(失效指针)出现,有效规避错误操作。
写个小程序:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *p;
p = NULL;
p = (char *)malloc(100);
if (p != NULL) // 如果内存分配成功
{
printf("memory allocated at:%x\n", p);
}
else
{
printf("not enough memory!\n");
}
free(p);
p = NULL;
return 0;
}
下章节讲到结构体我将会写一个与结构体相关的小程序
————————书接上回,讲一下传值调用——————————