C语言—初识C语言

什么是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等。

  • 语言指的是人和人交流的语言(eg:汉语,英语,日语……),而计算机语言指的是人和计算机交流的语言(eg:C/C++/JAVA/python/Go……)
  • C语言是广泛应用于底层开发的(eg:OS、驱动程序),还可以应用于上层开发(eg:应用程序、早期的wps就是用C语言写的)
  • C语言是有国际标准的,C语言标准的演变C89、C90、C99、C11……
  • 计算机语言的演变:二进制语言、汇编语言(由助记符(ADD\Sub……)组成)、B语言、C语言。其中早期C语言设计上是有些不足的,C语言被各个厂商修改之后各不相同了,从而在编译器上也就运行不了了,这时就出现了国际标准,它规定C语言是什么标准
  • 编写C语言用的比较流行的编译器有:Clang、GCC、MSVC(微软的vs)
  • VS编译器称为集成开发环境(集成了MSVC这样的编译器),它可以编写以及编译C语言的代码

用VS编写第一个C语言程序

创建项目

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

创建源文件

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

编写代码

在这里插入图片描述
在这里插入图片描述
注:

  • 编写代码时首先要写出主函数(main函数),因为C语言是从主函数的第一行开始执行的,所以C语言代码中必须得有main函数(入口)
  • printf 是库函数用于在屏幕上打印信息的
  • printf 函数的使用,也需要引用头文件stdio.h

main函数总结:

  • main函数是程序的入口,一个工程中main函数有且仅有一个

  • 当出现程序函数"int main()"已有主体这样的错误时,说明一个工程中出现了多个main函数

  • 一个工程中可以有多个.c文件,但是多个.c文件中只能有一个main函数

编译代码以及运行

在这里插入图片描述

在这里插入图片描述

注意:以上是在VS2019下运行的,如果在VS2013中运行需要注意当运行时会发现程序执行的太快看不到结果,这时需要设置VS2013中的属性(VS2019不需要设置项目属性)

设置项目属性:右击项目->点击属性

在这里插入图片描述

注意:VS2013和VS2019的使用大同小异

数据类型

在这里插入图片描述
出现这么多种的类型,是因为计算机语言在编写程序时需要解决生活中问题,计算机语言必须有能力来描述生活中问题(eg:年龄、体重、收入……)以及类型丰富选择性更多空间利用率更高。

每种类型的大小

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));  //8	
	return 0;
}

注:

  • %d用于打印整数、%lf用于打印double类型、%f用于打印float类型

  • sizeof是关键字也是操作符,用来计算类型或者变量所占空间的大小

  • sizeof的单位是字节byte

  • 二进制由0-1组成、八进制由0-7组成、十进制由0-9组成、十六进制由0-f组成

  • 计算机中最小的单位是bit(比特位),一个比特位是存放一个二进制位的

  • 计算机中的单位由小到大:bit、byte(1byte=8bit)、kb(1kb=1024byte)、mb(1mb=1024kb)、gb(1gb=1024mb)、tb(1tb=1024gb)、pb(1pb=1024gb)、……

  • long和int类型大小相同,这是因为C语言标准规定sizeof(long)>=sizeof(int)即可(在VS中采用的是大小相等)

注意:存在这么多的类型,其实是为了更加丰富的表达生活中的各种值。

变量、常量

常量描述的是不能改变的量
变量描述的是能被改变的量

定义变量的方法

int age = 150;
float weight = 45.5f;   //代表单精度,如果不加f默认代表双精度(d)
char ch = 'w';

变量的分类:局部变量、全局变量

局部变量:在大括号内部定义的变量
全局变量:在大括号外部定义的变量

变量的使用

//全局变量 - {}外部定义的
int a = 100;

int main()
{
    
    
	//局部变量 - {}内部定义的
	
	int a = 10;

	printf("%d\n", a);

	return 0;
}

在这里插入图片描述

注意:当局部变量和全局变量名字冲突的情况下,局部优先。不建议把全局变量和局部变量的名字写成一样的

在这里插入图片描述

注意:

  • 当在VS中使用scanf函数时会发生这样的报错在这里插入图片描述
    这时需要在源文件开头(第一行)输入这段代码即可使用scanf函数
