C语言基础内容介绍
1. C语言基础
首先让我们开看看C语言如何实现代码
#include<stdio.h> //预处理指令 - 包含一个头文件
int main() //主函数
{
int x; //定义一个int类型,名为x的变量
x = 100; //为x赋值
printf("hello world\n"); //使用printf()函数打印字符串
printf("%d\n",x); //打印变量x
return 0; //返回0,结束该函数
}
该函数的运行结果是:第一行打印了 hello world ;第二行打印了 100
注意:以上代码中双斜杠 “//” 后面的文字属于注释,不会影响代码的运行。
下面我就来具体介绍上面代码,让我们初步了解C语言。
(1)#include指令和头文件
#include属于预处理指令。#include<stdio.h>的作用是:把头文件stdio.h的内容拷贝到当前的位置。
为什么要包含头文件stdio.h?
当我们使用库函数时,必须调用其所对应的头文件,才可以正常使用。在上面的代码块中,我们使用了printf()函数,对应stdio.h头文件。
stdio = standard(标准) input(输入) output(输出)
.h 是头文件的后缀
(2)主函数 main()
主函数是程序的入口,是程序开始运行的地方。在任何情况下,无论主函数在程序的什么位置,整个程序的流程都以从进入主函数作为开始,以退出主函数作为结束。
(3)变量
上面的 int x; 即定义了一个变量,变量需要先像这样定义,之后才能赋值 x = 100。
当然也可以在变量定义的同时赋值,如 int x y = 20; 就是定义了x、y,并且同时将y赋值为20 。
【变量分为 全局变量、局部变量】
全局变量:定义在代码块{ }外的变量,在哪都能用。
局部变量:定义在代码块{}内的变量,不能在其范围外使用。 局部变量与全局变量名字相同时,局部变量优先。
【作用域】
变量的名字哪里可以用,哪里就是它的作用域。
局部变量作用域在其所在代码块的内部。
同理,全局变量的作用域是整个工程。
(4)printf()函数
在上面代码中,我们使用了printf()函数
//printf = print fuction(打印函数)
printf("% c\n",ch)//是固定格式
//意思是“我以字符的形式打印ch”
printf() – 格式输出到屏幕。
声明:int printf( const char *format [, argument]… );
返回值:正确返回输出的字符总数,错误返回负值。
printf()的使用大体包含两种情况:
1.printf(“字符串”); 在这种情况下,函数可以直接打印字符串的内容。
2.peintf(“%d”,x); 意思是以十进制整型的形式打印变量x。%d的意思即为打印十进制整型(整型即整数),写在双引号中;逗号后面写变量名。
(5)转义字符
转移字符,即在字符上假如反斜杠\,转变字符原来的意思。
转义字符成体算一个字符,如\n看起来是两个字符,确实整体是一个字符
\n为转移字符,把n变成了 换行符
同理,\t - 水平制表符
输入\t,输出时在其位置加好几个空格
若想要打印出一个 ’
printf(“ % c\n”,’’’) 报错
printf(“ % c\n”,’\’’) ,输出’
\’表示字符常量单引号
同理,若要用字符串形式打印双引号也要加\
printf(“ % s\n”,“\“”) ,输出 “
若\ddd.,ddd表示1~3个八进制数字
如\32,八进制32 = 十进制 3 * 8 ^ 1 + 2 * 8 ^ 0 =26
结果打印出ASCII表中26对应的字符
32 -> 十进制26 -> ASCII表中代表的字符\xdd十六进制同理
(6)注释
C语言注释风格:/xxxxxxxxx/,不能嵌套
C++风格的注释://xxxxxxxxxxx,可以注释一行或多行
(7)返回值
返回值:子程序或者函数执行结束后返回给主程序或者调用函数的函数的值
return 0 代表程序正常退出,返回到主程序继续往下执行。
2. 基本数据类型
数据类型:作用是向内存申请空间来创建变量
注意:字符’A’要用单引号,数字则不用。
char a = ’A’ 的意思是向内存申请一个字节间存放字符A
类型名 | 整型 | 短整型 | 长整型 | 无符号整型 | 字符型 | 单精度浮点型 | 双精度浮点型 | 指针类型(*) | 空类型 |
---|---|---|---|---|---|---|---|---|---|
符号 | int | short( int) | long( int) | unsigned | char | float | double | void | |
大小(字节) | 4 | 2 | 4/8 | 1 | 4 | 8 | 4/8 |
计算机中的单位
bit——比特位 (1或0占一个比特位)
byte——字节(一个字节 = 八个比特位)
kb——1024字节
mb——1024kb
gb——1024mb
tb——1024gb pb——1024tb如一个short占2个字节 = 16个比位, 可表示2 ^ 16个数字,数字范围是(0,2 ^ 16 - 1)
为什么要分数据类型? 由于short可表示数字区间为(0,2 ^ 16 - 1) int可表示数字区间为(0,2 ^ 32 - 1) 因为 short age = 20 是0000000000010100 int age = 20 是0000000000000000000000000000010100 显然short更节省空间
基于以上理解
char ch = ’A’
意思为:向内存申请1个字节存放字符A
int number = 20
意思为:向内存申请4个字节存放整数2
定义变量:数据类型加变量名再赋值
char ch = ’A’
int number = 20
float num = 10.3f
3. 函数
scanf() 函数
上面我们已经讲过printf函数,这里我再介绍一个比较常用的函数 scanf()
scanf() :格式化输入函数
要被声明在头文件stdio.h里
它是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中P.S .VS编译器有scanf_s,我们自己在写代码时建议不要用,因为该函数不具有跨平台性,其他编译器承认scanf_s
那么想解决问题可以如下,从报错提示中复制,加在源文件的第一行
#define _CRT_SECURE_NO_WARNINGS 1
自定义函数初识
除了库函数,我们也可以自己定义函数
#include<stdio.h>
int ADD(int x ,int y) //函数的定义
{
int z = x + y;
return z;
}
int main()
{
int num1 = 10;
int num2 = 20;
int sum = 0; e
sum = ADD(num1, num2); //函数的调用
printf(“sum = % d\n”,sum);
return 0;
}
上面的 int ADD(int x ,int y),ADD是函数名,ADD前的int是函数的返回类型,括号内的是函数传递的参数。
函数代码块内部为函数执行的步骤和返回值。
函数定义完毕后,再主函数调用函数
sum = ADD(num1, num2);
用变量sum接收函数,接受的值为函数的返回值
4. 常量和变量常变量
变量:如字面意思,即可通过赋值等操作改变其内容
如 int num = 3,num为变量
常量1: 字面常量
e.g.普通数字5
常量2、const修饰的常变量
#include<stdio.h>
int main()
{
const int num = 4;
printf("%d\n",num);
num = 8;
printf("%d\n",num);
return 0;
}
以上代码结果会报错
因为const指的是常属性
这里int num前加const,num就是const修饰的常变量。
前面num赋值是4,后面就不能在赋值8了。
(本质是变量,但有常属性,在要使用常量的地方不能用)
常量3:#define 定义的标识符常量
#include<stdio.h>
#define A 10
int main()
{
printf(“d% \n”,A);
return 0;
}
打印结果是 10,这种方式A本质变为常量
常量4:枚举常量
枚举关键词 — enum(枚举的英文)
用法如下
#include<stdio.h>
enum Color
{
Red,
Yellow, //左边是枚举常量
Blue
};
int main()
{
enum Color A = Blue;
//…
return 0;
}
1、若上面为
printf(“ % d\n”,Red);
printf(“ % d\n”,Yellow);
printf(“ % d\n”,Blue);
则输出 0 1 2
枚举常量是有值的,且固定是0 1 2…
2、上面若在…加上A = Yellow.,也是可以的,A是变量,可改,是Yellow本身不能改
5. 字符串和数组
数组
数组是用来存放相同数据类型的变量的一块连续的空间。
例如: int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }
每个元素下标分别为 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
元素下标 = 元素序号-1数组:向内存申请一整块空间存放这十个数
arr = array (数组 )
printf(“%d\n”,arr[4]),即打印数组中下标是4的元素“5”
字符串的特点
字符串是特殊的数组,字符串的元素只能是char类型,且结尾必定是字符串的结束标志 ‘\0’。
字符串:由双引号引起来的一串字符
e.g.“abcdef” ,“ ”是空字符串(类比空集)
注意:单引号引起来的叫字符
’a’是字符
注:字符串的结束标志是一个\0的转义字符。在计算字符串长度时,\0是结束标志,不算作字符串的内容。
“abc”比{’a’,’b’,’c’}多一个0
char arr1[] = “abc”;
char arr2[] = { ’a’,’b’,’c’,0 };
printf(“% s\n”,arr1[]);
printf(“% s\n”,arr2[]);
如果在arr2中加0,则打印结果均为abc
这是因为“abc”中含’a’,’b’,’c’,’\0’
’\0’为字符串结束标志,也是一个字符,\0值是0上面char arr2[] ={ ’a’,’b’,’c’ };
无’\0’做完结束标志,代码一直往后打印随机值
字符串长度 - strlen
计算字符串长度:strlen = string(字符串) + length(长度)
要引头文件#include<string.h>
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abc";
char arr2[] = { 'a','b','c'};
printf(" % d\n",strlen(arr1));
printf(" % d\n",strlen(arr2));
return 0;
}
// 最后输出结果为 3
// 随机数
对应上面的代码:
对应字符串arr1:\0 是结束标志,strlen不会算其长度,所以“abc长度是3”
对于数组arr:strlen计算完c后,因为没结束标志,后面是随机数,所以直到随机数为0时才会停止,因此长度随机。
字符串拷贝 - strcpy
strcpy = string copy(字符串拷贝)
strcpy是库函数,对于头文件 string.h
strcpy的作用是可以拷贝数组的内容
struct cc
{
char a[23];
int b;
};
int main()
{
struct cc qwer = { "12345",12 };
printf("%d\n", qwer.b);
qwer.b = 123;
printf("%d\n", qwer.b);
printf("%s\n", qwer.a);
strcpy(qwer.a, "45678");//字符串拷贝函数
printf("%s\n", qwer.a);
return 0;
}
以上我们用到了结构体,再之后会介绍到
strcpy(qwer.a, “45678”);,这一步,是将 “45678” 的内容拷贝到数组 a 中。
6. 基本操作符和关键字
一. 操作符
移位操作符: << , >>
转换成二进制以后挪移
int a = 3,a << 1 or a >> 1
0011(3) 变成 0110(6) 或者 0001(1)
按位与:&
二进制中,两个数都是1,则输出1,否则输出0
101 & 011 = 001
按位或:|
二进制中,有1就输出1,否则输出0
101 | 011 = 111
按位异或:^
二进制中,两数相同输出0,不同则为1
101 ^ 011 = 110
除:/ 按位余:%
5÷2 = 2余1
5\2 = 2 5 & 2 = 1
赋值操作符:=
= 赋值 ;== 判断是否相等
下面是 = 赋值 和 == 判断相等的易错点:
int main()
{
int a = 0;
if (a = 2)
printf("hh");
return 0;
}
看似什么也不输出,其实输出hh,因为 = 是赋值。
int main()
{
int a = 0;
if (2==a)
printf("hh");
return 0;
}
所以最好用以 2a 的形式,这样就算错写为=,也只是报错,不会出现bug。
复合赋值符
a += 10 相当于 a = a+10
a -= 10 相当于a = a-10
a &= 2 相当于a = a & 2
a >>= 1相当于a = a >> 1
逻辑反操作:!
0 - 假,非0 - 真
!真 = 假,例如:!1 = 0
!假 = 真,例如:!0 = 1
负号:-
将正数变为负数
按(2进制)位取反:~
a = 01010
b = ~a , b = 10101
++ 和 –
++或-- 在变量前面,那么该变量先自加/自减,后将变量的值带入表达式
++或-- 在变量后面,那么先将自加/自减前的值带入表达式,之后该变量进行自加/自减
int a = 10
int b = a++(a–)(先使用,后++ or --)
以上,b先变为a原来的值10,a再++变成11(9)
相反,int b = ++(–)a(先++ or --,后使用)
若a原本是10,则输出11 11(9 9)
强制类型转换:(类型)
int a = (int)2.22
这样原本double的2.22就转成了int
关系操作符
大于:>
大于等于:>=
小于:<
小于等于:<=
用于测试不相等:!=
用于测试相等:==
逻辑操作符
&& 逻辑与
|| 逻辑或
&& 逻辑与
判断二者是否为真,均为真,输出真(1)
|| 逻辑或
判断二者有没有真,若有真,输出真(1)
条件(三目)操作符:exp1 ? exp2 : exp3
以上的意思为:
判断表达式1成立吗?
若成立,执行表达式1;
若不成立,执行表达式2
下标引用操作符:[ ]
用来寻找数组下标对应都元素
如:arr[4],即下标为4的元素
arr[4] = *(arr + 4)
函数调用操作符:()
用来调用函数的操作符
int Add(int x, int y) {
return x + y;
}
int sum = Add(a,b); 在此处调用了自定义函数
结构体成员调用操作符: . ->
. 结构体变量.成员
-> 结构体指针->成员
二. 关键字
typedef
typedef 类型重定义
如,原本需要用 unsigned int num = 20
现在可以 将 unsigned int 这一类型定义成 u 来使用
int main()
{
typedef unsigned int u;
u a = 5;
return 0;
}
上面代码即typedef的应用
sizeof
sizeof :计算变量 / 数组 / 类型所占空间,单位是字节
sizeof 有多种书写形式,如下
sizeof(a) sizeof a sizeof(int)
但是不能 sizeof int
static
1、修饰局部变量,使其生命周期变长
若无static,出了a所在的括号,a的值就被删除;加上static后,出括号又进去时,a上一次的赋值仍被保留。
void pri()
{
static int a = 2;
a += 2;
printf("a = %d\n", a);
}
int main()
{
int i = 0;
while (i <= 8)
{
pri();
i++;
}
return 0;
}
static:修饰全局变量,改变变量的作用域和函数的链接属性(外部链接属性变内部链接属性)
原本全局变量或函数只要声明,就可在全部源文件中使用 static,让静态的全局变量或函数只能在其自己的源文件内部使用。
extern
extern : 声明外部符号或外部函数
在一个源文件中,声明另一个源文件中的全局变量或函数,使其在这里也可使用。
define
define 的作用如下
1.定义标识符常量 #define MAX 100
2.可以定义宏-带参数 #define MAX(x, y) (x>y?x:y)
7. 选择与循环语句
(1)选择语句初识
一. if 语句
if 和 else 可以成对使用,意思是如果 if 的条件成立,执行 if 后面的语句。否则执行else后面的语句。
本质是:如果表达式结果为真(非0),则语句执行
#include<stdio.h>
int main()
{
int input = 0;
printf(“进入大学\n”);
printf(“你要好好学习吗?(1 / 0) > :”);
scanf(“ % d”, & input);
if (input == 1)
printf(“好offer”\0)
else
printf(“卖红薯” \0)
return 0;
}
如果一个 if 不够,可以追加多个 else if,代码如下:
#include<stdio.h>
int main()
{
int age;
scanf("%d", &age);
if (age < 18)
printf("未成年\n");
else if (age >= 18 && age < 30)
printf("青年\n");
else if (age >= 30 && age < 50)
printf("壮年\n");
else if (age >= 50 && age <= 90)
printf("老年\n");
else
printf("??\n");
return 0;
}
if 语句可以嵌套使用,代码如下:
int main()
{
int age;
scanf("%d", &age);
if (age < 18)
printf("未成年");
else
{
if (age >= 18 && age < 30)
printf("青年");
else
{
if (age >= 30 && age<50)
printf("壮年");
else
{
if (age >= 50 && age < 90)
printf("老年");
else
printf("??");
}
}
}
return 0;
}
如果条件成立,若要执行多条语句,则应使用代码块。
注意:if else 整体算一个语句
int main()
{
int i;
scanf("%d", &i);
int a = 0;
if (i < 3)
{
i++;
a += 2;
}
else
{
i--;
a -= 2;
}
printf("%d,%d", i, a);
return 0;
}
下面是常见的错误:
else 匹配的是离他最近的未被匹配的 if。
如下,看似打印hhhhh,确实什么也打不了。
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hh");
else
printf("hhhhhh");
return 0;
}
else 匹配离他最近的未被匹配的 if,和格式无关,正常格式应该是下面这样
int main()
{
int a = 0;
int b = 2;
if (a == 1)
if (b == 2)
printf("hh");
else
printf("hhhhhh");
return 0;
}
二. switch case 语句
该语句从switch(变量) 进入选择结果,按照变量的值跳入对应的case,并向下执行。
注意:
switch\case后面一定是整形表达式,因为switch判断条件是等于,精度要求高。(字符型、布尔型、枚举型都可以转化成整形进行判断)
case:后面必须是常量表达式
break:用来跳出循环,否则程序从在你跳入的case一直向下执行,直到switch语句结束或者遇到下一个break
default(默认):如果输入的变量不在case:的范围内,则执行default(默认) (确实用法相当于else)
switch 语句中可以嵌套 if 或 switch 语句
int main()
{
int day;
scanf("%d", &day);
switch (day)
{
case 1:
printf("星期1\n");
break;
case 2:
printf("星期2\n");
break;
case 3:
printf("星期3\n");
break;
case 4:
printf("星期4\n");
break;
case 5:
printf("星期5\n");
break;
case 6:
printf("星期6\n");
break;
case 7:
printf("星期7\n");
break;
default:
printf("输入错误");
}
return 0;
}
case后面不一定都加break
下面这种情况若加break,则会从中间跳出,执行不到printf,如下代码不加break
int main()
{
int day;
scanf("%d", & day);
switch (day)
{
case 1:
case 2:
case 3:
case 4:
case 5:
printf("工作日\n");
break;
case 6:
case 7:
printf("休息日\n");
break;
default:
printf("输入错误\n");
}
return 0;
}
(2)循环语句初识
一. while 语句
while(真),进入循环,直到条件为假,循环结束
while 语句中常见的配套语句:
continue:终止本次循环continue后面的代码,并跳回循环代码块,回到开头的判断部分。
break:直接跳出循环。
int main()
{
int i = 0;
while (i < 10)
{
i++;
if (5 == i)
continue; /*break;*/
printf("%d\n", i);
}
return 0;
}
二. for 语句
for循环:for(表达式1; 表达式2; 表达式3)。
三个表达式作用分别是:(初始化; 判断 ; 调整)。
执行顺序:表达式1(初始化)->表达式2(判断)->执行循环体内语句->表达式3(调整)->2判断->循环体内语句执行->3。
需要注意的是:for 循环中也可出现 break 和 continue。
int main()
{
int i;
for (i = 1; i<=10;i++)
{
printf("%d\n", i);
}
return 0;
}
对比下面两段代码
二者区别在于 i++(调整) 的位置不同,for语句的调整在循环代码块外部(顶端),while语句的调整在代码块内部
所以读取continue跳到循环语句开端处后,for语句执行调整 会继续运行 ,但while 语句不会调整变量的数组 进入死循环
代码1:
int main()
{
int i;
for (i = 1; i <= 10; i++)
{
if (5 == i)
continue;
printf("%d ", i);
}
return 0;
}
代码2:
int main()
{
int i=1;
while (i <= 10)
{
if (5 == i)
continue;
printf("%d ", i);
i++;
}
return 0;
}
8. 指针
指针是个变量,存放内存单元的地址
int main()
{
int a = 10; //在内存中开辟一块空间
int* p = &a; //&a为取出a的地址
//将a的地址存在指针变量p中
return 0;
}
指针类型
为何区分指针变量的类型?
如 char* int* 同为指针变量的数据类型(指针类型),都占相同的字节,表达的地址也相同(都为变量的地址),那么区分类型的意义是什么?
【指针类型意义.1】解引用时能够访问的字节数
int main()
{
int a = 0x11223344;//通过查看内存,内容是11 22 33 44
//int* pa = &a;
//*pa = 0; //(这里*pa认为它指向int)经过此操作,a变为00 00 00 00
char* pc = &a; //通过查看内存,内容是11 22 33 44
*pc = 0; //(这里*pc认为它指向char)经过此操作,a变为11 22 33 00
//指针类型决定了指针进行解引用操作时,能够访问空间的大小
// int*p ;*p能够访问4个字节
//char*p ;*p能够访问1个字节
//double*p ;*p能够访问8个字节
return 0;
}
【指针类型意义.2】指针±整数
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = &a;
printf("%p\n", pa);// 000000CFFF55F894
printf("%p\n", pa + 1);// 000000CFFF55F898 指针跨度为4字节 (一指针对应一字节)
printf("%p\n", pc);// 000000CFFF55F894
printf("%p\n", pc + 1);// 000000CFFF55F895 指针跨度为1字节
}
如上所示,指针类型不同,其向前或者向后走的字节跨度不同
指针±整数的具体应用
int main()
{
int arr[10] = { 0 };
int* p = &arr;//若int*,解引用时每4字节赋值一个1
char* pc = &arr;//若char*,解引用时每一字节赋值一个1,导致不想要的结果
int i = 0;
for (i = 0; i < 10; i++)
{
*(pc + i) = 1;
printf("%d", arr[0]);
}
return 0;
}
9. 结构体
结构体基础知识
结构体相当于一个类型,先在主函数前定义,之后才可使用此类型。
结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。
结构体成员可以是 [标量(普通变量)、数组、指针、甚至是其他结构体]。
结构体类型的声明
说明方式如下:
struct tag //struct:结构体关键字 tag:结构体标签 struct tag:结构体类型
{
member - list;//成员列表;
}variable - list;//变量列表;(在这里可以创建全局变量)但是写代码尽量不用全局变量
e.g. 描述一个学生 - 一些数据
名字
年龄
电话
性别
定义一个结构体类型(不占用空间)
struct stu // struct-结构体关键字 stu-结构体标签 struct stu-新创建的结构体类型
{
char name[20];//字符串,用于存汉字
int age;
char tele[12];//11位电话号+'\0'
char sex[5];
}s1,s2,s3; //s1,s2,s3;是三个[全局]的结构体变量
结构体的[声明]是一条语句,结尾要加 ;
int main()
{
struct stu s;//创建结构体变量(向内存申请了空间)
return 0;
}
结构体类型名的重定义
原本结构体类型名称太长,可以用typedef定义类型名字, typedef放开头 , 新名字放结尾分号前。
(虽然定义了新名字但是原本的名字还是可以使用)
typedef struct stu
{
char name[20];
int age;
char tele[12];
char sex[5];
}stu;//新变量名
int main()
{
stu s1;
struct stu s2;
return 0;
}
结构体变量的定义和初始化
typedef struct stu
{
char name[20];
int age;
char tele[12];
char sex[5];
}stu1;
int main()
{
struct stu s1 = { "张三",18,"11451419198","男" };
stu1 s2 = { "李四",19,"14544191981","女" };
return 0;
}
结构体类型中指针的使用
结构体常用的两个操作符: . 和 ->
可以通过这两个操作符找到并调用结构体的成员。
结构体变量.成员
结构体指针->成员
struct A
{
char no1[15];
int no2;
};
int main()
{
struct A No = { "abcde",123 };
struct A* pNo = &No;
printf("%s\n",( * pNo).no1);
printf("%d\n",( * pNo).no2);
printf("%s\n", pNo->no1);
printf("%d\n", pNo->no2);
return 0;
}