Cheat Notes C

==  Cheat Notes C ==
  |== segmentation fault ==
  |== Initialization ==
  |== sizeof ==
  |== malloc/calloc ==
  |== Big Endian ==
  |== Memory Segments ==
  |== bit operation ==
  |== boolean ==
  |== int main(void) ==
  |== for/while ==
  |== scanf ==
  |== function ==
  |== string ==
  |== static ==
  |== const ==
  |== value, reference, address ==
  |== register variable ==
  |== extern variable ==
  |== Preprocessor ==
  |== pointer ==
  |== typedef ==
  |== Data structure alignment ==
  |== file ==
  |== rand ==
  |== KR/ANSI/GNU ==


== segmentation fault == Back to top
segmentation fault: http://en.wikipedia.org/wiki/Segmentation_fault
segmentation fault段错误往往是由于访问了一个不可存取的内存地址,或者是试图以一种不可不允许的方式访问某块内存(如写入一块只读内存等)。segmentation是个历史名词,是由内存的段式管理和页式管理而来。
不同os对段错误触发的信号可能不一致,在类unix系统中,信号是SIGSEGV ,此信号被发送到触发段错误的进程。
引起段错误的原因有以下几类:
1)存取空指针
2)试图访问进程没有权限访问的内存(如在进程空间中访问内核结构)
3)试图访问一个不存在的内存地址(在进程内存空间之外)
4)试图写入只读内存(如代码段)
5)缓冲区溢出
6)使用未初始化的指针
------------------------
 int main(void) {
     char *s = "hello world";
     *s = 'H';
 }
    char str[]="come on, boy."; //memory allocated
    //char *str;
    //str="come on, boy."; //字符串常量,
----------------------
 $ gcc -ansi sigbus.c -o sigbus
 $ ./sigbus 
 Bus error
 $ gdb ./sigbus
 (gdb) r
 Program received signal [[SIGBUS]], Bus error.
 0x080483ba in main ()
 (gdb) x/i $pc
 0x80483ba <main+54>:    mov    DWORD PTR [eax],0x2a
 (gdb) p/x $eax
 $1 = 0x804a009
 (gdb) p/t $eax & (sizeof(int) - 1)
 $2 = 1
-----------------
http://hi.baidu.com/zengzhaonong/item/4e6cd90ea4060b113a53ee9e
http://www.blogbus.com/logs/31603211.html


== Initialization ==


变量的初始化
    1) 对malloc分配的内存进行memset清零操作。(可以使用calloc分配一块全零的内存)
    2) 对一些栈上分配的struct或数组进行初始化。(最好也是清零)


以下这种情况,则不需要。
        char *pstr;  /* 一个字符串 */
        pstr = ( char* ) malloc( 50 );
        if ( pstr == NULL ) exit(0);
        strcpy( pstr, "Hello Wrold" );
但如果是下面一种情况,最好进行内存初始化。(指针是一个危险的东西,一定要初始化)
        char **pstr;  /* 一个字符串数组 */
        pstr = ( char** ) malloc( 50*sizeof(char*) );
        if ( pstr == NULL ) exit(0);
        
        /* 让数组中的指针都指向NULL */
        memset( pstr, 0, 50*sizeof(char*) ); 
而对于全局变量,和静态变量,一定要声明时就初始化。因为你不知道它第一次会在哪里被使用。所以使用前初始这些变量是比较不现实的,一定要在声明时就初始化它们。如:
    Links *plnk = NULL;  /* 对于全局变量plnk初始化为NULL */


== sizeof ==
C++标准规定,int占一个机器字长。在32位系统中int占32位,也就是4个字节,而在老式的16位系统中,int占16位,即2个字节。而C++标准中只限制规定short int不能超过int的长度,具体长度的可以由C++编译器的实现厂商自行决定。目前流行的32位C++编译器中,通常int占4字节,short int占2字节。其中short int可以简写为short。类似地,C++标准只限制了long int不得小于int的长度,具体也没有作出限制。
sizeof(char)=1
sizeof(int)=4 (-2^31 ~ 2^31-1) (-214783648~214783647)
sizeof(short)=2 (-2^15 ~ 2^15-1)
sizeof(unsigned)=2 (0 ~ 2^32-1)
sizeof(long)=8 (-2^63 ~ 2^63-1)
sizeof(unsigned long)=8 (0 ~ 2^64-1)
sizeof(float)=4 (-2^128 ~ 2^128) // float:1bit(符号位) 8bits(指数位) 23bits(尾数位)
sizeof(double)=8 (-2^1024 ~ +2^1024) // double:1bit(符号位) 11bits(指数位) 52bits(尾数位)
// float:2^23 = 8388608,一共七位,这意味着最多能有7位有效数字,但绝对能保证的为6位,也即float的精度为6~7位有效数字
// double:2^52 = 4503599627370496,一共16位,同理,double的精度为15~16位。
----------------
请sizeof类型而不是变量
    pScore = (int*) malloc( SUBJECT_CNT );
    memset( pScore, 0, sizeof(pScore) );
    ...
    