#define _CRT_SECURE_NO_WARNINGS 1
  • scanf_s函数是VS编译器提供的,不是C语言标准规定的,因此如果使用了在VS中能跑,但在其他平台就不行了(可移植性太差了),综上使用scanf函数

  • scanf_s函数和scanf函数的使用方法是有区别的

  • 当该文件中添加这句代码时#define _CRT_SECURE_NO_WARNINGS 1,每当创建新源文件时都会有这句代码

VS2019路径:
在这里插入图片描述

VS2013路径:
在这里插入图片描述

变量的作用域和生命周期

作用域

作用域(scope),程序设计概念,通常来说,一段程序代码中所用到的名字并不总是有效/可用的而限定这个名字的可用性的代码范围就是这个名字的作用域。

  1. 局部变量的作用域是变量所在的局部范围。
  2. 全局变量的作用域是整个工程。

注:当在整个工程中一个源文件创建的全局变量(eg:int a=10;)另一个源文件想要使用就需要提前声明该变量(extern int a;)

生命周期

变量的生命周期指的是变量的创建到变量的销毁之间的一个时间段

  1. 局部变量的生命周期是:进入作用域生命周期开始,出作用域生命周期结束。
  2. 全局变量的生命周期是:整个程序的生命周期。

常量

C语言中的常量分为以下以下几种:

  • 字面常量
int main()
{
    
    
	//字面常量
	3.14;
	10;
	'a';
	"abcdef";


	return 0;
}
  • const 修饰的常变量
int main()
{
    
    
	//const修饰的常变量
	const int num = 10;//num就是常变量 - 具有常属性(不能被改变的属性)
	return 0;
}

在这里插入图片描述
注意:const修饰的变量是常变量,不是常量,更不能作为数组的元素个数

  • #define 定义的标识符常量
#define MAX 10000
int main()
{
    
    
	// #define 定义的标识符常量
	//MAX = 20000;//会报错,不能修改
	int n = MAX; 
	printf("n = %d\n", n); //10000
	return 0;
}
  • 枚举常量
//性别
enum Sex
{
    
    
	//这种枚举类型的变量的未来可能取值
	//枚举常量
	MALE = 3,//赋初值
	FEMALE,
	SECRET
};

int main()
{
    
    
	//枚举常量可以一一列举的常量

	enum Sex s = MALE;
	//MALE = 3;//不能修改,会报错
	printf("%d\n", MALE);    //3
	printf("%d\n", FEMALE);  //4
	printf("%d\n", SECRET);  //5

	return 0;
}

字符串+转义字符+注释

字符串

由双引号(Double Quote)引起来的一串字符称为字符串字面值(String Literal),或者简称字符串。

在这里插入图片描述
在这里插入图片描述

%s打印的是字符串,是以\0为结束标志。而arr2数组没有\0因此后面是随机值直到遇到\0结束

在这里插入图片描述

strlen函数是计算字符串的长度(不计入\0),遇到\0结束返回字符串长度

总结:字符串的结束标志是一个 \0 的转义字符。在计算字符串长度的时候 \0 是结束标志,不算作字符串内容。

转义字符

转义字符顾名思义就是转变意思(转变他原来的意思)。

下面看一些转义字符:
在这里插入图片描述
补充:

  • 三字母词:??+特殊字符组成的,三个特定字符组成的对应的一个字符(eg:??) -> ] )。要想打印出??+特定的字符需要\ ? \ ?+特定字符这样即可。(现在编译器不支持,不好验证)
  • \ddd:把八进制数转换成十进制之后,以它为ACSII码值代表的那个字符

经典笔试题:

//程序输出什么?
#include <stdio.h>
int main()
{
    
    
	printf("%d\n", strlen("abcdef"));
	// \32被解析成一个转义字符
	printf("%d\n", strlen("c:\test\328\test.c"));   //14
	//c : \t e s t \32 8 \t e s t . c
	return 0;
}

注释

  1. 代码中有不需要的代码可以直接删除,也可以注释掉
  2. 代码中有些代码比较难懂,可以加一下注释文字(注释是用来解释复杂代码的)

