1.什么是C语言?
c语言是一门通用计算机编程语言,广泛应用于底层开发。c语言的设计目标是提供一种能以简易的方式编译,处理低级存储器,产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。
尽管c语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性,以一个标准规格写出的c语言程序可在许多电脑平台上进行编译,甚至包含一些嵌入式处理器(单片机或称MCU)以及超级电脑等作业平台。
二十世纪八十年代,为了避免各开发厂商用的c语言语法产生差异,由美国国家标准局为c语言制定了一套完整的美国国家标准语法,称为ASCII,作为C语言最初的标准。
c语言是一门面向过程的计算机编程语言,与c++,java等面向对象的编程语言有所不同。
其编译器主要有Clang、GCC、WIN-TC、MSVC、Turbo C等。
2.第一个c语言程序
#include <stdio.h>
int main(){
printf("Hello World");
return 0;
}
- #include <stdio.h> : 包含stdio.h头文件
- std : standard标准
- i : input输入
- o : output输出
- .h : 头文件后缀名
- int main(){return 0;} : 定义主函数(程序入口),返回值为int类型
- printf() : 库函数,打印数据到控制台
3.数据类型
为了更加丰富的表达生活中的各种值,c语言提供了内置数据类型。
c语言中支持的数据类型char(字符型),short(短整型),int(整型),long(长整型),long long(更长的整型),float(单精度浮点型),double(双精度浮点型)。
每种类型在内存中所占空间大小是多少呢?
#include <stdio.h>
int main(){
// %zu占位符:打印size_t类型的数据
// sizeof操作符:计算传入参数在内存中所占空间的字节大小
printf("%zu\n",sizeof(char)); // 1
printf("%zu\n",sizeof(short)); // 2
printf("%zu\n",sizeof(int)); // 4
printf("%zu\n",sizeof(long)); // 4 long类型需要大于等于int类型,此处采用等于
printf("%zu\n",sizeof(long long)); // 8
printf("%zu\n",sizeof(float)); // 4
printf("%zu\n",sizeof(double)); // 8
retrun 0;
}
4.变量、常量
在生活中,有些值是可变的,有些值是不可变的,在c语言中,通常用变量来表示可变的值,使用常量来表示不可变的值。
4.1.定义变量的方法
// 定义了一个int类型的变量,变量名为age,值为20,会向内存申请4个字节大小的空间存储值
int age = 20;
char c = 'A';
double price = 22.45;
4.2.变量的分类
- 局部变量 :定义在局部代码块中的变量,存储在栈区,随着栈的销毁而销毁。
- 全局变量 :定义在局部代码块外的变量,存储在静态区,随着程序的结束而销毁。
#include <stdio.h>
// 全局变量
int global = 10;
int main(){
// 局部变量
int local = 20;
// 定义一个和全局变量同名的局部变量
int global = 30; // ok
printf("%d\n",global); // 当局部变量与全局变量同名是,局部变量优先使用(就近原则)
return 0;
}
4.3.变量的使用
#include <stdio.h>
int main(){
int n1 = 0;
int n2 = 0;
printf("请输入两个整数:");
// scanf():接收键盘输入
scanf("%d %d", &n1, &n2); // &:取地址操作符
int sum = n1 + n2;
printf("n1+n2=%d\n",sum);
return 0;
}
4.4.变量的生命周期和作用域
作用域:作用域(scope)是程序设计概念,通常来说,一段程序代码中所使用到的名字并不总是有效/可用的,而这个名字的可以使用的代码范围就是这个名字的作用域。
- 局部变量作用域:变量所在的局部代码段内。
- 全局变量作用域:整个工程项目。
生命周期:变量从创建到销毁的过程。
- 局部变量生命周期:程序执行到定义局部变量时创建,所在局部代码段执行完毕时销毁。
- 全局变量生命周期:在进入main函数前创建,程序结束时销毁。
4.5.常量
c语言中常量和变量的定义有所差异,c语言中的常量分为以下几种:
- 字面常量
- const修饰的常变量
- #define定义的标识符常量
- 枚举常量
#include <stdio.h>
// #define定义的标识符常量
#define NUM = 12
// 枚举常量:在生活中,有一些常量是可以直接列举出来的(季节、月份...)
// 自定义类型:enum Season类型
enum Season{
// 枚举常量,enum Season类型的变量的赋值只能为以下
SPRING,SUMMER,AUTUMN,WINTER // 枚举常量是有值的,从左到右为:0,1,2,3...
};
int main(){
// 字面常量
// 10 "m1kasa" 'A' 3.14
// const修饰的常变量
const int n1 = 10; // 当我们希望变量n1的值不能改变时,可以使用const修饰成常变量
// const修饰的常变量本质上还是变量,只不过具有了常量的属性(值不可改变)
// n1 = 20; // err
// 我们可以直接使用NUM,也可以进行赋值或运算等操作
int n2 = NUM; // 赋值给其他变量
int n3 = NUM + 10; // 运算
printf("NUM=%d\n", NUM); // 直接使用
printf("n2=%d\n", n2);
printf("n3=%d\n", n3);
// 声明enum Season类型的变量
enum Season season = SPRING;
season = AUTUMN; // ok
// season = M1KASA; // err
printf("%d\n",season); // 2
return 0;
}
5.字符串+转义字符+注释
5.1.字符串
“m1kasa”这种由双引号引起来的一串字符称为字符串字面值,也称字符串。
ps:字符串的结束标记是'\0'转义字符,在计算字符串长度时'\0'是结束标记,不算做字符串内容。
#include <stdio.h>
#include <string.h> // 包含strlen()的头文件
int main(){
// 在c语言中是没有字符串类型的,字符串都是通过字符数组存储在内存中的,字符串以转义字符'\0'结尾,'\0'会存储在内存中,但是不作显示
// 如果我们在声明字符数组的时候指定数组的长度,内存为该数组开辟指定长度的字节空间,如果存入字符串的值长度较大,则会丢失一部分字符串,如果存入的字符串的值长度较小,同样会开辟指定大小的空间
char arr[10] = "hello";
// 如果我们在声明字符数组的时候不指定数组的长度,内存则会根据给出的字符串的长度来开辟对应大小的空间('\0'也要占一个字符)
char arr1[] = "onefly";
// arr2在内存中其实是以字符数组进行解析的,当我们以字符串形式输出arr2时,内存会逐个取出arr2中的字符进行输出,但是由于他不是以字符串进行解析的,所以当输出完整个字符数组后,还是没遇到字符串的结束标记'\0'
// 此时,内存会继续向后面的空间取出字符(内存中开辟的空间是连续的,即开辟的空间前面与后面都有空间),直到取到'\0'为止
char arr2[] = {'o','n','e','f','l','y'};
// strlen() 库函数,返回指定字符串的长度
int len = strlen(arr);
printf("%d\n",strlen(arr1));
printf("%d\n",strlen(arr2));
printf("%d\n",len);
printf("%s\n",arr1);
printf("%s\n",arr2);
return 0;
}
5.2.转义字符
转义字符:指的是在ASCII码表和Unicode等字符集中的无法被键盘录入的字符、被当作特殊用途而需要转换回他原来的意义的字符。而转义字符的转义是指字符已经被转换了意义。
转义字符 | 解释 |
\? | 在书写多个连续问号时使用,防止他们被解析成三字母词 |
\' | 用于表示字符常量' |
\" | 用于表示一个字符串内部的双引号 |
\\ | 用于表示一个反斜杠,防止它被解释为转义序列符 |
\a | 警告字符:蜂鸣 |
\b | 退格符 |
\f | 换页符 |
\n | 换行符 |
\r | 回车符 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1-3个八进制的数字,如 \130 ---> ‘X’ \130 ---> 十进制88 ---> 对应的ASCII码值:'X' |
\xdd | dd表示2个十六进制的数字, 如 \x30 --> '0' \x30 ---> 十进制48 ---> 对应的ASCII码值:'0' |
#include <stdio.h>
int main(){
// 在控制台打印"D:\vscode\Microsoft VS Code\Code.exe"
printf("d:\\vscode\\Microsoft VS Code\\Code.exe\n");
// 以下程序输出什么?
printf("%d\n",strlen("c:\test\130\t1.txt")); // 13
return 0;
}
5.3.注释
- 在代码中有不需要的代码可以删除,也可以注释掉。
- 代码中有复杂的逻辑代码,通过注释(对代码进行解释),提高代码的可读性。
注释有两种风格:
- c语言风格的注释:/* */
- c++语言风格的注释://
6.分支语句
此处只介绍if-else语句
#include <stdio.h>
int main(){
int flag = 0;
printf("你会好好学习吗?:(选择1 or 0)");
scanf("%d",&flag);
// 在c语言中,0为假,非0为真
if(flag){
printf("加油,你会成功的\n");
} else {
printf("不学习也没关系,你也会成功的\n");
}
return 0;
}
7.循环语句
此处只介绍while语句
#include <stdio.h>
int main(){
// 在控制台打印10次"一点浩然气,千里快哉风".
int i = 0;
while(i < 10){
printf("一点浩然气,千里快哉风\n");
i++;
}
return 0;
}
8.函数
函数是完成某个特定功能的代码集合,我们可以将复杂逻辑,重复率高的代码抽取到函数中,以此来提高代码的复用性,可维护性,可读性。
#include <stdio.h>
// 定义函数,完成计算两数之和
// 函数名:sum
// 函数返回类型:int
// 函数接收参数:(int n1, int n2)
int sum(int n1, int n2){
return n1 + n2;
}
int main(){
int a = 0;
int b 0;
printf("请输入两个整数:");
scanf("%d %d", &a, &b);
int sum = sum(a,b);
printf("两数之和:%d\n", sum);
return 0;
}
9.数组
思考:如果我们目前有十个整数,如何进行存储呢?定义十个变量存储 no ----------> 数组 yes
数组:一组相同类型的元素的集合。
9.1.数组定义
int arr[5] = {11,22,33,44,55}; // 定义了一个长度为5的整型数组并初始化
9.2.数组的下标
c语言规定,数组的每个元素都有一个下标,下标是从0开始的,数组可以通过下标来访问。下标的范围是0-元素个数-1
int val = arr[index]; // 我们可以通过下标来访问对应的元素
9.3.数组的使用
#include <stdio.h>
int main(){
// 定义数组
int arr[5] = {2,4,6,8,10};
int index = 0; // 下标从0开始
int len = sizeof(arr) / sizeof(arr[index]); // 计算数组长度 数组占内存字节大小 / 单个元素所占内存字节大小
// 使用while循环遍历数组
while(index < len){
printf("%d\t",arr[index]);
index++;
}
return 0;
}
10.操作符
此处以简单介绍为主。
算数操作符 | +(加)、-(减)、*(乘)、/(除)、%(取模) |
移位操作符 | >>(右移)、<<(左移) |
位操作符 | &(与)、^(异或)、|(或) |
赋值操作符 | =(赋值)、+=(加等)、-=(减等)、*=(乘等)、/=(除等)、%=(模等)、&=(与等)、^=(异或等)、|=(或等)、>>=(右移等)、<<=(左移等) |
单目操作符 | !(非)、-(负值)、+(正值)、&(取地址符)、sizeof(返回参数所占内存空间的字节大小)、~(按位取反)、++(自增)、--(自减)、*(解引用操作符/间接访问操作符)、(类型)(强制类型转换) |
关系操作符 | >(大于)、>=(大于等于)、<(小于)、<=(小于等于)、!=(不等于)、==(等于) |
逻辑操作符 | &&(逻辑与:两个都为真,则为真)、||(逻辑或:有一个为真,则为真) |
三目操作符 | exp1 ? exp2 : exp2; 先判断exp1是否正确,如果为真,则运算exp2,如果为假,则运算exp2 |
逗号表达式 | (exp1,exp2,exp3...expN); 运算规则为从左至右依次运算,返回最右侧的表达式的运算结果 |
下标引用、函数调用和结构成员 | []、()、.、-> |
// 算数操作符
#include <stdio.h>
int main(){
// /(除) %(取模)
int n1 = 7 / 2; //在c语言中,如果/左右两侧都是整数,则计算结果也为整数,省略小数部分
float n2 = 7.0 / 2; // 如果/左右有任何一侧为浮点数,则计算结果会保留小数部分
int n3 = 7 % 2;
printf("%d\n",n1); // 3
// %.2f表示打印float类型的浮点数,保留小数点后两位(四舍五入)
printf("%.2f\n",n2); // 3.50
printf("%d\n",n3);
return 0;
}
赋值操作符:
#include <stdio.h>
int main(){
// +=
int n1 = 5;
n1 += 10; // n1 = n1 + 10
printf("%d\n",n1); // 15
return 0;
}
单目操作符:
#include <stdio.h>
int main(){
// !、&、sizeof、++、强制类型转换
int flag = 0;
if(!flag){
printf("flag为假\n");
}
int* p = &flag; // 取到flag变量在内存中的地址赋给指针变量p
int size = sizeof(p); // 返回指针p的占用内存空间的字节大小
printf("%zu\n",size); // 32位机:4;64位机:8
int num = 0;
num++; // num自增:num = num + 1
printf("%d\n",num);
// 自增分为前++(先自增,后运算)和后++(先运算,后自增)
int n1 = 10;
// int n2 = n1++; // 先运算int n2 = n1;后自增n1++
int n2 = ++n1; // 先自增n1++;后运算int n2 = n1
printf("%d\n",n1); // 11 11
printf("%d\n",n2); // 10 11
// 在c语言中字面浮点数默认类型是double,但是我们可以把它进行强制类型转换
int n3 = (int)3.14; // 精度会丢失,只保留整数部分
return 0;
}
关系操作符:
#include <stdio.h>
int main(){
// ==
int n = 1;
if(n == 1){
printf("m1kasa");
}
return 0;
}
逻辑操作符:
#include <stdio.h>
int main(){
// && ||
int flag1 = 1;
int flag2 = 2;
if(flag1 && flag2){
printf("逻辑与:两者都为真,则为真");
}
int flag3 = 1;
int flag4 = 0;
if(flag3 && flag4){
printf("逻辑或:有一者为真,则为真");
}
return 0;
}
三目操作符:
#include <stdio.h>
int main(){
// 三目操作符
int n1 = 10;
int n2 = 20;
// 求两数更大值
int max = n1 > n2 ? n1 : n2;
printf("%d\f",max);
return 0;
}
逗号表达式:
#include <stdio.h>
int main(){
// 逗号表达式
int n1 = 10;
// 逗号表达式运算规则:从左至右依次运算,再将最右侧的表达式的结果返回
// n1 = n1 + 5 -> 15
// int n3 = n1 -> 15
// n3 = n3 -> 15 打印的是这个赋值后的n3,而不是自增后的(int i = 1;i = i++;最后i为1)
// n3++ -> 16
int n3 = (n1 += 5,n3 = n1,n3++); // ps:后++:先运算,后自增
printf("%d\n",n3); // 15
return 0;
}
11.常见关键字
11.1.typedef
typedef关键字顾名思义类型定义,但这里应该理解为类型重命名/取别名。
#include <stdio.h>
typedef unsigned int u_int; // 使用typedef关键字给unsigned int取别名为u_int
// 使用typedef为自定义枚举类型取别名
typedef enum Gender{
MALE,FEMALE
} e_gender;
int main(){
// typedef关键字
// 定义一个无符号整型变量
unsigned int n1 = 20; // 可以看到我们声明式较复杂的,所以我们可以使用typedef关键字给unsigned int 类型重命名
u_int n2 = 20; // 这里的u_int的本质就是unsigned int类型
e_gender gender = MALE; // 这里的e_gender本质就是enum Gender类型
return 0;
}
11.2.static
static静态关键字,可以修饰局部变量,全局变量,函数。
首先我们需要了解内存的布局:栈区(存储局部变量)、堆区、静态区(存储全局变量)
静态局部变量:局部变量通常是存储在栈区的,但是静态局部变量会在程序编译时就创建并存储在静态区中(后续可以对此变量进行操作,但是不再创建),同时,静态局部变量的生命周期改变了(程序编译时创建,程序结束时销毁)。
#include <stdio.h>
void test(){
static int i = 1; // 此变量在程序编译时就创建并存储在静态区中,后续可以对此变量进行操作,但是不再创建
i++;
printf("%d\t",i);
}
int main(){
int i = 0;
while(i < 10){
test();
i++;
}
return 0;
}
静态全局变量:全局变量在执行main方法前创建并存储在静态区,程序结束时销毁。全局变量是具有外部链接属性的,即我们可以在别的源文件(.c)中通过extern关键字声明外部符号链接到全局变量。但是静态全局变量会将其修改成内部链接属性,外部源文件(.c)不能够链接到此全局变量,只有在定义静态全局变量的源文件内才可以使用。
#include <stdio.h>
// 全局变量
int n1 = 10;
// 静态全局变量
static n2 = 20
int test(){
return n2; // 静态全局变量只有在定义该变量的源文件内才可以使用
}
#include <stdio.h>
// 通过extern关键字声明外部符号链接到其他源文件定义的全局变量
extern int n1;
// extern int n2; // err:静态全局变量无法在其他源文件使用
int main(){
printf("%d\n",n1);
// printf("%d\n",n2);
return 0;
}
静态函数:函数也是具有外部链接属性的,如果我们使用了static修饰成为静态函数后,函数也变成了内部链接属性,只有在定义该函数的源文件中可以调用该函数,其他源文件无法通过extern关键字链接到此函数调用。
#include <stdio.h>
void test1(){
printf("我是全局函数");
}
static void test2(){
printf("我是静态全局函数");
}
#include <stdio.h>
// 通过extern声明外部符号,链接到其他源文件定义的函数
extern void test1();
// extern void test2(); // err:静态函数,除了定义该函数的源文件,其他源文件语法链接到并调用
int main (){
test1();
// test2();
return 0;
}
11.3.register
register:寄存器
计算机有哪些存储空间?寄存器(集成到cpu) -> 高速缓存 -> 内存 -> 硬盘,从左至右存储空间越大,读写效率越低,造假越低。
当我们希望提高对某个变量读写效率,就可以使用register修饰该变量,用于建议编译器将此变量存储到寄存器中(不一定成功,只是建议,具体结果看编译器)。
#include <stdio.h>
int main(){
register int num 10; // 建议编译器将num变量存储到寄存器中,以此来提高对此变量的操作效率
return 0;
}
12.#define定义标识符常量和宏
#include <stdio.h>
// #define定义标识符常量
#define MAX = 120
// #define定义宏
// ADD :宏名
// (n1,n2) :宏参数
// ((n1) + (n2)) :宏体,实现两数相加
#define ADD(n1,n2) ((n1) + (n2))
int main(){
// 使用标识符常量
int n = MAX;
int arr[MAX];
printf("%d]n",MAX)
// 使用宏
int a = 10;
int b = 20;
int sum = ADD(a,b); // 宏的本质为替换:((a) + (b))
return 0;
}
13.指针
13.1.内存
内存时电脑上特别重要的存储器,计算机中程序的运行都是在内存中运行的。
为了能有效的使用和管理内存,计算机把内存划分成了一个个小的内存单元,每个内存单元的大小为1个字节(1byte)
为了能有效快速的访问到内存的每个单元,计算机就给内存单元进行了编号,这些编号被称为内存单元的地址,也被称之为指针
地址编号从何而来?以一台32位计算机为例,32位计算机至多32根地址线,每根地址线都可以发送脉冲信号(1/0),这样的话,这台计算机就会有2^32个组合序列(也就是2^32字节 -> 4194304kb -> 4094MB -> 4GB),这些组合序列就是内存的地址编号。
#include <stdio.h>
int main(){
// 定义了一个int类型(4字节)变量num
int num = 10;
// 变量是存储在内存中的(在内存中分配空间的),每个内存单元都有地址,我们可以通过&取地址符取出变量的地址
// ps :这里num占4个字节,每个字节都有一个地址,那么&num取出的是那个字节的地址呢? --> 取出的是第一个字节的地址(较小的地址)
printf("%p\n",&num); // %p 地址占位符
// 有时候,我们需要将这个地址保存起来供其他地方使用,如何保存呢? --> 指针变量
// * 表示这是一个指针变量
// int 表示指针保存的地址指向的对象的类型为int类型
int* p = #
// 我们可以通过解引用操作符 * ,返回指针指向的对象,可以直接对该对象进行操作
*p = 20; // 将20赋给p指针指向的对象(内存单元)
printf("%d\n",num); // 20
// 其他类型的变量也是一样的
char c = 'A';
char* cp = &c;
printf("%p\n",cp);
return 0;
}
13.2.指针变量的大小
#include <stdio.h>
int main(){
// 指针变量在内存中占用空间的字节大小是多少呢?
// 我们要知道指针变量保存的就是地址,
// 在32位机中,这个地址是通过32位来表示的,也就是4个字节
// 在64位机中,这个地址是通过64位来表示的,也就是8个字节
printf("%zu\n",sizeof(char*)); // 32位机:4 64位机:8
printf("%zu\n",sizeof(short*)); // 32位机:4 64位机:8
printf("%zu\n",sizeof(int*)); // 32位机:4 64位机:8
printf("%zu\n",sizeof(long*)); // 32位机:4 64位机:8
printf("%zu\n",sizeof(float*)); // 32位机:4 64位机:8
printf("%zu\n",sizeof(double*)); // 32位机:4 64位机:8
return 0;
}
14.结构体
结构体是c语言中特别重要的知识点,结构体使得c语言有能力描述复杂类型;
#include <stdio.h>
// 在c语言中,只定义了char,short,int,long,float,double等简单类型,但是c语言提供了自定义类型来供我们描述更加复杂的对象 enum枚举类 struct结构体
// 定义结构体:描述学生类
struct Stu{
// 结构体成员
char name[10]; // 姓名
int age; // 年龄
char gender[5]; // 性别
char stuId[15]; // 学号
}
int main(){
// 初始化结构体
struct Stu stu = {"m1kasa",20,"male","20221001"};
// 结构成员访问操作符 . 通过对象
printf("%s-%d-%s-%s", stu.name, stu.age, stu.gender, stu.stuId);
// 结构成员访问操作符 -> 通过指针
struct Stu* sp = &stu;
printf("%s-%d-%s-%s", sp->name, sp->age, sp->gender, sp->stuId); // 直接获取指针保存的地址指向的对象的成员
return 0;
}
标签:main,return,int,C语言,初识,printf,n1,include
From: https://blog.csdn.net/m1kasaaa/article/details/139842571