当前位置:网站首页>C专家编程 第2章 这不是Bug,而是语言特性 2.4 少做之过
C专家编程 第2章 这不是Bug,而是语言特性 2.4 少做之过
2022-08-03 16:09:00 【weixin_客子光阴】
/*少做之过的特性就是语言应该提供但未能提供的特性,
*如标准参数处理以及把lint程序错误从编译器中分离出来
*/
/*2.4.1 用户名中若有字母f,便不能收到邮件*/
/*用户名的第二个字母是f,邮件确实无法发送到他们那里*/
/*许多人对ANSI C采用argc、argv的约定向C程序传递参数感到惊奇,但事实就是如此。
*UNIX的约定有有所提升,达到了一个标准的层次,但此时却成了这个邮件Bug的原因之一
*/
/*分析方法像试探法*/
//5.
if (argv[argc - 1][0] == '-' || (argv[argc - 2][1] == 'f')) {
readmail(argc, argv);
} else {
sendmail(argc, argv);
}
/*能正确发送邮件*/
mail -h -d -f -/usr/linden/mymailbox
/*读取邮件,而不是发送邮件*/
mail effie Robert
/*修改方案*/
if (argv[argc - 1][0] == '-' || argv[argc - 2][0] == '-' && (argv[argc - 2][1] == 'f')) {
readmail(argc, argv);
} else {
sendmail(argc, argv);
}
/*许多操作系统(如VAX/VMS)能够在程序中区分运行时选项和其他参数(如文件名),
*但UNIX却不能,ANSI C也不能 */
//6.
/*软件信条*/
/*Shell参数解析*/
/*不充分的参数解析问题出现在UNIX的许多地方。找出目录中的那些文件是
*链接文件,你可能输入下面的命令
*/
ls -l | grep ->
/*缺少重定向的名字,->被shell翻译成重定向符*/
ls -l | grep "->"
/*grep先看到减号,然后把整个参数翻译成大于号的一种未知组合形式,然后退出
*,要解决问题,必须放弃使用ls命令*/
file -h * | grep link
/*创建一个文件,文件名以连字符开头,然后去发现无法用rm命令把连字符去掉。
*一种解决方法是给出文件的完整路径名,这样rm就不会把连字符当做选项开关
*并依次翻译文件名*/
/*有些C程序员采用了一种约定,即带“--”的参数表示“从这里开始,没有参数是
*选项开关,即使它是以连字符开头”
*一种更好的解决方法是把包袱扔给系统而不是用户,使用参数处理器把参数分成选项
*开关和非选项开关两种。目前这种简单的argv机制由于使用得太广,因而不可能对它
*作任何修改。
*/
/*2.4.2 空格---最后的领域*/
/*这里的几个例子,空格从根本上改变了程序的意思或程序的有效性
*“\”字符用于对一些字符进行“转义”,包括newline(这里指回车键)。
*被转义的newline在逻辑上把下一行当做当前行的延续,它可用于连接
*长字符串。如果在“\”和回车键之间不小心留上一两个空格就会出现问题,
*\ newling \newling 效果是不一样的。
*因为你是在寻找无形的东西(在应该是newline的地方出现了一个空格
*,注意newline并不是一个有形的字符,所以“\”后面有没有空格
*在实际代码中根本看不出来)。newline在典型情况下用于转义连续多行
*的宏定义。转义newline的另一种用处是延续一个字符串常量,如下:
*/
//7.
char a[] = "Hi! How are you? I am quite a \
long string, folded onto 2 lines";
/*这种多行字符串常量的问题被ANSI C通过引入相邻字符串常量自动连接的约定得以解决*/
//8.
/*如果所有的空格都弃之不用,也会陷入麻烦*/
z = y+++x;
//correct
z = y++ + x;
//error
z = y+ ++x
/*ANSI C规定了一种逐渐为人熟知的“maximal munch strategy”(最大一口策略)
*这种策略表示,如果下一个标记有超过一种的解释方案,编译器将选取能组成最长
*字符序列的方案。
*/
z = y+++++x;
//唯一有效的编排方式是:
z = y++ + ++x;
//它还是会出现编译错误。
///*有两个指向int的指针并想对int数据进行除法运算时,代码如下
// *除法运算符“/”与“*”操作符之间缺少空格。它们紧贴在一起,
// *被编译器理解成注释的开始部分,并把它与下一个“*/”之间的所有
// *代码都变成注释的内容。
// */
//9.
int ratio = *x / *y;
//int ratio = *x/*y;
/*打算结束注释时却由于意外未能结束*/
int hashval = 0;
/*PJW hash function from "Compilers: Principles, Techniques, and Tools"
*by Aho, Sethi, and Ullman, Second Edition.(*/)
while (cp < bound) {
unsigned long overflow;
hashval = (hashval << 4) + *cp++;
if ((overflow = hashval & (((unsigned long)OxF) << 28)) != 0) {
hashval ^= overflow | (overflow >> 24);
}
hashval %= ST_HASHSIZE; /*选择起始桶*/
/* 搜索每个表,这次搜索名字。如果失败,保存该字符串
* 进入字符串的指针,然后返回它
*/
for (hp = &st_ihash; ;hp = hp->st_hnext) {
int probeval = hashval; /*下一个探测值*/
}
}
/*2.4.3 c++的另一种注释形式//
*把该符号以后直至行末的内容均作为注释内容。
*/
a //*
//*/ b
/*上面的代码在C语言中表示a/b,但在C++语言中表示a。C风格的注释在C++语言中依然有效*/
/*2.4.4 编译器日期被破坏*/
/* 将源文件的timestamp转换为表示当地格式日期的字符串
* 调用stat()得到UNIX格式的源文件修正时间
* 调用localtime()将其转换成tm结构
* 最后调用strftime()函数,把tm结构转换成以当地日期
* 格式表示的ASCII字符串
*/
/*症状就是表示日期的字符串被破坏*/
#include <stdio.h>
#include <time.h>
char *localized_time(char *filename) {
struct tm *tm_ptr;
struct stat stat_block;
char buffer[120];
/*获得源文件的timestamp,格式为time_t*/
stat(filename, &stat_block);
/*把UNIX的time_t转换为tm结构,里面保存当地时间*/
tm_ptr = localtime(&stat_block.st_mtime);
/*把tm结构转换为以当地日期格式表示的字符串*/
strftime(buffer, sizeof(buffer), "%a %b %e %T %Y", tm_ptr);
return buffer; //program takes place
}
int main() {
char *p = localized_time("test.txt");
printf("date = %s\n", p);
return 0;
}
/*buffer是一个自动分配内存的数组,是该函数的局部变量。当控制流离开
*声明自动变量(即局部变量)的范围时,由于该变量已被销毁,谁也不知道
*指针所指向的地址的内容是什么
*在C语言中,自动变量是在堆栈中分配内存。当包含自动变量的函数或代码退出
*时,它们所占用的内存便被回收,它们的内容肯定会被下一个所调用的函数覆盖
*。这一切取决于堆栈中先前的自动变量位于何处,活动函数声明了什么变量,
*以及写入了什么内容。原先的变量地址的内容既可能被立即覆盖,也可能稍后
*才能覆盖,这就是日期破坏问题难以发现的原因。
*/
//1.返回一个指向字符串常量的指针
char *func() {
return "Only works for simple strings";
/*只适用于简单的字符串*/
}
/*如果字符串常量存储于只读内存区但以后需要改写它时,你也会有麻烦*/
//2.使用全局声明的数组
char *func() {
...
my_global_array[i] =
...
return my_global_array;
}
/*它的缺点在于任何人都有可能在任何时候修改这个全局数组,而且该函数的下一次调用
*也会覆盖该数组的内容。
*/
//3.使用静态数组
char *func() {
static char buffer[20];
...
return buffer;
}
/*这就可以防止任何人修改这个数组。只有拥有指向该数组的指针的函数(通过参数传递给它)
*才能修改这个静态数组。但是,该函数的下一次调用将覆盖这个数组的内容,所以调用者必须
*在此之前使用或备份数组的内容。与全局数组一样,大型缓冲区如果闲置不用,是非常浪费
*内存空间的。
*/
//4.显式分配一些内存,保存返回的值。
char *func() {
char *s = malloc(120);
...
return s;
}
/*这个方法具有静态数组的优点,而且在每次调用时都创建一个新的缓冲区,所以该函数以后的
*调用不会覆盖以前的返回值。它适用于多线程的代码(在某一时刻具有一个以上的活动线程的
*程序)。它的缺点在于程序员必须承担内存管理的责任。
*/
//5.也许最好的解决方法就是要求调用者分配内存来保存函数的返回值。为了提高安全性,
//调用者应该通知指定缓冲区的大小(就像标准库中fgets()函数所要求的那样)
void func(char *result, int size) {
...
strncpy(result, "That's be in the data segment, Bob", size);
}
buffer = (char*)malloc(size);
func(buffer, size);
...
free(buffer);
/*如果程序员可以在同一代码块同时进行malloc和free操作,内存管理是最为轻松的。
*这个解决方案可以实现这一点
*/
return local_array;
/*return local_array
*"function returns pointer to automatic"(函数返回一个指向自动变量的指针)。
*/
/*2.4.5 lint程序不应该被分离出来*/
/*lint程序能够检测到问题,并向你发出警告
*把编译器中所有的语义检查措施都分离出来。错误检查有一个单独的程序完成,这个
*程序被称为lint。这样编译器可以做得更小、更快而且更简单。
*/
/*小启发*/
/*早用lint程序,勤用lint程序*/
/*lint程序是软件的道德准则,当你做错事,他会告诉你那里不对。
*应该始终使用lint程序,按照它的道德标准办事。
*/
/*source base 源代码基础
*the lint merge from hell(地狱般的lint考验)
*/
/*几个真正严重的bug
*实参的类型在函数和调用之间发生了转变
*一个期望接受3个参数的函数实际上只传递它一个参数,该函数从堆栈中再
*抓两个参数。
*变量在设置(初始化或赋值)前使用
*/
/*经验不断证明,把lint程序作为一个独立的工具通常意味着把lint程序束之高阁。*/
/*2.5 轻松一下---有些特性确实就是Bug */
/*当然,任何人都知道从相对论的角度讲,信息也是有质量的*/
边栏推荐
猜你喜欢
我写了个”不贪吃蛇“小游戏
Small Tools(4) 整合Seata1.5.2分布式事务
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 02
AI+BI+可视化,Sugar BI架构深度剖析
如何使用MATLAB绘制极坐标堆叠柱状图
【Unity入门计划】基本概念(8)-瓦片地图 TileMap 01
【带你了解SDN和网络虚拟化】
How to get the 2 d space prior to ViT?UMA & Hong Kong institute of technology & ali SP - ViT, study for visual Transformer 2 d space prior knowledge!.
可复现、开放科研、跨学科合作:数据驱动下的科研趋势及应用方案
To add digital wings to education, NetEase Yunxin released the overall solution of "Internet + Education"
随机推荐
【There is no tracking information for the current branch. Please specify which branch you want to 】
Leetcode76. 最小覆盖子串
Analysis of ffplay video playback principle
为什么我强烈推荐使用智能化async?
leetcode:187. 重复的DNA序列
spark入门学习-2
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 02
如何启动 NFT 集合
【系统学习编程-编程入门-全民编程 视频教程】
Cookie和Session的关系
Leetcode76. Minimal Covering Substring
unity用代码生成LightProbeGroup
Kubernetes 笔记 / 目录
MySQL窗口函数
【Unity入门计划】制作RubyAdventure01-玩家的创建&移动
Not to be ignored!Features and advantages of outdoor LED display
C语言02、语句、函数
袁小林:沃尔沃专注于出行的安全感,并且把它做到极致
如何设计大电流九线导电滑环
[Unity Getting Started Plan] Basic Concepts (8) - Tile Map TileMap 01