C语言---初始C语言

1、初始C语言

1、编译器主要有:Clang、GCC、WIN-TC、MSVC、Turbo C等

什么是编译?

test.c----------------------------->test.exe

这个过程需要经过编译链接等过程,而众多编译器实现的功能就是把我们写的test.c进行编译。

2、VS2022(IDE)安装

官网:https://visualstudio.microsoft.com/zh-hans/?rr=https://www.microsoft.com/zh-cn/

在这里插入图片描述

在这里插入图片描述

文件说明:

  • .c结尾的文件称为源文件
  • .h结尾的文件称为头文件

下面创建一个源文件

在这里插入图片描述

在这里插入图片描述

3、第一个C语言项目:

#include <stdio.h>
int main()
{
	printf("hehe\n");         
	return 0;
}

//VS中点击F5运行
//C语言中一定要有main函数,main函数是程序的入口
//标准的主函数的写法
int main()
{
    return 0;
}
//这里有个对应的关系,return “0”和int相对应,int是整数类型,0是整数
//printf是个库函数,是别人开发好我们可以直接使用的,所以说在使用之前我们需要声明一下,所以“#include <stdio.h>”就是个说明
//std---代表标准的意思,i---代表input,o---代表output,所以说stdio.h就是标准的输入输出的头文件

4、数据类型

char                    //字符数据类型
short                   //短整型
int                     //整形
long                    //长整型
long long               //更长的整形
float                   //单精度浮点数
double                  //双精度浮点数
int main()
{
	printf("%d\n", 100);       "%d"以整形的方式打印,可以把"%d"---改为"%zu"
	return 0;
  • 每一个数据类型有多大呢?这里使用一个库函数来显示出来
int main()
{
	printf("%d\n", sizeof(char));
	printf("%d\n", sizeof(short));
	printf("%d\n", sizeof(int));
	printf("%d\n", sizeof(long));
	printf("%d\n", sizeof(long long));
	printf("%d\n", sizeof(float));
	printf("%d\n", sizeof(double));
	return 0;
}

1
2
4
4
8
4
8
//以上单位为“字节”
//可以发现int数据类型大小怎么和long的数据类型大小一样呢?
//原因:C语言规定:sizeof(long)>=sizeof(int)

4.1、%+字母

%d-----------打印整型(有符合整型)
    
%u-----------用于打印unsigned int num = 0这样的数据类型,这是无符号整型
    
//有符号整形包含正负数,无符号整型不包含复数。

%c-----------打印字符
    
%s-----------打印字符串
    
%f-----------打印float类型的数据
    
%lf----------打印double类型的数据
    
%zu/zd----------打印sizeof()的返回值
 
%p-----------打印地址
    
%02d--------------输出02/05...这样的效果
    
%2d---------------输出 2/ 5   //也就是说前面的0被空格替代了。
    
%-2d-------------左对齐
    
%.1f--------------打印小数点后一位的小数
    
float a = 0.0f       //float类型数据进行初始化
    
double a = 0.0       //double类型数据进行初始化
    //一般的直接赋值3.14编译器认为它是double类型的数据,为不是float类型的。
a=2 c=1 b=5 8
a=2 c=1 b=7 10
            12
            14
            16

4.2、计算机单位

bit---------比特位,计算机二进制101010101010。存储一个“1”或者“0”就占用一个bit位

byte-------字节 ,1字节=8bit

kb----------1kb=1024个byte

Gb----------

tb------------

pb------------

4.3、简述变量的本质

int main()
{
	int age = 22;         //age = 22赋值的这个过程叫做初始化,如果不进行赋值初始化,那么这个变量是随机值,这样一来这个变量不好掌控,所以尽量进行初始化。
	return 0;
}

//int整数类型大小为4字节,实际上就是向内存申请了一个4字节的内存大小,命名为age,用来存储“22”这个数据的。

5、变量和常量:局部变量和全局变量

不变的量/不可修改的量,C语言中用常量来表示,变得值C语言中用变量来表示

5.1、定义变量的方法

int main()
{
	int age = 22;
	return 0;
}

5.2、变量的分类

  • 局部变量
  • 全局变量
#include <stdio.h>

int global = 2019;    //全局变量
int main()
{
	int local = 2018;
	int global = 2020;     //局部变量
	printf("global = %d\n", global);          //那这里global值为什么?
	return 0;
}

输出:
    2020

所以可以得出结论:当全局变量和局部变量名字相同的情况下,局部变量优先。当然如果只有全局变量那么就会显示全局变量

错误代码示例:同一个范围里面不能声明重复的变量:

int main()
{
	int local = 2018;
	int local = 2020;    
	return 0;
}
//会报错

5.3、变量的使用—写一个代码,计算2个整数的和

#include <stdio.h>
//scanf是一个输入函数
int main()
{
	int num1 = 1;
	int num2 = 2;              //初始化
	
	//输入两个整数
	scanf("%d %d",&num1,&num2);

	//求和
	int sum = num1 + num2;

	//输出
	printf("%d\n", sum);
	return 0;

}

此时运行会发现报如下错误:

在这里插入图片描述

解决办法:在源文件的第一行写入如下代码:

#define _CRT_SECURE_NO_WARNINGS

//但是我们也能看到提示说可以用scanf_s这个函数代替,值得说明一下:scanf_s是VS编译器自己提供的函数,而非标准C提供的函数。

5.4、变量的作用域和生命周期

__作用域:__作用域(scope)是程序设计概念,通常来说,一段程序代码中所用到的名字并不是总是有效的/可用的

简单来说就是:这个变量在哪里起到作用/使用,哪里就是它的作用域

  • 局部变量的作用域是变量所在的局部范围
  • 全局变量的作用域是整个工程

5.4.1、局部变量的作用域是变量所在的局部范围

代码演示:

#include <stdio.h>
int main()
{
	{
		int a = 10;
		printf("a=%d\n", a);           //这个可以输出
	}
	printf("a=%d\n", a);               //但这个就会报错
	return 0;
}

但如果是这样的呢?

#include <stdio.h>
int main()
{
	int a = 10;
	{
		
		printf("a=%d\n", a);
	}
	printf("a=%d\n", a);
	return 0;
}


输出:
    a=10
    a=10
//此时a变量的作用域是整个的第一个大括号,所以都会输出

5.4.2、全局变量的作用域是整个工程

代码演示:

#include <stdio.h>
int a = 10;
int main()
{
	{
		printf("a=%d\n", a);
	}
	printf("a=%d\n", a);
	return 0;
}

输出:
    a=10
    a=10

5.4.2.1:补充:声明来自外部的符号

extern int a;      //声明变量
extern int Add(int x,int y);         //声明函数

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

1、局部变量的生命周期是:进入作用域之后生命周期开始,出作用域生命周期结束/销毁。

2、全局变量的生命周期是:整个程序的生命周期。

比如:

#include <stdio.h>

void test()
{
	int n = 1;
	n++;
	printf("%d ", n);
}

int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		i++;
	}
	return 0;
}

