想必刚接触C语言的同学们不知道C语言是什么?,有什么用,那么你来对了,本系列就会带你入门C语言,从入门到“入土”,开玩笑的,正如标题所说,本教程首先对C语言有一个初步的认识,能够看懂别人写的是什么,有一个大概的框架,那么,我要开始讲解了。 这一章主要是初始C语言的一个大纲。
C语言是什么?
语言:汉语,日语,英语等。语言是一个自然语言,是人与人交流的语言。
计算机语言:类比过来,是人与计算机之间的交流。
C语言是一门通用计算机编程语言,广泛应用于底层开发。C语言的设计目标是提供一种能以简易 的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语 言。 尽管C语言提供了许多低级处理的功能,但仍然保持着良好跨平台的特性
什么是集成开发环境呢?
集成开发环境(IDE,Integrated Development Environment )是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。
像vscode就是一种编辑器,它不是集成开发环境,但是它可以添加许多的插件来编辑代各种形式的代码。
像vs2022就是一种集成开发环境,在平时使用很方便,但是存储空间比较大,毕竟有好有缺点嘛,不能两者兼容。
但是不要使用那种上古编译器比如VC++,DEV++…
接下来我们就开始正式写第一个C语言程序
- 进入官网下载VS2022,不要去别的地方下载那些盗版的!!!点击进入官网
- 打开下载
- 打开软件,创建一个新项目
hello word!
,那么我们怎么打印出hello word
,开始我们学习编程之旅,下面就跟着我来一起学习。main
函数的第一行开始的。main
函数有且只有一个。printf
是编译器的头文件引入的,可以直接使用。<stido.h>
是头文件,后缀为.c
是源文件,后缀.h
为头文件。#include<stdio.h>
int main()
{
printf("hello world!\n");
return 0;
}
int main(){ ... return 0}
,通常就是这样写的,也是最普遍的写法写出了我们的第一个C语言程序,接下来我们来说一说C语言中的数据类型
在了解数据类型前,我们先探究为什么要写程序?
char //字符数据类型
short //短整型
int //整形
long //长整型
long long //更长的整形
float //单精度浮点数
double //双精度浮点数
计算机中常见的单位:bit(比特),byte(字节),KB,MB,GB,TB,PB。
1 byte = 8 bit
1 KB = 1024 byte
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
1 PB = 1024 TB
在生活中有些值是不变的(比如:圆周率,性别,身份证号码,血型等等),有些值是可变的(比如:年龄,体重,薪资等等),引入了变量和常量的概念。
首先,先讲述一下变量:
语法形式为:type + name。代码如下:
int age = 150;
float weight = 45.5f; //浮点数默认为双精度浮点数,在后面加个f,说明是单精度浮点数类型
char ch = 'w';
关键字
命名字。变量分为局部变量和全局变量
#include <stdio.h>
int global = 2019;//全局变量
int main()
{
int local = 2018;//局部变量
//下面定义的global会不会有问题?
int global = 2020;//局部变量
printf("global = %d\n", global);
return 0;
}
总结:
上面的局部变量
global
变量的定义其实没有什么问题的!
当局部变量和全局变量同名的时候,局部变量
优先使用。
了解了什么是变量,现在我们就要具体地去使用这个变量了,让我们一起来看看
#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;
}
在vs编译时会发现,我们在这里会出现4996警告
。
如何消除这个警告呢?
scanf()
改成scanf_s()
,scanf()
也是vs提供的,这种方式很简单,但是代码的跨平台,可移植性就变差了,不推荐这种方法。.c
文件中添加#pragma warning(disable:4996)
这样就不会提示这个告警了。newc++file.cpp
,打开文件#define _CRT_SECURE_NO_WARNINGS 1
,后面还要加上个【1】不要忘了再次创建一个.c
文件,可以看到自动就有那一行了
清楚了如果使用变量去编写程序,接下来我们来看一下变量的作用域和生命周期
作用域:
作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的通常来说,一段代码中所用到的变量名并不总是有效的,而限定这个名字的可用性的代码范围就是这个变量名的作用域。
了解了其基本概念,我们就到代码中来演示一下
int main()
{
{
int a = 10;
printf("a = %d", a);
}
printf("a = %d", a);
return 0;
}
test.c
这个文件定义一下这个变量b,用这个extern
声明一下即可
生命周期:
变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段
局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
全局变量的生命周期是:整个程序的生命周期
讲完了变量,接下去我们来讲讲常量
C语言中常量分为一下几种:
const
修饰的常变量#include <stdio.h>
//举例
enum Sex
{
MALE,
FEMALE,
SECRET
};
//括号中的MALE,FEMALE,SECRET是枚举常量
//#define的标识符常量 演示
#define MAX 100
int main()
{
//字面常量演示
3.14;//字面常量
1000;//字面常量
//const 修饰的常变量
const float pai = 3.14f; //这里的pai是const修饰的常变量
pai = 5.14;//是不能直接修改的!
printf("max = %d\n", MAX);
//枚举常量演示
printf("%d\n", MALE);//0
printf("%d\n", FEMALE);//1
printf("%d\n", SECRET);//2
//注:枚举常量的默认是从0开始,依次向下递增1的
return 0;
}
常量就是不变的量,所以叫常量
概念:双引号引起的一串字符叫做字符串
说完了字符串了基本概念,了解了什么是字符串,接下去我们来看看如何入求解字符串的长度
\0
表示这个字符串已经结束了。但是在计算字符串长度的时候,这个\0
是不计算在内的\0
之前出现多少个字那我们如何去求解一个字符串的长度呢?
strlen()
,可以计算一个字符串的长度,也就是\0之前的长度,这个函数我们后面会细讲printf("len = %d\n", strlen("abc"));
前面我们说到过char类型可以定义一个字符变量,不仅如此,在这里,我们还可以定义一个字符数组,什么是字符数组呢?就是这个数组中存放的都是字符,我们来看一个具体的定义
char arr[] = "abcdef";
char arr2[] = { 'a','b','c','d','e','f' };
int main()
{
printf("abcndef");
printf("abc\ndef");
}
n
,若是在其前面加上了一个\
,则它的意思就发生了变化,在转义字符里面就叫做【\n换行】,所以可以看到打印到【abc】就发生了换行,在后一行打印了def用一个表格给大家呈现:
转义字符 | 释义 |
---|---|
? | 在书写连续多个问号时使用,防止他们被解析成三字母词 |
\ ’ | 用于表示字符常量 |
\“ | 用于表示一个字符串内部的双引号 |
\ \ | 用于表示一个反斜杠,防止它被解释为一个转义序列符 |
\a | 警告字符,蜂鸣 |
\b | 退格符 |
\f | 进纸符 |
\n | 换行 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ddd | ddd表示1~3个八进制的数字。如:\130X |
\xdd | dd表示2个十六进制数字。 如:\x30 0 |
我们挑重点的说一下
有关【\ddd】,表示的就是八进制,也就是通过八进制去计算,如果用%d打印出来的就是计算的结果,如果使用%c打印出来的就是这个数字所对应的ASCLL码符号
对于十六进制也是同理,/xdd值就是十进制,然后看上面的打印也是同理
对于【\x3a】的话就是用a*160
然后我们来说一道有关转义字符的笔试题
printf("%d\n", strlen("c:\test\628\test.c"));
分析
首先我们要先了解一下注释是什么,它可以用来干嘛
int main()
{
//下面是创建一个整型变量并赋值10
int a = 10;
int b = 100;
//C++ 注释风格 - C99
/* C语言的注释风格
int c = 10;
int d = 20;
printf("ff");
*/ // - 嵌套的话此处结束注释
//不支持嵌套注释
return 0;
}
/**/
/*
和return 0;
上面的那个先匹配了,所以就不会到下面的具体我们通过代码来看一下
int main()
{
int input = 0;
printf("你要好好学习吗(1/0)\n");
scanf("%d", &input);
if (input == 1)
{
printf("拿一个好offer\n");
}
else
{
printf("回家种田\n");
}
return 0;
}
然后我们再来说说另一种形式,也就是循环语句
然后我们通过一段代码再来看一下
int main()
{
int line = 0;
printf("好好学习\n");
while (line < 20000)
{
printf("写代码:%d\n", line);
line++;
}
if (line == 20000)
printf("好offer\n");
return 0;
}
while
,还有do…while()
,for
这些,我们在后续都会讲到函数其实就是将一个功能单独封装成为一个模块,然后这个模块可以被多次调用,以便来实现代码的简化
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;
}
int Add(int m, int n)
{
return (m + n);
}
int main()
{
int sum1 = 0;
int sum2 = 0;
int sum3 = 0;
sum1 = Add(1, 2);
sum2 = Add(3, 4);
sum3 = Add(5, 6);
printf("sum1 = %d\n", sum1);
printf("sum2 = %d\n", sum2);
printf("sum3 = %d\n", sum3);
return 0;
}
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
[]
,括号里可以写上你准备为这个数组开辟多大的空间,比如说上面写的是10,那么这个数组中最多能存下的数据也就只有10个,但是若你不写的话,就可以存多个,后面会教大家一个方法去计算没有给出数据具体大小如何去求这个数组的大小int sz = sizeof(a)/sizeof(a[0]);
char b[] = { 'a','b','c'};
double c[] = {2.5,6.9,7.7,1.5 }
知道了数组怎么声明,那我们声明的这些数组怎么获取到呢?
下标
去访问的a数组中下标为8的元素,打印出来的就会是9//下标访问数组元素
printf("%d\n", a[8]);
具体大家看图示就能一目了然了
可以看到,这里arr数组的大小是10,那我们去访问18这下标会怎么样呢,去编译器里看看
从图中可以看出,总共下标也就只有9,你访问到了,那就会产生越界访问,那么你访问到的就会是一个随机值
int main()
{
int arr[10] = { 0 };
//0 ~ 9 - 输入10个值给数组
int i = 0;
while (i < 10)
{
scanf("%d", &arr[i]);
i++;
}
i = 0;
while (i < 10)
{
printf("%d ", arr[i]);
i++;
}
return 0;
}
C语言中操作符不少,这里我们做简要介绍,后续会详细讲解
int a = 7 / 2;
printf("%d\n", a);
float f = 7 / 2;
printf("%f\n", f);
我们来看看结果
int main()
{
// % 取余操作符,关注的是除法后的余数
int a = 7 % 2; //商3余1
printf("%d\n", a);
return 0;
}
左移就是扩大
右移就是缩小
重点来说一下单目运算法
非0
表示真!a
就是【真】,就会打印“hehe”语句//C语言是如何表示真假的呢?
//0表示假,非0表示真
//-1 表示的就是真
int main()
{
//把假变成真,把真变成假
int a = 10;
if (a)
printf("haha\n");
if(!a) //这个不执行
printf("hehe\n");
return 0;
}
int a = -10;
printf("%d\n", +a);
printf("%d\n", -a);
sizeof
是一个操作符int a = 10;
char c = 'w';
int arr[10] = { 0 };
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(c)); //1
printf("%d\n", sizeof(arr)); //40
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(int)); //4
printf("%d\n", sizeof(c)); //1
printf("%d\n", sizeof(char)); //1
printf("%d\n", sizeof int);
上面我们有提到过,当我们没有对一个数组设定初始化元素个数时,可以使用sizeof()去计算这个数组中有多少元素
printf("%d\n", sizeof(arr));;
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
然后我们来说一下 【前置、后置–】与【 前置、后置++】
int a = 10;
int b = ++a; //先++后赋值
// a = a + 1
// b = a
printf("%d %d\n", a, b); //11 11
int c = 10;
int d = c++; //先赋值后++
// d = c;
// c = c + 1
printf("%d %d\n", c, d); //11 10
int a = 10;
int b = --a; ///先--在赋值
printf("%d %d\n", a, b); //9 9
int c = 10;
int d = c--; ///先赋值后--
printf("%d %d\n", c, d); //9 10
int a = 10;
printf("%d\n", a--); //先使用,后--
printf("%d\n", a);
对于前置、后置的±,老是喜欢出一些面试题,把你搞晕,其实根本没什么意义,我们来看看
int a = 1;
int b = (++a) + (++a) + (++a);
printf("%d\n", b);
从VS来看,这段代码的运行结果是12,但是在Linux的gcc编译器中,运行结果竟然是10
一段代码在不同编译器运行结果不同,说明这段代码其实有问题的,准确的说是存在歧义的
我们来讲单目操作符的最后一个,强制类型转换
首先来看这两句代码,你认为这会输出什么
int a = 3.14;
printf("%d\n", a);
int a = 10;
int b = 20;
if (a == b)
printf("haha\n");
if(a != b)
printf("hehe\n");
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (arr1 == arr2)
printf("==\n");
else
printf("!=\n");
//两个字符串不可以用“==”来判断是否相等,使用strcmp(库函数)
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (strcmp(arr1,arr2) == 0)
printf("==\n");
else
printf("!=\n");
这样的话结果就正确了
int a = 5;
int b = 4;
int c = a && b;
printf("c = %d\n", c);
if (a && b)
printf("hehe\n");
int a = 0;
int b = 0;
int c = a && b;
printf("c = %d\n", c);
if (a || b)
printf("hehe\n");
int a = 10;
int b = 0;
if (a > 5)
b = 3;
else
b = -3;
printf("b = %d\n", b);
a > 5 ? b = 3 : b = -3;
b = (a > 5 ? 3 : -3);
我们先来说一下其运算规则:从左向右依次计算,整个表达式的结果是最后一个表示式的结果
int a = 3;
int b = 5;
int c = 0;
int d = (a += 2, b = b - c + a, c = a + b);
//a = 5 b = 10 c = 5 + 10 = 15
printf("d = %d\n", d);
printf("%d %d %d\n", a, b, c);
【下标引用操作符】
[ ]
,我们在定义数组的时候指定的数组大小int arr[10] = { 0 };
arr[4] = 5;
printf("%d\n", arr[4]);
int arr[10] = { 0 };
4[arr] = 5;
printf("%d\n", 4[arr]);
接下来的话是这个叫【函数调用】的操作符
int Add(int x, int y)
{
return (x + y);
}
int main()
{
int c = Add(2, 3); //()是函数调用操作符,操作数是:Add 2 3
printf("c = %d\n", c);
return 0;
}
了解了C语言中的常见运算符,接下来我们来看看C语言中的关键字
首先对于关键字,我们要注意的两点是
1、关键字是直接使用的,我们得了解
2、变量名不能是关键字
从下面这些可以看出,在C语言中,关键字还是蛮多的
//auto int a = 10;
int a = 10; //auto可以省略
首先你要知道在计算机中的数据是存放在哪里的
①内存 ②硬盘 ③高速缓存 ④寄存器
然后我们再通过这张图来了解一下
对于计算机中的寄存器,其实它所空间是很小的,单位只有字节,但是它的读写速度非常快,可以直接与CPU【中央处理器】进行交互
然后越往下这个空间越大,内存的话现在普遍都是8G,16G这样,大一点有32G,不会像很早之前的只有2MB这样;对于硬盘的话,我们去市场上买也是500G,1个T这样的大小
我们的寄存器,因为它的读取速度很快,因此CPU直接到寄存器里面拿数据,但这个时候寄存器内存不够大了怎么办呢?装不过这么多,这个时候我们所知道的高速缓存,也就是Cache,会给寄存器提供内存,那高速缓存里又不够了,这个时候就继续让内存给它提供。这样的话整体地提高了计算机的运行效率
下面就是【register】这个关键字的用法,在定义这个变量b的时候加上了这个关键字,就是【建议】编译器把这个变量放到寄存器中,这里要注意,只是建议,而不是直接放入
具体再如何使用大家可以去查阅一些资料,这里不做过多详解
//建议把b放到寄存器中
register int b = 10;
首先就是这个【typedef】,这个关键字的话是用来重命名的,用在结构体上会比较多
typedef struct Linknode{
int data[MaxSize];
struct Linknode* next;
}LNode;
typedef unsigned int uint;
int main()
{
unsigned int num1 = 0;
uint num2 = 0; //与num1一样
return 0;
}
最后再来说一下这个static关键字
首先我们来看看static对于局部变量的修饰
void test()
{
int a = 3;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
static int a = 3;
①普通的局部变量是放在内存的栈区上的,进入局部范围,变量创建,出了局部范围变量销毁
②当static修饰局部变量时,局部变量是在静态区开辟空间的,这时的局部变量,出了作用域变量不销毁,下次进去作用域,使用的是上一次遗留的数据(改变了存储位置,由栈区–>静态区,使得变量的生命周期发生了变化)
4~13
,每一次进入这个test()函数时,这个变量a将不再被执行,也就是只会在第一次进入这个函数的时候定义,之后就会一直存在,知道这个程序结束时它才会被销毁,所以这个变量a每次保留的便是上一次++后的结果add.c:
int g_val = 2022;
test.c:
extern int g_val; //声明外部符号
int main()
{
printf("%d\n", g_val);
return 0;
}
可以看到,这个外部命令无法被解析
全局变量是具有外部链接属性的,如果全局变量被static修饰,外部链接属性就变成了内部链接属性,其他源文件就没法再通过链接找到这个符号
所以可以得出结论,static修饰后的局部变量只能在自己所在的.c文件内部使用~
#define MAX 100
int main()
{
printf("%d\n", MAX);
int a = MAX;
int arr[MAX] = { 0 };
printf("%d\n", a);
return 0;
}
看到如上代码,我使用#define定义了一个MAX常量,并且其值为100,在main函数中,你就可以直接使用这个常量,对它进行打印、赋值
当然除了定义整型数据常量,其他类型也是可以的,例如字符串也可以
#define STR "abcdef"
printf("%s\n", STR);
讲完使用#define去定义常量,我们再来说说宏定义,它也是利用#define去声明的
//函数
int Add(int x, int y)
{
return x + y;
}
//宏
#define ADD(x,y) ((x) + (y)) //为了保持整体性
除了求和的功能外,其实你还可以定义其他功能,例如说比较两个数的较大值,就可以像下面这么去写,使用一个三目运算符即可
#define MAX(x,y) ((x) > (y) ? (x) : (y))
对于指针这一块的话,是直接和计算机中的内存发生关系的,所以我们先来讲讲内存相关的知识,带大家先行了解一下底层的知识
对于内存,大家在日常生活中应该也有听到过,例如我们为电脑、笔记本买的内存条,以及我们手机的内存,对于这个内存来说,一般都是8G或是16个G,与内存相对应的,那就是硬盘,一般的话都是500G或是1个T这样
在计算机中,内存是一块连续的存储空间,但是这样就无法分配到每一个部分进行使用,这个时候呢就将内存分成了一块块小的内存单元,那为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号就被称为该内存单元的地址
知晓了地址的基本概念后,我们再来到编译器中去看看这个地址究竟是怎样分部的
int main()
{
int a = 4;
}
&a // 拿到的值第一个字节的地址
int a = 4;
int* pa = &a;
既然这个指针变量存放了变量的地址,那么可不可以通过这个指针变量去访问到这个地址并且把它打印出来呢?答案是可以的,这就是涉及到我们的下一个知识点,就是指针的解引用
利用【*】这个操作符
*pa
小结
①指针其实就是地址,地址就是内存单元的编号
②把地址进行存储的时候,就可以放到一个变量中,这个变量就是【指针变量】
③我们说的指针,实际上在专业术语中叫做【指针变量】
④【*】星号解引用可以通过存放变量的地址快速访问到那个变量在内存中所存放的位置,继而获取内容
sizeof()
这个关键字int a = 10;
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(int));
int a = 10;
int* pa = &a;
printf("%d\n",sizeof(pa));
printf("%d\n",sizeof(int*));
要看到不是地址,其实是存储的地址线,没错,就是硬件上的地址线
了解了上面这些,知道了指针变量的大小取决于地址的大小,下面我们来看看这些指针变量的大小是多少,我是在32位环境下运行的
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(long*));
printf("%d\n", sizeof(long long*));
printf("%d\n", sizeof(float*));
printf("%d\n", sizeof(double*));
可以看到,均为4,它们都是指针变量,指针变量求它的大小看的是什么,看到就是地址的大小,上面说了,我是在32位环境下运行的,因此就是32根地址线,需要32个bit,也就是4个byte去存放
可以看到,报了很多Warning,这是为什么呢,明明这个代码就是可以运行的,而且还可以出结果这里报了一个【符号不匹配】的问题,为什么呢?这里明确说一下,sizeof()计算数据字节大小的时候默认返回的类型是unsigned int,也就是无符号整型,但%d是用来打印整型变量的,所以这里才会出现【符号不匹配】的问题
对于结构体,也是C语言中比较重要的一个部分,因为C语言是一门面向过程的语言,它不像C++、Java那样面向对象,具有类,可以在类内定义成员变量和成员方法,所以C中就有了结构体这一个东西,可以用来描述复杂对象
我们以学生类型来做一个封装
struct stu {
char name[20]; //姓名
int age; //年龄
float score; //成绩
};
struct stu s1 = { "zhangsan",20,80.5f };
struct stu s2 = { "lisi",25,90.5f };
那初始化了这些变量后如何将他们打印出来呢,也就访问到每一个同学的信息
//格式:结构体变量.结构体成员
printf("%s %d %f\n", s1.name, s1.age, s1.score);
printf("%s %d %f\n", s2.name, s2.age, s2.score);
好,初识C语言就到这里结束了,希望对大家有帮助!!!