C语言基础~看这篇文章就足够了(一)

编译型语言和解释型语言的区别

编译型语言

c语言是标准的编译型语言,属于高级语言,执行流程如下:
C语言 => 汇编语言 => 机器语言(二进制) => CPU执行
不可跨平台,效率高,灵活度高,可移植性高

解释型语言

解释型语言例如java、python、ruby等,同为高级语言,执行流程如下:
JAVA => 字节码 => 解释器编译 => CPU执行
可跨平台,效率较低

使用vscode编写c语言

具体就说三个步骤

  • 安装vscode(版本可以选择中等往下的,新版本许多东西不兼容)
  • 扩展安装,分别是c/c++(c和c++代码提示和语法识别)和Code Runner(将c语言编译成可执行文件)
  • 如果是Mac OS系统需要自行安装Xcode

编写c语言第一个程序helloWorld

#include <stdio.h>
//预处理器指令,告诉 C 编译器在实际编译之前要包含 stdio.h 文件。
int main(void)//main是主函数,程序从这里开始执行
{   
    printf("hello World!");//printf是输出函数   
    return 0;//程序正常结束
    //注释会被编译器忽略
}

在这里插入图片描述

C语言基本语法

一、基本数据类型

基于windows

说明字符型短整型长整型单精度浮点型双精度浮点型无类型
数据类型charshortintlongfloatdouble
长度(字节数)124448

类Unix系统下(包括Unix、Linux、Max OS、BSD、Solaris等)
long占据字节数长度为8

//变量声明示例
int num,sum;//多个变量声明以逗号分隔
char str = "欢迎阅读";

c语言在声明变量时需要指定类型,这种类型语言称为强类型编程语言。
类型一旦声明以后,该变量便不能够赋值其他类型的数据,除非进行强制数据类型转换。

关于puts和printf

puts 是 output string 的缩写,只能用来输出字符串,不能输出整数、小数、字符等
printf 是print format 的缩写,意思是“格式化打印”,它比puts更加强大,可以输出目前的所有类型,并且可以定义输出格式,所以目前使用printf更多
这几个点需要注意一下:
1、puts()可以直接输出字符串,例如

puts("hello,菜鸟")

2、printf是print和format的组合体,必须使用格式控制符来进行输出,否则会报错,例如

printf("%s","hello 菜鸟");

3、介绍几个常用格式控制符
格式控制符也可以理解为是一个占位符,占据一个位置加以填充

  • %d 输出一个int类型(%hd输出short int类型;%ld输出long int类型)
  • %c 输出一个字符
  • %s 输出一个字符串
  • %f 输出一个小数
    还有很多格式控制符,这里就不多做介绍
从键盘输入数据
  • scanf()和printf使用方式类似,作用相反,可以输入多种类型的数据
  • getchar()、getche()、getch()用于输入单个字符
  • gets()获取一行数据,并作为字符串处理

这里单独讲点用的最多的scanf
需要特别注意的一点是,scanf的变量前面要带一个&符号。&符号称为取地址符,就是获取该变量在内存中的地址

scanf("%d", &num);

注意:这里必须使用双引号,单引号会报错,C语言控制这方面挺严的

在这里插入图片描述

C语言中的正负数

如果之前接触过的应该知道,整数类型的数在内存中的存储机制是这样的:它分为两个部分,符号位和数值位。符号位位于最前方(在符号位中,用0表示正数,1表示负数),每一个数值保存的时候都是自动带上符号位的,那么这样看,加入我们取int类型的整数(占4个字节,也就是32位内存),也就是说有一位为符号位,那么其他31位便为数值位
在这里插入图片描述
如果说我们确定一个数位恒正的情况下,那么我们不需要符号位,那么我们一个int类型便有32位内存都可以用来存储数值,可以存储更多的数值,进行性能的优化,那么如何删除符号位?代码如下,使用关键字unsigned

unsigned int num = 1000;
强制类型转换

C语言中还包括一种自动类型转换,但是自动类型转换会有缺陷,多数情况下会导致数值失真,精度降低,在这种情况下我们会使用强制类型转换。
强制类型转换,代码格式如下:

(char) num;//意为将num转化为字符串类型

注意:无论是自动类型转换还是强制类型转换,转换的结果都是临时的,会被放在临时的内存当中,不会改变原来的值以及类型

