Ugly Code

看到几行令人纠结的代码:

const int size = 1000;  // array of 1000 integers
int array [size];
int n = 0;
// read an integer into the n+1 th element of array
while (cin >> array[n++]);
n--; // it got incremented once too many times 
//下面处理array与n的代码省略

搞不明白这里

while (cin >> array[n++]);
n--; // it got incremented once too many times 

为什么要这样,明明可以这样写的

while (cin >> array[n])n++;

这种代码看的让人蛋疼。

gcc中关于C/C++标准的选项

man gcc里面说到gcc使用指定的标准编译C/C++的选项是:-std=standard,其中standard可以是

c89(传说中的c89)
iso9899:1990
    Support all ISO C90 programs (certain GNU extensions that conflict with ISO C90
    are disabled). Same as -ansi for C code.

iso9899:199409
    ISO C90 as modified in amendment 1.

c99(传说中的c99)
c9x
iso9899:1999
iso9899:199x
    ISO C99.  Note that this standard is not yet fully supported; see
    <http://gcc.gnu.org/gcc-4.2/c99status.html> for more information.  The names c9x
    and iso9899:199x are deprecated.

gnu89
    GNU dialect of ISO C90 (including some C99 features). This is the default for C code.

gnu99
gnu9x
    GNU dialect of ISO C99.  When ISO C99 is fully implemented in GCC, this will
    become the default.  The name gnu9x is deprecated.

c++98
    The 1998 ISO C++ standard plus amendments. Same as -ansi for C++ code.

gnu++98
    GNU dialect of -std=c++98.  This is the default for C++ code.

还有一个选项就是-ansi,上面也说到了。

以上是在gcc4.2.4里面看到的。

《编程之美》中不要被阶乘吓倒的一个注解

这一节中的第二个问题:求N!的二进制表示中最低位1的位置。其中第二个解法提出N!含有质因数2的个数等于N减去N的二进制表示中1的数目,不过没有给出证明,自己推导了一下,如下所示。

假设N=anan-1...a1a0,其中ai属于{0, 1},an=1。由于N!中含有质因数2的个数为

[N/2] + [N/4] + [N/8] + [N/16] + ...

=anan-1...a1 + anan-1...a2 + ... + anan-1 + an = S       (1)

以上式子两边同时乘以2,得到

anan-1...a10 + anan-1...a20 + ... + anan-10 + an0 + 0 = 2S      (2)

(2) - (1)错位减,得到:

anan-1...a10 - a1 - a2 - ... - an=S

整理得到

N-(a0 + a1 + a2 + ... + an) = S

注意到ai属于{0, 1},因此就得到证明。

 

Linux目录权限相关问题

      最近老是忘记这个东西,现在总结一下不然下次又忘掉了。

      R:就是读权限,用ls命令的的时候必须有这个权限,但是单纯的R权限又没有多大作用,一般是要带X(执行)权限。

      W:写权限,比较好理解,删除,修改,创建文件都需要写权限,需要W权限的相关命令是mv,rm等等,但是依旧有一个问题,如果没有X权限还是没多大用处。

      X:执行权限,如果在文件上的话比较好理解就是“可执行”的意思,但是用在目录这里则是表示“可以进入”的意思,就是你要进入这个目录时候就要求这个权限,比如cd,ls命令都需要相应目录的X权限。

C语言结构体赋值相关

看UNPv1的时候,在第四章看到ipv6结构体的赋值,里面说到C语言的结构体变量无法直接赋值为该结构的常值结构,但是可以用变量赋值,并且如果是初始化的时候也是可以的,废话少说,还是上代码说的明白:

typedef struct A { 
        int a;
        char b;
} A;

int main()
{
    
        A b = {2, 'b'}; //OK
        A a = {1, 'a'}; //OK
        /* a = {1, 'b'} //非法,结构体赋值不能用常量结构赋值,不过可以这样: */
        a = b; /* 合法,b是同类型的结构体变量 */

        return 0;
}

