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,具体流程如图所示:
上面提到了类型转换,那么什么是类型转换呢?
类型转换:
(1)默认由窄转换为宽的数据类型。
(2)同级别,无符号比有符号宽。例如:同级别unsigned int > int
(3)整型起,整数默认为int,小数默认为double。
我们还需要知道常见的各种类型数据的大小:
下面我们举一个实例来更好的了解一下类型转换问题:
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。
更多详细例子请查看该链接:产生可执行的.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(整形表达式)
{
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,如图所示:
更多详细指针运算请查看:指针加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()和数组、字符数组、字符串之间的作用