Friday, 30 September 2016

STL的erase()陷阱-迭代器失效总结

今天调试代码,定位一个compiler 死循环问题。 最终定位出来的原因给作者在下面这篇文章中总结的基本一样。需要指出的是这是程序员及其容易犯的一个错误。忘记将std::list的成员函数erase 返回给iter 本身。这样导致iter 成为类似野指针(虽然iter 不是指针,但是本质上是包裹起来的指针),行为将变得undefined。 如果在某些平台上,比如Linux,操作系统没有立即回收内存,有可能该问题本隐藏起来。但是在其他立即回收内存(或硬删除而不是移动到缓存)的平台上,则会造成死循环。

下面是问题代码,及修复代码:


//这里假设上下文已经设置好了myList (有了元素)
// str 是作为函数入参数传进来, const char * 类型

原代码 (错误使用方法3,相对于下面链接中的两种错误方法) :
 std::list<const char *>::iterator myList ;
  for(std::list<const char *>::iterator I = myList.begin(), E = myList.end(); I != E; ++I) {
         if (!strcmp(*I, str)) {
            _myList.erase(I);
            --I;
          }
 }

修复代码 (正确使用方法3,相对于下面链接中的两种正确方法):
 std::list<const char *>::iterator myList ;
  for(std::list<const char *>::iterator I = myList.begin(), E = myList.end(); I != E; ++I) {
         if (!strcmp(*I, str)) {
            I = _myList.erase(I);
            --I;
            }
  }
// comment : erase 返回指向删除元素下一个位置元素的指针,先-- 在++,仍正确指向
// 删除元素的下一个位置,可以正常工作。


另外两种错误及正确方法,见下面的链接。 个人比较喜欢正确方法三,虽然需要强制--,再++,但是毕竟避免了把++操作从for () 语句中移动到句外的情况。
总而言之,凡是让程序员记着干某些事情的设计都不是好设计。因为他们总是忘记。 比如这里的让他们记得把erase()返回值付给iter 本身,以及记得new 了以后一定要delete。正如用auto_ptr 或Java 自动垃圾回收方法可以解决到处泛滥的内存泄露问题。或许我们也应该设计一种方法,让erase() 接口不用强求程序员记着赋值给iter。 强制给出void,并让iter 自动指向下个元素也不是一个好方法。程序员使用不当的话,有可能导致跳过元素。笔者没有什么好的idea。如果各位路过的大神,有什么好的想法,欢迎留言讨论。


http://blog.csdn.net/lanbing510/article/details/8796048

下面材料整理自Internet&著作。
STL中的容器按存储方式分为两类,一类是按以数组形式存储的容器(如:vector 、deque);另一类是以不连续的节点形式存储的容器(如:list、set、map)。在使用erase方法来删除元素时,需要注意一些问题。

      在使用 list、set 或 map遍历删除某些元素时可以这样使用:

正确使用方法1       std::list< int> List;
      std::list< int>::iterator itList;
      for( itList = List.begin(); itList != List.end(); )
      {
            if( WillDelete( *itList) )
            {
               itList = List.erase( itList);
            }
            else
               itList++;
      }

       或

正确使用方法2       std::list< int> List;
      std::list< int>::iterator itList;
      for( itList = List.begin(); itList != List.end(); )
      {
            if( WillDelete( *itList) )
            {
               List.erase( itList++);
            }
            else
               itList++;
      }

      
      下面是两个错误的使用方法:

错误使用方法1       std::list< int> List;
      std::list< int>::iterator itList;
      for( itList = List.begin(); itList != List.end(); itList++)
      {
            if( WillDelete( *itList) )
            {
               List.erase( itList);
            }
      }

         或
错误使用方法2       std::list< int> List;
      std::list< int>::iterator itList;
      for( itList = List.begin(); itList != List.end(); )
      {
            if( WillDelete( *itList) )
            {
               itList = List.erase( ++itList);
            }
            else
               itList++;
      }

      正确使用方法1:通过erase方法的返回值来获取下一个元素的位置
      正确使用方法2:在调用erase方法之前先使用 “++”来获取下一个元素的位置
      错误使用方法1:在调用erase方法之后使用“++”来获取下一个元素的位置,由于在调用erase方法以后,该元素的位置已经被删除,如果在根据这个旧的位置来获取下一个位置,则会出现异常。
      错误使用方法2:同上。

Thursday, 29 September 2016

