C陷阱与缺陷——第3章 语义陷阱

1. 指针和数组

C语言中只有一维数组,而且数组的大小必须在编译器就作为一个常数确定下来,然而在C语言中数组的元素可以是任何类型的对象,当然也可以是另外的一个数组,这样,要仿真出一个多维数组就不是难事。

对于一个数组,我们只能够做两件事:确定数组大小;获得指向该数组下标为0的元素的指针。

int calendar[12][31];

以上语句声明了calendar是一个数组,该数组拥有12个数组类型的元素,其中每个元素都是一个拥有31个整型元素的数组。

如果calendar不是用于sizeof的操作数,那么calendar总是被转换成一个指向calnedar数组的起始元素的指针。

任何指针都是指向某种类型的变量。

给一个指针加上一个整数,与给该指针的二进制表示加上同样的整数,两者的含义截然不同。

把两个指针相减也是有意义的,但是这两个指针必须指向同类型的变量,否则结果未定义。

当p是int指针,a是int一维数组名时,以下写法正确

p = a;

以下写法错误:

p = &a;

因为&a是一个指向数组的指针,而p是一个指向整型变量的指针。*a是数组a中下标为0的元素的引用。*(a+i)即数组a中下标为i的元素的引用,简记为a[i]。多数情况下i[a]和a[i]的意义相同,但是不推荐使用前面的写法。

calendar[4]是calendar数组的第5个元素,calendar[4]的行为表现为一个有着31个整型元素的数组的行为。

声明指向数组的指针的方法,举例如下:

int calendar[12][31];
int (*monthp)[31];
monthp = calendar;

2. 非数组的指针

假设我们用两个字符串s和t,我们希望将这两个字符串连接成单个字符串r,借助库函数strcpy和strcat正确写法如下:

char *r, *malloc();//声明malloc原型,这样后面就不用再写转换成char*类型了
r = malloc(strlen(s) + strlen(t) + 1); //字符串结尾为'\0',strlen不用加上这个计数1
if (!r)//当malloc无法完成内存分配时,会返回NULL
{complain();//报错exit(1);//退出
}
strcpy(r, s);
strcpy(r, t);//使用一段时间后free(r); //对动态分配内存程序员负责回收

主要注意到点是:

  • 字符串以空字符'\0'作为结束标志,库函数strlen返回参数中字符串所包含的字符数组,而结尾标志的空字符并未计算在内;
  • malloc函数有可能无法提供请求的内存,这种情况malloc函数通过返回一个空指针来作为“内存分配失败”事件的信号
  • malloc分配的内存使用完后应该及时释放,否则会导致内存泄露

3. 作为参数的数组声明

C语言中会自动地将作为参数的数组声明转换为相应的指针声明,也就是说下面两种写法完全等价

int strlen(char s[])
{}
int strlen(char* s)
{}

但是需要注意的是,在其他情况下这两者并不会等价,如

extern char* hello;
extern char hello[];

4. 注意整体代替部分的错误

指针的赋值并不会复制它们指向的内容,因此如下语句

char *p, *q;
p = "xyz";
q = p;

的结果如下:

5. 空指针并非空字符串

当常数0被转换成指针使用时,这个指针绝对不能不能被解除引用dereference

if (p == (char *) 0)...

以上写法正确,因为没有解除引用

if (strcmp(p, (char *) 0) == 0)...

以上写法错误,因为strcmp会查看指针所指向内存的内容

同样以下写法也是错误的:

int *p = NULL;
printf(p);
printf("%s", p);

6. 边界计算与不对称边界

如果一个数组有10个元素,那么这个数组下标的允许范围是0-9

在多数C语言实现中,--n >= 0至少和n-->0一样快

可以用

if (bufptr == &buffer[N])

代替

if (bufptr > &buffer[N - 1])

数组中实际不存在的溢界元素的地址位于数组所占内存之后,这个地址可以用于进行赋值和比较。但不允许进行解引用。

7. 求值顺序

C语言中只有4个运算符(&&、||、?:和,)存在规定的求值顺序:

  • &&和||首先对左侧操作数求值,只有在需要时才对右侧操作数求值
  • a?b:c有三个操作数,操作数a首先被求值,根据a的值再求b或者c
  • 逗号运算符,首先对左侧操作数求值,然后丢弃该值,再对右侧操作数求值

注意:分隔函数参数的逗号并非逗号运算符,例如f(x,y)中求值顺序顺序是未定义的,而在g((x,y))中先求x的值,然后求y的值。

C语言中其他所有运算符对其操作数求值的顺序是未定义的,特别地,赋值运算符并不保证任何求值顺序。

比如,从数组x中复制前n个元素到数组y中,以下做法是不对的:

i = 0;
while(i < n)y[i] = x[i++];

因为这里假设y[i]的地址i在自增操作前执行前被求值

正确的做法是:

i = 0;
while(i < n)
{y[i] = x[i];i++;
}

 8. 运算符&&、||和!和按位运算符&、|、~

按位运算符&、|、~对操作数的处理方式是将其视作一个二进制位序列,分别对其每个位进行操作。

逻辑运算符&&、||和!对操作数的处理方式是将其视作要么是真,要么是假,通常将0视为假,而非0视作真。

9. 整数溢出

当两个操作数都是有符号整数时,溢出有可能发生,而且溢出的结果是未定义的。