输出:
    2 2 2 2 2 2 2 2 2 2

这个重点就是看test()中a变量的声明周期,a变量从调用test函数到{开始就创建了,然后到}就销毁了,所以说输出一直是22222222…,而不是2,3,4,5,6…

6、常量

常量就是不变的量/不可修改的量

C语言中的常量和变量的定义形式有所差异。

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

  • 字面常量
  • const修饰的常__变__量
  • #define定义的标识符常量
  • 枚举常量

6.1、字面常量

#include <stdio.h>
//1.字面常量
int main()
{ 
	14;          //整形常量
	3.14;       //浮点型常量
	'w';        //字符常量,单个字符用单引号''
	"asdf";     //字符串常量,字符串用双引号""
	return 0;
}

6.2、const修饰的常__变__量

为啥叫做常变量呢?首先我们先来看一下常规的变量:

int main()
{ 
	int a = 10;
	a = 20;                 //进行变量赋值,变量改变,最终输出20
	printf("%d\n", a);
	return 0;
}

上面的a变量是可以被修改的,那如果我们不行要修改呢?我们只需要声明const即可

int main()
{ 
	const int a = 10;            //
	printf("%d\n", a);
	return 0;
}

补充:在C语言中,const修饰的a,本质是变量,但是不能被修改,有常量的属性。

6.3、define定义的标识符常量

#define	MAX 100                       //MAX就是个标识,这里定义的值也可以为字符串,浮点型...
#include <stdio.h>
int main()
{
	printf("%d\n", MAX);
	return 0;
}

 输出:
    100

define定义的常量既可以用来直接打印也可以用来赋值。

#define	MAX 100
#include <stdio.h>
int main()
{
	printf("%d\n", MAX);
	int a = MAX;
	printf("%d\n", a);
	return 0;
}

输出:
    100
    100

6.4、枚举常量

#include <stdint.h>

enum Color
{
	//下面的三个变量就是枚举常量
	RED,
	GREEN,
	BLUE
};

int main()
{
	enum Color c = RED;
	return 0;
}

7、字符串

1、首先char是字符类型,并不是字符串类型,那么C语言中有没有字符串类型呢?答案是没有!

那我们怎么表示字符串呢?使用双引号""来表示

"hello bit.\n"

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

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

2、那我们怎么存储字符串呢?我们如何认识到__\0__的重要性呢?

#include <stdio.h>
int main()
{
	char arr1[] = "abcdef";         //存储字符串的形式
	char arr2[] = { 'a','b','c','d','e','f' };
	printf("%s\n", arr1);
	printf("%s\n", arr2);            //%s是以字符串的形式显示
	return 0;
}

