C语言入门初识(下)

操作符

C语言这个高级语言是非常灵活的,他提供了很多操作符,使他使用起来非常灵活。
在这是C语言初识带大家来了解一下基础的操作符,关于指针和二进制的操作符在后面会介绍

算数操作符

算数操作符
+ - * / %

这几个操作符中,+ - * / 对应着+ - × ÷,其中 / 两个操作数至少有一个为浮点型该表达式的结果均为浮点数,例如:5/2.0 5.0/2 5.0/2.0结果都为2.5,5/2结果为2
% 是取余操作符,表达式的结果为两个整数相除的余数(浮点数没有余数)。例:5%2的结果为1

赋值操作符

赋值操作符含义
=a=10(变量a赋值10)
+=a=a+10(把变量a加10)
-=a=a-10
*=a=a*10
/=a=a/10
#include<stdio.h>
int main()
{
	int a = 10;
	int b = 10;
	a = a + 5;
	b += 5;
	printf("%d\n%d\n", a, b);
	return 0;
}

上述代码输出结果为2个15,其他赋值操作符可以类比次来验证

还有其他对二进制操作的运算符在以后的篇章中会讲解,这里只是了解一下

单目操作符

何为单目操作符?例如a+b表达式中+有两个操作数,称为双目操作符。同理可得单目操作符只有一个操作数

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

! 逻辑取反操作符,在条件语句的判断中0为假,非0为真
!把条件语句非0变为0,0变为1

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

上面代码运行结果输出为10 0 test1,反映了!对操作数的影响,但最终不会改变操作数的值,

+    -   单目操作符对对字面数字常量的取正取反
#include<stdio.h>
int main()
{
	int a = -10;
	int b = 10;
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

该代码最终输出结果为-10 和10,这样+可以不写,默认为+10

sizeof操作符

int a = 10;
printf("%d\n",sizeof(int));
printf("%d\n",sizeof(a));
printf("%d\n",sizeof a);

上述代码输出结果都是4(byte),第三个printf能输出结果说明了sizeof是操作数而不是函数,

sizeof对数组的应用

int arr[]={1,2,3,4,5,6,7,8,9,10};
printf("%d\n",sizeof(arr));
printf("%d\n",sizeof(arr[0]));
printf("%d\n",sizeof(arr)/sizeof(arr[0]));

上述代码输出结果为40 4 10 ,第一个含义为整个数组的大小,第二个含义为数组单个元素的大小,第三个含义为数组元素个数。

前置++,后置++,前置–,后置–

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

上述代码,结果为11 10 11 11。
原因是后置++在语句中先运算在加加,所以b1 = a1++,就是把a的值赋值给b然后a再加加。
前置++在语句中先加加再运算,所以b2 = a2++,就是把a1先加加,然后再把a1赋值个b1

(类型)强制类型转换,在程序中不同类型间的赋值会引起编译器报错。

int a = 3.14;
printf("%d\n",a);

上述会使编译器报错,要想避免这种情况我们就用到了强制类型转换符。

int a = (int)3.14;
printf("%d\n",a);

上述代码则不会报错

单目操作符还有* &在接下来的指针初识会介绍,~会在后面章节介绍

关系操作符

关系操作符== != > < >= <= 分别表示相等,不相等,大于,小于,大于等于,小于等于。

逻辑操作符

逻辑与逻辑或
&&|\

&&与||是双目操作符类似于并且和或且
&&两侧表达式全为真则为真
&&两侧表达式有一个为真则为真

例子

#include<stdio.h>
int main()
{
	int a = 0;
	int b = 1;//一个为真
	if (a && b)
	{
		printf("test1\n");
	}
	if (a || b)
	{
		printf("test2\n");
	}
	a = 1;//全为真
	if (a && b)
	{
		printf("test3\n");
	}
	if (a || b)
	{
		printf("test4\n");
	}
	a = b = 0;//全为假
	if (a && b)
	{
		printf("test5\n");
	}
	if (a || b)
	{
		printf("test6\n");
	}
	return 0;
}

输出结果
在这里插入图片描述
该程序把两端操作数一个为真,两个为真,全为假做了三种讨论验证了逻辑操作符的作用,逻辑操作符还有一些应用在后面会介绍,再者只是先了解一下。

三目操作符

表达式1 ? 表达式2 : 表达式3 ;
表达式1成立最终结果为表达式2,表达式1不成立最终结果为表达式3;

#include<stdio.h>
int main()
{
	int a = 10;
	int b = 20;
	int max = a > b ? a : b;
	printf("max = %d", max);
	return 0;
}

上面的这串代码是用三目操作符求两数最大值

逗号表达式

exp1, exp2, exp3, …expN
该逗号表达式从左到右依次计算最后结果为最后一个表达式的结果

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

最后输出结果为5 10 25 25,验证了逗号表达式的计算过程。

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

[] () . -> 前两个为下标引用和函数调用马上进行解释,后两个为结构体成员在下面的结构体部分会讲解
[] - 下标引用
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };这条代码是数组的定义和初始化下面用下标引用操作符来操作数组中的元素。
arr[3] = 12;//改变数组元素的值
printf("%d",a[0]);//访问并输出数组的元素
[]的操作数为arr和0,所以[]为双目操作符

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