二、基本分支和循环结构

代码从头执行到尾部,这种按顺序一条一条执行的结构我们称为顺序结构

if、else if 、else分支以及switch

这里我们还是简单写点,就不做多讲,基本的分支

#include <stdio.h>
int main(void)
{   
    int num;
    printf("%s","请输入数字:\n");
    scanf("%d", &num);
    if(num==0){
        printf("%s","我等于零\n");
    }else if(num==10){
        printf("%s","我等于10\n");
    }else{
        printf("%s","结束\n");
    }
    return 0;
}

关于switch结构,switch是类似于if、else的条件分支结构。
具体代码格式如下:

switch(num){
	case 1:printf(num); break;
	case 2:printf(num); break;
	default:printf("error\n");
}

记得代码体中添加break停止代码,否则满足条件后面的代码将全部执行

注意

c语言当中没有定义布尔类型,因此判断真假:
以0为假,以非0为真,字符集那些也算非0哦

关系运算符

简单的表格大家自行看,都是基础的东西

关系运算符>>=<<===!=
含义大于大于或等于小于小于或等于等于不等于

逻辑运算符

逻辑运算符简单的分为:与(&&)、或(||)、非(!)
简单的总结一下:
与:全真为真,条件执行(同 and)
或:一真为真,条件执行(同or)
非:取反当前值,判断条件执行

三元运算

三元运算也叫三目运算
代码示例如下:
表达式1?表达式2:表达式3;

int num = 10;
char res;
res = num==10?num==1:num==2;
printf("%s",res);

解读以上代码,意思为:
当num等于10时,执行num1;
当num不等于10时,执行num
2;

基本循环语句

while循环

基本循环语句为while以及for循环

while循环的一般形式为:
while(表达式){
    语句块
}
do-while循环的一般形式为:
do{
    语句块
}
while(表达式);

区别就是一个先判断再执行代码块,一个先执行一遍代码块然后判断要不要再次执行

for循环

直接上代码吧,没什么特别好讲的,就是基础的循环,和大多数语言都一样

int i, sum=0;
for(i=1/*语句①*/; i<=100/*语句②*/; i++/*语句③*/){
    sum+=i;
}
printf("%d\n",sum);

以下两个关键字只作用于循环和开关中;
break 跳出所有循环;
continue 语句的作用是跳过循环体中剩余的语句而强制进入下一次循环

三、数组的基本使用

我们知道想要把数据放入内存当中,就必须分配内存空间。
强类型语言,对类型的把控相当严格,放入代码方便大家理解。

char str[3];//此段代码意为,声明了一个char类型的数组,该数组长度为3,数组名为str

注意在一个类型的数组中不能够放入其他类型的数据。
数组中值的取出,使用的是下标,下标数从0开始,C语言中的数组不能够直接通过打印数组名来输出数组值(例如:printf("%S",str))。数组值是直接存在内存空间,只能通过下标来查找(例如str[0])。
相当于是声明了多个变量,用来保存不同的数据。

数组的初始化:

int nums[3] = {0,1,2}

如上诉代码所示,数组的初始化就是在数组定义的时候给数组中的每一项进行赋值操作。

注意:如果赋值的元素小于数组总体元素,那么数组剩下的所有未赋值的元素将自动初始化为0

二维数组

二维数组大家不要理解那么复杂,想象成数组的嵌套
在这里插入图片描述
看到我画的这个图应该就很明了了,总的来说就是一个盒子装另一个盒子。
除开二维数组以后还有三维数组,也都是按照这个方式一步一步进行嵌套的。

二维数组的初始化

int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
//或者说分开写
int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};
字符数组

在这里注意一下,C语言当中是没有string类型的,那么如何保存字符串呢?C语言提供了两种方式,其中一种便是将字符串保存在数组当中。
注意: 将字符串保存在数组当中有两种方式
第一种:

char str[3];
//声明变量不赋值,后续手动赋值,例如:  str[0] = "a"
//特别需要注意的重点,画圈记好。一个str[i]是一个字符,也就是一个字节。不能够用来存汉字!!!一个汉字三个字节,一个char是一个字节是存不下的。

第二种:

