前言:
学习C语言,先了解C语言的起源,学习C语言的语法,从简单的代码开始练习。先了解C语言的基础知识,对C语言有个大概认识。每次学习都要划重点。本节介绍C语言的数据类型、变量常量、字符串、选择语句、循环语句、函数、数组、常见关键字等。
目录
1. C语言的历史
C语言诞生于美国的贝尔实验室,由丹尼斯·里奇(Dennis MacAlistair Ritchie)以肯尼斯·蓝·汤普森(Kenneth Lane Thompson)设计的B语言为基础发展而来,在它的主体设计完成后,汤普森和里奇用它完全重写了UNIX,且随着UNIX的发展,c语言也得到了不断的完善。为了利于C语言的全面推广,许多专家学者和硬件厂商联合组成了C语言标准委员会,并在之后的1989年,诞生了第一个完备的C标准,简称“C89”,也就是“ANSIC”,截至2020年,最新的C语言标准为2018年6月发布的“C18”。
C语言是一种结构化语言,它有着清晰的层次,可按照模块的方式对程序进行编写,十分有利于程序的调试,且c语言的处理和表现能力都非常的强大,依靠非常全面的运算符和多样的数据类型,可以轻易完成各种数据结构的构建,通过指针类型更可对内存直接寻址以及对硬件进行直接操作,因此既能够用于开发系统程序,也可用于开发应用软件。
丹尼斯·里奇,C语言之父,UNIX之父。曾担任朗讯科技公司贝尔实验室下属的计算机科学研究中心系统软件研究部的主任一职。1978年与布莱恩·科尔尼干一起出版了名著《C程序设计语言(The C Programming Language)》,现在此书已翻译成多种语言,成为C语言方面最权威的教材之一。2011年10月12日(北京时间为10月13日),丹尼斯·里奇去世,享年70岁。
丹尼斯·里奇,美国计算机科学家,对C语言和其他编程语言、Multics和Unix等操作系统的发展做出了巨大贡献。
1.1 什么是C语言
GCC,GNU组织开发的开源免费的编译器
MinGW,Windows操作系统下的GCC
Clang,开源的BSD协议的基于LLVM的编译器
Visual C++:: cl.exe,Microsoft VC++自带的编译器
2. 第一个C语言程序
- .c结尾的文件称为源文件
- .h结尾的文件称为头文件
#include <stdio.h>
int main()
{
//解释:
//main函数是程序的入口
//打印hello world,打印有打印函数printf
//printf 是一个库函数,库函数的使用得包含一个头文件stdio.h
printf("hello bit\n");
printf("he he\n");
return 0;
}
解析:1.程序的第一行 #include <stdio.h> 是预处理器指令,告诉 C 编译器在实际编译之前要包含 stdio.h 文件。
2.下一行 int main() 是主函数,程序从这里开始执行。
3.下一行 printf(...) 是 C 中另一个可用的函数,会在屏幕上显示消息 "Hello, World!"。
4.下一行 return 0 ;终止 main() 函数,并返回值 0。
3. 数据类型
3.1 基本类型
(1)整型类型。
整型类型包含:基本整型(int)、短整型(short int)、长整型(long int)、双长整型(long long int)、字符型(char)和布尔型(bool)。
注:整型数据可在类型符号前面加修饰符:signed(有符号整数类型)和unsigned(无符号整数类型),若不指定则默认为“有符号类型”。
(2)浮点类型。
浮点类型包含:单精度浮点型(float)、双精度浮点型(double)和复数浮点型(单精度复数浮点型(float_complex)、双精度复数浮点型(double_complex)和长精度复数浮点型(long long_complex))。(双精度的精度比单精度更高)
3.2 枚举类型:
(1)枚举类型。枚举类型就只有枚举类型(enum)。
3.3 空类型:
(1)空类型。空类型就只有空类型(void)。
3.4 派生类型:
(1)派生类型。派生类型包含:指针类型(*)、数组类型([])、结构体类型(struct)、共用体类型(union)和函数类型。
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
数据类型的思维导图:
2. 数据类型的大小
(1)计算中常用的存储单位
计算机的单位
二进制: 0~1
十进制:0~9
八进制:0~7
换算单位
1 bite -------- 比特(位)
1 字节 --------1 byte = 8 bit;
1千字节 ------1 KB = 1024 byte;
1兆字节 ------1 MB = 1024 KB;
1吉字节 ------1 GB = 1024 MB;
1太字节 ----- 1 TB = 1024 GB;
1 拍字节 ---- 1 PB = 1024 TB;
由图可知,最小的存储单位为bit(位或者比特),即为二进制数所存储的位。其次为byte(字节),换算单位位:1字节=8位 。
(2)数据类型的大小
在C语言中,要求数据类型的大小,需要用到一个函数sizeof,这个函数的作用是可以求出一个对象(数据类型或者数据对象)的长度(占用内存的大小,以byte为单位)。
以下为计算的代码展示:
#include <stdio.h>
int main()
{
//%d - 打印一个10进制的数
printf("short:%d\n",sizeof(short));
printf("unsgned short:%d\n", sizeof(unsigned short));
printf("int:%d\n", sizeof(int));
printf("unsigned int:%d\n", sizeof(unsigned int));
printf("long:%d\n", sizeof(long));
printf("unsigned long:%d\n", sizeof(unsigned long));
printf("long long:%d\n", sizeof(long long));
printf("unsigned long long:%d\n", sizeof(unsigned long long));
printf("float:%d\n", sizeof(float));
printf("double:%d\n", sizeof(double));
printf("char:%d\n", sizeof(char));
return 0;
}
整形数据类型 | 大小(byte) |
char
//
字符数据类型
| 1 |
short //短整型 | 2 |
unsigned short //无符号短整型 | 2 |
int //整形 | 4 |
unsigned int //无符号整形 | 4 |
long //长整型 | 4 |
unsigned long //无符号长整型 | 4 |
long long //更长的整形 | 8 |
unsigned long long //无符号更长整型 | 8 |
float //单精度浮点数 | 4 |
double //双精度浮点数 | 8 |
由运行结果可知:
char类型的数据类型大小为1字节即8位bite。
short类型的数据类型大小为2字节即16位bite。
int类型、long类型、float类型的数据类型大小为4字节即32位。
long long类型、double类型的数据类型大小为8字节即64位。
注意,若整数数据类型前面加unsigned即为无符号整数数据类型,数据类型大小不变。
3.1 %+字母
%d ----------- 打印整形(有符合整形)
%u ----------- 用于打印unsigned int num = 0这样的数据类型,这是无符号整形
//有符号整型包含正负数,无符号整形不包含复数
%c ----------- 打印字符
%s ----------- 打印字符串
%f ----------- 打印float 类型的数据
%lf ---------- 打印double类型的数据
%zu/zd ------- 打印sizeof()的返回值
%p ----------- 打印地址
%02d --------- 输出02/05...这样的效果
%2d ---------- 输出2/5 //也就是说前面的0被空格替代了
%-2d --------- 左对齐
%.1f --------- 打印小数点后一位小数
float a = 0.0f //float类型的数据进行初始化
double a = 0.0 //double类型数据进行初始化
//一般的直接赋值3.14编译器认为它是double类型的数据,而不是float类型的。
4. 变量和常量
4.1 变量定义的方式:
类型 变量名 = 初始值;
例如:
int age = 24;
char ch = 'A';
float height = 176.14;
4.2 变量的分类
在C语言中,变量分为局部变量和全局变量。
局部变量:一般将定义在函数中的变量称为局部变量,它只能在函数内部使用。
全局变量:定义在全局作用域中的变量,即函数外的变量,称之为全局变量,全局变量的生命周期随程序启动而 生,随程序结束而消亡,在任何函数中都可以使用。
(1) 变量的类型:局部变量、全局变量
int num1 = 20;//全局变量,定义在代码块({})之外的变量
int main()
{
int num2 = 10;//局部变量,定义在代码块({})之内的变量
return 0;
}
(2) 当局部变量与全局变量名称相同的时候,局部变量优先。
#include<stdio.h>
int a = 100;
int main()
{
int a = 10;
printf("%d\n", a);
return 0;
}
4.3 变量的使用
//类型 变量名;
//C语言为了描述变化的值,使用变量
//C语言为了描述不变的值,使用常量
//注:变量在创建的时候,不初始化是一种不好的代码风格
#include<stdio.h>
int main()
{
char ch = 'w';//字符使用单引号引起来的
//'a' 'b' 'c'
float weight = 45.5f;
double pai = 3.14;
//类型 变量名
//C语言为了描述变化的值,使用变量
//C语言为了描述不变的值,使用常量
int age;
char c;
//变量在创建的时候,不初始化是一种不好的代码风格
return 0;
}
#include<stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = sum1 + sum2;
printf("sum = %d\n", sum);
return 0;
}
//这里介绍一下输入,输出语句
//scanf
//printf
注意:全局变量使用起来方便,但为了防止冲突和安全性,尽量避免定义全局变量。
注意:当局部变量和全局变量同名的时候,局部变量优先使用。
//{}就是一个代码块
//在代码块内部定义的变量就是局部变量
//在代码块外部定义的变量就是全局变量
int a = 100;//全局变量
#include<stdio.h>
int main()
{
int a = 10;//局部变量
//局部变量和全局变量的名字,冲突的情况下,局部优先
printf("%d\n", a);
return 0;
}
//{}就是一个代码块
//在代码块内部定义的变量就是局部变量
//在代码块外部定义的变量就是全局变量
4.4 变量的作用域和生命周期
4.5 常量
常量就是不变的量/不可修改的量。
C语言中的常量和变量的定义的形式有所差异。
常量分为以下几种:
- 字面常量
- const 修饰的常变量
- #define 定义的标识符常量
- 枚举常量
4.5.1 字面常量
#include<stdio.h>
int main()
{
//1.字面常量演示
14; //整型常量
3.14; //浮点型常量
'w'; //字符常量,单个字符用单引号
"abcdef"; //字符串常量,字符串用双引号
return 0;
}
4.5.2 const 修饰的常变量
为什么叫常变量?首先我们先来了解下常规的变量:
#include<stdio.h>
int main()
{
int a = 10;
a = 40; //进行变量赋值,变量改变,最终输出40
printf("%d\n", a);
return 0;
}
上边的a变量是可以被修改的,如果我们不需要修改呢?我们只需要用声明 const 即可。
int main()
{
//const修饰的常变量
const float pai = 3.14f;//这里的pai是const修饰的常变量
pai = 5.14;//err //不能直接被修改
return 0;
}
注:上面例子上的 pai 被称为 const 修饰的常变量,const 修饰的常变量在C语言中只是在语法层面限制了变量 pai 不能直接被改变,但是 pai 本质上还是一个变量的,所以叫常变量。
4.5.3 #define 定义的标识符常量
//#define 的标识符常量
#define MAX 100
int main()
{
printf("max=%d\n", MAX);
return 0;
}
//输出: 100
define 定义的常量既可以用来直接接打印也可以用来赋值。
4.5.4 枚举常量
#include<stdio.h>
//举例
enum Sex
{
//下面三个变量就是枚举变量
MALE,
FEMALE,
SECRET
};
int main()
{
//枚举常量演示
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
//注:枚举常量的默认是从0开始,依次向下递增1的
return 0;
}
5. 字符串 + 转义字符 + 注释
5.1 字符串
1.首先char是字符类型,并不是字符串类型,c语言中是没有字符串类型的。
那怎么表示字符串呢?使用双引号""来表示。
"hello bit.\n"
#include <stdio.h>
//下面代码,打印结果是什么?为什么?(突出'\0'的重要性)
int main()
{
char arr1[] = "bit";
char arr2[] = {'b', 'i', 't'};
char arr3[] = {'b', 'i', 't', '\0'};
printf("%s\n", arr1); //bit
printf("%s\n", arr2); //打印结果不确定
printf("%s\n", arr3); //bit
return 0;
}
输出结果:
为什么会出现如此现象?
首先分析第一行和第三行,arr1数组,字符串后面隐藏了\0, arr3数组,字符串后面跟着\0,所以程序在执行时,遇见了\0便结束了,所以输出是bit.
第二个arr2数组,没有\0所以程序在't'之后不会结束,直到遇见\0什么时候程序才会停止。
3.下面再举个例子说明下\0的重要性。
这里我们使用一个库函数:strlen---用于输出字符串的长度,调用它需要引用头文件:#include<string.h>.
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = {'a','b','c','d','e','f'};
printf("%d\n", strlen(arr1));
printf("%d\n", strlen(arr2));
return 0;
}
//输出
//6
//22
同样的道理arr1遇见f后面的\0之后就停止了,并且\0不算字符串内容,所以输出字符串长度为6.
而arr2遇见f后并没有\0,所里就不会停止,继续向后遍历,直到遇到\0,所里最后输出22.
要想arr2正常输出字符串长度,只需手动添加\0。
5.2 转义字符
#include <stdio.h>
int main()
{
printf("c:\code\test.c\n");
return 0;
}
实际上程序运行的结果是这样的:
#include<stdio.h>
int main()
{
//问题1:在屏幕上打印一个单引号',怎么做?
printf("%c\n", '\'');
printf("%s\n", "\"");
return 0;
}
下面看一些转义字符:
转义字符 | 释义 |
\?
| 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\'
| 用于表示字符常量' |
\“
| 用于表示一个字符串内部的双引号 |
\\
| 用于表示一个反斜杠,防止它被解释为一个转义序列符。 |
\a
| 警告字符,蜂鸣 |
\b
| 退格符 |
\f
| 进纸符 |
\n
| 换行 |
\r
| 回车 |
\t
| 水平制表符 |
\v
| 垂直制表符 |
\ddd
| ddd表示1~3个八进制的数字。 如: \130 X |
\xdd
| dd表示2个十六进制数字。 如: \x30 0 |
后面两个转义符代码演示:
#include<stdio.h>
int main()
{
//强调:'\130'不是4个字符,而是一个字符
printf("%c\n", '\130');
//同理,'\x62'不是4个字符,而是一个字符
printf("%c\n", '\x62');
return 0;
}
练一道笔试题
#include<stdio.h>
int main()
{
printf("%d\n", strlen("abcdef"));//6
// \62被解析成一个转义字符
printf("%d\n", strlen("c:\test\628\test.c"));//14
return 0;
}
答案:14
分析:\t 算一个字符, \62算一个字符, 所以一共有14个字符。
5.3 注 释
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
/*C语言风格注释
int Sub(int x, int y)
{
return x-y;
}
*/
int main()
{
//C++注释风格
//int a = 10;
//调用Add函数,完成加法
printf("%d\n", Add(1, 2));
return 0;
}
- C语言风格的注释 /*xxxxxx*/
- 缺陷:不能嵌套注释
- C++风格的注释 //xxxxxxxx
- 可以注释一行也可以注释多行
6.顺序结构,选择语句
1.顺序结构顺序结构的程序设计是最简单的,只要按照解决问题的顺序写出相应的语句就行,它的执行顺序是自上而下,依次执行
2.选择结构顺序结构的程序虽然能解决计算、输出等问题,但不能做判断再选择。对于要先做判断再选择的问题就要使用选择结构。选择结构的执行是依据一定的条件选择执行路径,而不是严格按照语句出现的物理顺序。
#include <stdio.h>
int main()
{
int coding = 0;
printf("你会去敲代码吗?(选择1 or 0):>");
scanf("%d", &coding);
if(coding == 1)
{
prinf("坚持,你会有好offer\n");
}
else
{
printf("放弃,回家卖红薯\n");
}
return 0;
}
7.循环语句
循环结构可以减少源程序重复书写的工作量,用来描述重复执行某段算法的问题,这是程序设计中最能发挥计算机特长的程序结构,C语言中提供四种循环,即goto循环、while循环、do while循环和for循环。四种循环可以用来处理同一问题,一般情况下它们可以互相代替换,但一般不提倡用goto循环,因为强制改变程序的顺序经常会给程序的运行带来不可预料的错误。
特别要注意在循环体内应包含趋于结束的语句(即循环变量值的改变),否则就可能成了一个死循环,这是初学者的一个常见错误。
三个循环的异同点:用while和do…while循环时,循环变量的初始化的操作应在循环体之前,而for循环一般在语句1中进行的; while循环和for循环都是先判断表达式,后执行循环体,而do…while循环是先执行循环体后判断表达式,也就是说do…while的循环体最少被执行一次,而while循环和for就可能一次都不执行。另外还要注意的是这三种循环都可以用break语句跳出循环,用continue语句结束本次循环,而goto语句与if构成的循环,是不能用break和 continue语句进行控制的。
//while循环的实例
#include <stdio.h>
int main()
{
printf("加入比特\n");
int line = 0;
while(line<=20000)
{
line++;
printf("我要继续努力敲代码\n");
}
if(line>20000)
printf("好offer\n");
return 0;
}
8. 函数
定义函数:
C程序是由一组变量或是函数的外部对象组成的。 函数是一个自我包含的完成一定相关功能的执行代码段。我们可以把函数看成一个“黑盒子”,你只要将数据送进去就能得到结果,而函数内部究竟是如何工作的,外部程序是不知道的。外部程序所知道的仅限于输入给函数什么以及函数输出什么。函数提供了编制程序的手段,使之容易读、写、理解、排除错误、修改和维护。
返回类型 函数名(函数形式参数)
例: int Add (int x, int y)
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
//上述代码,写成函数如下:
#include <stdio.h>
int Add(int x, int y)
{
int z = x+y;
return z;
}
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("输入两个操作数:>");
scanf("%d %d", &num1, &num2);
sum = Add(num1, num2);
printf("sum = %d\n", sum);
return 0;
}
函数的特点就是简化代码,代码复用。
9.数组
#include<stdio.h>
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素
printf("%d\n", arr[3]); //遍历数组
return 0;
}
arr[10] 数组中的10,表示要存储10个int类型的元素,当然不写也可以,程序会自动分配。
9.2 数组的下标
int arr[10] = {0};
//如果数组10个元素,下标的范围是0-9
9.3 数组的使用
#include <stdio.h>
int main()
{
int i = 0;
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
for(i=0; i<10; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
10 操作符
比较特别的是, 比特右移(>>)运算符可以是算术(左端补最高有效位)或是逻辑(左端补0)位移。例如,将 11100011 右移 3 比特,算术右移后成为 11111100,逻辑右移则为 00011100。因算术比特右移较适于处理带负号整数,所以几乎所有的编译器都是算术比特右移。运算符的优先级从高到低大致是:单目运算符、算术运算符、关系运算符、逻辑运算符、条件运算符、赋值运算符(=)和逗号运算符。
10.1 算术操作符+ - * / (得整数部分) %(取模,得余数)+ - * / (得整数部分) %(取模,得余数) //7 / 2 --------> 3 //7 % 2 --------> 1 //如果想要打印出小数,只需要计算两边有一个浮点数就会执行浮点数的触发 #include<sdtio.h> int main() { float a = 7 / 2.0; printf("%.1f\n", a); //.1就是保留小数点后一位 return 0; }
10.2 移位操作符
>> <<10.3 位操作符& ^ |10.4 赋值操作符= += -= *= /= &= ^ = |= >>= <<=a += 3 等同于 a = a + 3;
!
| 逻辑反操作 |
- | 负值 |
+ | 正值 |
& | 取地址 |
sizeof | 操作数的类型长度(以字节为单位) |
~ | 对一个数的二进制按位取反 |
-- | 前置、后置-- |
++ | 前置、后置++ |
* |
间接访问操作符
(
解引用操作符
)
|
(类型) | 强制类型转换 |
#include<stdio.h>
int main()
{
int flag = 0; //flag初始化为0,所以flag此时为假
if(!flag)
printf("现在为真\n");
return 0;
}
2.sizeof 的使用介绍
#include<stdio.h>
int main()
{
int a = 10;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
//因为sizeof是一个操作符,所以不带()也可以使用,输出为4
printf("%d\n", sizeof a);
return 0;
}
sizeof 也可用来统计数组的大小。
3. ++,-- 的使用
--的用法和++,同样的用法。
先说后置++,eg : a++, 后置++的原则:先使用,后++
#include<stdio.h>
int main()
{
int a = 10;
//先使用后++,也就是先int a = b, 然后a = a + 1
int b = a++;
printf("%d\n", b);
printf("%d\n", a);
return 0;
}
输出:
//10
//11
前置++, eg : ++a, 前置++遵循一个原则:先++,后使用
#include<stdio.h>
int main()
{
int a = 10;
//先++后使用,也就是先a = a + 1, 然后int b = a
int b = ++a;
printf("%d\n", b);
printf("%d\n", a);
return 0;
}
输出:
//11
//11
4. 强制类型转换
#include<stdio.h>
int main()
{
int a = (int)3.14;
printf("%d\n", a);
return 0;
}
//输出:
//3
10.6 关系操作符
>
>=
<
<=
!=
用于测试
“
不相等
”
==
用于测试
“
相等
”
|
&&
逻辑与/并且
||
逻辑或/或者
|
exp1 ? exp2 : exp3
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = a > b ? a : b;
printf("%d\n", c);
return 0;
}
//输出:
//20
#include<stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 0;
int d = (c = a - 2, a = b + c, c - 3);
//最后一个表达式c-3=5, 所以最终输出结果5
printf("%d\n", d);
return 0;
}
//输出: 5
10.10 下标引用、函数调用和结构成员[] () . ->下标引用printf("%d\n", arr[0]);
函数调用Add();
11 常见的关键字
- 关键字又称为保留字,就是已被C语言本身使用,不能作其它用途使用的字。例如关键字不能用作变量名、函数名等标识符由ISO标准定义的C语言关键字共32个:
- auto | double | int | struct | break | else | long | switch case | enum | register | typedef | char | extern | return | union const | float | short | unsigned | continue | for | signed | voiddefault | goto | sizeof | volatile | do | if | while | static | inlinerestrict _Bool _Complex _Imaginary _Generic
auto | break | case | char | const | continue | default |
do | double | else | enum | extern | float | for |
goto | if | int | long | register | return | short |
signed | sizeof | static | struct | switch | typedef | union |
unsigned | void | volatile | while |
- 基本数据类型void:声明函数无返回值或无参数,声明无类型指针,显示丢弃运算结果。(C89标准新增)
- char:字符型类型数据,属于整型数据的一种。(K&R时期引入)
- int:整型数据,表示范围通常为编译器指定的内存字节长。(K&R时期引入)
- float:单精度浮点型数据,属于浮点数据的一种。(K&R时期引入)
- double:双精度浮点型数据,属于浮点数据的一种。(K&R时期引入)
- _Bool:布尔型(C99标准新增)
- _Complex:复数的基本类型(C99标准新增)
- _Imaginary:虚数,与复数基本类型相似,没有实部的纯虚数(C99标准新增)
- _Generic:提供重载的接口入口(C11标准新增)
- 类型修饰关键字short:修饰int,短整型数据,可省略被修饰的int。(K&R时期引入)
- long:修饰int,长整型数据,可省略被修饰的int。(K&R时期引入)
- long long:修饰int,超长整型数据,可省略被修饰的int。(C99标准新增)
- signed:修饰整型数据,有符号数据类型。(C89标准新增)
- unsigned:修饰整型数据,无符号数据类型。(K&R时期引入)
- restrict:用于限定和约束指针,并表明指针是访问一个数据对象的唯一且初始的方式。(C99标准新增)
- 复杂类型关键字struct:结构体声明。(K&R时期引入)
- union:联合体声明。(K&R时期引入)
- enum:枚举声明。(C89标准新增)
- typedef:声明类型别名。(K&R时期引入)
- sizeof:得到特定类型或特定类型变量的大小。(K&R时期引入)
- inline:内联函数用于取代宏定义,会在任何调用它的地方展开。(C99标准新增)
- 存储级别关键字auto:指定为自动变量,由编译器自动分配及释放。通常在栈上分配。与static相反。当变量未指定时默认为auto。(K&R时期引入)
- static:指定为静态变量,分配在静态变量区,修饰函数时,指定函数作用域为文件内部。(K&R时期引入)
- register:指定为寄存器变量,建议编译器将变量存储到寄存器中使用,也可以修饰函数形参,建议编译器通过寄存器而不是堆栈传递参数。(K&R时期引入)
- extern:指定对应变量为外部变量,即标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。(K&R时期引入)
- const:指定变量不可被当前线程改变(但有可能被系统或其他线程改变)。(C89标准新增)
- volatile:指定变量的值有可能会被系统或其他线程改变,强制编译器每次从内存中取得该变量的值,阻止编译器把该变量优化成寄存器变量。(C89标准新增)
流程控制关键字
- 跳转结构return:用在函数体中,返回特定值(如果是void类型,则不返回函数值)。(K&R时期引入)
- continue:结束当前循环,开始下一轮循环。(K&R时期引入)
- break:跳出当前循环或switch结构。(K&R时期引入)
- goto:无条件跳转语句。(K&R时期引入)
- 分支结构if:条件语句,后面不需要放分号。(K&R时期引入)
- else:条件语句否定分支(与if连用)。(K&R时期引入)
- switch:开关语句(多重分支语句)。(K&R时期引入)
- case:开关语句中的分支标记,与switch连用。(K&R时期引入)
- default:开关语句中的“其他”分支,可选。(K&R时期引入)
编译
- #define预编译宏#if 表达式 #else if 表达式 #else #endif条件编译#ifdef 宏 #else #endif 条件编译#ifndef 宏 #else #endif 条件编译与条件编译
11.1 关键字typedef
//将unsigned int 重命名为uint_32, 所以uint_32也是一个类型名
typedef unsigned int uint_32;
int main()
{
//观察num1和num2,这两个变量的类型是一样的
unsigned int num1 = 0;
uint_32 num2 = 0;
return 0;
}
11.2 关键字static
在C语言中:static是用来修饰变量和函数的
- 修饰局部变量-称为静态局部变量
- 修饰全局变量-称为静态全局变量
- 修饰函数-称为静态函数
11.2.1 static 修饰局部变量
//代码1
#include <stdio.h>
void test()
{
int n = 0;
n++;
printf("%d ", n);
}
int main()
{
int i = 0;
for(i=0; i<10; i++)
{
test();
}
return 0;
}
输出:
分析:上面说过变量的生命周期从进入作用域开始创建,到出作用域销毁。
在第一遍调用test函数时,n这个变量从{开始创建,打印}出作用域开始销毁,然后中间n++,所以最终打印出来n=2,这个时候test函数调用完成,此时已经没有n这个东西了,因为已经销毁了,然后第二遍调用testh函数,然后从头开始创建n变量并赋值n=1,然后打印n=2,然后销毁n变量,所以最终打印效果:2 2 2 2 2 2 2 2 2 2.
下面时使用static关键词,来修饰局部变量:
//代码2
#include <stdio.h>
void test()
{
//static修饰局部变量
static int i = 0;
i++;
printf("%d ", i);
}
int main()
{
int i = 0;
for(i=0; i<10; i++)
{
test();
}
return 0;
}
对比代码 1 和代码 2 的效果理解 static 修饰局部变量的意义。结论:static修饰局部变量改变了变量的生命周期让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。总结:static修饰局部变量的时候,局部变量出了作用域,局部变量不会被销毁。
本质上,static修饰局部变量的时候,改变了变量的存储位置。
内存时一块比较大的存储空间,在使用内存是会划分不同的功能区域。
在学习编程语言的时候,栈区、堆区、静态区。
变量被staic修饰后,此变量就从栈区移动到了静态区,但代码这里n直接就是在静态区了,不存在转换,这里只是特殊说明一下,所以说static修饰局部变量的时候,改变了变量的存储位置。
11.2.2 修饰全局变量
//代码1
//add.c
int g_val = 2018;
//test.c
int main()
{
printf("%d\n", g_val);
return 0;
}
//代码2
//add.c
static int g_val = 2018;
//test.c
int main()
{
printf("%d\n", g_val);
return 0;
}
test.c ------------------------------------------> test.exe
编译 -- 链接 -- 运行
- 一个全局变量在整个工程的其他文件内部能被使用,是因为全局变量具有(外部连接)属性.
- 当一个全局变量被 static 修饰的时候,这个变量的外部链接属性就变成了内部链接属性。
- 使得这个全局变量只能在自己所在的源文件内部使用,其他文件不能使用。
给我们的感觉是作用域变小了
11.2.3 修饰函数
//代码1
//add.c
int Add(int x, int y)
{
return c+y;
}
//test.c
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
//代码2
//add.c
static int Add(int x, int y)
{
return c+y;
}
//test.c
int main()
{
printf("%d\n", Add(2, 3));
return 0;
}
结论:
一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
12. #define 定义常量和宏
//define定义标识符常量
#define MAX 1000
//define定义宏
#define ADD(x, y) ((x)+(y))
#include <stdio.h>
int main()
{
int sum = ADD(2, 3);
printf("sum = %d\n", sum);
sum = 10*ADD(2, 3);
printf("sum = %d\n", sum);
return 0;
}
13. 指针
#include <stdio.h>
int main()
{
int num = 10;
#//取出num的地址
//注:这里num的4个字节,每个字节都有地址,取出的是第一个字节的地址(较小的地址)
printf("%p\n", &num);//打印地址,%p是以地址的形式打印
return 0;
}
& - 取地址操作符&a,取出的是所占4个字节空间的第一个字节的地址(地址小的那一个字节)。
int num = 10;
int *p;//p为一个整形指针变量
p = #
#include <stdio.h>
int main()
{
int num = 10;
int *p = #
*p = 20;
return 0;
}
#include <stdio.h>
int main()
{
char ch = 'w';
char* pc = &ch;
*pc = 'q';
printf("%c\n", ch);
return 0;
}
13.2 指针变量的大小
#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%d\n", sizeof(char *));
printf("%d\n", sizeof(short *));
printf("%d\n", sizeof(int *));
printf("%d\n", sizeof(double *));
return 0;
}
14 结构体
struct Stu
{
char name[20];//名字
int age; //年龄
char sex[5]; //性别
char id[15]; //学号
};
//打印结构体信息
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(
14.1 用结构体类型来创建一个变量,并打印信息。
我们知道创建一个变量很简单,都是数据类型+变量名, 比如:
char ch;
int a;
double d;
那同样上面的struct Stu 结构体也是个类型,然后他也能创建变量:struct Stu s,这个s就是变量,可以发现struct Stu{...} 和int 的效果一样,只不过int 是解决单一变量问题的,而struct Stu解决复杂变量问题的,所以说struct Stu{...}和int 一样是不占空间的,只有当struct Stu s动作发生,创建出来的s变量才占用一点空间。
那要如何使用变量呢?
#include<stdio.h>
struct Stu
{
char name[20];
int age;
char sex[5];
char id[15];
};
int main()
{
struct Stu S = {"zhangsan","31","nan","21112032"};
printf("%s %d %s %s\n", S.name, S.age, S.sex, S.id);
return 0;
}
14.2 能不能用指针去实现打印信息的效果。
代码如下:
#include<stdio.h>
struct Stu
{
char name[20];
int age;
char sex[5];
char id[15];
};
void print(struct Stu* ps)
{
printf("%s %d %s %s\n", (*ps).name, (*ps).age, (*ps).sex, (*ps).id);
printf("%s %d %s %s\n", ps->name, ps->age, ps->sex, ps->id);
//效果一样,形式:结构体对象.结构体成员变量
}
int main()
{
struct Stu S = { "zhangsan","17","nan","21112032" };
print(&S);
return 0;
}
15 【普及】关键字register - 寄存器
由于CPU处理数据速度非常之快,以至于内存赶不上CPU的速度,所以当处理数据时,内存中的数据向上一级存放,于是就放到高速缓存、寄存器里面。
那进入整体,在写代码时我们也可以写出寄存器变量:
#include<stdio.h>
int main()
{
//寄存器变量
register int num = 3; //建议把'3'存放在寄存器里面
return 0;
}
当把'3' 数据存放在寄存器里面了,这样在读取此数据时会更快,但是需要注意一点,这里只是建议存放,那到底最后是否存放在寄存器里面时编译器说了算。
初始C语言知识补充
(1)C语言是一门通用计算机编程语言,广泛应用于底层开发。那么什么叫做底层开发呢?
一般我们所购买的电脑都是硬件设备,需要在电脑上安装操作系统,比如windows / Linux / macos等,但是操作系统并不是直接控制电脑硬件,而是通过中间层-- - 驱动(各种驱动软件),来间接操控硬件设备。在操作系统安装完成后,如果我们需要丰富多彩的体验和功能,就需要安装应用软件,比如社交聊天用的QQ、微信、网络资源管理和共享的云盘网盘、娱乐放松的电脑游戏,这些都存在与应用层。通常我们将应用层往下称为底层。
(2)常见文件后缀.c-- - 源文件 (c语言格式source).h-- - 头文件(head).cpp-- - 源文件(c++格式).exe-- - 可执行程序
(3)一个工程中有且仅能有一个main函数(主函数),一个工程中可以有多个.c源文件,多个.h头文件,但是多个.c文件中有且仅能有一个main函数。分析理解:main函数是整个工程程序执行的入口,如果出现了多个main函数,那么程序就不知道该从哪个入口进去,不知道执行哪个main函数,就会出错!
(4)变量的声明和定义、函数的声明和定义可以放到main函数外面,但是执行类的语句不能放到main函数外面
例如:全局变量a的声明和定义可以放到main函数外面,但是打印a的值这条执行语句不能放到main函数外面。
再补充:
C语言-格式输入输出中“%d,%o,%x,%e,%f等的含义
格式说明由“%”和格式字符组成,如%d%f等。它的作用是将输出的数据转换为指定的格式输出。格式说明总是由“%”字符开始的。不同类型的数据用不同的格式字符。
格式字符有d,o,x,u,c,s,f,e,g等。
如下所示:
%d - 整型输出,%ld - 长整型输出,\n\n
%o - 以八进制数形式输出整数,\n\n
%x - 以十六进制数形式输出整数,\n\n
%u - 以十进制数输出unsigned型数据(无符号数)。\n\n
%c - 用来输出一个字符,\n\n
%s - 用来输出一个字符串,\n\n
%f - 用来输出实数,以小数形式输出,(备注:浮点数是不能定义如的精度的,所以“%6.2f”这种写法是“错误的”!!!)\n\n
%e - 以指数形式输出实数,\n\n
%g - 根据大小自动选f格式或e格式,且不输出无意义的零。\n\n
scanf(控制字符,地址列表) \n
格式字符的含义同printf函数,地址列表是由若干个地址组成的表列,可以是变量的地址,或字符串的首地址。如scanf(\"%d%c%s\",&a,&b,str);
以下是运算符的优先级表:
()、 []、 -> 、 .、!、 ++、 -- | 圆括号、方括号、指针、成员、逻辑非、自加、自减 |
++ 、 -- 、 * 、 & 、 ~ 、! | 单目运算符 |
+、 - 、 sizeof、(cast) | |
* 、 / 、% | 算术运算符 |
+ 、 - | 算术运算符 |
<< 、 >> | 位运算符 |
< 、 <= 、 > 、 >= | 关系运算符 |
== 、!= | 关系运算符号 |
& | 位与 |
^ | 位异或 |
| | 位或 |
&& | 逻辑与 |
|| | 逻辑或 |
? 、: | 条件运算符 |
/= 、%= 、 &= 、 |= 、 ^= | 赋值运算符 |
= 、 += 、 -= 、 *= 、 | |
, | 顺序运算符 |