例如:

在这里插入图片描述

注释有两种风格:

  • C语言风格的注释 /* xxxxxx */ 缺陷:不能嵌套注释
  • C++风格的注释 //xxxxxxxx ,可以注释一行也可以注释多行

选择语句

例如:

int main()
{
    
    
	int input = 0;//输入的值
	printf("好好学习吗(1/0)?>:");
	scanf("%d", &input);

	if (input == 1)
	{
    
    
		printf("未来可期\n");
	}
	else
	{
    
     
		printf("回家种地\n");
	}
	return 0;
}

循环语句

例如:

int main()
{
    
    
	printf("变成小白\n");
	int line = 0;
	//循环
	while(line<30000)
	{
    
    
		printf("写代码:%d\n", line);
		line++;
	}
	if (line == 30000)
	{
    
    
		printf("编程大牛\n");
	}

	return 0;
}

函数

#include <stdio.h>
int main()
{
    
    
    int num1 = 0;
   int num2 = 0;
    int sum = 0;
    printf("输入两个操作数:>");
    scanf("%d %d", &a, &b);
    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;
}

注意:

  • 函数的特点就是简化代码,代码复用,减少代码冗余
  • 一般不建议把函数的名字写成全大写

数组

数组是一组相同类型的元素的集合

数组的定义:

int arr[10] = {1,2,3,4,5,6,7,8,9,10};//定义一个整形数组,最多放10个元素

数组的使用:

int main()
{
    
    

	//数组 - 一组相同类型的元素的集合
	
	//数组是用下标来访问的,从0下标开始

	//10个整形1-10存起来
	int arr[10] = {
    
    1,2,3,4,5,6,7,8,9,10};
	int i = 0;
	while (i < 10)
	{
    
    
		printf("%d ", arr[i]);
		i++;
	}
	char ch[5] = {
    
    'a', 'b', 'c'};//不完全初始化,剩余的默认为0

	return 0;
}

注:这样写int arr[3];只是创建了数组arr并未初始化数组元素的值

在这里插入图片描述
上面这样初始化是错误的

补充:
在这里插入图片描述
注意:在C99 标准中引用一个概念—变长数组(支持数组创建的时候,用变量指定大小的,但是这个数组不能初始化),但是VS2019 不支持C99中的变长数组

操作符

算术操作符

+  -  *  /  %

/号两端如果都是整数执行整数除法(无论用什么类型存储结果)。/号两端有一端是小数则执行小数除法

移位操作符

>>   <<

移位操作符:移动的是二进制位。如果是左移操作符空缺位永远补0

位操作符

& ^ |

赋值操作符

= += -= *= /= &= ^= |= >>= <<=

单目操作符

  • ! 逻辑反操作 应用于条件判断
  • -负值
  • +正值
  • & 取地址
  • sizeof 操作数的类型长度(以字节为单位)
  • ~ 对一个数的二进制按位取反
  • – 前置、后置–
  • ++ 前置、后置++
  • *间接访问操作符(解引用操作符)
  • (类型) 强制类型转换

双目操作符指的是有两个操作数;单目操作符指的是只有一个操作数
C语言规定:0表示假,非0表示真;如果一个数为0那么对这个数进行逻辑反操作那么这个数是1

sizeof的使用:
在这里插入图片描述

在这里插入图片描述

~的使用:
在这里插入图片描述

  1. ~按(二进制)位取反是把所有二进制位中数字(包括符号位)由1变成0,0变成1
  2. 整数在内存中存储的是按补码的形式存储的,%d打印的是原码
  3. 一个整数的二进制表示有三种:原码、反码、补码
  4. 正整数的原码、反码、补码相同,而负整数的原码和正整数的低31位比特位一致,符号位由0变为1;反码是将除符号位之外的所有比特位按位取反;补码是将反码加1

前置++和后置++的使用:
在这里插入图片描述
前置–、后置–和前置++、后置++的使用一样

int main()
{
    
    
	int a = 1;
	int b = (++a) + (++a) + (++a);//12
	printf("%d\n", b);
	return 0;
}

注意:上面的代码是有错误的不建议去研究,因为在不同编译器上运行结果可能是不同的,VS下运行结果是12

