C语言中逗号表达式的实用意义或者产生的原因?

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言中逗号表达式的实用意义或者产生的原因?相关的知识,希望对你有一定的参考价值。

希望有人可以给我讲一下C语言中“逗号表达式”的实用意义或者产生的原因,而不是使用方法~谢谢~

下面是C语言中所使用的运算符的优先级和结合性:
优先级 运算符 结合性
(最高) () [] -> . 自左向右
! ~ ++ -- + - * & sizeof 自右向左
* / % 自左向右
+ - 自左向右
<< >> 自左向右
< <= > >= 自左向右
== != 自左向右
& 自左向右
^ 自左向右
| 自左向右
&& 自左向右
|| 自左向右
?: 自右向左
= += -= *= /= %= &= ^= |= <<= >>= 自右向左
(最低) , 自左向右
还有指针运算符、sizeof运算符、数组运算符[]等等

一、赋值运算符
赋值语句的作用是把某个常量或变量或表达式的值赋值给另一个变量。符号为‘=’。这里并不是等于的意思,只是赋值,等于用‘==’表示。
注意:赋值语句左边的变量在程序的其他地方必须要声明。
得已赋值的变量我们称为左值,因为它们出现在赋值语句的左边;产生值的表达式我们称为右值,因为她它们出现在赋值语句的右边。常数只能作为右值。
例如:
count=5;
total1=total2=0;
第一个赋值语句大家都能理解。
第二个赋值语句的意思是把0同时赋值给两个变量。这是因为赋值语句是从右向左运算的,也就是说从右端开始计算。这样它先total2=0;然后total1=total2;那么我们这样行不行呢?
(total1=total2)=0;
这样是不可以的,因为先要算括号里面的,这时total1=total2是一个表达式,而赋值语句的左边是不允许表达式存在的。

二、算术运算符
在C语言中有两个单目和五个双目运算符。
符号 功能
+ 单目正
- 单目负
* 乘法
/ 除法
% 取模
+ 加法
- 减法
下面是一些赋值语句的例子, 在赋值运算符右侧的表达式中就使用了上面的算术运算符:
Area=Height*Width;
num=num1+num2/num3-num4;
运算符也有个运算顺序问题,先算乘除再算加减。单目正和单目负最先运算。
取模运算符(%)用于计算两个整数相除所得的余数。例如:
a=7%4;
最终a的结果是3,因为7%4的余数是3。
那么有人要问了,我要想求它们的商怎么办呢?
b=7/4;
这样b就是它们的商了,应该是1。
也许有人就不明白了,7/4应该是1.75,怎么会是1呢?这里需要说明的是,当两个整数相除时,所得到的结果仍然是整数,没有小数部分。要想也得到小数部分,可以这样写7.0/4或者7/4.0,也即把其中一个数变为非整数。
那么怎样由一个实数得到它的整数部分呢?这就需要用强制类型转换了。例如:
a=(int) (7.0/4);
因为7.0/4的值为1.75,如果在前面加上(int)就表示把结果强制转换成整型,这就得到了1。那么思考一下a=(float) (7/4);最终a的结果是多少?
单目减运算符相当于取相反值,若是正值就变为负值,若是负数就变为正值。
单目加运算符没有意义,纯粹是和单目减构成一对用的。

三、逻辑运算符
逻辑运算符是根据表达式的值来返回真值或是假值。其实在C语言中没有所谓的真值和假值,只是认为非0为真值,0为假值。
符号 功能
&& 逻辑与
|| 逻辑或
! 逻辑非
例如:
5!3;
0||-2&&5;
!4;

当表达式进行&&运算时,只要有一个为假,总的表达式就为假,只有当所有都为真时,总的式子才为真。当表达式进行||运算时,只要有一个为真,总的值就为真,只有当所有的都为假时,总的式子才为假。逻辑非(!)运算是把相应的变量数据转换为相应的真/假值。若原先为假,则逻辑非以后为真,若原先为真,则逻辑非以后为假。
还有一点很重要,当一个逻辑表达式的后一部分的取值不会影响整个表达式的值时,后一部分就不会进行运算了。例如:
a=2,b=1;
a||b-1;
因为a=2,为真值,所以不管b-1是不是真值,总的表达式一定为真值,这时后面的表达式就不会再计算了。