此时,sizeof(pScore)返回的就是4(指针的长度),不会是整个数组,于是,memset就不能对这块内存进行初始化。


另外一点,sizeof一般用于分配内存,这个特性特别在多维数组时,就能体现出其优点了。如,给一个字符串数组分配内存,
/* 
 * 分配一个有20个字符串,
 * 每个字符串长100的内存 
 */


char* *p;
p = (char**) calloc ( 20, sizeof(char*) );
for ( i=0; i<20; i++){
    p[i] = (char*) calloc ( 100, sizeof(char) );
}


== malloc/calloc ==
//Zeroing out the memory may take a little time, so you probably want to use malloc() if that performance is an issue. If initializing the memory is more important, use calloc(). For example, calloc() might save you a call to memset().


== Big Endian ==
http://en.wikipedia.org/wiki/Endianness
假设变量x类型为int,位于地址0x100处,它的十六进制为0x01234567,地址范围为0x100~0x103字节,其内部排列顺序依赖于机器的类型。大端法从首位开始将是:0x100: 01, 0x101: 23,..。而小端法将是:0x100: 67, 0x101: 45,..。


0x0A0B0C0D: 8bit: 16bit: Arch
big-endian: ... 0x0A 0x0B 0x0C 0x0D ... : ... 0x0A0B 0x0C0D ... : Motorola 6800,Motorola 68000,PowerPC 970,System/370,SPARC(除V9外)
little-endian: ... 0x0D 0x0C 0x0B 0x0A ... : ... 0x0C0D 0x0A0B ... : x86,MOS Technology 6502,Z80,VAX,PDP-11
middle-endian: ... 0x0B 0x0A 0x0D 0x0C ... : 


Bi-endian: ARM, PowerPC (除PowerPC 970外), DEC Alpha, SPARC V9, MIPS, PA-RISC and IA64


== Memory Segments ==
http://www.geeksforgeeks.org/memory-layout-of-c-program/
Memory Layout of C Programs
A typical memory representation of C program consists of following sections.
1. Text segment
2. Initialized data segment
3. Uninitialized data segment
4. Stack
5. Heap
------------------
http://blog.csdn.net/jxhui23/article/details/8064766
Linux进程的五个段
BSS段:BSS段(bss segment)存放程序中未初始化的全局变量(??int i)的一块内存区域。BSS是英文Block Started by Symbol的简称。BSS段属于静态内存分配。


数据段:数据段(data segment)存放程序中已初始化的全局变量(static int i;)的一块内存区域。数据段属于静态内存分配。


代码段:代码段(code segment/text segment)存放程序执行代码(for...)的一块内存区域。大小在程序运行前就已经确定,并且内存区域只读, 某些架构也允许代码段为可写,即允许修改程序。也有可能包含一些只读的常数变量(#define N 100),例如字符串常量等。


堆(heap):存放进程运行中被动态分配(malloc,calloc)的内存段,它的大小并不固定,可动态扩张或缩减。当进程调用malloc等函数分配内存时,新分配的内存就被动态添加到堆上(堆被扩张);当利用free等函数释放内存时,被释放的内存从堆中被剔除(堆被缩减)


栈(stack):栈又称堆栈,是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(int i)(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进后出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区。


它是由操作系统分配的,内存的申请与回收都由OS管理。


== bit operation ==
& 按位与
| 按位或
^ 按位异或
~ 取反
<< 左移: 10001111 >> 3 = 11100011
>> 右移: 10001111 << 3 = 11111000


“位域”是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。
每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字
节的二进制位域来表示。


truct bs
{
int a:8;
int b:2;
int c:6;
}data;
说明 data 为 bs 变量,共占两个字节。其中位域 a 占 8 位,位域 b 占 2 位,位域 c 占 6
位。


对于位域的定义尚有以下几点说明:
1) 一个位域必须存储在同一个字节中,不能跨两个字节。
2) 由于位域不允许跨两个字节,因此位域的长度不能大于一个字节的长度,也就是说
不能超过 8 位二进位。
3) 位域可以无位域名,这时它只用来作填充或调整位置。无名的位域是不能使用的