输出结果:

在这里插入图片描述

为什么会出现如此现象呢?

  • 首先分析第一个,arr1数组,字符串后面隐藏了\0,所以程序在执行时,遇见了\0便结束了,所以说输出就是abcdef
  • 那么第二个arr2数组,没有\0所以程序在’f’之后并不会结束,直到什么时候遇见\0什么时候程序才会停止。因此输出以上内容。

那如果arr2数组我们向正常输出呢?我们可以手动添加一个\0

char arr2[] = { 'a','b','c','d','e','f',"\0"};

!!!可见字符串中\0的重要性

那如果是这样的呢?

#include <stdio.h>
#include <string.h>
int main()
{
	char arr[3] = { 'b','i','t' };
	printf("%d\n", strlen(arr));
	return 0;

输出:

在这里插入图片描述

为什么呢?明明我声明了arr[3],arr数组有三个元素,但为什么该字符串的长度还是随机值呢?因为我们赋值初始化就三个元素,而我恰恰声明了三个空间,所以‘t’后面并没有\0,所以会一直检索到后面的\0才会输出。

那换一种写法:

char arr[4] = { 'b','i','t' };     //上述代码就改变这一行,arr[3]--->arr[4]

输出:3

原因:我们声明了分配4个存储空间,而后面要存储的元素只有3个,所以第四个空间直接是0值,相当于\0的作用,所以到此停止,该字符串的长度为3。

3、下面我们在举个例子说明下\0的重要性

这里我们使用一个库函数:strlen—用于输出字符串的长度,这个需要头文件:#include <string.h>

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[] = "abcdef";
	char arr2[] = { 'a','b','c','d','e','f'};
	printf("%d\n", strlen(arr1));
	printf("%d\n", strlen(arr2));
	return 0;
}

输出结果:
在这里插入图片描述

同样的道理arr1遇见f后面的\0之后就停止了,并且\0不算字符串内容,所以输出字符串长度为6

而arr2遇见f后面并没有\0,所以不会停止,继续向后遍历,直到遇到\0,以你输出不是6而是33

那如果想要arr2正常输出字符串长度我们只需要手动添加\0即可

char arr2[] = { 'a','b','c','d','e','f','\0'};

8、转义字符

常见转移字符,下面所有的转义字符,都是一个字符,注意:是一个字符

转义字符释义
?在书写连续多个问号时使用,防止它们被解析成三字母词
\’用于表示字符常量’
\"用于表示一个字符串内部的双引号
\\用于表示一个反斜杠。防止它被解释为一个转义序列符
\a警告字符,蜂鸣
\b退格符
\f进纸符
\n换行
\r回车
\t水平制表符
\v垂直制表符
\dddddd表示1-3个八进制的数字,如:\130 输出:X
\xdddd表示两个十六进制数字,如:\x30 输出:0

后面两个转义符代码演示:

#include <stdio.h>
int main()
{
	printf("%c\n", '\130');   //特别强调,'\130'不是4个字符,而是一个字符
	printf("%c\n", '\x62');   //同理,'\x62'不是4个字符,而是一个字符
	return 0;
}

输出:

在这里插入图片描述

8.1、关于转义字符的一道题

统计下面字符串的长度

#include <stdio.h>
#include <string.h>
int main()
{
	printf("%d\n", strlen("c:\test\628\test.c"));
	return 0;
}

答案:14

分析:\t算一个字符,\62算一个字符,所以一共由14个字符

那为啥\62算一个字符呢?我们说\ddd是需要三个数字的呀?为什么后面的“8”不算呢?

因为\ddd表示的是八进制数字,八进制数字是从0~7的,是不包含8的,所以只有\62算一个转义符。

9、注释

10、选择语句和循环语句

1、选择语句

if (aa == 1)
{
	printf("ok,去玩");
}
else
{
	printf("废了");
}

2、循环语句

C语言中如何实现循环呢?

  • while语句
  • for语句
  • do…while语句
while ()
{
    
}

11、函数

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>

int Add(int x, int y)
{
	int z = 0;
	z = x + y;
	return z;
}

int main()
{
	int n1 = 0;
	int n2 = 0;
	//输入
	scanf("%d %d", &n1, &n2);
	//求和
	int sum = Add(n1,n2);
	//输出
	printf("%d\n",sum);
	return 0;
}

在这里插入图片描述

12、数组

问题:要存储0~9的数字,怎么存储?

C语言中给数组的定义:一组相同类型元素的集合

#include <stdio.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    printf("%d\n", arr[3]);       //遍历数组
	return 0;
}

arr[10],这个10,就是表示我要存储10个int类型的元素,当然不写10也行,程序会自行分配