四、关系运算符
关系运算符是对两个表达式进行比较,返回一个真/假值。
符号 功能
> 大于
< 小于
>= 大于等于
<= 小于等于
== 等于
!= 不等于
这些运算符大家都能明白,主要问题就是等于==和赋值=的区别了。
一些刚开始学习C语言的人总是对这两个运算符弄不明白,经常在一些简单问题上出错,自己检查时还找不出来。看下面的代码:
if(Amount=123) ……
很多新人都理解为如果Amount等于123,就怎么样。其实这行代码的意思是先赋值Amount=123,然后判断这个表达式是不是真值,因为结果为 123,是真值,那么就做后面的。如果想让当Amount等于123才运行时,应该if(Amount==123) ……

五、自增自减运算符
这是一类特殊的运算符,自增运算符++和自减运算符--对变量的操作结果是增加1和减少1。例如:
--Couter;
Couter--;
++Amount;
Amount++;

看这些例子里,运算符在前面还是在后面对本身的影响都是一样的,都是加1或者减1,但是当把他们作为其他表达式的一部分,两者就有区别了。运算符放在变量前面,那么在运算之前,变量先完成自增或自减运算;如果运算符放在后面,那么自增自减运算是在变量参加表达式的运算后再运算。这样讲可能不太清楚,看下面的例子:
num1=4;
num2=8;
a=++num1;
b=num2++;

a =++num1;这总的来看是一个赋值,把++num1的值赋给a,因为自增运算符在变量的前面,所以num1先自增加1变为5,然后赋值给a,最终a也为5。b=num2++;这是把num2++的值赋给b,因为自增运算符在变量的后面,所以先把num2赋值给b,b应该为8,然后num2自增加1变为 9。
那么如果出现这样的情况我们怎么处理呢?
c=num1+++num2;
到底是c=(num1++)+num2;还是c=num1+(++num2);这要根据编译器来决定,不同的编译器可能有不同的结果。所以我们在以后的编程当中,应该尽量避免出现上面复杂的情况。

六、复合赋值运算符
在赋值运算符当中,还有一类C/C++独有的复合赋值运算符。它们实际上是一种缩写形式,使得对变量的改变更为简洁。
Total=Total+3;
乍一看这行代码,似乎有问题,这是不可能成立的。其实还是老样子,'='是赋值不是等于。它的意思是本身的值加3,然后在赋值给本身。为了简化,上面的代码也可以写成:
Total+=3;
复合赋值运算符有下列这些:
符号 功能
+= 加法赋值
-= 减法赋值
*= 乘法赋值
/= 除法赋值
%= 模运算赋值
<<= 左移赋值
>>= 右移赋值
&= 位逻辑与赋值
|= 位逻辑或赋值
^= 位逻辑异或赋值
上面的十个复合赋值运算符中,后面五个我们到以后位运算时再说明。
那么看了上面的复合赋值运算符,有人就会问,到底Total=Total+3;与Total+=3;有没有区别?答案是有的,对于A=A+1,表达式A被计算了两次,对于复合运算符A+=1,表达式A仅计算了一次。一般的来说,这种区别对于程序的运行没有多大影响,但是当表达式作为函数的返回值时,函数就被调用了两次(以后再说明),而且如果使用普通的赋值运算符,也会加大程序的开销,使效率降低。

七、条件运算符
条件运算符(?:)是C语言中唯一的一个三目运算符,它是对第一个表达式作真/假检测,然后根据结果返回两外两个表达式中的一个。
<表达式1>?<表达式2>:<表达式3>
在运算中,首先对第一个表达式进行检验,如果为真,则返回表达式2的值;如果为假,则返回表达式3的值。
例如:
a=(b>0)?b:-b;
当b>0时,a=b;当b不大于0时,a=-b;这就是条件表达式。其实上面的意思就是把b的绝对值赋值给a。

八、逗号运算符
在C语言中,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
假设b=2,c=7,d=5,
a1=(++b,c--,d+3);
a2=++b,c--,d+3;
对于第一行代码,有三个表达式,用逗号分开,所以最终的值应该是最后一个表达式的值,也就是d+3,为8,所以a=8。对于第二行代码,那么也是有三个表达式,这时的三个表达式为a2=++b、c--、d+3,(这是因为赋值运算符比逗号运算符优先级高)所以最终表达式的值虽然也为8,但a2=3。

还有其他的如位逻辑运算符,位移运算符等等,我们等到讲位运算时再说明。