强制类型转换的使用:
在这里插入图片描述
注意:如果想要避免警告,需要使用强制类型转换。不推荐去使用(因为要使用强制类型转换说明设计有缺陷)

关系操作符

在这里插入图片描述

逻辑操作符

  • && 逻辑与
  • || 逻辑或

逻辑与是所有条件都为真时,结果才为真,否则就为假;逻辑或是只要有一个事件为真,结果就为真,否为为假

条件操作符(三目操作符)

exp1 ? exp2 : exp3

exp1成立,exp2计算,整个表达式的结果是exp2的结果;exp1不成立,exp3计算,整个表达式的结果是exp3的结果。

条件操作符的使用:

逗号表达式

exp1, exp2, exp3, …expN

逗号表达式是逗号隔开的一串表达式

逗号表达式的使用:

int main()
{
    
    
	(2, 4 + 5, 6);   //逗号表达式
	int a = 0;
	int b = 3;
	int c = 5;
	        //a=5       c=1        b=3
	int d = (a = b + 2, c = a - 4, b = c + 2);
	//逗号表达式,是从左向右依次计算的
	//整个表达式的结果是最后一个表达式的结果

	printf("%d\n", d);  //3
	return 0;
}

注:逗号表达式是从左向右依次计算的,整个表达式的结果是最后一个表达式的结果。

下标引用、函数调用和结构成员

[] () . ->(间接引用操作符)

下标引用、函数调用操作符的使用:
在这里插入图片描述

注:. -> * &下面会提到

常见关键字

  • 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

常见关键字需要注意的事项:

  1. C语言提供的,不能自己创建关键字
  2. 变量名不能是关键字

auto关键字

auto是自动变量,每个局部变量(自动创建自动销毁的)都是auto修饰的自动变量,基本都省略掉了。

auto的使用:

auto int a=10;

注:auto 新的C语言语法中也有其他用法 ( 暂时不考虑)

extern关键字

extern是用来声明外部符号的

extern关键字的使用:
在这里插入图片描述

在这里插入图片描述

注:

  • 声明变量的时候不需要它的值,只要指定名字和类型即可
  • 声明函数的时候写不写函数参数名都行,只要指定函数名和返回类型以及函数参数类型即可

register关键字(寄存器关键字)

register寄存器关键字作用是用register创建的变量建议把它放到寄存器里,具体将变量放不放进去由编译器说了算,register只是起到建议的作用

register关键字的使用:

register int num = 100;//建议num的值存放在寄存器中

注意:register关键字的应用——大量频繁被使用的数据想放在寄存器中,目的是提升效率。目前编译器比较智能了,它会自动判断该变量是否需要放入寄存器中

补充:

  • 计算机中数据可以存储到以下中去:
    在这里插入图片描述
    注意:这样设计目的是提高CPU的计算速度,提高效率(早期CPU计算数据通过从内存中拿数据,而内存访问存储数据的效率相比于CPU太低了,从而导致CPU计算效率的降低)

signed关键字

signed有符号的 ;unsigned 无符号的

typedef关键字

typedef 顾名思义是类型定义,这里应该理解为类型重命名(类型重定义)。

typedef关键字的使用:

typedef unsigned int u_int;
//将unsigned int 重命名为u_int, 所以u_int也是一个类型名
int main()
{
    
    
	unsigned int num = 100;
	u_int num2 = 100;

	return 0;
}

static关键字

在C语言中static是用来修饰变量和函数的(static - 静态的)

static关键字的使用:

static修饰局部变量
在这里插入图片描述
static修饰局部变量,改变了局部变量的生命周期(本质上是改变了变量的存储类型)

static修饰全局变量
在这里插入图片描述
static修饰全局变量,使得这个全局变量只能在自己所在的源文件(.c)内部可以使用,而其他源文件不能使用

注意:static修饰的局部变量只限于在该函数内部有效,出其作用域该变量无效(否则会报错)

static修饰函数

在这里插入图片描述
static修饰函数,使得函数只能在自己所在的源文件内部使用,不能在其他源文件内部使用。本质上static是将函数的外部链接属性变成了内部链接属性(和static修饰全局变量一样)

