文章目录

  • 一、指针与数组
  • 二、非数组指针
  • 三、作为参数的数组声明
  • 四、避免“举隅法”
  • 五、空指针并非空字符串
  • 六、边界计算与不对称边界
  • 七、求值顺序
  • 八、运算符&& || 和!
  • 九、整数溢出
  • 十、为函数main提供返回值

一、指针与数组

C语言中数组值得注意的地方有以下两点:
1.C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。然而,C语言中数组的元素可以是任何类型的对象,当然也可以是一个数组。这样,”仿真“出一个多维数组就不是一件难事。
(注:C99标准允许变长数组(VLA)。GCC编译器中实现了变长数组,但细节与C99标准不完全一致。)
2.对于一个数组,我们只能够做两件事:确定数组大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,实际上都是通过指针进行的。

给指针加上一个整数,如p+1,则p指向下一内存中的数据,而给指针的二进制表示(指针指向的地址)加上同样的整数,实际上是将p指向的地址+1,效果不一样。

如果两个指针指向的是同一个数组中的元素,我们可以把这两个指针相减。若两个指针指向的是不同数组中的元素,即使它们指向的地址在内存中位置正好间隔一个数组元素的整数倍,所得的结果仍然无法保证其正确性。

数组命除了被用作sizeof的参数这一情况外,其他所有情况下数组命都代表指向数组a中下标为0的元素的指针。

二维数组遍历代码如下:

  int i[12][31];int (*p)[31];for (p = i; p < &i[12]; p++)
{int *dp;for(dp = *p; dp < &(*p)[31]; dp++){*dp = 0;}
}

二、非数组指针

在使用内存分配函数(malloc)的时候,需要注意的是,如果分配字符串空间,一定要注意’\0’字符,该字符在使用strlen函数求字符串长度的时候会被忽略。使用malloc函数对应的内存用完要使用free函数释放内存。
示例代码如下:

  char *r;char s[] = "Hello";char t[] = "World!";r = malloc(strlen(s) + strlen(t) + 1);if (r != NULL)
{strcpy(r, s);strcat(r, t);
}printf("%s\n", r );free(r);

三、作为参数的数组声明

C语言会自动将作为参数的数组声明转换为相应的指针声明。
例子1:

int strlen(char s[])
{}

与下面的写法完全相同

int strlen(char* s)
{}

指针并不是所有情况下都指向数组首地址。
例子2:

extern char* hello;

extern char hello[];

前者只声明了一个字符型指针,而后者声明了字符数组,二者代表的概念不一样。

四、避免“举隅法”

”举隅法“是文学修辞上的手段,以隐喻表示代指物与被指物的相互关系。《牛津英语词典》对”举隅法“(synecdoche)的解释是:以含义更宽泛的词语来代替含义相对较窄的词语,或者相反。

C语言中的一个常见陷阱:混淆指针与指针所指向的数据。
复制指针并不同时复制指针所指向的数据,它们指向的空间相同,而两个数组才指向的是两块不同的空间。

五、空指针并非空字符串

C语言中将一个整数转换为一个指针,最后得到的结果取决于具体的C编译器实现。存在一个特殊情况0,编译器保证由0转换而来的指针不等于任何有效指针。不能使用空指针所指向的内存中存储的内容。

    char *p;
//    p = (char*)3;
//    p = NULL;p = (char*)0;printf("%s\n", p);  //未定义行为
//    printf("%d\n", p);    //打印出具体数字

六、边界计算与不对称边界

此处存在”栏杆错误“,也常被称为”差一错误“(off-by-one error)
避免”差一错误“的两个通用原则:
1.首先考虑最简单情况下的特例,然后将得到的结果外推
2.仔细计算边界,绝不掉以轻心

例子1:

static char buffer[N];
static char *bufptr;
char* clearbuffer(char* source);
void bufferwrite(char* source, int len)
{char* data = source;while (len > 0){//        if (N - (&buffer[N] - bufptr) < N)
//        {//            *bufptr++ = *data++;
//            len--;
//        }
//        else
//        {//           bufptr = clearbuffer(buffer);
//        }if (bufptr == &buffer[N]){int l = sizeof(buffer);bufptr = clearbuffer(buffer);}else{int k, rem;rem = N - (bufptr - buffer);k = len > rem? rem:len;memcpy(bufptr, data, k);bufptr += k;data += k;len -= k;}}
}
char* clearbuffer(char* source)
{memset(source, 0, N);return source;
}

七、求值顺序

C语言中只有”&&“、”||“、”? :“、”,“四个运算符,存在规定的求值顺序。
”&&“运算符和”||“运算符首先对左操作数求值,只在需要时才对右操作数求值。

运算符”? :“有三个操作数:在a?b:c中,操作数a首先被求值,根据a的值再求操作数b或者操作数c的值。

逗号操作符首先对左操作符求值,然后该值被”丢弃“,再对右操作数求值。
(注:分隔函数参数的逗号并非逗号运算符。例如,x和y在函数f(x,y)中的求值顺序是未定义的,而在函数g((x,y))中是确定的,先求x后求y。在后一个例子中,函数g只有一个参数,这个参数的值是这样求到的,先求x的值,然后x的值被”抛弃“,接着求y的值。)

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

八、运算符&& || 和!

C语言中有两类逻辑运算符,某些时候可以互换:按位运算符& |和~。以及逻辑运算符&& ||和!。
逻辑运算符&&和||在左侧操作数的值能够确定最终结果时根本不会对右侧操作数求值。运算符&左右两边的操作数都必须被求值。

九、整数溢出

C语言中存在两类整数运算,有符号运算与无符号运算。在无符号运算中,没有所谓的”溢出“一说。所有无符号运算都是以2的n次方为模,这里n是结果中的位数。如果超出表示范围,则从0开始继续运算。
如果算术运算符的一个操作数是有符号整数,另一个操作数是无符号整数,那么有符号整数会被转换成无符号整数,”溢出“也不可能发生。
当两个操作数都是有符号整数时,”溢出“就有可能发生,而且”溢出“的结果是未定义的。

十、为函数main提供返回值

函数如果未显示声明返回类型,那么函数返回类型的默认类型是整型。
main函数的返回值在不使用的情况下无关紧要,如果返回值表示函数是否执行成功,则需要明确具体的返回值,不能不写返回值,否则有可能系统会判断函数执行失败。

《C陷阱与缺陷》——第三章(语义陷阱)相关推荐

  1. C陷阱与缺陷 第3章 语义“陷阱” 3.4 避免“举偶法”

    避免"举偶法"     "举偶法"(synecdoche)是一种文学修辞上的手段,有点类似于以微笑表示喜悦.赞许之情,或以隐喻表示指代物与被指代物的相互关系.在 ...

  2. 《C陷阱与缺陷》----第三章 语义陷阱

    第三章. 语义陷阱 3.1 指针与数组 3.2 非数组的指针 3.3 作为参数的数组声明 3.4 空指针并非空字符串 3.5 边界计算与不对称边界 3.6 求值顺序 3.9 整数溢出 3.10 为函数 ...

  3. c陷阱与缺陷第三章——Semantic Pitfalls

    c陷阱与缺陷第三章--Semantic Pitfalls 3.1 Pointers and array Array a[i] == i[a] Array subscripting Constrains ...

  4. C陷阱与缺陷--读书笔记3 语义“陷阱”

    第三章 一.知识点 1.C语言中的数组值得注意的地方有以下两点:(P41) (1).C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来.然而,C语言中数组的元素可以是任何类型的对象 ...

  5. 《C陷阱与缺陷》----第二章 语法陷阱

    第二章 语法陷阱 2.1 理解函数声明 2.1.1 如何理解函数声明 2.1.2 举例理解声明 2.1.2.1 例子1 2.1.2.2 例子2 2.2 运算符的优先级 2.2.1 常见错例 2.2.1 ...

  6. C陷阱与缺陷 第2章 语法“陷阱” 2.6 “悬挂”else引发的问题

    "悬挂"else引发的问题     if (x == 0)      if (y == 0) error();     else {         z = x + y;     ...

  7. 《C陷阱与缺陷》第一章【词法“陷阱”】

    前言: 先在这里和关注我的小伙伴们说一声对不起,因为我已经连续三天没更新文章了.是因为学校的线上课程结束了,线下几乎每一天都是满课,写博客的时间少了很多,不过我会在五一假期期间尽量把之前的补回来. 这 ...

  8. 读书笔记--C陷阱与缺陷(三)

    第三章 1. 指针与数组 书中强调C中数组注意的两点: 1)     C语言只有一维数组,但是数组元素可以是任何类型对象,是另外一个数组时就产生了二维数组.数组大小是常数(但GCC实现了变长数组..) ...

  9. c缺陷与陷阱 第3章 语义陷阱

    1. 指针与数组 c语言中的数组要注意以下两点: 下面的方法存在问题,因为数组的大小必须在编译期作为一个常数确定下来. a[3][4] 本质也是一个一维数组(包含3个元素),只不过一维数组中的元素是也 ...

  10. 《C语言陷阱与缺陷》第二章【语法陷阱】上

    目录 ✊ 一.理解函数声明 ☝️ 二.运算符的优先级问题 ✌️ 三.注意作为语句结束标志的分号 ✊ 一.理解函数声明 先来看下面这条语句: (* (void(*) () ) 0 ) (); 这样的表达 ...

最新文章

  1. 【发现】ASP.NET DEVELOPMENT SERVER 未能开始侦听端口xxxxx以一种访问权限不允许的方式做了一个访问套接字的尝试——解决方法...
  2. python猜拳游戏三局两胜制_猜拳游戏三局两胜------java实现代码
  3. 百分点宣布完成C轮融资2500万美元 将进一步开放云平台应用
  4. Linux 系统备份恢复工具 SYSTEM-RESCUE-CD 的使用
  5. 2020年CNCF和开源项目开发速度
  6. System中不存在类型或命名空间名称Transactions
  7. 1×pbs缓冲液配方_【应用】蒸渗仪与氧化还原电位的测量1
  8. 13. 查看网络端口、配置网络
  9. HDL4SE:软件工程师学习Verilog语言(四)
  10. HTML5期末大作业:5G网络网页设计——3页(代码质量好) 学生DW网页设计作业源码 web课程设计网页规划与设计
  11. 计算机内存的存储原理,存储器原理
  12. 学习笔记之——基于深度学习的分类网络
  13. python生词本查单词译文_Kindle 阅读器“生词本”功能详细使用说明
  14. 数据分析和挖掘常用方法
  15. 数据库驱动加载失败问题
  16. 新华三智慧计算联盟,集结!
  17. Python,江湖救急
  18. 银行普惠金融可持续发展能力建设——风控科技应用
  19. 写出Java的第一个程序
  20. 江南百景图显示服务器错误,江南百景图通讯失败请保持网络畅通并重试

热门文章

  1. 嵌入式软件开发工程师面试指南_总结
  2. 谈网站域名,备案到上线
  3. 对未来计算机网络的展望作文300,2017年关于网络的300字作文篇我与网络
  4. 乐心健康QQ微信支付宝刷步网页版源码
  5. 转——解决游戏王DL的steam版锁区问题
  6. clistctrl获取选中行数据_自动化报表——课程3:数据清洗
  7. python多线程下载视频_python 实现多线程下载m3u8格式视频并使用fmmpeg合并
  8. 最佳实践|用腾讯云AI图像搜索打造属于自己的拍立淘
  9. 【180928】中国象棋游戏源码
  10. rel=nofollow 是什么意思