九、优先级和结合性
从上面的逗号运算符那个例子可以看出,这些运算符计算时都有一定的顺序,就好象先要算乘除后算加减一样。优先级和结合性是运算符两个重要的特性,结合性又称为计算顺序,它决定组成表达式的各个部分是否参与计算以及什么时候计算。
参考技术A 逗号表达式的出现,是出于一种“粘合”的用意。
在C语言中,表达式的地位是非同寻常的,而有时我们的某些表达式在用意上需要成为一个整体时,逗号表达式就站出来了。同时,C语言的某些规定使得某些地方只能用一个表达式,如for循环中的三个表达式,此时用逗号的“粘合”作用来建立一个表达式就非常必要了。本回答被提问者采纳

C语言有这个就够了七.实用调试技巧

(一)什么是BUG

历史上第一个bug

【C语言有这个就够了】七.实用调试技巧_调试

导致程序运行错误的对象

(二)调试是什么

调试就是破案的过程,因为有人写代码是这样的:

【C语言有这个就够了】七.实用调试技巧_优秀代码_02

1.调试

又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。


2.调试的基本步骤

  • 发现错误的存在
  • 以隔离,消除等方式对错误进行定位
  • 确定错误的产生原因
  • 提出纠正错误的解决办法
  • 对程序错误予以改正,重新测试

3.debug和release的介绍

debug称为调试版本,它包含调试信息,且不做任何优化,便于存程序员调试程序

【C语言有这个就够了】七.实用调试技巧_调试_03

release称为发布版本,它往往是进行了各种优化,使程序在代码大小和运行速度上都是最优的,以便用户使用

【C语言有这个就够了】七.实用调试技巧_优秀代码_04

我们可以看到,同一个代码,在不同版本下,内存大小不同。

同样对于这个代码:

#include <stdio.h>
int main()

int i = 0;
int arr[10] = 0 ;
for (i = 0; i <= 12; i++)

arr[i] = 0;
printf("gaga\\n");

return 0;

【C语言有这个就够了】七.实用调试技巧_解决bug_05

【C语言有这个就够了】七.实用调试技巧_解决bug_06

如果是 debug 模式去编译,程序的结果是死循环;

如果是 release 模式去编译,程序没有死循环;

这样看来,release的确会优化。

(三) Windows环境调试介绍

1.准备

选择debug模式

2.快捷键

2.1 F5

启动调试,经常用来直接跳到下一个断点处。

注:F5通常与F9配合使用(在代码错误处用F9设置断点,用F5直接跳到断点处)

2.2 F9

创建断点和取消断点(代码执行到断点处停下来)

断点的重要作用,可以在程序的任意位置设置断点。

这样就可以使得程序在想要的位置随意停止执行,继而一步步执行下去


2.3 F10

逐过程,通常用来处理一个过程,一个过程可以是一次函数调用,或者是一条语句。


2.4 F11

逐语句,就是每次都执行一条语句,但是这个快捷键可以使我们的执行逻辑进入函数内部(这是最常用的)

3.调试的时候查看程序当前信息

【C语言有这个就够了】七.实用调试技巧_调试_07

合理运用,可以更快找出bug。

(四) 一些调试的实例

1.实现代码:求 1!+2!+3! ...+ n! ;不考虑溢出

int main()

int i = 0;
int sum = 0;//保存最终结果
int n = 0;
int ret = 1;//保存n的阶乘
scanf("%d", &n);
for(i=1; i<=n; i++)

int j = 0;
for(j=1; j<=i; j++)

ret *= j;

sum += ret;

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

【C语言有这个就够了】七.实用调试技巧_调试_08

我们发现i=3时,ret为12,结果错误;

仔细分析,发现ret未初始化;

在​​ int j=0;​​​前增加​​ret=1;​

2.研究程序死循环的原因

#include <stdio.h>
int main()

int i = 0;
int arr[10]=1,2,3,4,5,6,7,8,9,10;
for (i = 0; i <= 12; i++)

arr[i] = 0;
printf("gaga\\n");

return 0;

1.栈区的默认使用规则:

先使用高地址处的空间

再使用低地址处的空间

2.数组随着下标的增长,地址由低到高变化

【C语言有这个就够了】七.实用调试技巧_调试_09

3.过程中数组越界可能可能遇到i,在越界改变时可能把i变为0,导致程序死循环

【C语言有这个就够了】七.实用调试技巧_优秀代码_10

(五)如何写出好代码

1.优秀的代码

代码运行正常

 bug很少

 效率高

 可读性高

 可维护性高

 注释清晰

 文档齐全

2.技巧

使用assert

