对C你了解多少呢?是仅仅停留在听说过,还是已经小有基础,但无论是哪一种情况,对于初学者,我们首要的任务其实是建立一个学习的整体框架,先知其然,再知其所以然,我们常常把一门课程学习的开始称之为打地基,然而并非如此,打地基之前还有一个极为重要的步骤,就是画图纸,本篇目的就是要带领大家先绘制一份C语言学习 的图纸,有了这张图,你将会发现今后打地基盖房子的效率会成倍的增加,好啦,话不多说,Let's go!!!【ps:这个系列的文章会在我的学习过程中一直更新,直到我学习完成,新手上路,若有不妥之处,欢迎大家批评指正!!】
初识C语言
什么是C语言
语言
不同个体之间信息交互的一种载体,人与人,人与动物,动物与动物......之间都有互相“交流”的语言。而人和计算机之间交流的语言就是“计算机语言”,目前已经有上千种计算机语言了
c/c++/Java/Python......
其中C语言:通用的计算机编程语言,广泛应用于底层软件开发
- 什么是底层开发?
我们刚买来电脑其实就是一个硬件,通常在硬件上会装上一个操作系统用于操控,为了实现这种操控,在操作系统和硬件之间还有一层驱动层,操作系统调用驱动来控制硬件(现在的电脑很少单独装驱动,大部分集成到电脑里了。操作系统上又可以装应用软件,此时就到达了我们日常使用的层面。QQ/百度网盘......
常见操作系统:Windows/Linux/Mac......等
操作系统及驱动层叫底层软件,操作系统之上的应用软件等叫做上层软件,而C语言就很擅长底层软件的开发,但它也可以用于上层应用软件的开发,如QQ就是C语言开发的。
。
C语言的国际标准:ANSI C
计算机语言的发展经历低级到高级演变
二进制的指令(最早期)----->二进制过于复杂,出现汇编指令,助记符(用特殊的符号来替代二进制指令)------>汇编语言仍然复杂,出现B语言------->C语言(此时已经算是高级语言了)
C语言的形成也不是一蹴而就的,在开发过程中出现许多不同的语法体系,为避免各个开发厂商用的C语言语法产生差异,也为了统一,美国国家标准总局制定了一套完整的美国国家标准语法,称为 ANSI C,定为C语言的最初标准,2011年12月8日国际标准化组织(ISO)和国际电工委员会(IEC)发布的C11**标准**是C语言的第三个官方标准,也是C语言的最新标准,该标准更好的支持了汉字函数名和汉字标识符,一定程度上实现了汉字编程。
代码的书写环境
编辑器:只能编辑代码,不能直接在里面生成可执行程序
编译器:既可以编辑代码,又可以在代码编辑完成后生成可执行程序
C语言是一门面向过程的计算机编程语言,与C++,Java等面向对象的编程语言有所不同。C语言的编译器主要有Clang、GCC、WIN-TC、SUBLIME、MSVC、Turbo C等
编译型语言:将写好的的代码“test.c/cpp”经过编译、链接形成一个可执行程序“test.exe",如C/C++
编辑型语言:通过解释器直接实现一定的功能,如Python
如何写第一个C语言程序
1.打开VS
2.创建新项目(创建空项目)
3.创建源文件,注意改后缀为.c(.cpp是c++类型的源文件,如果不改后续按照c++的语法规则执行)
- 所有C语言程序的基本结构如下:
#include <stdio.h>//std-标准;i-input;o-output
int main()//C语言规定main函数是程序的入口,一个C语言程序之中,main函数有且只有一个
{
printf("hehe\n");//printf是一个库函数,专门用于打印数据的,使用时要和库里打招呼,即为第一行代码的意义
return 0;//返回的0是整数,和int(整形)相呼应,是一个历史习惯
}
//C语言代码中一定有main函数
//VS环境中如何运行代码:ctrl+f5
数据类型
为什么写代码:为了解决生活中的问题
以购物为例:东西的名称是个字符串,价格是一个数值,可能为整数、小数,是完全不同的数据类型
所以为了更好用计算机地描述这些数据,C语言抽象出来的一些数据类型
数据类型常见分类
C语言中的表述//名称//所占内存(单位为:字节)
char//字符数据类型//1
short//短整型//2
int//整形//4
long//长整型//4
long long//更长的整形//8
float//单精度浮点型//4
double//双精度浮点型//8
计算机中的单位
bit--位,计算机中最小的单位,一个比特位只能存储一个二进制位
Byte--字节,一个字节等于八个比特位//1B=8b
KB--千//1KB=1024B(2^10)
MB--百万(兆)//1MB=1024KB(2^20)
GB--10亿//1GB=1024MB(2^30)
TB--10,000亿//1TB=1024GB(2^40)
PB--10,000,000亿//1PB=1024TB(2^50)
EB--10,000,000,000亿//1EB=1024PB(2^60)
电信领域通常将比特bit称为小b,字节Byte称为大B。
**如何用一个小程序来计算输出各种数据类型的长度?**
#include <stdio.h>
int main()
{//sizeof()只是一个操作符,并非函数
printf("%zu\n",sizeof(char));//sizeof()的意义就是计算某类数据所占内存空间的大小,此处返回的就是一个char类型的数据在内存所占空间的大小
printf("%zu\n",sizeof(short));//sizeof()函数的输出使用%zu而非%d
printf("%zu\n",sizeof(int));
printf("%zu\n",sizeof(long));
printf("%zu\n",sizeof(long long));
printf("%zu\n",sizeof(float));
printf("%zu\n",sizeof(double));//错误警示(引号位置使用错误):printf("%zu\n,sizeof(double))");
return 0;
}
变量、常量
变量:分为全局变量和局部变量
#include <stdio.h>
int main()
{
int age = 20;//根据想要存放的数据类型来创建变量,而创建变量的本质就是申请内存空间
double price = 66.6;//此处含义为:向内存申请8个字节用于存放名为price的浮点型变量
return 0;
}
- 全局变量和局部变量的变量名称可以相同,程序运行时局部变量优先,但建议全局变量和局部变量尽量不要重名,一个大括号内不能定义相同名称的变量。
#define _CRT_SECURE_NO_WARNINGS
//计算两个整数的和
#include <stdio.h>
int main()
{
int num1 = 0;
int num2 = 0;//变量在创建的时候最好给他一个值,称为变量的初始化
//输入两个整数
scanf("%d %d", &num1, &num2);//注意&
//求和
int sum = num1 + num2;
printf("%d", sum);
return 0;
}
//当提示错误时在提示框输出栏寻找问题复制#define _CRT_SECURE_NO_WARNINGS到源文件的第一行
*关于在VS环境中出现scanf函数的相关问题的解决*
一劳永逸法:寻找在VS的安装路径下的newc++file.cpp的文件。在VS工程中创建新的.c或者.cpp文件的时候,都是拷贝newc++file.cpp这个文件的!因此找到这个文件修改内容后即可成为之后的默认形式
变量的作用域和生命周期
作用域
1.局部变量:局部变量所在的局部范围(即大括号内)
2.全局变量:整个工程,可以实现不同源文件之间的应用
生命周期
1.局部变量:进入作用域开始,出作用域结束
2.全局变量:整个程序的生命周期
常量:不能改变的量
- 字面常量:30、14.22
- 字符常量:'a'
- 字符串常量:"ahdud"
- const修饰的常变量:const修饰的常变量,本质是变量,但不能被修改,有了常量的属性
正确示例:
错误示例:
怎么证明它本质为变量?:用数组,数组里,例如定义数组时,如:a[10]中括号里面可以放具体的数字常量,但不可以放变量属性的东西,其中就包括const修饰的常变量。
- define定义的标识型常量:也不能修改
正确示例:
错误示例:
- 枚举常量:enum(是枚举的关键字)
#include <stdio.h>
enum Colour
{
//枚举常量
RED,
GREEN,//同样他们的值也是不允许改变的
BLUE
};
int main()
{
int num = 10;
enum Colour c = RED;//这里的enum Colour和int作用相同,这里的意思是当用这个类型去创建变量时,才能向内存申请空间
}
字符串+转义字符+注释
字符串
一对双引号引起的一串字符叫做字符串,字符串的结束标志是一个转义字符'\0',分配空间的时候不能够忽略'\0'也需要一个空间,但是计算字符长度时却不包括'\0'
*字符串结束标志的重要性!!
存储空间其实是很大的一块空间,在不规定创建的数组空间大小时,可以想象成把一组元素插入到一个线性空间内,前面和后面都有剩余空间,此时如果没有结束标志,他就会一直读取直到他找到\0才结束,此时计算其长度的值是一个随机值。
#include <stdio.h>
int main()
{
//100-int类型
//auco%#-char类型
//‘a’单引号引起一个字符
//"jsdjbcs"双引号引起的一串字符叫做字符串常量,也叫字符串字面值,简称字符串
//C语言中没有字符串类型
//如何存放字符串?用char定义一个数组
char arr1[] = "abcdef"; //[]中整数可写可不写,系统会自动识别需要给字符串分配的空间!!注意,此时字符串末尾隐藏着一个结束标志\0
char arr2[]={'a','b','c','d','e','f'};//没有\0,此时输出到f时不会停止,会继续往下走,直到找到\0
return 0;
}
运行结果展示:
*我们还可以直接对字符串长度进行计算来验证
strlen()的用法:这是计算字符串长度的一种函数,调用时需在开头加上#include<string.h>,打印使用%d,因为计算出的长度是整型
#include <stdio.h>
#include <string.h>
//注意\0,虽然占据内存空间,但计算字符串长度时不算\0
//验证如下
int main()
{
//strlen()是求字符串长度的函数,他是库函数,故需要调用含有他的头文件#include <string.h>
char arr1[] = "abcdef"; //求得长度是6
char arr2[] = { 'a','b','c','d','e','f' };//求得长度是50
char arr3[] = { 'a','b','c','d','e','f','\0'};//求得长度是6
printf("%d,%d,%d", strlen(arr1), strlen(arr2), strlen(arr3));//运行结果为6,50,6
return 0;
}
转义字符:即转变原来字符的意思
初级阶段打印的类型有哪些?
%d-打印整型
%c-打印字符
%s-打印字符串
%f-打印float类型数据
%lf-打印double类型数据
%zu-打印sizeof的返回值
转义字符分类:
\? 在书写多个问号时使用,防止编译器把他解析成三字母词中的一部分,使其变成单纯的?
\‘让其变成一个单纯的’,有时候需要打印出‘
\''用于表示一个字符串内部的双引号”
\\防止后面的\被解释成一个转义字符,让其变成一个单纯的\
\a触发蜂鸣
\b退格符,用得少
\ f进纸符,用得少
\n换行
\r回车
\t水平制表符,相当于电脑上的tab键
\v垂直制表符
\ddd,ddd表示 1个8进制的数字
\xdd,dd表示一个16进制的数字
- ASCII编码
都知道计算机是用2进制进行储存的,而我们打出的各种各样的字符要想能存储在电脑中就必须要有一个数字替代他们,因此就有ASCII编码表,将上述转义符表示的8进制或者16进制的数字转化成10进制,在通过ASCII表查询找到其代表的特殊字符,就可以实现这些字符的存储和调用了!!
*转义字符也是字符,空格也是字符 ,在计算字符串长度的时候,除了\0之外,其他的转义字符都要计算上。注意:“\628”算两个字符!!因为是八进制数,所以不可能有8,所以这个被解析成有两个字符,\62和8。
注释
- C语言的注释风格(嵌套注释的时候有缺陷)
/*
注释内容
*/
- C++的注释风格
//注释内容
习惯去写注释:可以梳理思路、对复杂的代码进行了解释,对后期修复bug提供便利。
基本语句:
C语言是结构化的程序设计语言,生活中的任何问题都能描述成下面三种结构或者三种结构的组合
顺序结构
循环结构
选择结构
选择语句
if(条件)
{
}
else
{
}
#include <stdio.h>
int main()
{
int x = 0;
printf("你想要成功吗?\n");
printf("想成功选1,不想选0\n");
scanf("%d", &x);
if (x == 1)
{
printf("恭喜你得到大厂offer!");
}
else
{
printf("只能乞讨了!!!");
}
return 0;
}
循环语句
while循环
for循环(后期)
do....while循环(后期)
//循环语句和选择语句的嵌套
#include <stdio.h>
int main()
{
int i = 0;
printf("想进大厂,就要写代码");
while (i < 2000)
{
printf("写代码%d天\n", i);
i = i + 1;
}
if (i >= 2000)
{
printf("成功拿到offer!");
}
else
{
printf("继续加油");
}
return 0;
}
函数
把函数想象成一个工厂,给他一些原材料(输入),经过加工,得到一个产品(输出)
//自定义函数
#include <stdio.h>
int Add(int x, int y)//Add叫函数名;int x,int y叫函数参数;大括号里的内容叫函数体;开头的int是函数的返回类型
{
int z = 0;
z = x + y;
return z;
}
{
return (x+y);//这个是上面函数体简化形式,更加直观
}
int main()
{
int n1 = 0;
int n2 = 0;
scanf("%d %d", &n1, &n2);
int sum = Add(n1, n2);
printf("%d\n",sum);
return 0;
}
数组:函数的特点就是简化代码,代码复用。
数组是一组相同类型元素的组合
C语言规定,数组的下标从0开始,因此通过下标就可以访问某个元素,创建的数组类型有多种,取决于你要存放的数据类型
//数组
#include <stdio.h>
int main()
{
int arr1[] = { 10,11,12,13,14,15,16,17,18,19 };
float arr2[] = { 4.66,9.87,1.23,6.54,8.69,2.45 };
printf("%d\n", arr1[8]);//打印结果为18
printf("%f", arr2[4]);打印结果为6.54
return 0;
}
操作符
C语言是非常灵活的,和其提供了非常丰富的操作符有极大的关系!
操作符的分类
1.算数操作符:基本的一些运算
+,-,*,/,%(取模)
- 除号操作符(/)的两端都是整数的时候,执行的是整数除法,如果两端有一个浮点数,就执行浮点数的除法,此时注意定义的数据类型要和给的数据相符。例:7/2要定义成整数类型,而7/2.0就要定义成浮点数类型。
- 对于取模操作符(%),其两个操作数只能是整数
- 想打印一位小数就打印.1f,即,printf("%.1f"),2位3位小数同理
#include <stdio.h>
int main()
{
int a = 7 / 2;
float b = 7 / 2.0;
printf("%d\n", a);//除号的两端都是整数的时候,执行的是整数除法,结果保留整数部分,为3
printf("%.3f", b);//打印结果保留三位小数,结果为3.500
return 0;
}
int main()
{
int a = 7%2;
printf("%d", a);//对于取模操作符,其两个操作数只能是整数,结果为余数1
return 0;
}
2.移位操作符:涉及二进制的运算,暂时不说
<<,>>
3.位操作符:之后再讲
&,^,|
4.赋值操作符:
=:初次出现的变量是初始化,后面的是赋值
+=:意思就是给他本身加一个值再赋值给他
-=:意思就是给他本身减一个值再赋值给他
#include <stdio.h>
int main()
{
int a = 20;
a += 3;//效果和a=a+3一样
printf("%d", a);
return 0;
}
5.单目操作符:后面只有一个操作数,而(a+b:+两边都有操作数,叫双目操作符)
!:逻辑反操作,把真的变成假的,假的变成真的,C语言中0表示假,非0表示真
#include <stdio.h>
int main()
{
int a = 0;
if (!a)//如果!a判断的结果为真,则打印对不起
printf("对不起");
else
printf("谢谢");//如果!a判断的结果为假则打印谢谢
return 0;
}
-:负值
+:正值
&:取地址,等到指针的时候再讲
sizeof:可以直接算变量,也可以算其类型,对类型不可以去掉括号,对于名称可以去掉括号,也从侧面说明了证明它不是函数,计算结果单位都是字节。
*注意用它来计算数组的时候注意数组里面放的是什么类型的数据,不同的数据类型对应不同的内存大小,例如整形一个元素占4个字节
#include <stdio.h>
int main()
{
double a = 10.1;
int arr[] ={9,2,5,8,3};
printf("%d\n", sizeof(arr));//结果为20,每个整形长度是4个字节,五个则长为20个字节
printf("%d\n", sizeof(arr[3]));//结果为4
printf("%d\n", sizeof(a));//printf("%d\n", sizeof a );//可以不给a加括号,说明sizeof不是函数
//a是一个双精度浮点数,长度为8字节
printf("%d\n", sizeof(arr)/sizeof(arr[4]));//结果为5,这是在计算元素的个数
return 0;
}
波浪号:对一个数的二进制按位取反,后期再讲
++:
b=a++,后置,先使用再++,相当于b=a;a=a+1
b=++a,前置,先++再使用,相当于a=a+1;b=a
--:和++类比
#include <stdio.h>
int main()
{
int a = 10;
int b = 0;
int c = 0;
b = a++;//相当于b=a;a=a+1;
c = ++a;//相当于a=a+1;c=a;
printf("%d\n", b);//得到10
printf("%d", c);//得到12
return 0;
}
*:间接访问操作符,后面讲
(类型):强制的类型转换
#include <stdio.h>
int main()
{
float a = (float)8;//把整型强制转换为单精度浮点型,后赋值给单精度浮点型变量a
printf("%.2f", a);//打印结果为8.00
return 0;
}
6.关系操作符:
1.>
2.>=
3.<
4.<=
5.!= 用于测试“不相等”
6.== 用于测试“相等”
#include <stdio.h>
int main()
{
int a = 10; int b = 5;
if (a == 10)
printf("hehe");//打印结果为hehe
if (b == 10)
printf("hehe");//无打印结果
return 0;
7.逻辑操作符:
&& : 并且,逻辑与
||: 或者,逻辑或
#include <stdio.h>
int main()
{
int a = 10;
int b = 0;
if (a && b)//a和b同为真结果才为真
printf("1hehe\n");//无打印结果
if (a || b)//a和b只要一个是真才为真
printf("2hehe\n");//成功打印出了结果
return 0;
8.条件操作符:(三目操作符)有三个操作数
exp1?exp2:exp3
exp1为条件,exp1成立,执行exp2;exp1不成立,执行exp3
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 3;
int d = 2;
int r = (a >= b ? c : d);//输出结果为2,说明第一个表达式不成立,故执行第3个表达式
printf("%d", r);
return 0;
}
9.逗号表达式:
特点是从左向右依次计算,整个表达式的结果是最后一个表达式的结果
10.下标引用、函数调用和结构成员
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
int c = 0;
int d = (c = a - 2, a = b + c, c - 3);
printf("%d", d);//运行结果为5
return 0;
}
(1)下标引用操作符[ ]
例如arr[3]:[ ]就是下标引用操作符,arr和3就是[ ]的两个操作数,访问下标时数组后面的[ ]里可以为变量
#include <stdio.h>
int main()
{
int arr[] = { 1,4,6,2,8,3 };
int b = arr[5];//此时就代表b等于这个数组中下标为5的元素
int c = 4;
printf("%d\n", b);//输出3
printf("%d",arr[c]);//输出8,不是定义数组,而仅仅访问下标时数组后面的[]里可以为变量
return 0;
}
(2)函数调用操作符:()
#include <stdio.h>
int Add(int x, int y)
{
return x + y;
}
int main()
{
int a = 10;
int b = 20;
int sum = Add(a, b);//这里的()就代表要调用Add()这个函数,而函数名、和其所有的变量都是()的操作数
printf("%d", sum);
return 0;
}
(3)结构成员(后期讲)
关键字
是C语言本身内置的,关键字不是自己创建出来的,也不能自己创建
下面是一些常见的关键字,了解一下,主要目的是在今后学的时候有个印象,也为了在今后在命名变量时能避开这些关键字,因为变量名不能和关键字一样!!!
- 变量的命名规则:
1.变量的名称要有意义,如要表示年龄,就设变量为age
2.变量名称必须是字母数字和下划线,但不能以数字开头,也不能有其他的特殊字符
3.变量的命名不能是关键字
举例:
用于循环:for,while,do while,break,continue
auto:自动 ,其实是被省略的,局部变量
表示数据类型:char,short,int,long,long long,float,double
表示有无符号:signed,unsigned
const:表示常属性
enum:枚举
struct:结构体
union:联合体(共用体)
void:无(函数的返回类型,函数参数)
extern:声明外部符号的
register:寄存器
static:静态的
return:函数返回值
sizeof:计算大小
typedef:类型重命名
还有goto,default,case,switch,if else----------等等
几个重要关键字的介绍
- 关键字typedef
顾名思义是类型定义,这里应该理解为类型重命名,一般用于把复杂的类型名称简化,也只能用于类型名称的重命名。
例如:unsigned int 无符号整型 ,表示起来很麻烦,就可以在进入主函数之前给他重命名为uint,之后直接用它来创建的变量类型和unsigned int创建的变量类型是完全一样的
//1.
#include <stdio.h>
typedef unsigned int uint;//把unsigned int这个数据类型重新取个名字叫做uint
int main()
{
unsigned int num1 = 5;
uint num2 = 10;//这个时候num1和num2的创建方式是一样的
printf("%d %d", num1,num2);
return 0;
}
//2.
#include <stdio.h>
typedef struct Node
{
int date;
struct Node* next;
}Node;//表示一个名为struct Node的类型,被重命名为Node
int main()
{
struct Node n1;
Node n2;//n1和n2的定义类型是完全一样的
return 0;
}
- 关键字static (静态)
用法:1.修饰局部变量2.修饰全局变量3.修饰函数
1.当其其修饰局部变量,它的生命周期发生了改变,每次出作用域时不会被销毁,直到整个程序结束,本质上,static修饰局部变量的时候,改变了变量的存储位置,由栈区到了静态区,变为了静态变量
C语言存储区域分为以下三个区域
** 栈区 **:存放局部变量......等(这里的局部变量一旦出了其所在范围即作用域就会销毁)
** 堆区 **:动态内存管理
** 静态区 **:存放静态变量(程序结束才销毁),全局变量也存放在此区域
//1.
#include <stdio.h>
void test()
{
int a = 0;
a++;//a自加得1
printf("%d", a);//打印得1
}//一旦a出了其所在局部范围,就被销毁,此时,再执行这个函数时,又会重新定义一个变量a,并给它赋值为0
int main()
{
int i = 0;
while (i < 10)//定义一个循环,当i<10的时候
{
test();//执行函数test
i++;//i自加1
}
return 0;
}//此段代码运行的结果是十个1
//2.
#include <stdio.h>
void test()//不需要任何返回值的时候就用void来定义函数
{
static int a = 0;//在调试的时候发现这行代码没有对应的汇编代码,事实上这个变量在编译期间就已经设定好了,因此在实际这行代码不参与程序执行
a++;
printf("%d", a);
}//此时,被static修饰的变量a作用域发生了改变,因此出了局部范围变量a仍然存在
int main()
{
int i = 0;
while (i < 10)//定义一个循环,当i<10的时候
{
test();//执行函数test
i++;//i自加1
}
return 0;
}//此段代码运行的结果是1到10
2.当其修饰全局变量时,全局变量的外部链接属性就变成了内部链接属性,即本质是改变链接属性,此时其他源文件就不能再使用这个全局变量了,在使用时的作用域变小了,原来可以用于整个工程的所有源文件
*注意当一个源文件要用另一个源文件定义的全局变量时,要注意用声明外部符号extern。
#include <stdio.h>
extern int a;//代表int a是同一个工程中另一个源文件定义的全局变量
int main()
{
printf("%d", a);//声明之后在这里就可以正常使用,要用另一个源文件中定义的函数时也一样
return 0;//但用声明外部符号时,必须把函数名即参数都写上,例:extern int Add(int x, int y);
}//注意!!一旦要引用的源文件在其本文件中被static修饰,即使声明了外部来源,也不可以在这里使用了
3.修饰函数,与全局变量十分类似,函数也具有外部链接属性,一个函数本来是具有外部链接属性的,但被修饰的时候,外部就变成内部了,其他源文件就无法使用了
*注意当一个源文件要用另一个源文件定义的函数时时,同样要注意用声明外部符号extern。
- 关键字register寄存器
电脑的存储设备有哪些?(由低级到高级,形成一个金字塔的结构,越往上访问速度越快,空间越小,造价越高)
CPU:是中央处理器,来进行计算机的计算工作,最开始的时候CPU从内存里面拿数据用来处理,随科技发展,CPU速度越来越快,内存速度已经跟不上了,此时就要找更加快的设备,因此出现了高速缓存器,再就是寄存器,但寄存器虽然速度快,但空间小,不能存放所有的数据,此时计算机运行过程中,寄存器就会向下拿一些必要的数据来传给CPU
写代码的时候可以自己创建寄存器变量,用于建议把你认为会经常用到的数据存放在寄存器中,但仅仅只是建议,最终编译器来决定(编译器现在很聪明了,自己会判断分配)
#include <stdio.h>
int main()
{
register int name = 3;//代表建议把3这个数据放在寄存器中
return 0;//但不是肯定会实现,最终由编译器来判断实现
}
- 关键字#define:定义标识符常量和宏
宏类似于函数,但本质不同,宏是宏,函数是函数,后期会讲。宏有,宏名,宏参数(这个参数是无类型的),宏体(类比函数体),宏是完成替换的
//1.
#include <stdio.h>
#define ADD 100//define定义标识符常量
int main()
{
int arr[ADD] = { 1,3,5,8,10 };
printf("%d", arr[4]);
return 0;
}
//2.
#include <stdio.h>
#define ADD(x,y,z) (x)*(y)/(z)//define定义的宏
int main()
{
int a = 10;
int b = 5;
int c = 25;
int answer = ADD(a, b, c);
printf("%d", answer);
return 0;
}
指针:用来存放地址
内存:计算机中的程序运行都是在内存中进行的
内存空间很大,如何很好的管理它,我们给每个小空间编号,这样找起来就会很方便
内存会划分为一个个的内存单元(一个内存单元大小:1Byte,1个字节),每个内存单元都有一个编号。
*为什么不用最小的单位bit?,我们最小的数据类型 char, 一个字符需要的空间就要一个字节,对应8个bit,如果最小单元为字节,只需一个地址就可以表示它的地址,而如果bit是最小存储单元,就要用8个不同的数字来表示一个相同的数据的地址,划分太过于细,没必要。
*32位电脑,电脑要访问内存空间的时候会生成地址,32位电脑在生成地址的时候是怎么生成的呢?我们电脑的硬件里有电线,32位电脑对应32根地址线,在电脑通电的时候,电线就会产生电信号,电信号表现出来就是1/0,就是正负脉冲,32根地址线同时产生脉冲的情况就有2^32种情况,也就是2^32个地址,他们就可以作为每个内存单元的编号,也就是电脑此时可以管理的内存大小为2^32个字节,换算成GB就是4GB,而在现在常见的64位机器上,我们可以管理8G,16G甚至32G的空间,为了表示方便我们把每个地址编号用16进制来表示。
那么这些空间的地址应该存放在哪里呢?我们通常用指针变量来存放一个变量的地址
int* p=&a;int*是一个类型,*说明p是指针变量,int 说明p指向的对象是int类型的
此代码的含义即位:创建指针变量p来存储a的地址,p变量的类型就是int*,代表它是一个指向对象为整型的指针变量
什么是指针变量?我们之前说每个内存单元都有编号,这个编号其实就是地址,而地址也被称为指针,所以三者其实是一个意思,指针就是地址也就是那个对应内存单元的编号,而p是用来存这个指针(也就是用来存地址、编号)的,因此把存放指针的,也就是存放的地址的变量叫做指针变量在指针变量的眼里,所有给他的数值都被认为是地址:后期详细介绍,暂时不赘述。
我们存放地址的原本意图一般不是单纯的想知道这个数值,一般是为了方便我们通过这个地址,轻松找到在这个地址里存放的对象,现在p里面存放了a的地址,就说明我们可能会通过p找到a,那怎么找呢?
#include <stdio.h>
int main()
{
int a = 10;//意思就是,我要向内存申请四个字节存放10
&a;//&是取地址操作符,这里的意思是我要取出存放a的地址的编号
printf("%p\n", &a);//%p是打印地址,每次打印出的值是不一样的,因为每次程序执行,变量都会重新创建
int* p = &a;//创建指针变量来存储a的地址
return 0;//此时a占四个地址,那么我们认为a的地址就是它所占第一个字节的地址
}
*p;//*是解引用操作符,意思是通过p存放的地址,找到p所指向的对象,a就是p指向的对象,即*p=a
指针变量的大小
#include <stdio.h>
int main()
{
char a = 'h';
char* k = &a; //创建个类型为char*的指针变量来储存字符型变量a的值
printf("%p\n", k);
*k = 'w';//*是解引用操作符,意思是通过k存放的地址,找到k所指向的对象
printf("%c", *k);//这里也可以直接打印a,即*k =a,此时找到了a,并给它重新赋值为'w'因此最终的打印结果也是w
return 0;
}
不管是什么类型的指针都是在创建指针变量 指针变量是用来存放地址的,指针变量的大小取决于存放一个地址需要多大空间 32位机器上的地址:32个bit位--4byte,所以指针变量的大小是4个字节 32位机器上的地址:64个bit位--8byte,所以指针变量的大小是8个字节 因此打印地址结果都是4或8
#include <stdio.h>
int main()
{
printf("%zu", sizeof(int*));//不管是什么类型的指针都是在创建指针变量
printf("%zu", sizeof(char*));//指针变量是用来存放地址的
printf("%zu", sizeof(short*));//指针变量的大小取决于存放一个地址需要多大空间
printf("%zu", sizeof(float*));//32位机器上的地址:32个bit位--4byte,所以指针变量的大小是4个字节
printf("%zu", sizeof(long long*));//64位机器上的地址:64个bit位--8byte,所以指针变量的大小是8个字节
return 0;//因此打印结果都是4或8
}//sizeof的结果最好用%zu打印
- 关于*的归属,一般把它归给int,但是要注意,当同时定义多个指针变量的时候,int *p1,p2,p3;的写法是错误的,应写成int* p1,*p2,*p3;
结构体
我们目前介绍的数据类型只有char,int,short,long……
但事实上好像不能描述一些复杂的个体:比如描述一个人:名字+年龄+性别+地址+电话……;书:书名+作家+出版社+定价+书号……
这个时候,C语言就给了自定义类型的能力,在自定义类型中,有一种叫结构体,结构体是把一些单一类型组合在一起的做法
例,描述一本书,我就定义一个结构体类型,叫struct Book,我们知道类型通常是用来创建变量的,这个时候你自己定义了的struct Book这个新类型同样可以创建变量,我们假设创建变量b,即struct Book b.
#include<stdio.h>
struct Book
{
char name[20];//这些都是结构体成员
char man[20];
char home[10];
int price;//int类型的数据不需要定义数组!!!易错点
char num[20];
};
int main()
{
struct Book s = { "anni","zhangsan","chubanshe",10,"1352688" };
printf("%s %s %s %d %s\n", s.name, s.man, s.home, s.price, s.num);
return 0;
}
我们创建的这个类型实际上是不占内存空间的,怎么理解呢?我们盖房子的时候需要先绘制一张图纸,有了图纸我们才能照着图纸盖出来房子,同样的道理,创建了一个类型就相当于画了一张图纸,而定义的变量s,才是你盖得房子,由它向内存申请了空间,具体讲解见代码。
//1.
#include <stdio.h>
struct Book
{
char name[20];//这些都是结构体成员
char man[20];
char home[10];
int price[10];
char num[20];
};
void print(struct Book* h)//这里表示创建一个struct Book*类型的指针变量h
{
printf("%s %s %s %d %s\n", (*h).name, (*h).man, (*h).home, (*h).price, (*h).num);//这里必须加括号,表示通过h存放的地址,分别找到h存放的结构体对象,并打印内容
printf("%s %s %s %d %s\n", h->name, h->man, h->home, h->price, h->num);//表示打印指针变量h,分别指向的结构体对象
}//结构体指针变量->成员名,这种用法的前提是你得到了一个指针
//2.
#include <stdio.h>
int main()
{
struct Book s = { "anni","zhangsan","chubanshe",10,"1352688" };
printf("%s %s %s %d %s\n", s.name, s.man, s.home, s.price, s.num);//结构体对象。成员名,这种用法的前提是你得到了一个结构体对象
print(&s);//我们现在把这个结构体类型的变量取地址并且传参给函数print,上面定义一个函数print()
return 0;
}
至此我们就把C语言学习的图纸画好啦,今后学习过程中,定将会事半功倍,如果有哪一部分理解起来觉得吃力,不必担心,我们现在只是和它们混个眼熟,后期对每部分内容都会详细分享,希望可以帮助你愉快的掌握C语言,下期开始,就正式到了打地基的阶段,我会先详细分享C语言的三种重要语句,控制语句、分支语句和循环语句,尽请期待吧!!(ps:如果你是期末不挂党,作为一个普通一本的老学姐可以告诉你,能看待这里,你的任务基本完成了70%,接下来只要反复去看你不理解的部分,再做些题目,60分定会轻松到手,如果你追求更高的分数,你可以继续去参考我接下来的笔记,祝大家都能高分通过鸭!!!!)
**一个了解计算机行业消息的公众号:readhub