()函数调用操作符

#include<stdio.h>
int Max(int a, int b)
{
	return (a > b ? a : b);
}
int main()
{
	int a = 0;
	int b = 0;
	scanf("%d %d", &a, &b);
	int max = Max(a, b);
	printf("%d", max);
	return 0;
}

上面是调用创建使用求最大值的函数,Max(a,b)中Max a b 都为()的参数

关键字

关键字类型关键字
循环有关的关键字for; while; do while(); break; continue; goto;
分支有关的关键子break; if else ; switch;case;default;
数据类型关键字int;short;long;char;float;double;signed;unsigned;enum;struct; union,void
计算大小sizeof
类型重命名typedef
函数返回值return
储存说明符auto,register,extern,static
其他volatile

变量的命名与关键字

b变量命名的规则
1.不能是关键字
2.名字必须是数字,字母,下划线组成,不能有特殊字符同时不能以数字开头
3. 名字应该有意义例如 int age;int salary;

auto 关键字,声明变量的存储周期为自动的,一般变量定义中不写都默认auto。

typedef

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

#include<stdio.h>
typedef unsigned int uint;
int main()
{
	unsigned int a = 10;
	uint b = 10;
	return 0;
}

上述代码中变量a和b的数据类型是相同的,但是uint的写法更为简洁。

static

static有三种作用分别作用在局部变量,函数和函数

1.修饰局部变量

#include<stdio.h>
void test1()
{
	int a = 1;
	a++;
	printf("%d ", a);
}
void test2()
{
	static int a = 1;
	a++;
	printf("%d ",a);
}
int main()
{
	int i = 0;
	while (i < 10)
	{
		test1();
		i++;
	}
	printf("\n");
	i = 0;
	while (i < 10)
	{
		test2();
		i++;
	}
	return 0;
}

输出结果
2 2 2 2 2 2 2 2 2 2
2 3 4 5 6 7 8 9 10 11
其中test1()函数中a是局部变量,函数开始变量创建,函数结束就销毁。每次调用函数就重新创建,所以每次输出的结果是都一样的,都是2。
在test2()函数中a用static修饰,a出了作用域不削毁。
本质上static修饰局部变量时改变了局部变量的存储位置,影响了变量的生命周期,声明周期变长成为和程序的生命周期一样。

内存空间分为栈区,堆区,静态区。

局部变量,函数形参……栈区
动态内存管理malloc\free calloc realloc堆区
静态变量,全局变量静态区

实际上就是把局部变量从栈区改变到了静态区,在程序编译链接过程中就开始产生了在test2()中就不会再次创建.
编译+链接之后就是可执行程序。

2.修饰全局变量

add.c

int a = 2022;
static int b = 2022;

test.c

#include<stdio.h>
extern int a;
extern int b;
int main()
{
	printf("%d %d", a, b);
	return 0;
}

输出结果

输出结果

通过上面代码,你会发现变量b会无法解析,但是a可以。
static修饰全局变量时,这个全局变量就由外部链接属性变为内部连接属性。其它源文件(.c)就不能使用这个全局变量。我们在使用时就感觉作用域变小了。
这样用的好处就是避免不同的源文件内的同名的变量名有冲突,并且也保证了全变量不能随便在其他源文件内修改确保了变量的安全性。

static修饰函数

test1.c

int Max(int x, int y)
{
	return x > y ? x : y;
}
static int Min(int x, int y)
{
	return x < y ? x : y;
}

test2.c

#include<stdio.h>
extern int Max(int x, int y);
extern int Min(int x, int y);
int main()
{
	int a = 30;
	int b = 20;
	int max = Max(a, b);
	int min = Min(a, b);
	printf("%d %d", max, min);
	return 0;
}

程序运行结果
运行结果

一个函数本来具有外部链接属性,但是被static修饰的时候外部链接属性就变成了内部连接属性,其它源文件(.c)就不能使用了。

register寄存器变量

register int num = 3;这串代码建议: 存放在寄存中。

内存示意图