C语言预处理指令总结

        预处理指令严格来说不属于程序语句的一部分,所有的预处理语句在程序编译之前被预处理器处理完成。每一条预处理语句总是以"#"字符开始,并且不能超过一行,一旦遇到换行符语句就被当做结束。一般来说,预处理语句的后面不能加分号。另外,唯一的一种能将预处理语句扩展到多行的办法就是在换行符之前加一个反斜杠('\')。

 

宏定义(#define,#undef)

#define指令主要用于定义常量还有就是定义带有参数的”函数“。

#define PI 3.1416

#define MAX(a,b) (a)>(b)?(a):(b)

有一个值得注意的就是括号的使用。

#undef用于取消之前定义过的宏定义。

”函数“型宏定义接受两个特殊的运算符(#,##)

使用#使得#之后的第一个参数作为一个带引号的字符串返回。例如

#define str(x) #x
cout << str(test);

被处理后相当于

cout << "test";

使用##连接##之前和之后的内容。例如

#define glue(a,b) a ## b
glue(c,out) << "test";

处理后相当于

cout << "test";

 

条件包含指令(#ifdef, #ifndef, #if, #endif, #else ,#elif) 

#ifdef micro
some codes here
#endif

如果宏被使用#define 定义,命令后边的代码被编译。

#if expression
some code here
#endif

如果表达式为真,命令后边的代码被编译。

#ifndef跟#ifdef相反。

#if 和#elif后面 的表达式用于比较操作时候只允许用整数。例如

#if VERBOSE == "on" // NOT ALLOWED
  print("trace message");
#endif
#if VERBOSE >= 2.0 // NOT ALLOWED
  print("trace message");
#endif

另外,#ifdef和#ifndef也可以分别用运算符 defined!defined取得相同的效果。例如

#if !defined TABLE_SIZE
#define TABLE_SIZE 100
#elif defined ARRAY_SIZE
#define TABLE_SIZE ARRAY_SIZE
int table[TABLE_SIZE];


行控制(#line)

语法:

#line line_number "filename"

#line命令可以简单的改变下一行__LINE____FILE__的值,文件名是可选的,__LINE____FILE__表示当前文件名和当前行的行号。

这条命令

 #line 10 "main.cpp"

…改变更改下一行行号改为10,当前文件名为”main.cpp”。


错误指令(#error)

#error message

当编译器遇到#error命令的时候会被迫停止编译,并输出错误信息和出错行号

 

文件包含(#include)

最常用,一共有两种方式

#include "file"
#include <file> 

双引号表示在当前目录搜索,然后在系统定义的目录搜索,而尖括号表示直接在系统目录搜索。

 

Pragma 指令(#pragma)

 #pragma lexems

#pragma命令赋予程序员控制编译器的能力,由于#prama的实现依赖于编译器,使用方法因编译器而异。一个选项可能完全改变程序执行顺序。

 

预定义的宏名

    __LINE__
    __FILE__
    __DATE__
    __TIME__
    __cplusplus
    __STDC__
  • __LINE____FILE__ 变量记录了编译器的当前处理行和当前处理文件。
  • __DATE__ 变量包含当前文件被编译的日期,格式为:月/日/年。
  • __TIME__ 变量包含当前文件被编译的时间,格式为:时:分:秒。
  • __cplusplus 变量只有在C++程序中才被定义;一些老编译器也可以为c_plusplus
  • __STDC__ 变量在编译C程序的时候被定义,许多编译器在编译C++代码时也会定义此变量。

Reference:

  1. www.cppreference.com/wiki/cn/preprocessor/start
  2. en.wikipedia.org/wiki/C_preprocessor
  3. www.cplusplus.com/doc/tutorial/preprocessor/

 

[面试题]找规律

2
12
1112
3112
132112

——————————
请问下一行是什么并说说规律?
 

答案:

1113122112

这个是王小云在ccav上做节目时用到的,除第一行外,每一行都是在描诉上一行,12表示有1个2,1112表示1个1,1个2,依次类推。

 

Reference: http://bbs.chinaunix.net/thread-1745371-1-1.html

[技巧]不使用算术运算符让一个数加一(用位运算)

代码如下:

 

int add(int n)
{
// 这里假设n>=0,不过对于n<0的情况也是成立的
	int c=1;
	int d;
	while(c!=0)
	{
		d=n&c;
		n=n^c;
		c=d<<1;
	}
	
	return n;
}

 原理就是:

对于一个二进制编码的数字,想要加一可以这样做:从最低位开始向最高位寻找第一个位为0的位,将这个位置1,而后边的位全部置0。

[算法] check divisibility

看到一个算法题:不使用除法运算符(/)和模运算(%)判断一个数是否被形如2^n-1的数整除。

代码如下:

 

int checkdiv(int n,int k)
{
// 返回 n%(2^k-1)的结果
// n>=0,k>=2
	if(n == 0)
		return 0;
	
	int m = (1<<k)-1;  // m=2^k-1
	while(n > m)
	{
		n = (n>>k) + (n&m);
	}
	
	if(m == n)
		return 0;
	else // m>n
		return n;
}

原理如下:

n可以表示为

n=(n>>k)*(1<<k)+(n&m)

因此只需证明((n>>k)*(2^k)) ≡ (n>>k) (mod (2^k-1)),显然这是成立的。

 

 

 

 

一些感想

  1. 鄙视一个人并不会让你得到什么,相反,你有可能得罪到别人。
  2. 当你受打击时侯,去做其他事吧,不要再回忆以前受打击的经历,那只会让你的心情更糟。
  3. 当你遇到别人问一个你觉得很简单的问题时,尽量不要嘲笑或者鄙视,你可以选择沉默,因为有些细节你无法了解。
  4. 如果你对别人的鄙视或者嘲笑感到不爽,保持沉默吧,用行动才是最好的回应方式。
  5. 多做实事。