C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题

文章目录

      • 一、常用运算符
      • 二、位运算与类型转换
      • 三、前置++与后置++
      • 四、switch注意点、处理数字问题通用方法
      • 五、数组问题

一、常用运算符

1.逻辑与
表达式1&&表达式2,若表达式1为假,则表达式2不执行
       因为表达式1若为假,表达式2无论为真还是假与上表达式1都是假,无意义所以表达式2不执行。

2.逻辑或
表达式1 || 表达式2,若表达式1为真,则表达式2不执行
       因为表达式1若为真,表达式2无论为真还是假或上表达式1都是假,无意义所以表达式2不执行。

3.逻辑非
逻辑表达式中加入:非(!),若该表达式为假返回为数字0;若为真返回为数字1
我们来看一个非常简单的例子(最后输出为1):

int main()
{
	int a = 10;
	a = !!a;
	printf("%d\n", a);

	return 0;
}

       首先我们要明白什么是假?什么是真?
       0为假,非0才为真。
        上述代码定义了一个整型变量a,将数字10赋给了它。又将!!a再次赋给a,由于a为数字10(为真),!a即为假(数字0),因此!!a又变为真(数字1)。

4.小于、大于符号的判断
主要是一个连续比较的问题:输出结果为什么符号?

int main()
{
	if (10>5>3)
	{
		printf(">\n");
	}
	else
	{
		printf("<\n");
	}
	
	return 0;
}

       来看这段代码,当执行到if(10>5>3)这句时,先判断10>5为真,真为1;1再和3比较,1<3所以输出"<"号。
 

二、位运算与类型转换

关于位运算的介绍,之前写过一篇简单的博客:位运算符各种操作
整数的字节大小:4字节
这里仅仅举一个简单的例子:下述代码打印的值是多少?

int main()
{
	char a = 0x76;//4字节
	printf("%x\n", a<<8 | 0x12);

	return 0;
}

       定义了一个char类型的a,将0x76赋值给它。而16进制以0x开头,所以0x76为16进制表示的,0x12也为16进制表示的,我们知道%x是以16进制输出的。因此,前面我们提到整数的字节大小为4字节(32位),因此0x76二进制为:00000000 00000000 00000000 01110110,由于字符和整型的运算会将运算提升至整型运算(类型转换),左移8位后变为7600(十六进制),再或上0x12最后为7612,具体流程如图所示:
C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题_第1张图片
上面提到了类型转换,那么什么是类型转换呢?
类型转换
(1)默认由窄转换为宽的数据类型。
(2)同级别,无符号比有符号宽。例如:同级别unsigned int > int
(3)整型起,整数默认为int,小数默认为double。
C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题_第2张图片
我们还需要知道常见的各种类型数据的大小:
C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题_第3张图片
下面我们举一个实例来更好的了解一下类型转换问题:

int main()
{
	char a = 197;
	char b = 255;
	char c = -1;
	int d = a;
	printf("d1 = %d\n", d);
	d = b;
	printf("d2 = %d\n", d);
	d = c;
	printf("d3 = %d\n", d);

	unsigned char e = 197;
	unsigned char f = 255;
	unsigned char g = -1;
	d = e;
	printf("d4 = %d\n", d);
	d = f;
	printf("d5 = %d\n", d);
	d = g;
	printf("d6 = %d\n", d);

	return 0;
}

该段代码输出结果为:
在这里插入图片描述
根据前面我们了解到的数据类型转换与各种类型数据的大小:
为什么输出d1 = -59;d2 = -1; d3 = -1;d4 = 197;d5= 255; d6 = 255;
      由于char类型的取值范围为:-128——127,则char a = 127;   char b = 255;这两句在赋值时要自身进行相应转换。char a转换为-59(若为负数,可用256减去该数本身再加负号也可快速得出转化结果);char b转换为-1;char c还是-1;再执行int d = a;d = b;d = c;//由窄类型向宽类型转换,且int 类型范围约为-20亿——20亿足够包含char类型的取值:因此输出d1 = -59;d2 = -1;d3 = -1。
      unsigned char e = 197;unsigned char f = 255;由于为无符号char类型:0-255 ;197,255均在其范围内输出:d4 = 197,d5 = 255。同理,g转化为正数为255再赋值给d6为255。
C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题_第4张图片
更多详细例子请查看该链接:产生可执行的.exe文件与类型转换小结
 

三、前置++与后置++

++a:表示取a的地址,增加它的内容,然后把值放在寄存器中;
a++:表示取a的地址,把它的值装入寄存器,然后增加内存中的a的值;(也就是说操作的时候用到的都是寄存器里面的值,即自增前的值)
1.我们先来看一个简单的例子:问a,b,c各打印出什么?

int main()
{
	int a = 0;
	int b = 0;
	int c = 0;

	if (++a && ++b || ++c)
	{
		a++;
		b++;
		c++;
	}
	printf("a = %d,b = %d,c = %d\n", a, b, c);

	return 0;
}

结果为(2,2,1)。想清楚没有,为什么呢?
       a,b,c的值开始都被赋为0,当遇到if时候,先++a:a变为1与上++b:此时b的值也为1。所以与的结果为真,又遇到 || ,前面我们提到若 || 前面条件为真后面就不会执行了,此时:a,b,c的值为:1,1,0。再执行if内语句后,变为2,2,1最后输出。
 
2.再来看一个例子:问它打印出什么?