main(){
struct bs
{
unsigned a:1;
unsigned b:3;
unsigned c:4;
} bit,*pbit;
bit.a=1;
bit.b=7;
bit.c=15;
printf("%d,%d,%d\n",bit.a,bit.b,bit.c);
pbit=&bit;
pbit->a=0;
pbit->b&=3;
pbit->c|=1;
printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);


== boolean ==
C doesn't have any built in boolean types. What's the best way to use them in C?
typedef int bool;
#define true 1
#define false 0


== int main(void) ==
int main(void)
http://tieba.baidu.com/p/311373516
在 C89 中,main( ) 是可以接受的。Brian W. Kernighan 和 Dennis M. Ritchie 的经典巨著 The C programming Language 2e(《C 程序设计语言第二版》)用的就是 main( )。不过在最新的 C99 标准中,只有以下两种定义方式是正确的: 
           int main( void ) 
           int main( int argc, char *argv[] ) 
如果不需要从命令行中获取参数,请用int main(void) ;否则请用int main( int argc, char *argv[] ) 。 
main 函数的返回值类型必须是 int ,这样返回值才能传递给程序的调用者(如操作系统)。


== for/while ==
尽量用for而不是while做循环
当while的语句块变大后,你的程序将很难读,用for就好得多:


    for ( p=pHead;  p; p=p->next ){
    ..
    }


== scanf ==
1) scanf 函数中没有精度控制,如:scanf("%5.2f",&a);是非法的。不能企图用此语句输入小数为 2 位的实数。
2) scanf 中要求给出变量地址,如给出变量名则会出错。如 scanf("%d",a);是非法的,应改为 scnaf("%d",&a);才是合法的。
3) 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔则可用空格,TAB 或回车作间隔。C 编译在碰到空格,TAB,回车或非法数据(如对“%d”输入“12A”时,A 即为非法数据)时即认为该数据结束。
用 scanf 函数输入字符串时,字符串中不能含有空格,否则将以空格作为串的结束符。为了避免这种情况,可多设几个字符数组分段存放含空格的串。
4) 在输入字符数据时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
例如: scanf("%c%c%c",&a,&b,&c);
输入为: d e f, 则把'd'赋予 a, ' ' 赋予 b,'e'赋予 c。
只有当输入为: def时,才能把'd'赋于 a,'e'赋予 b,'f'赋予 c。
如果在格式控制中加入空格作为间隔, 如:
scanf ("%c %c %c",&a,&b,&c);
则输入时各数据之间可加空格。
5) 如果格式控制串中有非格式字符则输入时也要输入该非格式字符。
例如: scanf("%d,%d,%d",&a,&b,&c);
其中用非格式符“ , ”作间隔符,故输入时应为: 5,6,7
又如: scanf("a=%d,b=%d,c=%d",&a,&b,&c);
则输入应为: a=5,b=6,c=7
6) 如输入的数据与输出的类型不一致时,虽然编译能够通过,但结果将不正确。


== function ==
function:
如函数值为整型,在函数定义时可以省去类型说明。 func() <=> int func()
---------------------------
void:
为了使程序有良好的可读性并减少出错, 凡不要求返回值的函数都应定义为空类型。
---------------------------


== string ==
char st[15];
printf("input string:\n");
scanf("%s",st);
printf("%s\n",st);


array in scanf:
在前面介绍过,scanf 的各输入项必须以地址方式出现,如 &a,&b 等。但在前例中却是以数组名方式出现的,这是为什么呢?
这是由于在C语言中规定,数组名就代表了该数组的首地址。整个数组是以首地址开头的一块连续的内存单元。
---------------------------