并且这些元素在内存中都是有顺序的,从0开始…

这些序号,叫做数组的下标,下标是从0开始的。

遍历全部元素:

#include <stdio.h>
int main()
{
	int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
	int i = 0;
	while (i < 10)
	{
		printf("%d\n", arr[i]);
		i++;
	}
	return 0;
}

那如果是这样的呢?

int arr[10] = { 0 };

那这10个空间存储的全部是0

在这里插入图片描述

13、操作符

13.1、算数操作符

+    -    *    /(得整数部分)    %(取模,得余数)

//  7 / 2    ----->3
//  7 % 2    ----->1
    
//那如果想要打印出小数呢?只需要两端有一个浮点数就会执行浮点数得出发
#include <stdio.h>
int main()
{
	float a = 7 / 2.0;
	printf("%.1f\n",a);          //.1就是保留小数点后一位
	return 0;
}

13.2、移位操作符

>>   <<

13.3、位操作符

&   ^   |

13.4、赋值操作符

=   +=   -=   *=   /=   &=   ^=   |=   >>=   <<=
  • a = a+3 a += 3是一样的效果,那后面的那都是这个原理

13.5、单目操作符

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

在C语言中0代表假,非0代表为真

13.5.1、!的介绍

#include <stdio.h>
int main()
{
	int flag = 0;           //flag初始化为0,所以flag此时为假
	if (!flag)              //那既然flag为假,那么!flag就为真
		printf("现在为真");
	return 0;
}

13.5.2、sizeof的使用介绍

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%d\n", sizeof(a));        //4
	printf("%d\n", sizeof(int));      //4
	printf("%d\n", sizeof a);         //因为size是个操作符,所以不带()也可以使用   输出:4

	return 0;
}

当然sizeof也可以统计数组的类型大小

#include <stdio.h>
int main()
{
	int arr[10] = {0};
	printf("%d\n", sizeof(arr));            //40
	printf("%d\n", sizeof(arr[0]));        //4,
	printf("%d\n", sizeof(arr) / sizeof(arr[0]));       //计算数组中元素的个数的方法
	return 0;
}

13.5.3、–、++的使用

这里只说明一个++即可,–的使用同理

1、先说后置++,eg:a++,后置++遵循一个原则:先使用,后++

#include <stdio.h>
int main()
{
	int a = 10;
	int b = a++;           //先使用,后++,也就是先 int b = a,然后在a = a+1
	printf("%d\n", b);
	printf("%d\n", a);
	return 0;
}

输出:
    10
    11

2、前置++,eg:++a。前置++遵循一个原则:先++,后使用

#include <stdio.h>
int main()
{
	int a = 10;
	int b = ++a;           //先++,后使用,也就是先 a = a+1,然后在int b = a
	printf("%d\n", b);
	printf("%d\n", a);
	return 0;
}

输出:
    11
    11

13.5.4、强制类型转换

#include <stdio.h>
int main()
{
	int a = (int)3.14;
	printf("%d\n", a);
	return 0;
}

输出:
    3

13.6、关系操作符

>
>=
<
<=
!=          //用于测试不相等
==          //用于测试相等

13.7、逻辑操作符

&&       逻辑与/并且
||       逻辑或/或者

13.8、条件操作符

exp1?exp2:exp3
    
exp1 ? exp2 : exp3
 真     算     不算      那么整个表达式的结果就是exp2表达式的结果
 假    不算     算       那么整个表达式的结果就是exp3表达式的结果

代码示例:

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int r = a > b ? a : b;      //比较最大值
	printf("%d\n", r);
	return 0;
}

输出:
    20

在这里插入图片描述

分析:如果a>b(exp1)成立,则a(exp2)是对的,那么b(exp3)是错的,因此整个表达式的结果就是exp2的结果,因为a=10,所以整个表达式的结果就是10,所以存进r=10

但是这里显然b(exp3)是对的,又因为b=20,所以r=20来进行存储,所以输出最大值为20。

13.9、逗号表达式

exp1,exp2,exp3,...expN

逗号表达式就是用逗号隔开的一串表达式。

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

#include <stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int c = 0;
	int d =(c = a - 2, a = b + c, c - 3);    //最后一个表达式c-3=5,所以最终输出结果5
	printf("%d\n", d);
	return 0;
}

输出:
    5

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

[]   ()   .   ->

13.10.1、下标引用

printf("%d\n", arr[0]);

13.10.2、函数调用

Add()
    

14、常见关键字简介

auto  break  case  char  const  continue  default  do  double  else  enum  extern  float  for  goto  if   int  register  return  short  signed  sizeof  static  struct  switch  typedef  union  unsigned  void  volatile  while

关键字是C语言内置的

15、关键词typedef和static

15.2、关键字typedef

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

typedef只能对类型进行重命名

