C语言入门篇 三.数组 四.操作符

目录

三.数组

1.数组的创建和初始化

2.数组在内存中的存储

3.数组传参

四.操作符

1.算术操作符 +    -   *   /   %

2.移位操作符

3.位操作符

&   //按位与

|    //按位或

^   //按位异或

4.赋值操作符 =

5.单目操作符

6.关系操作符

7.逻辑操作符

8.条件操作符

9.逗号表达式

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

1. [ ] 下标引用操作符

2. ( ) 函数调用操作符

3. . 和-> 操作符:访问一个结构体的成员

隐式类型转换

寻常算术转换

复杂表达式的求值


三.数组

1.数组的创建和初始化

数组初始化语法:

type_t arr_name [const_n];

(注:数组创建,在C99标准之前, [] 中要给一个常量才可以,不能使用变量。在C99标准支持了变长数组的概念,变长数组初始化的元素个数必须与数组的长度相匹配。)

int a; //如果a是全局变量则在静态区创建变量初始化为0,否则在栈区初始化为随机值。

int arr[]; //未初始化,是随机值。

初始化方法:

int arr1[10] = {1,2,3};// 不完全初始化,其他元素默认初始化为0
int arr2[] = {1,2,3,4};// 编译器自动分配4个int大小的数组
int arr3[5] = {1,2,3,4,5};
char arr4[3] = {'a',98,'c'};// b的ASCII码值是98,98与‘b’等价
char arr5[] = {'a','b','c'};// 初始化为包含a,b,c,d,'\0'四个字符的数组,4个字节大小。
char arr6[] = "abcdef";// 初始化的时候数组会多一个元素,最后一个元素是\0
// 二维数组初始化时行可省略,列不可省略。
int arr2[][5]={{1,2},{4,5},{5,6}};

2.数组在内存中的存储

(1)数组是使用下标来访问的,下标是从0开始。
(2)数组的大小可以通过计算得到。//例如:sz=sizeof(arr)/sizeof(arr[0])在一维数组求的是元素个数,二维数组则是行数,二维数组sz=sizeof(arr[0])/sizeof(arr[0][0])求的是列数
(3)数组在内存中是连续存放的,随着数组下标的增长,元素的地址,也在有规律的递增(递增数据元素的字节大小,例如int递增4)。
二维数组在内存中也是连续存储的。例如:
int arr[3][4];数组在内存中布局也是连续的。

3.数组传参

数组名是首元素地址,除了sizeof求数组大小和&加数组名求数组地址(数组指针步长为整个数组大小)两个情况除外。
数组名作为函数参数,fun(arr);实际上将首元素地址作为实参。
函数声明:1.void fun(int *arr);  2.void fun(int arr[]);  上述两个写法等价。
实现冒泡排序算法:
int main(void){
	int arr[10]={0,3,42,434,32,323,3234,1215,123,888};
	printf("给定10个数字,进行升序排列: \n");
    int sz=sizeof(arr)/sizeof(arr[0]);
	  int q=0;
	for(int i=0;i<=sz-1;i++){
	  for(int q=0;q<=sz-1-i;q++){
	  //一轮 
	  if(arr[q]>arr[q+1]){
		int tmp=0;
		tmp=arr[q+1];  
		arr[q+1]=arr[q];
		arr[q]=tmp;
	    }
		//一轮冒泡排序 
	}
}
	for(int i=0;i<=sz-1;i++){
	printf("%d \n",arr[i]);
    }
    return 0;
} 

四.操作符

操作符分为:以下几种。

1.算术操作符 +    -   *   /   %

1. + - * /可以作用于整数和浮点数。% 操作符的两个操作数必须为整数,返回的是整除之后的余数。
2. 对于 / 操作符如果两个操作数都为整数,执行整数除法。而只要有浮点数执行的就是浮点数除法。

2.移位操作符