== static ==
static关键字的3大作用
(1)先来介绍它的第一条也是最重要的一条:隐藏。
所有未加static前缀的全局变量和函数都具有全局可见性,其它的源文件也能访问。此例中,a是全局变量,msg是函数,并且都没有加static前缀,因此对于另外的源文件main.c是可见的(extern char a;    // extern variable must be declared before use)。


如果加了static,就会对其它源文件隐藏。例如在a和msg的定义前加上static,main.c就看不到它们了。利用这一特性可以在不同的文件中定义同名函数和同名变量,而不必担心命名冲突。Static可以用作函数和变量的前缀,对于函数来讲,static的作用仅限于隐藏,而对于变量,static还有下面两个作用。


(2)static的第二个作用是保持变量内容的持久。存储在静态数据区的变量会在程序刚开始运行时就完成初始化,也是唯一的一次初始化。共有两种变量存储在静态存储区:全局变量和static变量,只不过和全局变量比起来,static可以控制变量的可见范围,说到底static还是用来隐藏的。


(3)static的第三个作用是默认初始化为0。其实全局变量也具备这一属性,因为全局变量也存储在静态数据区。在静态数据区,内存中所有的字节默认值都是0x00.


最后对static的三条作用做一句话总结。首先static的最主要功能是隐藏,其次因为static变量存放在静态存储区,所以它具备持久性和默认值0。
------------------
存储类名 生命周期 作用域
extern 静态(程序结束后释放) 外部(整个程序)
static 静态(程序结束后释放) 内部(仅翻译单元,一般指单个源文件)
auto,register 函数调用(调用结束后释放)


== const ==
http://hi.baidu.com/onlys_c/item/48be6ccaf8920b3699b49813
-- const变量 & 常量 --
为什么我象下面的例子一样用一个const变量来初始化数组,ANSI C的编译器会报告一个错误呢?
const int n = 5;
int a[n]; 
1)、这个问题讨论的是“常量”与“只读变量”的区别。而ANSI C规定数组定义时维度必须是“常量”,“只读变量”也是不可以的。
2)、注意:在ANSI C中,这种写法是错误的,因为数组的大小应该是个常量,而const int n,n只是一个变量(常量 != 不可变的变量,但在标准C++中,这样定义的是一个常量,这种写法是对的),实际上,根据编译过程及内存分配来看,这种用法本来就应该是合理的,只是ANSI C对数组的规定限制了它。
3)、那么,在ANSI C 语言中用什么来定义常量呢?答案是enum类型和#define宏,这两个都可以用来定义常量。


-- const变量 & const 限定的内容 --
下面的代码编译器会报一个错误,请问,哪一个语句是错误的呢?
typedef char * pStr;
char string[4] = "abc";
const char *p1 = string;
const pStr p2 = string;
p1++;
p2++; 
问题出在p2++上。
1)、const使用的基本形式: const char m; 
限定m不可变。
2)、替换1式中的m, const char *pm; 
限定*pm不可变,当然pm是可变的,因此问题中p1++是对的。
3)、替换1式char, const newType m; 
限定m不可变,问题中的charptr就是一种新类型,因此问题中p2不可变,p2++是错误的。


-- const变量 & 字符串常量 --
请问下面的代码有什么问题?
char *p = "i'm hungry!";
p[0]= 'I'; 
上面的代码可能会造成内存的非法写操作。分析如下, “i'm hungry”实质上是字符串常量,而常量往往被编译器放在只读的内存区,不可写。p初始指向这个只读的内存区,而p[0] = 'I'则企图去写这个地方,编译器当然不会答应。


-- const变量 & 字符串常量2 --
请问char a[3] = "abc" 合法吗?使用它有什么隐患? 
在标准C中这是合法的,但是它的生存环境非常狭小;它定义一个大小为3的数组,初始化为“abc”,,注意,它没有通常的字符串终止符'\0',因此这个数组只是看起来像C语言中的字符串,实质上却不是,因此所有对字符串进行处理的函数,比如strcpy、printf等,都不能够被使用在这个假字符串上。


-- const & 指针 --
类型声明中const用来修饰一个常量,有如下两种写法,那么,请问,下面分别用const限定不可变的内容是什么?
1)、const在前面
const int nValue; //nValue是const
const char *pContent; //*pContent是const, pContent可变
const (char *) pContent;//pContent是const,*pContent可变
const char* const pContent; //pContent和*pContent都是const 
2)、const在后面,与上面的声明对等
int const nValue; // nValue是const
char const * pContent;// *pContent是const, pContent可变
(char *) const pContent;//pContent是const,*pContent可变
char* const pContent;// pContent是const,*pContent可变
char const* const pContent;// pContent和*pContent都是const 


const和指针一起使用是C语言中一个很常见的困惑之处,在实际开发中,特别是在看别人代码的时候,常常会因为这样而不好判断作者的意图,下面讲一下我的判断原则:


沿着*号划一条线,const和谁在一边,那么谁就是const,即const限定的元素就是它。你可以根据这个规则来看上面声明的实际意义,相信定会一目了然。


另外,需要注意:对于const (char *) ; 因为char *是一个整体,相当于一个类型(如 char),因此,这是限定指针是const。


== value, reference, address ==
http://en.wikipedia.org/wiki/Evaluation_strategy
------------
http://blog.sina.com.cn/s/blog_62cd3a270100qkcm.html
值传递好比是你把文件复制一份,通过网络传给他,然后他可以在他本机上对文件做任何的修改,修改会保存下来,但是你机器上的文件不会发生任何的变化。即形参与实参是两个不同的变量,各自占用不同的存储单元。