比如:

#include <stdio.h>
//将unsigned int 重命名为unit,所以unit也是一个类型名
typedef unsigned int unit;
int main()
{
	//观察num1和num2,这两个变量的类型是一样的
	unsigned int num1 = 0;
	unit num2 = 0;
	return 0;
}

15.3、关键字static

在C语言中:

static是用来修饰变量和函数的

  • 修饰局部变量—称为静态局部变量
  • 修饰全局变量—称为静态全局变量
  • 修饰函数—称为静态函数

15.3.1、static修饰局部变量

我们先看看段代码:

#include <stdio.h>

test()
{
	int n = 1;
	n++;
	printf("%d ", n);
}

int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		i++;
	}
	return 0;
}

输出:

在这里插入图片描述

__分析:__还记得上面所说得变量得生命周期,变量得生命周从进入作用域开始创建,到出作用域销毁。

在第一遍调用test函数时,n这个变量从{开始创建,到}开始销毁,然后中间n++,所以最终打印出来n=2,这个时候test函数调用完毕,此时已经没有n变量这个东西了,因为已经销毁了,然后第二遍调用test函数,然后从头开始创建n变量并赋值n=1,然后打印n=2,然后销毁n变量,所以最终打印效果:2 2 2 2 2 2 2 2 2 2

那么ok下面进入整体,我们使用static关键词,来修饰局部变量,看看有什么效果:

#include <stdio.h>

test()
{
	static int n = 1;                //这里使用static关键词修饰局部变量n变量
	n++;
	printf("%d ", n);
}

int main()
{
	int i = 0;
	while (i < 10)
	{
		test();
		i++;
	}
	return 0;
}

输出:

在这里插入图片描述

???为什么打印得效果不一样了呢?

原因分析:同样第一遍调用test函数,创建n变量并赋值n=1,然后n++,然后打印n=2,重点来了:由于n变量用static关键词修饰了,那么此时n变量即使出了此作用域后,n变量依然不会被销毁,这就是static关键词的作用此时n=2被储存起来了,然后第二遍调用test函数,此时“static int n = 1;”这一句话就相当于不起作用也能够了,由于时n=2,然后n++,所以n=3了,最后出test函数后,n变量依然不会被销毁,n=3就被存起来了,就这样循环下去,所以输出结果就如上。

总结:static修饰局部变量的时候,局部变量出了作用域,局部变量不会被销毁。

本质上,static修饰局部变量的时候,改变了变量的存储位置。

在细致解剖一下就是:

一块大内存,是分区域存储的,分为:栈区、堆区、静态区

在这里插入图片描述

变量被static修饰后,此变量就从栈区移动到了静态区,但代码这里n直接就是在静态区了,不存在转换,这里只是特殊说明一下,所以说static修饰局部变量的时候,改变了变量的存储位置。

15.3.2、static修饰全局变量

先来看段代码:

在这里插入图片描述

分析:我们把g_val变量放在一个源文件中,然后在另一个源文件中调用打印g_val变量,(两个源文件在同一个项目中)此时只需要使用关键字extern声明一下就行了

然后我们使用static关键字修饰一下g_val,这时在另一个源文件中就算使用关键字extern声明g_val变量,但g_val依然不能被使用:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gUmQ2Abk-1685426179194)(C:\Users\郑波\AppData\Roaming\Typora\typora-user-images\1684597115195.png)]

总结:全局变量时具有外部链接属性的,而一旦static修饰全局变量的时候,这个全局变量的外部链接属性就变成了内部链接属性,其它源文件就不能在使用这个全局变量了,那这个全局变量的作用域由整个工程变为了当前文件,且此时静态全局变量是存储在静态区里面的。

15.3.3、static修饰函数

先来看一段代码:实现两个数相加

#include <stdio.h>

int Add(int x,int y)
{
	return x + y;
}

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

输出:
    30

上面代码使用了一个Add函数用于实现两个数相加,我们可以把Add函数放在test01.c文件中去,然后main函数放在test02.c文件中去,然后调用Add函数。同样我们也使用extern关键词来声明一下Add函数(其实不声明也行但是有警告)

在这里插入图片描述

然后我们使用static关键字修饰一下Add函数,这时在另一个源文件中就算使用关键字extern声明Add函数变量,但Add函数依然不能被调用了:

在这里插入图片描述

总结:一个函数本来是具有外部链接属性的,而一旦static修饰的时候,这个函数的外部链接属性就变成了内部链接属性,其它源文件就不能在使用这个函数了。

15.4、【普及】关键字register—寄存器

在这里插入图片描述

由于CPU处理数据速度非常之快,以至于内存赶不上CPU的速度,所以当处理数据时,内存中的数据向上一级存放,于是就放到在高速缓存、寄存器里面。