fprintf ,printf 及sprintf的使用方法及区别

有时我们用printf 打印调试,发现printf 打印到一半停止,这时候就需要使用fprintf(stderr) 了,因为这么不会缓存,printf 打印之前 通常会有一个缓存,这样会造成内容丢失。 使用fprintf(file)也行。 因为printf 只能打印到stdout,而fprintf 则是指定输出对象,stderr,file 等等。大多数情况下printf 够用。但是当printf不够用时,无法打印出 全部log时,这时候就该fprintf 上场了。
下面是从网上找到的fprintf ,printf 及sprintf的使用方法及区别。


1: fprintf()
#include <stdio.h> 
int fprintf( FILE *stream, const char *format, ... );
fprintf()函数根据指定的format(格式)发送信息(参数)到由stream(流)指定的文件.因此fprintf()可以使得信息输出到指定的文件.比如
    char name[20] = "Mary";
    FILE *out;
    out = fopen( "output.txt", "w" );
    if( out != NULL )
    fprintf( out, "Hello %s\n", name );
对于其输出格式参数,和printf()一样.
fprintf()和printf()一样工作. fprintf()的返回值是输出的字符数,发生错误时返回一个负值.
在有些地方,有这样的定义:printf(...)=fprintf(stdout,...).
2:eg)
fprintf函数的用法!2007-12-13 21:46
fprintf是用于文件操作的,原型是int fprintf( FILE *stream, const char *format [, argument ]...);
举例用法:
#include <stdio.h>
#include <process.h>
FILE *stream;
void main( void )
{
int i = 10;
double fp = 1.5;
char s[] = "this is a string";
char c = '\n';
stream = fopen( "fprintf.out", "w" );
fprintf( stream, "%s%c", s, c );
fprintf( stream, "%d\n", i );
fprintf( stream, "%f\n", fp );
fclose( stream );
system( "type fprintf.out" );
}
屏幕输出:
this is a string
10
1.500000
printf就是在屏幕打印出一段字符串来啊
原型是int printf( const char *format [, argument]... );
是标准输出。
3:printf、sprintf与fprintf 的用法区分
1.printf 是和标准输出文件(stdout)关联的,fprintf 则没有这个限制.
 
2.fprintf是用于文件操作的,原型是int fprintf( FILE *stream, const char *format [, argument ]...);
3.sprintf是格式化输出到一个字符串,fprintf是格式化输出到一个stream,通常是到文件。
 
int   fprintf(   FILE   *stream,   const   char   *format   [,   argument   ]...);  
int   sprintf(   char   *buffer,   const   char   *format   [,  argument]   ...   )

file命令

转载:http://www.cnblogs.com/kerrycode/p/3806618.html


命令简介:

该命令用来识别文件类型,也可用来辨别一些文件的编码格式。它是通过查看文件的头部信息来获取文件类型,而不是像Windows通过扩展名来确定文件类型的。
执行权限 :All User
指令所在路径:/usr/bin/file

命令语法:


file [ -bchikLnNprsvz ] [ -f namefile ] [ -F separator ] [ -m magicfiles ] file ...
命令参数:

下表列出了部分常用的参数。
参数 长参数 描叙
-b
列出文件辨识结果时,不显示文件名称。
-c
详细显示指令执行过程,便于排错或分析程序执行的情形
-f
列出文件中文件名的文件类型
-F
使用指定分隔符号替换输出文件名后的默认的“:”分隔符。
-i
输出mime类型的字符串
-L
查看对应软链接对应文件的文件类型
-z
尝试去解读压缩文件的内容

--help 显示命令在线帮助

--version 显示命令版本信息

使用示例:

1:查看file命令的帮助信息
[root@DB-Server ~]# file --help
Usage: file [OPTION]... [FILE]...
Determine file type of FILEs.
 
  -m, --magic-file LIST      use LIST as a colon-separated list of magic
                               number files
  -z, --uncompress           try to look inside compressed files
  -b, --brief                do not prepend filenames to output lines
  -c, --checking-printout    print the parsed form of the magic file, use in
                               conjunction with -m to debug a new magic file
                               before installing it
  -f, --files-from FILE      read the filenames to be examined from FILE
  -F, --separator string     use string as separator instead of `:'
  -i, --mime                 output mime type strings
  -k, --keep-going           don't stop at the first match
  -L, --dereference          causes symlinks to be followed
  -n, --no-buffer            do not buffer output
  -N, --no-pad               do not pad output
  -p, --preserve-date        preserve access times on files
  -r, --raw                  don't translate unprintable chars to \ooo
  -s, --special-files        treat special (block/char devices) files as
                             ordinary ones
      --help                 display this help and exit
      --version              output version information and exit
当然你也可以使用 man file 获取更加详细的帮助文档信息。
2:查看文件类型
例如,如下所示,Temp.txt 文件类型为text,编码为UTF-8 Unicode
[root@DB-Server ~]# file Temp.txt 
 
Temp.txt: UTF-8 Unicode text, with very long lines, with CRLF line terminators
 
3:不输出文件名称,只显示文件格式以及编码
通过下面两个命令对时,就可以清晰的了解参数-b的作用。
[root@DB-Server ~]# file Temp.txt 
Temp.txt: UTF-8 Unicode text, with very long lines, with CRLF line terminators
[root@DB-Server ~]# file -b Temp.txt
UTF-8 Unicode text, with very long lines, with CRLF line terminators
clip_image001
 
4: 输出mime类型的字符串
[root@DB-Server ~]# file -i Temp.txt 
Temp.txt: text/plain; charset=utf-8
 
5: 查看文件中的文件名的文件类型
这个参数非常适合shell脚本去查找、判别某种文件类型的数据。
[root@DB-Server ~]# cat >test
/root/install.log 
it is only one test file
 
[2]+  Stopped                 cat > test
[root@DB-Server ~]# file -f  test
/root/install.log:        ASCII text
it is only one test file: ERROR: cannot open `it is only one test file' (No such file or directory)
[root@DB-Server ~]# 
[root@DB-Server ~]# 
clip_image002
 
5: 使用指定分隔符号替换输出文件名后的默认的“:”分隔符。
感觉这个参数很鸡肋!我搞明白这个参数的作用时,很是纳闷。
clip_image003
 
6:尝试去解读压缩文件的内容
[root@DB-Server ~]# file -z Temp.txt.gz 
Temp.txt.gz: UTF-8 Unicode text, with very long lines, with CRLF line terminators (gzip compressed data, was "Temp.txt", from Unix, last modified: Tue Jun 24 00:34:15 2014)
[root@DB-Server ~]# 
 
7: 查看软链接对应文件的文件类型
如下所示,创建一个软链接sfile,然后分别用file 和带参数的file -L查看
 
[root@DB-Server ~]# ln -s Temp.txt.gz sfile
[root@DB-Server ~]# file sfile 
sfile: symbolic link to `Temp.txt.gz'
[root@DB-Server ~]# file -L sfile 
sfile: gzip compressed data, was "Temp.txt", from Unix, last modified: Tue Jun 24 00:34:15 2014
[root@DB-Server ~]# 

Tuesday, 27 September 2016

程序员修炼之道读书笔记 31 Bend or Break (弯还是折?





将数据模型和视图分开。

好的代码,模块清晰,功能明确。烂代码完全一锅粥,改一处牵连一堆问题,牵一发而动全身。完全没法维护,熵极高。如果你的代码是这样的,那么是时候重构拆分了。

Monday, 26 September 2016

程序员修炼之道读书笔记 30 Finish What you Start


分配资源的函数或对象,同时应该负责释放资源。不要推迟到另外一个函数或对象去释放。这样容易造成潜在隐患。 比如在一个函数中open file 或者new 空间,也应该在该函数中close  file delete 空间。
whoever allocates a resource should be responsible for deallocating it

Some C and C++ developers make a point of setting a pointer to NULL after they deallocate the memory it references. This is a good idea.

Because this will prevent refer(deference) to this pointer later, which will cause runtime error.

Sunday, 25 September 2016

程序员修炼之道读书笔记 29 不要以为你的假设总是无条件成立




一分钟不一定60秒,因为有闰秒的情况 (可以为61秒或62秒)。
三角形内角和也不定是180度,因为有非欧几里得的几何。
C++/Java 中, a+1 不一定小于a ,因为会有溢出的情况。
阳历一个月也可能有出现小于28天的情况,19729月只有19天,为日历同步而进行的调整。
列举上面的例子,只为说明一个问题,程序员在编程时候,不要自作聪明的以为,某些假设是放之四海皆准的。 总有你想不到或者不知道的情况。为了安全起见,加上assertion验证你的假设,总是防御性编程的一种好方法。