地址传递好比是你把文件在网络上的地址告诉他人,他人通过网络访问你机器上的文件,他可以对文件进行修改并保存,此时,文件的内容就会发生变化。即形参与实参是相同的变量,占用同一段内存空间。


地址传递是通过把地址传递给函数,然后函数根据地址要对存储单元操作。打个比方说:你告诉某人一个房间号,让他根据房间号去拿去东西。
------------
void Exchg1(int x, int y){ // call by value
//void Exchg2(int *px, int *py){ // call by address
//void Exchg3(int &x, int &y){ // call by reference
  int tmp;
  tmp=x;
  x=y;
  y=tmp;
  printf(“x=%d,y=%d\n”,x,y)
}
int main(void){
  int a=4,b=6;
  Exchg1(a,b);  // call by value
  //Exchg2(&a,&b); // call by address
  //Exchg2(a,b); // call by reference
  printf(“a=%d,b=%d\n”,a,b)
}
输出的结果:
x=____, y=____
a=____, b=____
--------------------
而当用数组名作函数参数时,情况则不同。由于实际上形参和实参为同一数组,因此当形参数组发生变化时,实参数组也随之变化。
---------------------------
注意实参 pointer_1 和 pointer_2 是指针变量,在函数调用时,将实参变量的值传递给形参变量。采取的依然是“值传递”方式。
因此虚实结合后形参 p1 的值为&a,p2 的值为&b。这时 p1 和 pointer_1 指向变量 a,p2 和pointer_2 指向变量 b。
------------------------------
#include <stdio.h>


void swap(int *i, int *j);


int main(void)
{
  int num1, num2;


  num1 = 100;
  num2 = 800;


  printf("num1 = %d num2 = %d\n", num1, num2);
  swap(&num1, &num2);
  printf("num1 = %d num2 = %d\n", num1, num2);


  return 0;
}


/* Exchange the values by pointers. */
void swap(int *i, int *j)
{
  int temp;


  temp = *i;
  *i = *j;
  *j = temp;
}


== register variable ==
register int i,f=1;


为了提高效率,C 语言允许将局部变量得值放在 CPU 中的寄存器中,这种变量叫“寄存器变量”,用关键字 register 作声明。
1) 只有局部自动变量和形式参数可以作为寄存器变量;
2) 一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;
3) 局部静态变量不能定义为寄存器变量。
---------------------------


== extern variable ==
如果在定义点之前的函数想引用该外部变量,则应该在引用之前用关键字 extern对该变量作“外部变量声明”。表示该变量是一个已经定义的外部变量。有了此声明,就可以从“声明”处起,合法地使用该外部变量。
a.c:
...
int A=13,B=-8;
...
b.c:
int main(){
    extern A,B;
    printf("%d\n",max(A,B));
}
---------------------------


== Preprocessor ==
http://en.wikipedia.org/wiki/Preprocessor
预处理命令包含命令#include,宏定义命令#define 等
--MICRO-------------------------
在带参宏定义中,形式参数不分配内存单元,因此不必作类型定义。
#define SQ(y) ((y)*(y))


1. 预处理功能是C语言特有的功能,它是在对源程序正式编译前由预处理程序完成的。程
序员在程序中用预处理命令来调用这些功能。
2. 宏定义是用一个标识符来表示一个字符串,这个字符串可以是常量、变量或表达式。在
宏调用中将用该字符串代换宏名。
3. 宏定义可以带有参数,宏调用时是以实参代换形参。而不是“值传送”。
4. 为了避免宏代换时发生错误,宏定义中的字符串应加括号,字符串中出现的形式参数两
边也应加括号。
5. 文件包含是预处理的一个重要功能,它可用来把多个源文件连接成一个源文件进行编
译,结果将生成一个目标文件。
6. 条件编译允许只编译源程序中满足条件的程序段,使生成的目标程序较短,从而减少了
内存的开销并提高了程序的效率。
7. 使用预处理功能便于程序的修改、阅读、移植和调试,也便于实现模块化程序设计。
--#,##-------------------------
#define tempfile(dir) #dir "%s"
#define cat(x, y) x##y
#define xcat(x, y) cat(x,y)


printf("%s", tempfile(/usr/tmp)); //"/usr/tmp" "%s"
printf("%s", cat(var, 123)); // var123
printf("%s", cat(cat(1,2),3)); // cat ( 1 , 2 )3
printf("%s", xcat(xcat(1, 2), 3)); // 123