尽量使用const

养成良好的编码风格

添加必要的注释

避免编码的陷阱

3.案例模拟实现strcpy

#include<stdio.h>
int main()

char arr1[] = "#########";
char arr2[] = "ajfhk";
strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;

【C语言有这个就够了】七.实用调试技巧_解决bug_11

那为什么没有打印后面的#######?

通过调试发现:

【C语言有这个就够了】七.实用调试技巧_解决bug_12

拷贝了一个\\0过去,打印时遇到\\0停止

自己的strcpy

5分

#include<stdio.h>
void my_strcpy(char* dest, char* src)

while (*src != \\0)

*dest = *src;
src++;
dest++;

*dest = *src;//0

int main()

char arr1[] = "#########";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;


6分

#include<stdio.h>
void my_strcpy(char* dest, char* src)

while (*dest++ = *src++)

;



int main()

char arr1[] = "#########";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;


7分

#include<stdio.h>

void my_strcpy(char* dest, char* src)

if (dest != NULL && src != NULL)

while (*dest++ = *src++)

;




int main()

char arr1[] = "#########";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;

防止输入为NULL的时候,整个程序崩盘;但这种更改方式也没有进行拷贝,而且没提示;


8分

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, char* src)

assert(dest != NULL);//断言
assert(src != NULL);
while (*dest++ = *src++)

;



int main()

char arr1[] = "#########";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;

通过assert判断,并且及时报错

但是,当程序员写反时*src++=*dest++,依然有可能运行成功(只要arr1<arr2)

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, char* src)

assert(dest != NULL);
assert(src != NULL);
while (*src++=*dest++)

;



int main()

char arr1[] = "#####";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;

【C语言有这个就够了】七.实用调试技巧_调试_13

9分

#include<stdio.h>
#include<assert.h>
void my_strcpy(char* dest, const char* src)//加上const,反了会报错。

assert(dest != NULL);
assert(src != NULL);
while (*dest++ = *src++)

;



int main()

char arr1[] = "#####";
char arr2[] = "ajfhk";
my_strcpy(arr1, arr2);
printf("%s\\n", arr1);
return 0;

const使变量不能改变,注意

#include<stdio.h>
int main()

const int num = 10;
int* p = &num;
*p = 20;
printf("%d\\n", num);
return 0;

这里p改变了一个我们主观上不想改变的值,

#include<stdio.h>
int main()

const int num = 10;
int n=100;
const int* p = &num;//即使写错了,也能报错
//const放在*左边时,修饰*p,也就是说:不能通过p来改变*p(num)的值
*p = 20;
printf("%d\\n", num);
return 0;
#include<stdio.h>
int main()

const int num = 10;
int*const p = &num;
//const放在*右边时,修饰p,也就是说:不能改变p的值
*p = 20;
int n=10;
p=&n;
printf("%d\\n", num);
return 0;


10分

#include<stdio.h>
#include<assert.h>
char*my_strcpy(char* dest, const char* src)//加上const,反了会报错。

char*ret=dest;
assert(dest != NULL);
assert(src != NULL);
while (*dest++ = *src++)//把src指向的字符串拷贝到dest指向的字符串,包含\\0

;

return ret;


int main()

char arr1[] = "#####";
char arr2[] = "ajfhk";
printf("%s\\n",my_strcpy(arr1, arr2));//返回值时另一个函数的参数
return 0;

4.案例模拟实现strlen

#include<stdio.h>
#include<assert.h>

int my_strlen(const char* str)//*str指向的内容不会被改变,但是指针的位置可以改变

int count = 0;
assert(str != NULL);
while (* str != \\0)

count++;
str++;

return count;


int main()

char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\\n", len);
return 0;

​(七)编程常见的错误

1.编译型错误

直接看错误提示信息(双击),解决问题。或者凭借经验就可以搞定。相对来说简单。

2.链接型错误

看错误提示信息,主要在代码中找到错误信息中的标识符,然后定位问题所在。一般是标识符名不

存在或者拼写错误。

3.运行时错误

借助调试,逐步定位问题,最难搞。



以上是关于C语言中逗号表达式的实用意义或者产生的原因?的主要内容,如果未能解决你的问题,请参考以下文章

C语言 逗号表达式

c语言 逗号表达式

C语言逗号表达式

C语言逗号表达式问题?

c语言 求解逗号表达式的值

“算术表达式”是不是允许在 C 中用逗号分隔,或者我们需要为每个单独的语句?