注:

  • 栈区用来存储局部变量和函数参数、堆区用来动态内存分配的、静态区用来存储全局变量和static修饰的静态变量
  • 静态变量的生命周期跟全局变量的生命周期一样
  • 全局变量在其他源文件内部可以被使用是因为全局变量具有外部链接属性,但是被static修饰之后就变成了内部链接属性,其他源文件就不能链接到这个静态的全局变量了

总结:

  • static修饰局部变量改变了变量的生命周期,让静态局部变量出了作用域依然存在,到程序结束,生命周期才结束。
  • 一个全局变量被static修饰,使得这个全局变量只能在本源文件内使用,不能在其他源文件内使用。
  • 一个函数被static修饰,使得这个函数只能在本源文件内使用,不能在其他源文件内使用。

union关键字

union - 联合体(共用体)

void关键字

void - 无、空

注意:

  • define不是关键字是预处理指令 ,使用方法 #define
  • include不是关键字是预处理指令,使用方法 #include

#define 定义常量和宏

define 是一个预处理指令

作用一:define定义标识符常量(define定义符号)

#define MAX 1000  //MAX也可称为符号

int main()
{
    
    
	printf("%d\n", MAX);
	return 0;
}

作用二:define定义宏


//define 定义宏
#define ADD(X,Y) ((X)+(Y))

int main()
{
    
    
	printf("%d\n", 4*ADD(2, 3));

	return 0;
}

指针

内存

内存是电脑上特别重要的存储器,计算机中所有程序的运行都是在内存中进行的 。所以为了有效的使用内存,就把内存划分成一个个小的内存单元,每个内存单元的大小是1个字节。为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该内存单元的地址。

在这里插入图片描述

注:

  • 内存编号是通过地址线,地址线通电产生电信号转换成数字信号,对应的二进制序列就是内存地址(编号称为地址)
  • 一个内存单元是一个字节

取出变量地址(&操作符)以及定义指针变量:
在这里插入图片描述
指针的使用实例(*操作符):

//指针 就是 地址

int main()
{
    
    
	int a = 10;

	int* pa = &a;

	*pa = 20;//* 解引用操作 , *pa就是通过pa里边的地址,找到a并对a进行操作

	printf("%d\n", a);

	return 0;
}

指针变量的大小

在这里插入图片描述
总结:指针的大小是相同的。因为指针是用来存储地址的,指针需要多大空间取决于地址的存储需要多大空间。在32位机器上其实是32个比特位组成的二进制序列作为地址(存储需4字节);在64位机器上其实是64个比特位组成的二进制序列作为地址(存储需8字节)

注:指针大小在32位平台(x86)是4个字节,64位平台(x64)是8个字节。

结构体

结构体使得C语言有能力描述复杂类型。比如描述学生,学生包含: 名字+年龄+成绩 这几项信息。这里只能使用结构体来描述了。

结构体的创建:

//结构体可以让C语言创建新的类型出来

//创建一个学生

struct Stu
{
    
    
	char name[20];//成员变量
	int age;
	double score;
};

//创建一个书的类型
struct Book
{
    
    
	char name[20];
	float price;
	char id[30];
};

结构体的初始化以及.操作符和->操作符的使用:

//结构体可以让C语言创建新的类型出来

//创建一个学生

struct Stu
{
    
    
	char name[20];//成员变量
	int age;
	double score;
};

//创建一个书的类型
struct Book
{
    
    
	char name[20];
	float price;
	char id[30];
};

int main()
{
    
    
	struct Stu s = {
    
     "张三", 20, 85.5 };//结构体的创建和初始化
	printf("1: %s %d %lf\n", s.name, s.age, s.score);//结构体变量.成员变量名

	struct Stu* ps = &s;
	printf("2: %s %d %lf\n", (*ps).name, (*ps).age, (*ps).score);

	printf("3: %s %d %lf\n", ps->name, ps->age, ps->score);  //结构体指针->成员变量名

	return 0;
}

注:.和->用于访问和修改结构体中成员变量的值

猜你喜欢

转载自blog.csdn.net/AI_ELF/article/details/129316605