int main()
{
	int arr[] = {1,2,3,4,5};
	int *p = &arr[1];
	int i = 1;

	printf("%d,%d\n", p[i++], p[i++]);

	return 0;
}

       打印结果为:4,3。我们要了解一个东西:参数入栈C语言函数参数采用自右向左的入栈顺序,主要原因是为了支持可变长参数形式,C语言中可变参数都是从左到右,所以不管你有多少个参数反正将最右面的那个压入栈底,最左面的参数出入栈顶。
       主要由于这句:printf("%d,%d\n", p[i++], p[i++]);这句表达式有负作用,正常是不会这样写的,造成了参数入栈的问题:我们输出习惯是从左至右的,但栈是后进先出的。入栈是从右向左的,所以先输出右边的p[i++],入的p[1]的值arr[2]:3。i进行++再输出左边的p[i++],入的p[2]的值arr[3]:4。
 

四、switch注意点、处理数字问题通用方法

switch语句的一般形式:

switch(整形表达式)
{ 
    case 常量表达式1:  语句1;
    case 常量表达式2:  语句2;case 常量表达式n:  语句n;
    default:  语句n+1;
}

注意要点:
1、从哪进:表达式的值与case后的值相同则进入,case顺序不影响结果。
2、从哪出:遇到break或整个switch结束跳出。

处理数字问题通用方法,请查看该链接:
处理几类数字问题通用方法
 

五、数组问题

1.数组名含义
   1.1数组名表示整个数组情况
         1.在定义数组的同一个函数中,求sizeof(arr);
         2.在定义数组的同一个函数中,对数组名取地址再加1(&arr+1);
   1.2其他情况,arr表示首元素地址;
 
由于数组与指针有着十分紧密的联系,下面我们出现的问题部分和指针相联系。
案例1:数组名表示整个数组

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

	return 0;
}

输出结果为:
在这里插入图片描述
①sizeof求出整个数组大小为:40字节;
②取地址与取地址+1刚好间隔40个字节;
证实了前面数组名表示整个数组的两种特殊情况。
 
案例2:数组使用过程中退化为指针问题

void Fun(int arr[10])
{
	printf("%d\n", sizeof(arr));
}

int main()
{
	Fun(NULL);

	return 0;
}

这个时候输出为:
在这里插入图片描述
形参已经退化为指针了,指针的大小刚好为4字节(我们编译器设置为32位的)。
指针的大小:
Win32,x86,32位平台为4字节;x64,64位平台为8字节。

 
案例3:指针加1问题

int main()
{
	int arr[5] = {1,2,3,4,5};
	int *p1 = (int *)(&arr+1);//表示加整个数组大小
	printf("%d\n", p1[-2]);
	int *p2 = (int *)((int *)&arr+1);
	printf("%x\n", *p2);
	int *p3 = (int *)((int)&arr+1);
	printf("%x\n", *p3);

	return 0;
}

输出结果如下所示:
在这里插入图片描述
p1:打印结果为4,这种情况int *p1 = (int *)(&arr+1);表示加整个数组大小(走一个数组的大小),加到5的后面位置;因此p[i] = *(p+i);向后退2个位置,所以p1打印结果为:4;
p2:打印结果为2,首先对arr取地址,将其强转为整型指针,整型指针再+1(走一个单元格)解引用,即为2;
p3:打印结果为2000000,如图所示:
C语言复习1:常用运算符、位运算与类型转换、前置++与后置++、switch注意点、数组与部分指针问题_第5张图片
更多详细指针运算请查看:指针加1存在的可能及指针的算术运算
 
2.数组与指针区别与联系:
相同点:数组与指针表示相同操作:
int arr[10];
p = arr;
p[i] == **(p+i);
*(arr+i) = arr[i];
p[-1] = *(p-1);

int main()
{
	int arr[5] = {1,2,3,4,5};
	int *p1 = (int *)(&arr+1);//表示整个数组+1
	printf("%d\n", p1[-2]);
	int *p2 = (int *)((int)&arr+1);
	printf("%x\n", *p2);

	return 0;
}

打印结果如图所示:
在这里插入图片描述
2.不同点:
①大小不同;
②指针可以移动,数组名不能够改变;
③访问方式不同;

3.数组注意事项:
      1.越界问题
      2.数组作为参数传递,不仅要传地址,还需要传长度。
 
最后再补充一个:sizeof与strlen的区别:
      sizeof()只是计算类型或者变量内存大小的关键字,不是函数,没有所谓的头文件包含,就是C/C++的一个操作符。编译器自动识别,并且在编译阶段就自动计算了sizeof()的值。
  strlen()是系统(string.h/cstring)头文件中定义的,用来计算以’\0’结尾的字符串长度的函数。它并不是计算内存大小,仅计算字符串从开端到’\0’结尾字符的个数(不包含’\0’)。
例如:

char nzArr[10]="China";
int  nNum     =strlen(str); //结果为5
int  nLenb    =sizeof(str); //结果为10

sizeof()只跟你给该字符串数组定义了多大空间有关,而跟字符串是否结束无关。 所以当输入一行字符串,在不知道字符个数的时候,可以用strlen()函数来计算字符串长度,而sizeof()则不行。例如:

char str[100];
cin.getline(str,100); //获得流中一行输入的前100个字符,不足100到'\0结束'
int nLen = strlen(str);//得到实际字符串长度
int nNum = sizeof(str);//与输入字符串无关,都是字符串内存大小100

参考链接:C/C++中字符串数组及strlen()和sizeof()区别
总结sizeof()和strlen()和数组、字符数组、字符串之间的作用

你可能感兴趣的:(C语言学习,c语言,后端,字符串,指针,面试)