== pointer ==
注意实参 pointer_1 和 pointer_2 是指针变量,在函数调用时,将实参变量的值传递给形参变量。采取的依然是“值传递”方式。
因此虚实结合后形参 p1 的值为&a,p2 的值为&b。这时 p1 和 pointer_1 指向变量 a,p2 和pointer_2 指向变量 b。


指针变量可以进行某些运算,但其运算的种类是有限的。它只能进行赋值运算和部分算术运算及关系运算。
1. 指针运算符
1) 取地址运算符&: 其结合性为自右至左,其功能是取变量的地址。
2) 取内容运算符*: 其结合性为自右至左,用来表示指针变量所指的变量。在*运算符之后跟的变量必须是指针变量。
需要注意的是指针运算符*和指针变量说明中的指针说明符*不是一回事。在指针变量说明中,“*”是类型说明符,表示其后的变量是指针类型。而表达式中出现的“*”则是一个运算符用以表示指针变量所指的变量。
--2. 指针变量的运算
1) 赋值运算:指针变量的赋值运算有以下几种形式。
指针变量初始化赋值,前面已作介绍。
把一个变量的地址赋予指向相同数据类型的指针变量。
    int a,*pa;
    pa=&a; /*把整型变量 a 的地址赋予整型指针变量 pa*/
把一个指针变量的值赋予指向相同类型变量的另一个指针变量。
    int a,*pa=&a,*pb;
    pb=pa;
把数组的首地址赋予指向数组的指针变量。
    int a[5],*pa;
    pa=a;
    pa=&a[0];
    int a[5],*pa=a;
把字符串的首地址赋予指向字符类型的指针变量。
    char *pc;
    pc="C Language";
    char *pc="C Language";
把函数的入口地址赋予指向函数的指针变量。
    int (*pf)();
    pf=f;
2) 加减算术运算(指向数组的指针)
指针变量加 1,即向后移动 1 个位置表示指针变量指向下一个数据元素的首地址。而不是在原地址基础上加 1。例如:
    int a[5],*pa;
    pa=a;/*pa 指向数组 a,也是指向 a[0]*/
    pa=pa+2; /*pa 指向 a[2],即 pa 的值为&pa[2]*/
3) 两个指针变量之间的运算: 只有指向同一数组的两个指针变量之间才能进行运算,否则运算毫无意义。
两指针变量相减:所得之差是两个指针所指数组元素之间相差的元素个数。实际上是两个指针值(地址)相减之差再除以该数组元素的长度(字节数)。
两个指针变量不能进行加法运算。 例如,pf1+pf2 是什么意思呢?毫无实际意义。
-------------
3 两指针变量进行关系运算:指向同一数组的两指针变量进行关系运算可表示它们所
第 151 页指数组元素之间的关系。
例如:
pf1==pf2 表示 pf1 和 pf2 指向同一数组元素;
pf1>pf2 表示 pf1 处于高地址位置;
pf1<pf2 表示 pf2 处于低地址位置。
指针变量还可以与 0 比较。设 p 为指针变量,则 p==0 表明 p 是空指针,它不指向任何变量;p!=0 表示 p 不是空指针。空指针是由对指针变量赋予 0 值而得到的。
例如:
#define NULL 0
int *p=NULL;
对指针变量赋 0 值和不赋值是不同的。指针变量未赋值时,可以是任意值,是不能使用的。否则将造成意外错误。而指针变量赋 0 值后,则可以使用,只是它不指向具体的变量而已。
--------------------
*(p++)与*(++p)作用不同。若 p 的初值为 a,则*(p++)等价 a[0],*(++p)等价 a[1]。
--------------------
main(){
int *p,i,a[10];
p=a;
for(i=0;i<10;i++) *p++=i;
p=a;
for(i=0;i<10;i++) printf("a[%d]=%d\n",i,*p++);
}
------
void * memcpy ( void * destination, const void * source, size_t num );
------
strcpy(array2, array1);


== typedef ==
在可能的情况下使用typedef替代macro。当然有时候你无法避免macro,但是typedef更好。
typedef int* INT_PTR;
INT_PTR a , b;
 
# define INT_PTR int*;
INT_PTR a , b;
在这个宏定义中,a是一个指向整数的指针,而b是只有一个整数声明。使用typedef a和b都是 整数的指针。


---------
typedef是一个给类型起别名的关键字。不要小看了它,它对于你代码的维护会有很好的作用。比如C中没有bool,于是在一个软件中,一些程序员使用int,一些程序员使用short,会比较混乱,最好就是用一个typedef来定义,如:


    typedef char bool;
    