例如a和b是两个非负整型变量,我们需要检查a+b是否会溢出,以下的写法是错误的:

if (a + b < 0)complain();

正确的写法是将a和b都强制转换成无符号整数:

if ((unsigned)a + (unsigned)b > INT_MAX)complain();

也可以使用以下写法:

if (a > INT_MAX -b)complain();

10. 为函数main提供返回值

大多数C语言实现都通过main的返回值来告知操作系统该函数执行是成功还是失败,典型的处理方式是返回值0表示程序执行成功,返回值非0表示程序执行失败。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/234082.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【10】Python函数专题(上)

目录 1.什么是函数2.函数的参数2.1形参 和 实参2.2函数的传递方式2.3 不定长参数2.3.1 可变位置参数`*args`2.3.2可变关键字参数`**kwargs`2.3.3 小结2.4 参数的解包(也称拆包)1.什么是函数 在Python中,函数是一段可重复使用的代码块,用于执行特定任务。通过将代码封装在函…

Hive:从HDFS回收站恢复被删的表

场景 一张手工维护的内部表&#xff0c;本来排查没有使用&#xff0c;然后删掉了&#xff0c;发现又需要使用&#xff0c;只能恢复这张表了。 1.确认HDFS是否开启回收站功能 2.查看回收站中的数据 被删除的数据会放在删除数据时使用的用户目录下&#xff0c;如&#xff1a;使…

2023年掌控安全学院CTF暖冬杯——数据流分析

前言&#xff1a;打工仔一枚&#xff0c;第一波上新的3题misc 做完了 再打ISCTF随便记录一下 PS&#xff1a;环境关了&#xff0c;题目描述忘记了&#xff0c;反正就是找flag。 筛选HTTP数据流 导出数据流慢慢看 ctrl F 搜flag 看到一条 有flag.txt 的数据 导出另存.zip 这里…

Git提示 Connection closed by remote host

问题如下图&#xff1a; 解决&#xff1a; 删除./ssh目录下的config文件&#xff0c;如下图config文件是新增的 原因&#xff1a;不知道什么原因&#xff0c;连接外网后突然断开或导致自动增加config文件。

FPGA驱动CS4344 VHDL例程

CS4344是一款非常简单的I2S立体声24bit D/A芯片&#xff0c;采样率高达192KHz&#xff0c;相对于ADAU1761复杂的寄存器配置来说&#xff0c;CS4344非常友好&#xff0c;无需配置寄存器&#xff0c;只要按I2S时序输入数据&#xff0c;即可实现立体声输出&#xff0c;且10PIN TSS…

数据挖掘实战-基于word2vec的短文本情感分析(文末送书)

&#x1f935;‍♂️ 个人主页&#xff1a;艾派森的个人主页 ✍&#x1f3fb;作者简介&#xff1a;Python学习者 &#x1f40b; 希望大家多多支持&#xff0c;我们一起进步&#xff01;&#x1f604; 如果文章对你有帮助的话&#xff0c; 欢迎评论 &#x1f4ac;点赞&#x1f4…

12月7-8日泰国曼谷,Flat Ads与你相约Affilliate World Asia

12月7-8日,Flat Ads将参加在泰国曼谷举办的Affiliate World Asia Conference,与众多行业人士共话全球流量领域新洞察,探讨行业现状与未来趋势。 据悉,Affiliate World Asia(以下简称AWA)是全球瞩目的移动互联网联盟超级盛会,也是亚洲区域内最大规模的互联网流量大会。这一展会为…

面试题:说一下MyBatis动态代理原理?

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 1.MyBatis简介2.使用步骤2.1、引入依赖2.2、配置文件2.3、接口定义2.4、加载执行 3.原理解析 1.MyBatis简介 MyBatis是一个ORM工具&#xff0c;封装了JDBC的操作&a…

机器学习入门(第四天)——朴素贝叶斯

知识树 Knowledge tree P(y|x)&#xff0c;P给定x的条件下&#xff0c;y的概率。如&#xff1a;P(y我招女孩子喜欢的概率|我是学生) 一个小故事 A story 女朋友和妈妈掉河里&#xff0c;路人拿出3颗豆&#xff0c;两颗红豆1颗绿豆。如果我抽中红豆救女朋友&#xff0c;抽中绿…

安防监控系统的工作原理是什么?具体包含哪些组成部分?

关于安防监控系统&#xff0c;大家熟知的就是监控系统平台&#xff0c;其实不然&#xff0c;智能视频安防监控系统涵盖的内容非常多&#xff0c;今天小编就和大家一起来探讨一下。 安防监控视频系统主要分为以下7大类&#xff1a; 1、 摄像头采集图像 安防监控系统通常使用摄…

python 交互模式和命令行模式的问题

python 模式的冲突 unexpected character after line continuation character 理论上 ide里&#xff0c;输入 python 文件路径\文件.py 就可以执行 但是有时候却报错 unexpected character after line continuation character 出现上述错误的原因是没有退出解释器&#x…

采购业务中的组织概述

目录 一、采购和库存管理中组织单位的概览二、企业的组织结构三、采购中组织结构3.1采购组织3.2采购组 一、采购和库存管理中组织单位的概览 1、 客户端&#xff1a;在SAP ERP系统中&#xff0c;客户端通过三位数字定义&#xff0c;并代表这独立的数据记录和独立的业务流程。客…