上图体现了计算机内存设备的逻辑状况,电脑上的内存设备有寄存器(集成在cpu上),高速缓存,内存和硬盘。
最初的时候只有内存和硬盘,cpu直接从内存中读取和处理数据。后来经过硬件的发展有了高速内存,cpu从高速缓存中读取数据的速度快,后来又出现了寄存器集成在CPU上读取速度更加快速。因为由于寄存器和高速缓存的造价太高,所以为了节约成本其存储数据的容量并不大。
在处理数据时,编译器会使内存中的部分数据存储在高速缓存和寄存器中。因为寄存器存储空间有限,所以register只是建议存放在寄存中,并不是决定是否放在寄存器中,所以register在平时写代码时可以省略。

#define 定义常量和宏

前面我们介绍常量的时候介绍了#define定义的标识符常量。

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

上述代码应用了标识符常量的常量属性,给变量赋值,定义数组。

#define 定义的宏

#include<stdio.h>
#define ADD1(x,y)  x+y
#define ADD2(x,y)  ((x)+(y))
int main()
{
	int a = 5;
	int b = 3;
	int c = 2*ADD1(5, 3);
	int d = 2*ADD2(5, 3);
	printf("%d %d", c, d);
	return 0;
}

最后结果为13 16,为什么结果不一样呢?
这个是用表达式直接代替宏
变量c和d的计算过程:int c = 2*x+y; int d = 2*(x+y);
从这看出来宏是和函数有着区别的,详细的区别会在以后的章节讲解。

指针

内存

计算机的内存空间有4g\8g\16g……这些空间应该如何管理呢?
中国有14亿人口,家庭也有非常多,这个家庭在哪住着,就用到了地址,哪个省哪个市哪个小区几单元几号。内存空间也是类似的,我们以32位地址线为例,每根地址线都有正电和负电,正电代表1负电代表0,共有232个地址序列,默认1个地址表示1byte,一共表示4294967296个字节也就是4GB。现在大部分机器都是64根地址线能非常充裕的表示8G/16G/32G以及更多。
那么为什么用一个字节呢为什么不用比特呢?
用比特的话32地址线就无法表示4G空间了,再者也不需要管理的如此细致这样会很繁琐。

代表内存空间的编号就是地址,这个地址也被称为指针。
int a = 10;\\向内存申请4个字节存储10,
&a;\\&是取地址操作符
int * p = &a;\\p是存放指针(地址)的变量就叫做指针变量,
*p = 20;\\*是解引用操作符,意思就是通过p中存放的地址,找到p所指向的对象,p就是p指向的对象
int
p *是表示p是一个指针变量。int 说明p指向的对象是int类型。指向类型是char,double,指针变量类型就为char*,double*;

通过指针改变变量内容

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

结构体变量的大小

#include<stdio.h>
int main()
{
	printf("%zu\n", sizeof(char*));
	printf("%zu\n", sizeof(short*));
	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*));
	return 0;
}

上面这串代码输出的结果全为4或者全为8.
不管是什么类型的指针,都是在创建指针变量。指针变量是用来存放地址的,指针变量的大小取决于一个地址存放的时候需要多大空间
32位机器上的地址:32bit位-4byte,所以指针变量的大小为4个字节
64位机器上的地址:64bit位-8byte,所以指针变量的大小为8个字节

结构体

在描述一个基本数据的时候C语言有基本数据类型char,short,int ,long long,float,double.但是描述一些复杂的数据时例如:学生有名字,性别,学号年龄等一系列的基本数据类型组成 ,这时基本数据类型就显得不够了。C语言就给了自定义类型的能力,自定义类型中有一种叫:结构体,关键字struct
结构体就是把单一的数据类型组合在一起的做法。

#include<stdio.h>
struct Stu
{
	char name[20];
	int age;
	char sex[10];
};
int main()
{
	struct Stu s = { "zhangsan",19,"male" };
	printf("%s %d %s\n", s.name, s.age, s.sex);
	struct Stu* ps = &s;
	printf("%s %d %s\n", (*ps).name, (*ps).age, (*ps).sex);
	printf("%s %d %s\n", ps->name,ps->age,ps->sex);
	return 0;
}

上面出现了. 和 ->两个新的操作符,都为结构体成员操作符
.操作符是针对结构体变量的,结构体变量.成员名
->是针对结构体指针变量的,对于结构体指针获取成员有两种方法
第一种:*(结构体指针变量).成员名
第二种:结构体指针变量->成员名
第二种更常用也更为简洁一些

需要注意的一点是结构体类型不占用内存,结构体空间占用内存。这个怎么理解呢?可以用建筑图纸和建筑物来类比,建筑图纸只是一张纸而建筑物是一个具体的建筑,建筑图纸约等于不占空间,而建筑物占用空间。结构体类型就相当于结构体变量的一个图纸。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值