一般来说,一个C的工程中一定要做一些这方面的工作,因为你会涉及到跨平台,不同的平台会有不同的字长,所以利用预编译和typedef可以让你最有效的维护你的代码,如下所示:


    #ifdef SOLARIS2_5
      typedef boolean_t     BOOL_T;
    #else
      typedef int           BOOL_T;
    #endif
    
    typedef short           INT16_T;
    typedef unsigned short  UINT16_T;
    typedef int             INT32_T;
    typedef unsigned int    UINT32_T;
    
    #ifdef WIN32
      typedef _int64        INT64_T;
    #else
      typedef long long     INT64_T;
    #endif
    
    typedef float           FLOAT32_T;
    typedef char*           STRING_T;
    typedef unsigned char   BYTE_T;
    typedef time_t          TIME_T; 
    typedef INT32_T         PID_T;
    
使用typedef的其它规范是,在结构和函数指针时,也最好用typedef,这也有利于程序的易读和可维护性。如:


    typedef struct _hostinfo { 
        HOSTID_T   host; 
        INT32_T    hostId; 
        STRING_T   hostType; 
        STRING_T   hostModel; 
        FLOAT32_T  cpuFactor; 
        INT32_T    numCPUs;
        INT32_T    nDisks;
        INT32_T    memory;
        INT32_T    swap;
    } HostInfo;




    typedef INT32_T (*RsrcReqHandler)(
     void *info,
     JobArray *jobs, 
     AllocInfo *allocInfo,
     AllocList *allocList);


C++中这样也是很让人易读的:


    typedef CArray<HostInfo, HostInfo&> HostInfoArray;


于是,当我们用其定义变量时,会显得十分易读。如:


    HostInfo* phinfo;
    RsrcReqHandler* pRsrcHand;


这种方式的易读性,在函数的参数中十分明显。


关键是在程序种使用typedef后,几乎所有的程序中的类型声明都显得那么简洁和清淅,而且易于维护,这才是typedef的关键。


== Data structure alignment ==
http://en.wikipedia.org/wiki/Data_structure_alignment
http://blog.csdn.net/21aspnet/article/details/6729724
比如有些平台每次读都是从偶地址开始,一个int型(假设为 32位)如果存放在偶地址开始的地方,那么一个读周期就可以读出,而如果存放在奇地址开始的地方,就可能会需要2个读周期,并对两次读出的结果的高低字节进行拼凑才能得到该int数据,显然在读取效率上下降很多。


 对于标准数据类型,它的地址只要是它的长度的整数倍就行了,而非标准数据类型按下面的原则对齐:
  数组 :按照基本数据类型对齐,第一个对齐了后面的自然也就对齐了。 
  联合 :按其包含的长度最大的数据类型对齐。 
  结构体: 结构体中每个数据类型都要对齐。


struct stu{
   char sex;
   int length;
   char name[10];
}__attribute__ ((aligned (1))); // or __attribute__((packed))
使用最小的对齐方式,即对变量是一字节对齐,对域(field)是位对齐.


在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
另外,还有如下的一种方式:
· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
· __attribute__ ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。


== file ==


使用文件的方式共有 12 种,下面给出了它们的符号和意义。
文件使用方式
“rt”
意义
只读打开一个文本文件,只允许读数据
第 201 页“wt” 只写打开或建立一个文本文件,只允许写数据
“at” 追加打开一个文本文件,并在文件末尾写数据
“rb” 只读打开一个二进制文件,只允许读数据
“wb” 只写打开或建立一个二进制文件,只允许写数据
“ab” 追加打开一个二进制文件,并在文件末尾写数据
“rt+” 读写打开一个文本文件,允许读和写
“wt+” 读写打开或建立一个文本文件,允许读写
“at+” 读写打开一个文本文件,允许读,或在文件末追加数据
“rb+” 读写打开一个二进制文件,允许读和写
“wb+” 读写打开或建立一个二进制文件,允许读和写
“ab+” 读写打开一个二进制文件,允许读,或在文件末追加数据


---------------


int main(void){
    FILE *fp;
    char *line = NULL;
    size_t len = 0; //
    size_t read;


    if((fp=fopen("numbers.txt","rt+"))==NULL){
        printf("\nCannot open file strike any key exit!");
        exit(1);
    }


    while ((read = getline(&line, &len, fp)) != -1) {
        printf("Retrieved line of length %zu:\n", read);
        printf("%s", line);
    }


    if (line)
        free(line);


    return 0;
}