char str[] = {"欢迎学习C语言"};
//这是一种初始化声明赋值的方式,再次说明在字符数组中,str[]中括号中的是长度,也就是字节数
//如果我们需要打印它,那么应该从0个字节开始打印,于是就有
//printf("%s\n",&str[0]);    //欢迎学习C语言
//如果从str[2]开始那么对应的那个字将会出现乱码,因为一个字需要三个字节码来保存

重点:
还有一个重点是字符串以 ‘\0’ 作为结尾,所以’\0’也被称为字符串结束标志,或者字符串结束符。

C语言在处理字符串时,会从前往后逐个扫描字符,一旦遇到’\0’就认为到达了字符串的末尾,就结束处理。’\0’至关重要,没有’\0’就意味着永远也到达不了字符串的结尾。

'\0’是 ASCII 码表中的第 0 个字符,英文称为 NUL,中文称为“空字符”。该字符既不能显示,也没有控制功能,输出该字符不会有任何效果,它在C语言中唯一的作用就是作为字符串结束标志。

示例:

char str[8] = {"abcdefg"};

在长度空余的时候C语言的机制会自动给字符串末尾添加一个\0,使用一个字节的大小,所以在我们定一个具体大小字符数组的时候常多空余一个char。有时候也会手动添加一个\0,这样就不用计算什么时候设置多大的长度。

四、函数的基本使用

自定义函数:

dataType  functionName( arguments ){
    //body 函数体
    return  //返回的应当是对应dataType类型的值
}
//函数调用
functionName( arguments )

无返回值的函数,那么它的dataType为void,void表示“空类型”或者“无类型”。
需要注意的一点,函数不能够嵌套定义,每个函数处于平等地位

函数声明和定义

函数声明:
C语言代码由上到下依次执行,原则上函数定义要出现在函数调用之前,否则就会报错。但在实际开发中,经常会在函数定义之前使用它们,这个时候就需要提前声明。

函数声明给出了函数名、返回值类型、参数列表(重点是参数类型)等与该函数有关的信息,称为函数原型(Function Prototype)。函数原型的作用是告诉编译器与该函数有关的信息,让编译器知道函数的存在,以及存在的形式,即使函数暂时没有定义,编译器也知道如何使用它。

int add(int a,int b);

函数定义:

函数声明以后就可以使用这个函数了,即便没有定义函数体,我们只需要在后面补充函数体即可,也就相当于重写。

int add(int a,int b){
	return a+b;
}
局部变量和全局变量

准确来说,局部变量就是定义在函数内部,仅供当前函数使用,不存在变量提升的情况。
全局变量就是在函数外部定义的变量,它的作用域默认是整个程序,也就是所有的有效源文件。
提一个概念,作用域指的是变量的有效范围

静态变量:静态变量是以static关键字修饰的变量,作用域范围仅限当前文件

五、关于指针

如何理解指针,什么是指针?
我们把数据存储在内存上,不同类型的数据占据字节数不同。内存就像一个盒子,我们把数据放入其中,为了我们需要这些数据时能够准确的取出这些数据,那么我们就必须给他们一个独一无二的编号或者密文,也就是盒子对应的钥匙。于是我们把这样的编号称为地址或者指针

查看指针或者说是内存地址的方式:

int nums[4];
nums[0] = 0;
nums[1] = 1;
printf("%#X,%#X\n",&nums[0],&nums[1]);
//结果:0XE2CF9610,0XE2CF9614
printf("%#X\n",nums);
//结果:0XE2CF9610

%#X表示以十六进制形式输出,并附带前缀0X。nums[0]是一个具体的数,所以我们需要在前面加&来获得它的地址。
可以看到我们直接打印nums,得到的等同于nums[0]的地址。
所以我们就可以得到,打印nums是打印的数组内存地址开始的位置。

指针变量

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。

字符串声明的第二种方式

第二种方式就是使用指针变量,格式如下:

char *str = "欢迎使用C语言";//*表示这是一个指针变量

当然,指针变量也可以用来保存另一个内存地址

    int a = 15;
    int *p = &a;
    printf("%#X,%#X\n",&a,&*p);//0XEFBB2628,0XEFBB2628
    //不要忘记&取地址符号,不然结果是,重新开辟一个内存空间用来保存15这个数字

重点:数组字符串和指针字符串的区别:
它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区,第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其他数据)只有读取权限,没有写入权限。

在C语言程序被编译完成以后,所有的变量都会被替换成内存地址