那进入整体,在写代码时我们也可以写出寄存器变量:

#include <stdio.h>
int main()
{
	//寄存器变量
	register int num = 3;     //建议把‘3’存放在寄存器里面
	return 0;
}

当把‘3’数据存放在寄存器里面了,这样在读取此数据时会更快,但是需要注意一点,这里只是建议存放,那到底最后是否存放在寄存器里面时编译器说的算。

16、define定义常量和宏

16.1、#define定义常量

这个知识点就是前面讲到常量所涉及到的

#define	MAX 100                       //MAX就是个标识,这里定义的值也可以为字符串,浮点型...
#include <stdio.h>
int main()
{
	printf("%d\n", MAX);
	return 0;
}

 输出:
    100

16.2、#define定义宏

宏是有参数的

上面我们用了Add函数来实现两数之和,这里我们使用宏来代替Add函数实现此功能

#include <stdio.h>

//定义宏
#define Add(x,y) ((x)+(y));

int main()
{
	int a = 10;
	int b = 20;
	int c = Add(a, b);
	printf("%d\n", c);
	return 0;
}

在这里插入图片描述

17、指针

17.1、内存

内存是电脑上特别重要的存储器,计算机中程序的运行都是在内存中进行的。

所以为了有效的使用内存,就把内存划分一个个小的内存单元,每个内存单元的大小是__1个字节。__

为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,这些编号被称为该__内存单元的地址__

在这里插入图片描述

好的,那现在有个问题:如果有个整形变量数据为“10”,(int a = 10)那int数据类型需要占用4个字节,那就需要向内存申请4个存储单元,那么变量a的内存地址是哪个呢?如下图:

在这里插入图片描述

比如分配的内存地址如下图所示:分配的4个内存空间为:5,6,7,8
在这里插入图片描述

那么此时变量a的内存地址就是第五个地址(首地址)

17.2、如何打印变量内存地址,如何存储内存地址?

1、打印内存地址

&a //取地址操作符

#include <stdio.h>
int main()
{
	int a = 10;
	printf("%p\n", &a);           //打印内存地址
	return 0;
}

2、存储内存地址:

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;            //存储变量a的内存地址
	return 0;
}

17.3、指针变量

重点来了:

int* p = &a;       这个变量p就是指针变量

为什么变量p就被称作为是指针变量呢?

其实很简单,我们都知道每一个内存单元都会有一个编号,又叫做地址,而地址又被称作指针。

在这里插入图片描述

如果变量a的数据类型字符类型,就需要这样写:

#include <stdio.h>
int main()
{
	char a = 10;
	char* p = &a;            //存储变量a的内存地址
	return 0;
}

17.4、*p代表的意思和作用

看一段代码

#include <stdio.h>
int main()
{
	int a = 10;
	int* p = &a;            
	*p = 20;          //解引用操作符,意思就是通过p中存放的地址,找到p所指向的对象,*p就是p指向的对象
	printf("%d\n", a);
	return 0;
}

输出:
    20

*p = 20就感觉重新给a = 20赋值一样。(个人分析:*p和直接赋值还是又差别的,*p是找到内存地址进行赋值,这个操作a变量的地址是没有改变的,而重新赋值a变量的内存地址应该是改变了。)

17.5、指针变量的大小

#include <stdio.h>
int main()
{
	printf("%zu\n", sizeof(char*));
	printf("%zu\n", sizeof(int*));
	printf("%zu\n", sizeof(short*));
	printf("%zu\n", sizeof(float*));
	printf("%zu\n", sizeof(double*));
	return 0;
}

//64bit位的电脑上
输出:
    8
    8
    8
    8
    8
    
    
//32bit位的电脑上
    4
    4
    4
    4
    4

原因分析:不管是什么类型的指针,都是在创建指针变量。指针变量是用来存放地址的。指针变量的大小取决于一个地址存放的时候需要多大空间,而32位平台下的地址是32个bit位—即4byte,所以指针变量的大小是4个字节,而64位平台下地址是64个bit位—即8byte,所以指针变量的大小是8个字节。

18、结构体

结构体是C语言中特别重要的知识点,结构体使得C语言有能力描述复杂类型

比如描述学生,学生包含:名字+年龄+性别+学号这几项信息。因为学生是需要这几个信息同时包含的,属于复杂对象,所以用char,short,int,long…等数据类型不能够单一去表示。所以为了解决相应的问题,C语言就给自定义类型的能力就是结构体。

结构体就是把一些单一类型组合在一起。

那这里只能使用结构体来描述了:

struct Stu
{
    //以下都是结构体成员
	char name [20];
	int age;
	char sex[5];
	char id[15];
};

18.1、用结构体类型来创建一个变量,并打印信息

我们知道创建一个变量很简单,都是数据类型+变量名,比如:

int a 
char t
float dd

那同样上面的struct Stu结构体也是个类型,然后它也能创建变量:struct Stu S,这个S就是变量,

可以发现struct Stu{...}和int的效果一样,只不过int是解决单一变量问题的,而struct Stu解决复杂变量问题的,所以说struct Stu{...}int一样是不占空间的,只有当struct Stu S动作发生,创建出来的S变量才占用一点空间。

那如何使用变量呢?

#include <stdio.h>

struct Stu
{
	char name [20];
	int age;
	char sex[5];
	char id[15];
};

int main()
{
	struct Stu S = 
	{
		"zhangsan",17,"nan","123456789"
	};
    printf("%s %d %s %s\n",S.name,S.age,S.sex,S.id);  //打印的顺序需要相互对应
	return 0;
}

18.2、难度升级一下,问:能不能用指针区实现打印信息的效果?

代码如下:

#include <stdio.h>

struct Stu
{
	char name [20];
	int age;
	char sex[5];
	char id[15];
};

void print(struct Stu* ps)
{
	printf("%s %d %s %d\n", (*ps).name, (*ps).age, (*ps).sex, (*ps->id));
    printf("%s %d %s %d\n", ps->name, ps->age, ps->sex, ps->id);
    //效果一样,形式:结构体对象.结构体成员变量

}

int main()
{
	struct Stu S = 
	{
		"zhangsan",17,"nan","123456789"
	};
	print(&S);
	return 0;
}

就类比前面说到的指针变量那一节,道理一样。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/25375.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

利用栈和队列共同解决迷宫问题

文章目录 什么是迷宫问题&#xff1f;如何解决迷宫问题&#xff1f;DFS&#xff08;深度优先搜索&#xff09;BFS&#xff08;广度优先搜索&#xff09; 总结 什么是迷宫问题&#xff1f; 迷宫问题是一道经典的算法问题&#xff0c;旨在寻找一条从起点到终点的最短路径。通常迷…

桶排序 — 计数排序和基数排序

计数排序 int类型数组&#xff0c;其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲&#xff0c;数据状况是受限的。此时如果将数组从小到大进行排序&#xff0c;该如果实现&#xff1f; 这个实现很简单&#xff0c;实现一个统计数组范围从 0 ~ 150&#xff0c;新数组…

Flume的安装和使用

安装Flume 1.1访问Flume的官网&#xff08;http://flume.apache.org/download.html&#xff09;&#xff0c;下载Flume安装apache-flume-1.9.0-bin.tar.gz。或者下载我的百度网盘资源。把安装文件解压缩到windows操作“D:\”目录下&#xff0c;然后执行如下命令测试是否安装成…

【JavaSE】Java基础语法(十六):抽象类

文章目录 1. 抽象类的概述2. 抽象类的特点3. 抽象类的实用价值4. 抽象类的案例 1. 抽象类的概述 当我们在做子类共性功能抽取时&#xff0c;有些方法在父类中并没有具体的体现&#xff0c;这个时候就需要抽象类了&#xff01; 在Java中&#xff0c;一个没有方法体的方法应该定义…

在Linux设备上让程序在任意目录都能执行

目录 0. 前言1. 编写代码2. 创建软链接3. 其他Linux文章 0. 前言 在Ubuntu上使用espidf中往往需要先设置环境变量&#xff0c;再执行export.sh&#xff0c;对环境装的乱七八糟的我造成了很大的不便我希望无论我在哪个目录&#xff0c;都能快速执行某个命令 我先是使用了编写b…

微信小程序开发实战 ⑨(TabBar)

作者 : SYFStrive 博客首页 : HomePage &#x1f4dc;&#xff1a; 微信小程序 &#x1f4cc;&#xff1a;个人社区&#xff08;欢迎大佬们加入&#xff09; &#x1f449;&#xff1a;社区链接&#x1f517; &#x1f4cc;&#xff1a;觉得文章不错可以点点关注 &#x1f4…

【Unittest】自动化测试框架核心要素

1、什么是Unittest框架&#xff1f; python自带一种单元测试框架 2、为什么使用UnitTest框架&#xff1f; >批量执行用例 >提供丰富的断言知识 >可以生成报告 3、核心要素&#xff1a; 1). TestCase&#xff08;测试用例&#xff09; 2). TestSuite(测试套件)…

WalkRE--刷图流程(超具体)

1、打开WalkRE软件&#xff0c;界面如下&#xff1a; 2、选择“根据模板新建工程”。操作如下&#xff1a; 3、导入数据。需要准备好dxf格式的CAD地形数据。操作如下&#xff1a; 在空白处右键&#xff0c;先关闭所有层&#xff08;大部分层在刷图时用不上&#xff0c;仅打开刷…

如何监控电动车充电桩能耗?