== size_t ==
size_t一般用来表示一种计数,比如有多少东西被拷贝等。例如:sizeof操作符的结果类型是size_t,该类型保证能容纳实现所建立的最大对象的字节大小。 它的意义大致是“适于计量内存中可容纳的数据项目个数的无符号整数类型”。所以,它在数组下标和内存管理函数之类的地方广泛使用。


http://xidianzhangjun.blog.163.com/blog/static/115488771201095102357389/
编译器是gcc4.3.2,大部分头文件在/usr/include目录下,但是定义size_t和ssize_t的头文件stddef.h则位于/usr/lib/gcc/i486-linux-gnu/4.3.2/include目录下,是这样定义的:
#define __SIZE_TYPE  long unsigned int
........................
typedef __SIZE_TYPE__  size_t;
.........................
即size_t实际上是无符号长整型,在32位系统上位32位,在64位系统中位64位。


== rand ==
http://blog.csdn.net/hondely/article/details/6871095


srand(time(NULL));
rand()%n;  // 范围  0~n-1
n+rand()%(m-n+1);  // n~m  这样就可以了


(rand()%n)/(n*1.0); // printf ("%lf\t",(rand()%10)/10.0); 


(rand()%n)/(n*1.0)+(rand()%n)/(n*10.0); // 0.11, (rand()%n)/10^p, p 表示精确位数.
--------
using high-order bits, as in
   j = 1 + (int) (10.0 * (rand() / (RAND_MAX + 1.0)));
and never by anything resembling
   j = 1 + (rand() % 10);
(which uses lower-order bits).
---------
http://baike.baidu.com/view/3604994.htm
RAND_MAX是C中stdlib.h中宏定义的一个字符常量:
#define RAND_MAX Ox7FFF
其值最小为32767,最大为2147483647, 通常在产生随机小数时可以使用RAND_MAX。
------------
http://blog.csdn.net/poem_qianmo/article/details/7443540
通常rand()产生的随机数在每次运行的时候都是与上一次相同的,这是有意这样设计的,是为了便于程序的调试。若要产生每次不同的随机数,可以使用srand( seed )函数进行随机化,随着seed的不同,就能够产生不同的随机数。


如大家所说,还可以包含time.h头文件,然后使用srand(time(0))来使用当前时间使随机数发生器随机化,这样就可以保证每两次运行时可以得到不同的随机数序列(只要两次运行的间隔超过1秒)。


---------
注:rand()产生的是0 to RAND_MAX (32767)上的随机数,而32767不能被11整除。
因此 int N = rand() % 11; 得到的随机数,并不是从0-10一致分布的取9,10的概率取0-8几个数字的概率少了11/32767约为1/2978;
所以要取0~M的随机数,若M较小时这样作问题不大,但M较大时,例如M=30000,则取到0-2767的概率是取后面几个数字的两倍,严重不符合随机分布! 


---------
http://man7.org/linux/man-pages/man3/rand.3.html
-------------
http://stackoverflow.com/questions/8529665/changing-probability-of-getting-a-random-number


Suppose you want 0, 1, 2, 3 to have a distribution of 5%, 20%, 30%, 45%.
You could do it like this:


double val = (double)rand() / RAND_MAX;


int random;
if (val < 0.05)       //  5%
    random = 0;
else if (val < 0.25)  //  5% + 20%
    random = 1;
else if (val < 0.55)  //  5% + 20% + 30%
    random = 2;
else
    random = 3;


== ANSI or GNU ==
GNU C允许零长度数组,在定义变长对象的头结构时,这个特性非常有用。
struct var_data s{
      int len;
      char data[0];
};
--case范围--------
GNU C 支持case x...y 这样的语法,区间[x,y]的数都会满足这个case的条件,记得数据结构试验时,有的同学为了做菜单使用了仅100个case,还好我做的是GUI的
switch(c)
{
      case '0'...'9': c-='0';
      break;
      case 'a'...'f': c-='a'-10;
      break;
      case 'A'...'F': c-='A'-10;
      break;
}
这个case的特点大家都看得出来,比标准C少敲了多少case啊
----------
为了减轻K&R C和ANSI C标准,__STDC__("standard c")巨集可以被用来将代码分割为ANSI和K&R部分。
 #if __STDC__
 extern int getopt(int, char * const *, const char *);
 #else
 extern int getopt();
 #endif
上面最好使用"#if __STDC__"而不是"#ifdef __STDC__",因为一些实现可能会把__STDC__设置为0来表示不遵循ANSI C。"__STDC__"能处理任何没有被巨集替换或者值为0的标示符。因而即使巨集"__STDC__"没有定义来表示不遵循ANSI C,"__STDC__"仍然能像显示的那样工作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值