初识C语言
1. 什么是C语言?
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产 生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。 尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的 C语言程序可在许多电脑平台上进 行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。 二十世纪八十年代,为了避免各开发厂商用的C语言语法产生差异,由美国国家标准局为C语言制 定了一套完整的美国国家标准语 法,称为ANSI C,作为C语言最初的标准。 [1] 目前2011年12月8日,国际标准化组织(ISO)和 国际电工委员会(IEC)发布的C11 标准是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字 标识符,一定程度上实现了汉 字编程。 C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。 其编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等。
2. 第一个C语言程序
#include <stdio.h>
int main() //main 是程序的入口,有且只有一个
{
printf("hello world\n");
printf("The first day of relearning C\n");
return 0;
}
3. 数据类型
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
//C语言没有字符串类型,但是C语言字符串实际就是多个字符链接在一起,
//所以可以使用字符数组或者指针
各类型的大小
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(char)); //1
printf("%d\n", sizeof(short)); //2
printf("%d\n", sizeof(int)); //4
printf("%d\n", sizeof(long)); //4
printf("%d\n", sizeof(long long)); //8
printf("%d\n", sizeof(float)); //4
printf("%d\n", sizeof(double)); //8
printf("%d\n", sizeof(long double)); //12
return 0;
}
3. 变量、常量
3.1 定义变量的方法
int age = 150;
char a = 'w';
float hight = 180.5f;
"量"表示数据。变量,则表示一些不固定的数据,也就是可以改变的数据
3.2 变量分类
-
局部变量
局部变量也称为内部变量局部变量是在***代码块内***定义的, 其作用域仅限于代码块内, 离开该代码块后无法使用
int main(){
{
int i = 998; // 作用域开始
}// 作用域结束
printf("i = %d\n", i); // 不能使用
return 0;
}
-
全局变量
全局变量也称为外部变量,它是在代码块外部定义的变量
int i = 666; //全局变量
int main(){
printf("i = %d\n", i); // 可以使用
return 0;
}// 作用域结束
int call(){
int i = 888;
printf("i = %d\n", i); // 可以使用
return 0;
}
1.当局部变量和全局变量同名的时候,局部变量优先使用。
2.局部变量与全局变量可以同名
3.3 变量的使用
#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;
}
3.4 变量的作用域与生命周期
作用域
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的
而限定这个名字的可用性的代码范围就是这个名字的作用域。
1. 局部变量的作用域是变量所在的局部范围。
2. 全局变量的作用域是整个工程。
生命周期
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
2. 全局变量的生命周期是:整个程序的生命周期。
3.5 常量
C语言中的常量和变量的定义的形式有所差异。
C语言中的常量分为以下以下几种:
- 字面常量
- const 修饰的常变量
- #define 定义的标识符常量
- 枚举常量
#include <stdio.h>
//举例
enum Sex
{
MALE = 11,
FEMALE = 15,
SECRET
};
//括号中的MALE,FEMALE,SECRET是枚举常量
int main()
{
//字面常量演示
3.14;//字面常量
1000;//字面常量
//const 修饰的常变量
const float pai = 3.14f; //这里的pai是const修饰的常变量
//pai = 5.14;//是不能直接修改的!
//#define的标识符常量 演示
#define MAX 100
printf("max = %d\n", MAX);
//枚举常量演示
printf("%d\n", MALE);
printf("%d\n", FEMALE);
printf("%d\n", SECRET);
//注:枚举常量的默认是从0开始,依次向下递增1的
return 0;
}
4. 字符串+转义字符
4.1 字符串
"Have a good day!\n"
这种由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。
注:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串内容。
#include <stdio.h>
//下面代码,打印结果是什么?为什么?(突出'\0'的重要性)
int main()
{
char arr1[] = "wxb";
char arr2[] = {'w', 'x', 'b'};
char arr3[] = {'w', 'x', 'b', '\0'};
printf("%s\n", arr1);
printf("%s\n", arr2);
printf("%s\n", arr3);
return 0;
}
编译结果为:
4.2 转义字符
转义字符顾名思义就是转变意思。
下面看一些转义字符。
转义字符 | 释义 |
\? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\' | 用于表示字符常量 ' |
\" | 用于表示字符串内部的双引号 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | 表示1~3个八进制的数字。 |
\xdd | 表示2个十六进制的数字。 |
笔试题:
#include <stdio.h>
#include<cstring>
int main()
{
printf("%d\n", strlen("abcdef")); //6
// \62被解析成一个转义字符
printf("c:\test\628\test.c\n"); //c: est28 est.c
printf("%d\n", strlen("c:\test\628\test.c")); //14
return 0;
}
5. 注释
5.1 单行注释
- // 被注释内容
- 使用范围:任何地方都可以写注释:函数外面、里面,每一条语句后面
- 作用范围: 从第二个斜线到这一行末尾
- 快捷键(codeblocks):
ctrl+shift+c //注释
ctrl+shift+x //取消注释
5.2 多行注释
- /* 被注释内容 */
- 使用范围:任何地方都可以写注释:函数外面、里面,每一条语句后面
- 作用范围: 从第一个/*到最近的一个*/
5.3 注意:
- 单行注释可以嵌套单行注释、多行注释
- 多行注释可以嵌套单行注释
- 多行注释不能嵌套多行注释
6. printf函数
- printf函数称之为格式输出函数,方法名称的最后一个字母f表示format。其功能是按照用户指定的格式,把指定的数据输出到屏幕上
- printf函数的调用格式为:
printf("格式控制字符串",输出项列表 );
- 例如:
printf("a = %d, b = %d",a, b);
- 格式控制字符串
- 形式:%[标志][输出宽度][.精度][长度]类型
- 标志
- 格式:printf("a = %[标志][宽度]类型", a);
标志 含义
- 左对齐,默认右对齐 + 当输出值为正数时,在输出值前面加上一个+号,默认不显示 0 右对齐时,用0填充宽度。(默认用空格填充) 空格 输出值为正数时,在输出值前面加上空格,为负数时加上负号 # 对c、s、d、u类型无影响 # 对o类型, 在输出时加前缀o # 对x类型,在输出时加前缀0x #include <stdio.h> int main(){ int a = 1; int b = -1; // -号标志 printf("a =|%d|\n", a); // |1| printf("a =|%5d|\n", a); // | 1| printf("a =|%-5d|\n", a); // |1 | // +号标志 printf("a =|%d|\n", a); // |1| printf("a =|%+d|\n", a); // |+1| printf("b =|%d|\n", b); // |-1| printf("b =|%+d|\n", b); // |-1| // 0标志 printf("a =|%5d|\n", a); // | 1| printf("a =|%05d|\n", a); // |00001| // 空格标志 printf("a =|% d|\n", a); // | 1| printf("b =|% d|\n", b); // |-1| // #号 int c = 10; printf("c = %o\n", c); // 12 printf("c = %#o\n", c); // 012 printf("c = %x\n", c); // a printf("c = %#x\n", c); // 0xa }
- 格式:printf("a = %[标志][宽度]类型", a);
- 宽度
- 格式:printf("a = %[宽度]类型", a);
- 用十进制整数来指定输出的宽度, 如果实际位数多于指定宽度,则按照实际位数输出, 如果实际位数少于指定宽度则以空格补位
#include <stdio.h>
int main(){
// 实际位数小于指定宽度
int a = 1;
printf("a =|%d|\n", a); // |1|
printf("a =|%5d|\n", a); // | 1|
// 实际位数大于指定宽度
int b = 1234567;
printf("b =|%d|\n", b); // |1234567|
printf("b =|%5d|\n", b); // |1234567|
}
- 精度
- 格式:printf("a = %[.精度]类型", a);
- 精度格式符以"."开头, 后面跟上十进制整数, 用于指定需要输出多少位小数, 如果输出位数大于指定的精度, 则删除超出的部分
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.2f\n", a); // 3.14
}
- 动态指定保留小数位数
- 格式:printf("a = %.*f", f, a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.*f", 2, a); // 3.14
}
- 实型(浮点类型)有效位数问题
- 对于单精度数,使用%f格式符输出时,仅前6~7位是有效数字
- 对于双精度数,使用%lf格式符输出时,前15~16位是有效数字
- 有效位数和精度(保留多少位)不同, 有效位数是指从第一个非零数字开始,误差不超过本数位半个单位的、精确可信的数位
- 有效位数包含小数点前的非零数位
#include <stdio.h>
int main(){
// 1234.567871093750000
float a = 1234.567890123456789;
// 1234.567890123456900
double b = 1234.567890123456789;
printf("a = %.15f\n", a);
// 前8位数字是准确的, 后面的都不准确
//a = 1234.567871093750000
printf("b = %.15f\n", b);
// 前16位数字是准确的, 后面的都不准确
//b = 1234.567890123456900
}
- 长度
- 格式:printf("a = %[长度]类型", a);
长度 | 修饰类型 | 含义 |
hh | d、i、o、u、x | 输出 char |
h | d、i、o、u、x | 输出 short int |
l | d、i、o、u、x | 输出 long int |
ll | d、i、o、u、x | 输出 long long int |
#include <stdio.h>
int main(){
char a = 'a';
short int b = 123;
int c = 123;
long int d = 123;
long long int e = 123;
printf("a = %hhd\n", a); // 97
printf("b = %hd\n", b); // 123
printf("c = %d\n", c); // 123
printf("d = %ld\n", d); // 123
printf("e = %lld\n", e); // 123
}
7. scanf 函数
- scanf函数用于接收键盘输入的内容, 是一个阻塞式函数,程序会停在scanf函数出现的地方, 直到接收到数据才会执行后面的代码
- printf函数的调用格式为:
- scanf("格式控制字符串",地址列表);
- 例如:scanf("%d",&num);
-
基本用法:
- 地址列表项中只能传入变量地址, 变量地址可以通过&符号+变量名称的形式获取
#include <stdio.h> int main(){ int number; scanf("%d", &number); // 接收一个整数 printf("number = %d\n", number); }
- 地址列表项中只能传入变量地址, 变量地址可以通过&符号+变量名称的形式获取
8. putchar和getchar
8.1 putchar
-
向屏幕输入一个字符
#include <stdio.h>
int main(){
char ch = 'a';
putchar(ch); // 输出a
}
8.2 getchar
- 从键盘获得一个字符
#include <stdio.h>
int main(){
char ch;
ch = getchar();// 获取一个字符
printf("ch = %c\n", ch);
}
9. 关键字
-
关键字,也叫作保留字。是指一些被C语言赋予了特殊含义的单词
-
关键字特征:
- 全部都是小写
- 在开发工具中会显示特殊颜色
-
关键字注意点:
- 因为关键字在C语言中有特殊的含义, 所以不能用作变量名、函数名等
-
关键字分类
9.1 typedef
- C语言不仅提供了丰富的数据类型,而且还允许由用户自己定义类型说明符,也就是说允许由用户为数据类型取“别名”。
- 格式: typedef 原类型名 新类型名;
- 其中原类型名中含有定义部分,新类型名一般用大写表示,以便于区别。
- 有时也可用宏定义来代替typedef的功能,但是宏定义是由预处理完成的,而typedef则是在编译 时完成的,后者更为灵活方便。
-
基本数据类型
typedef int INTEGER
INTEGER a; // 等价于 int a;
- 也可以在别名的基础上再起一个别名
typedef int Integer;
typedef Integer MyInteger;
-
用typedef定义数组、指针、结构等类型将带来很大的方便,不仅使程序书写简单而且使意义更为 明确,因而增强了可读性。
-
数组类型
typedef char NAME[20]; // 表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量,
NAME a; // 等价于 char a[20];
- 结构体类型
- 第一种形式:
struct Person{
int age;
char *name;
};
typedef struct Person PersonType;
-
- 第二种形式:
typedef struct Person{
int age;
char *name;
} PersonType;
-
- 第三种形式:
typedef struct {
int age;
char *name;
} PersonType;
-
枚举
- 第一种形式:
enum Sex{
SexMan,
SexWoman,
SexOther
};
typedef enum Sex SexType;
-
- 第二种形式:
typedef enum Sex{
SexMan,
SexWoman,
SexOther
} SexType;
-
- 第三种形式:
typedef enum{
SexMan,
SexWoman,
SexOther
} SexType;
-
指针
- typedef与指向结构体的指针
// 定义一个结构体并起别名
typedef struct {
float x;
float y;
} Point;
// 起别名
typedef Point *PP;
-
- typedef与指向函数的指针
// 定义一个sum函数,计算a跟b的和
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
typedef int (*MySum)(int, int);
// 定义一个指向sum函数的指针变量p
MySum p = sum;
9.2 static
- 对局部变量的作用
- 延长局部变量的生命周期,从程序启动到程序退出,但是它并没有改变变量的作用域
- 定义变量的代码在整个程序运行期间仅仅会执行一次
#include <stdio.h>
void test();
int main()
{
test();
test();
test();
return 0;
}
void test(){
static int num = 0; // 局部变量
num++;
// 如果不加static输出 1 1 1
// 如果添加static输出 1 2 3
printf("num = %i\n", num);
}
- 结论
- static修饰局部变量改变了变量的生命周期
- 让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
- 对全局变量的作用
- 全局变量分类:
- 内部变量:只能在本文件中访问的变量
- 外部变量:可以在其他文件中访问的变量,默认所有全局变量都是外部变量
- 默认情况下多个同名的全局变量共享一块空间, 这样会导致全局变量污染问题
- 如果想让某个全局变量只在某个文件中使用, 并且不和其他文件中同名全局变量共享同一块存储空间, 那么就可以使用static
- 全局变量分类:
// A文件中的代码
static int num; // 不和B文件中的num共享
void test(){
printf("ds.c中的 num = %i\n", num);
}
// B文件中的代码
#include <stdio.h>
#include "ds.h"
int num; // 不和A文件中的num共享
int main()
{
num = 666;
test(); // test中输出0
return 0;
}
- 结论:
- 一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
- 对函数的作用
- 一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。
10. #define宏定义
- 被定义为“宏”的标识符称为“宏名”。在编译预处理时,对程序中所有出现的“宏名”,都用宏定义中的字符串去代换,这称为“宏代换”或“宏展开”。
- 宏定义是由源程序中的宏定义命令完成的。宏代换是由预处理程序自动完成的。在C语言中,“宏”分为有参数和无参数两种。
10.1 不带参数的宏定义
-
格式:#define 标识符 字符串
- 其中的“#”表示这是一条预处理命令。凡是以“#”开头的均为预处理命令。“define”为宏定义命令。“标识符”为所定义的宏名。“字符串”可以是常数、表达式、格式串等。
#include <stdio.h>
// 源程序中所有的宏名PI在编译预处理的时候都会被3.14所代替
#define PI 3.14
// 根据圆的半径计radius算周长
float girth(float radius) {
return 2 * PI *radius;
}
int main ()
{
float g = girth(2);
printf("周长为:%f", g);
return 0;
}
注意:
- 宏名一般用大写字母,以便与变量名区别开来,但用小写也没有语法错误
- 对程序中用双引号扩起来的字符串内的字符,不进行宏的替换操作
- 在编译预处理用字符串替换宏名时,不作语法检查,只是简单的字符串替换。只有在编译的时候才对已经展开宏名的源程序进行语法检查
- 宏名的有效范围是从定义位置到文件结束。如果需要终止宏定义的作用域,可以用#undef命令
#define PI 3.14 int main () { printf("%f", PI); return 0; } #undef PI void test() { printf("%f", PI); // 不能使用 }
- 定义一个宏时可以引用已经定义的宏名
- 可用宏定义表示数据类型,使书写方便
#define String char *
int main(int argc, const char * argv[])
{
String str = "This is a string!";
return 0;
}
10.2 带参数的宏定义
- C语言允许宏带有参数。在宏定义中的参数称为形式参数,在宏调用中的参数称为实际参数。对带参数的宏,在调用中,不仅要宏展开,而且要用实参去代换形参
- 格式:
#define 宏名(形参表) 字符串
注意:
- 宏名和参数列表之间不能有空格,否则空格后面的所有字符串都作为替换的字符串.
- 带参数的宏在展开时,只作简单的字符和参数的替换,不进行任何计算操作。所以在定义宏时,一般用一个小括号括住字符串的参数。
#include <stdio.h> // 下面定义一个宏D(a),作用是返回a的2倍数值: #define D(a) 2*a // 如果定义宏的时候不用小括号括住参数 int main () { // 将被替换成int b = 2*3+4;,输出结果10,如果定义宏的时候用小括号括住参数,把上面的第3行改成:#define D(a) 2*(a),注意右边的a是有括号的,第7行将被替换成int b = 2*(3+4);,输出结果14 int b = D(3+4); printf("%d", b); return 0; }
- 计算结果最好也用括号括起来
11. 流程控制
-
默认情况下程序运行后,系统会按书写顺序从上至下依次执行程序中的每一行代码。但是这并不能满足我们所有的开发需求, 为了方便我们控制程序的运行流程,C语言提供3种流程控制结构,不同的流程控制结构可以实现不同的运行流程。
-
这3种流程结构分别是顺序结构、选择结构、循环结构。
-
顺序结构:按书写顺序从上至下依次执行
-
选择结构:对给定的条件进行判断,再根据判断结果来决定执行代码
-
循环结构:在给定条件成立的情况下,反复执行某一段代码
顺序结构 选择结构
循环结构
11.1 选择结构
- if 选择结构
- 表示如果表达式为真,执行语句块1,否则不执行。
if(表达式) { 语句块1; } 后续语句; #include <stdio.h> int main() { int coding; printf("你会去敲代码吗?(选择1 or 0):>"); scanf("%d", &coding); if(1 == coding) //常数放在前面 { printf("坚持,你会有好offer\n"); } else { printf("放弃,回家卖红薯\n"); } return 0; }
- 如果表达式为真,则执行语句块1,否则执行语句块2;else不能脱离if单独使用。
if(age > 18){ printf("开网卡\n"); }else{ printf("喊家长来开\n"); } printf("买烟\n");
- 如果"表达式1"为真,则执行"语句块1",否则判断"表达式2",如果为真执行"语句块2",否则再判断"表达式3",如果真执行"语句块3", 当表达式1、2、3都不满足,会执行最后一个else语句。
众多大括号中,只有一个大括号中的内容会被执行。
只有前面所有添加都不满足, 才会执行else大括号中的内容。
if(age>40){
printf("给房卡");
}else if(age>25){
printf("给名片");
}else if(age>18){
printf("给网卡");
}else{
printf("给好人卡");
}
printf("买烟\n");
- switch 选择结构
- 格式:
switch(表达式){
case 常量表达式1:
语句1;
break;
case 常量表达式2:
语句2;
break;
case 常量表达式n:
语句n;
break;
default:
语句n+1;
break;
}
#include <stdio.h>
int main() {
int num = 3;
switch(num){
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
default:
printf("回火星去\n");
break;
}
}
注意:
- switch条件表达式的类型必须是整型, 或者可以被提升为整型的值(char、short)。
- case的值只能是常量, 并且还必须是整型, 或者可以被提升为整型的值(char、short)。
- case后面常量表达式的值不能相同。
- case后面要想定义变量,必须给case加上大括号。
#include <stdio.h> int main() { switch(1){ case 1:{ int num = 10; printf("num = %i\n", num); printf("星期一\n"); break; } case 2: printf("星期一\n"); break; default: printf("回火星去\n"); break; } }
- switch中只要任意一个case匹配, 其它所有的case和default都会失效. 所以如果case和default后面没有break就会出现穿透问题。
- switch中default可以省略。
- switch中default的位置不一定要写到最后, 无论放到哪都会等到所有case都不匹配才会执行(穿透问题除外)。
#include <stdio.h> int main() { switch(3){ case 1: printf("星期一\n"); break; default: printf("Other,,,\n"); break; case 2: printf("星期一\n"); break; } }
11.2 循环结构
- while
- do while
- for
11.2.1 while结构
- 格式:
while ( 循环控制条件 ) {
循环体中的语句;
能够让循环结束的语句;
....
}
exp:
int count = 0;
while (count < 3) { // 循环控制条件
printf("发射子弹~哔哔哔哔\n"); // 需要反复执行的语句
count++; // 能够让循环结束的语句
}
- while结构中的break
- break在while循环中的作用:
- 其实在循环中只要遇到break,就停止后期的所有的循环,直接终止循环。
- 所以:while中的break是用于永久终止循环的。
- break在while循环中的作用:
- while结构中的continue
- continue在while循环中的作用:
- continue是用于终止本次循环的,也就是本次循环中continue后边的代码不会再执行, 而是直接跳转到while语句的判断部分。进行下一次循环的入口判断。
11.2.2 do while循环
- 格式:
do {
循环体中的语句;
能够让循环结束的语句;
....
} while (循环控制条件 );
exp:
int count = 0;
do {
printf("发射子弹~哔哔哔哔\n");
count++;
}while(count < 10);
-
do-while循环执行流程
- 首先不管while中的条件是否成立, 都会执行一次"循环体"
- 执行完一次循环体,接着再次判断while中的条件是否为真, 为真继续执行循环体,为假跳出循环
- 重复以上操作, 直到"循环控制条件"为假为止
11.2.3 for循环
- 格式:
for(表达式1; 表达式2; 表达式3)
{
循环语句;
}
- 表达式1
- 表达式1为初始化部分,用于初始化循环变量的。
- 表达式2
- 表达式2为条件判断部分,用于判断循环时候终止。
- 表达式3
- 表达式3为调整部分,用于循环条件的调整。