一 背景 随着新能源汽车的快速发展&#xff0c;像特斯拉、BYD、蔚来、小鹏和理想等品牌的电动汽车在我们的日常生活中越来越多了&#xff0c;可见电动汽车如今已逐渐被我们所认可了。同汽油车需要加油一样&#xff0c;电动汽车需要充电&#xff0c;如此一来&#xff0c;电动汽…

C++进阶 —— multimap

目录 一&#xff0c;multimap介绍 类pair 函数模板make_pair 二&#xff0c;multimap使用 一&#xff0c;multimap介绍 multimap是关联式容器&#xff0c;按照特定顺序存储键值对<key、value>&#xff0c;其中多个键值对之间的key可以重复&#xff1b;键key通常用于排…

供应链 | 在线平台的研究与思考(一):销售渠道与模式选择

封面图来源&#xff1a; https://www.pexels.com/zh-cn/photo/4968391/ 编者按 当前&#xff0c;电商平台主要采用两种销售模式&#xff1a;代理和分销。商家根据自身情况选择线上或线下渠道&#xff0c;而电商平台会根据不同的线上商家选择适当的分销模式。本期编者精选的两…

C++环形缓冲区设计与实现:从原理到应用的全方位解析

C环形缓冲区设计与实现&#xff1a;从原理到应用的全方位解析 一、环形缓冲区基础理论解析&#xff08;Basic Theory of Circular Buffer&#xff09;1.1 环形缓冲区的定义与作用&#xff08;Definition and Function of Circular Buffer&#xff09;1.2 环形缓冲区的基本原理&…

Smartbi“三步走”构建智慧经营分析平台,实现国有企业监管报送和数智化转型

01. 现状与痛点 — 一直以来&#xff0c;国资国企都是促进我国经济高速发展的领头羊&#xff0c;但近年来受疫情冲击和国际经济下行影响&#xff0c;国资企业经营面临较大压力&#xff0c;同时为实现国有企业高质量发展&#xff0c;国务院国资委下发一系列政策要求&#xff…

linuxOPS基础_vmware虚拟机安装及介绍

虚拟机概念 什么是虚拟机&#xff1f; 虚拟机&#xff0c;有些时候想模拟出一个真实的电脑环境&#xff0c;碍于使用真机安装代价太大&#xff0c;因此而诞生的一款可以模拟操作系统运行的软件。 虚拟机目前有2 个比较有名的产品&#xff1a;vmware 出品的vmware workstatio…

chatgpt赋能python:Python如何创建一个DataFrame

Python如何创建一个DataFrame 在数据科学和分析领域中&#xff0c;DataFrame是一种非常常见的数据结构。它类似于电子表格&#xff0c;可以存储和处理包含多个列和行的数据。在Python中&#xff0c;pandas库提供了DataFrame数据结构的支持。 什么是DataFrame&#xff1f; Da…

C++ list类成员函数介绍

目录 &#x1f914;list模板介绍&#xff1a; &#x1f914;特点&#xff1a; &#x1f914;list内存结构图解&#xff1a; &#x1f914; list的成员函数&#xff1a; &#x1f60a;list构造函数&#xff1a; &#x1f50d;代码示例&#xff1a; &#x1f50d;运行结果&…

RobotFramework接口测试方案

1. Robot FrameWork介绍 1.1 介绍 Robot Framework是用于验收测试和回归测试的通用测试自动化框架。它使用易于理解的表格数据语法&#xff0c;非常友好的实现了关键字驱动和数据驱动模式。它的测试功能可以通过使用Python或Java实现的测试库进行扩展&#xff0c;用户可以使用…

【JavaSE】Java基础语法(十七)

文章目录 1. final2. 代码块2.1 代码块概述2.2 代码块分类 1. final fianl关键字的作用 final代表最终的意思&#xff0c;可以修饰成员方法&#xff0c;成员变量&#xff0c;类 final修饰类、方法、变量的效果 fianl修饰类&#xff1a;该类不能被继承&#xff08;不能有子类&a…

软件测试基础概念

目录 软件测试的生命周期如何描述一个bug如何定义bug的级别bug的生命周期产生争执怎么办&#xff08;处理人际关系&#xff09;如何开始第一次测试测试的执行和bug管理如何发现更多的bug 软件测试的生命周期 需求分析 – 测试计划 – 测试设计、测试开发 – 测试执行 – 测试评…

界面控件DevExpress WinForms全新的UI模板,解决各种业务线需求!

去年秋天DevExpress官方发布了一个新的 WinForms UI模板预览版&#xff08;第一个EAP只提供给DevExpress宇宙版激活的用户&#xff09; &#xff0c;这些精炼的、随时可用的“模板”旨在启动表单设计/开发过程。有了这个模板&#xff0c;用户可以创建/交付现成的UI解决方案&…