1.<< 左移操作符
2.>> 右移操作符  
移位操作符的操作数只能是整数,移位操作符移的是二进制位的补码形式,例如int a=5,a<<=1;实际效果是将a翻倍,a变成10。
注:整数的二进制表示有原码,反码,补码三种,最高位是符号位,整数在内存中存储的是补码的形式,打印出的是原码形式。
左移操作符
正整数的三者相同,负整数的反码将原码除符号位外的其他位取反,补码是反码加一。
例如:-5的二进制表示如下
在左移位操作时将补码左移,右边补0,左边超出的舍弃,例如a=5,a<<=1如下:

 若将int a=-5左移一位则是:

 转化为原码是10000000 00000000 00000000 00001010是-10,打印a得到的是-10(打印值打印的是原码)。

右移操作符而言, 有两种情况:

一种是算术右移,右丢弃,左补原符号位。

另一种逻辑右移,右丢弃,左补0,采用哪种移位取决于编译器,在vs2022采用的是算术右移。

3.位操作符

(双目操作符)位操作符有:
&   // 按位与
|    // 按位或
^   // 按位异或
注:他们的操作数必须是整数,操作的是整数的二进制补码形式。

&   //按位与

可以理解为将两个整数二进制补码各个比特位相乘,

例如a=3,b=-5,c=a&b; 

c是正整数原反补相同,所以c=3。
例如a=-3,b=-5,c=a&b;如下,将负整数转为补码,补码按位与,再转为原码即是c=-7。
 

|    //按位或

按位或操作同上,不过将按位与改成按位或,可以理解为二进制补码各位相加(1+1=1其实可以理解为是或门电路)

^   //按位异或

异或操作同上,不过规则是相同为0,相异为1,例如c=3^(-5)  结果是c=-8
易得a^a=0,a^0=a
利用异或可以实现两数交换(不借助第三个变量,但是不能实现浮点型数据的交换):
a=a^b;
b=a^b;
a=a^b;
原理是a=(a^b)^b=a^(b^b)=a^0;  //异或操作满足结合律和交换律
(也可以用a=a+b,b=a-b,a=a-b;但存在数字大小溢出问题)

4.赋值操作符 =

注意:

连续赋值:a = x = y+1; // 避免使用连续赋值,因为代码可读性差

复合赋值符:

+=  例如x=x+5;可写成x+=5;两者等价,下面的赋值操作符同理
-=、 *=、 /=、 %=、 >>=、 <<=、 &=、 |=、 ^=

5.单目操作符

如下:

!           逻辑反操作
-           负值
+           正值
&           取地址
sizeof      操作数的类型长度(以字节为单位)
~           对一个数的二进制补码按位取反
--          前置、后置--(前置就先--再使用,后置--则相反)
++          前置、后置++
*           间接访问操作符(也就是解引用操作符)
(类型)       强制类型转换
注意:
1.sizeof求变量内存空间大小时括号可省略,求类型时不能,这表明sizeof不是函数
2.sizeof(数组名)求的是数组空间的总大小
3.sizeof(s=a+2)求s所占空间大小,sizeof括号中表达式实际不运算,即s没变化,原因是sizeof在编译期间已计算了s所占空间大小,可以理解为被替换为非负整数了,运行期间不再运算。

6.关系操作符

>        大于          
>=      大于或等于
<        小于
<=      小于或等于
!=       判断“不相等”
==      判断“相等”
注意:“abc”==“sdw”比较的是字符串的首地址是否相等,而不是字符串的字典序大小(可以使用strcmp比较字符串的大小)。

7.逻辑操作符

&&       逻辑与(意思是并且,如果左操作数逻辑为假,就不执行右操作数)
||          逻辑或(意思是或者,如果左操作数逻辑为真,就不执行右操作数)
逻辑与和按位与的区别:
1&2----->0
1&&2---->1
逻辑或和按位或的区别:
1|2----->3
1||2---->1

8.条件操作符

又叫三目操作符,因为有三个操作数。

exp1 ? exp2 : exp3

表达式一为真时返回表达式2的结果,否则返回表达式3的结果。

9.逗号表达式

exp1, exp2, exp3,...expn
逗号表达式,从左向右依次执行,最后一个表达式的结果作为整个表达式的值。
例子:
a = get_val();
count_val(a);
while (a > 0)
{
        //业务处理
       a = get_val();
       count_val(a);
}
如果使用逗号表达式,可以改写为:
while (a = get_val(), count_val(a), a>0)
{
        //业务处理
}

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

1. [ ] 下标引用操作符