指针函数

字面意思理解,就是指针指向函数的内存区域,不需要使用()就可以调用执行

#include <stdio.h>
#include <string.h>

int add(int a,int b){
    return a+b;
};
int main(void)
{   
    //定义指针函数
    int (*padd)(int a,int b) = add;
    int res = (*padd)(1,9);//注意()的优先级高于*,第一个括号不能省略
    printf("%d\n",res);
    return 0;
}

指针的使用点就太多了,讲的并不多,谨记内存地址就可以了

六、结构体、共用体等

结构体

什么叫做结构体呢?
我们说数组是相同数据类型的数据的集合,那么结构体的作用便是用来储存不同结构类型的数据,它同数组一样在内存存储上是连续的,结构体使用关键字struct来定义,具体代码结构如下

struct person{
	char *name;//名字常为汉字所以使用指针常量
	int age;
	char *sex;
}

学习过数据库的或者说其他强类型语言的同志们应该就非常的好上手了,有那么点json对象的意思了。
结构体变量赋值:

person.name = "super";
person.age = 18;
person.sex = "男";
//可用于取值、修改值
//看起来有那么点对象调用方法的意思了吧

定义多个相同结构体变量:

struct{
    char *name ;  //姓名
    int age;  
    char *sex;  
} stu1,stu2;//定义多个相同结构的结构体,就像这样stu1,stu2结构相同,但是内存地址不同,不是一个东西,是不是有点像构造函数那个东西了。
//如果想给某一个变量初始化值
//struct{
    //char *name ;  //姓名
    //int age;  
    //char *sex;  
//} stu1,stu2 = {"super",18,"男"};

stu1.name = "stu1";
stu2.name = "stu2";
printf("%s\n",stu1.name);
printf("%s\n",stu2.name);

总的来说,结构体相当于一个用来储存多个不同类型数据的集合模版,也是一种数据类型,叫做复杂数据类型
注意结构体是一种数据类型,不占内存,结构体变量才是实际定义的变量,占据内存空间。

结构体数组

结构体数组的定义,以代码为例:

#include <stdio.h>
#include <string.h>
int main(void)
{   
    struct{
        char *name ;  //姓名
        int age;  
        char *sex;  
    }person[] = {
        {"张三",18,"男"},
        {"李四",19,"女"}
    };
    printf("%s\n",person[0].name);//张三
    printf("%s\n",person[1].name);//李四
    return 0;
}

实际上是以结构体为模版,定义了一个数组,数组中的每一项都是结构体的结构,调用方式就同数组调用方式相同。

枚举类型
enum week{Mon = 1, Tues, Wed, Thurs} num;

简单的做一下介绍,enum是声明枚举类型的关键字,week是枚举类型的名字,num为枚举变量,num才是实际上需要使用的东西
意思是num可能为{Mon , Tues, Wed, Thurs}中的任意一个值,如同Mon对应1一样,类似于键值对一一对应,当指定num为Mon时,那么它的值为1。枚举类型,理解为-不是你就是我,两个相对应。

Mon、Tues、Wed 这些名字都被替换成了对应的数字。这意味着,Mon、Tues、Wed 等都不是变量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面,放到代码区,所以不能用&取得它们的地址。这就是枚举的本质。

如果不太理解,这边推荐一个文章入口,可以去看看

C语言共用体

共用体相似与结构体,定义方式相同,使用方式也相同,那么他有什么不一样呢?
结构体和共用体的区别在于:结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。
这句话解释的比较透彻,说白了,不相同的地方就是,所创建出来的变量是否依赖同一个内存地址

共用体的定义:

union Tom{
	char *name;
	int age;
	int weight;
} Tom1,Tom2,Tom3;
//也可以分开定义
union Tom Tom1,Tom2,Tom3;
typeof

typeof是用来起别名的,这里简单提一下

使用格式:

	typedef  oldName  newName;
type char newChar;
//于是 char a,b. == newChar a,b;
const常量

有时候我们希望定义一个变量,它的值不能够被改变,那么由此有了常量const。

const int num = 18;

像这样定义那么num的值将不能够被修改。
为了区分变量和常量,我们常常将常量的首字母大写。

C语言关于文件读取的部分会单独写一篇。
如有遗漏或者错误请补充。

  • 3
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值