操作数:一个数组名 + 一个索引值
例如:arr[4]=5;(等价于*(arr+4)=5或*(4+arr)=5或4[arr]=5)

2. ( ) 函数调用操作符

接受一个或者多个操作数:第一个操作数是函数名,剩余的操作数就是传递给函数的参数。
例如:fun(a,b,c);

3. . 和-> 操作符:访问一个结构体的成员

用法

. :结构体.成员名 
-> :结构体指针->成员名
例如:
struct student{
    char name[20];
    char sex[5];
    int age;
};
struct student s = {name1,male,20};
char n1[20] = s.name; //使用.访问结构体成员
char*p = &s;
char s1[5] = p->sex; //使用->访问结构体成员,即用指针访问结构体成员,p是指针
int a1 = p->age; //使用->访问结构体成员,即用指针访问结构体成员,p是指针

隐式类型转换

什么是整形提升?为什么要整形提升?

        表达式求值的顺序一部分是由操作符的优先级和结合性决定,有些表达式的操作数在求值的过程中可能需要转换为其他类型。 C的整型算术运算总是至少以缺省整型类型的精度来进行的,为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换称为整型提升
        表达式的整型运算要在CPU的相应运算器件内执行,CPU内整型运算器(ALU)的操作数的字节长度一般就是int的字节长度,同时也是CPU的通用寄存器的长度。因此,即使两个char类型的相加,在CPU执行时实际上也要先转换为CPU内整型操作数的标准长度即int的字节长度。
        通用CPU(general-purpose CPU)是难以直接实现两个8比特位的数(即一字节)直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于int长度的整型值,都必须先转换为int或unsigned int,然后才能送入CPU去执行运算。
        整形提升是按照变量的数据类型的符号位来提升的 。(即正数补0,负数补1)
什么是截断?

        在C语言中,将int类型数据存入char类型变量会发生截断(truncation)操作。由于char类型的存储空间较小,通常只有一个字节(8位),而int类型的存储空间通常为4个字节(32位),因此将int类型数据存入char类型变量时,会将int类型数据的高位字节截断,只保留最低位的字节。

        具体来说,将int类型数据存入char类型变量时,会将int类型数据的最低位字节复制到char类型变量中,而高位字节将被丢弃。

        注意:截断时先将原数转为补码再进行截断。

实例如下:(char大多编译器是signed char)
  
//实例2,
int main()
{
    char c = 1;
    printf("%u\n", sizeof(c));
    printf("%u\n", sizeof(+c));
    printf("%u\n", sizeof(-c));
    return 0;
}

+和-会将c整形提升为int类型,故打印结果为:

寻常算术转换

实例2中的,c只要参与表达式运算,就会发生整形提升,表达式 +c ,就会发生提升,所以 sizeof(+c) 是4个字节. 表达式 -c 也会发生整形提升,所以 sizeof(-c) 是4个字节,但是 sizeof(c) ,就是1个字节.

如果某个操作符的各个操作数属于不同的类型,那么除非其中一个操作数的转换为另一个操作数的类型,否则操作就无法进行。

下面的层次体系称为寻常算术转换:

long double
double
float
unsigned long int
long int
unsigned int
int
如果某个操作数的类型在上面这个列表中排名较低,那么首先要转换为另外一个排名更高的操作数的类型后执行运算。但是算术转换要合理,要不然会有一些潜在的问题。

复杂表达式的求值

复杂表达式的求值有三个影响的因素。
1. 操作符的优先级
2. 操作符的结合性
3. 是否控制求值顺序。(有四个,分别为&&(逻辑与),||(逻辑或),?:(条件操作符)和,(逗号))例如:&&的左边表达式成立,不计算右边表达式。
两个相邻的操作符先执行哪个?取决于他们的优先级。如果两者的优先级相同,取决于他们的结合性。
注意:
非法代码如下(未定义行为)
//表达式1,只能保证,*的计算是比+早,但是优先级并不能决定第三个*比第一个+早执行。
a*b + c*d + e*f
//表达式2,左操作数获取在--前还是--后不确定
c + --c;
//表达式3,错误代码
i = i-- - --i * ( i = -3 ) * i++ + ++i
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值