Thursday, January 21, 2010

**作为C函数参数(2)

之前写了一篇<<**作为C函数参数>>的文章。 今天又看到这个样的代码。 不过, 比之前的多一点需要注意的地方
在glibc/login/login.c代码里:
static int
tty_name (int fd, char **tty, size_t buf_len)
{
.....((被切掉).....

  if (rv == 0)
    /*
      这里 *tty = buf已经修改了tty这个变量指向的空间.
      为什么不在改变前把传入的空间free掉呢?如:
      如果不free掉, 使用这个函数前需要有另外一个变量引用这块空间
      不用调用free, 是因为login里, _tty是一个局部变量, 会在函数退出后自己释放
    */
    *tty = buf;   /* Return buffer to the user.  */
  else if (buf != *tty)
    free (buf);   /* Free what we malloced when returning an error.  */

  return rv;
}

作者还是清楚的:

#ifdef PATH_MAX
  char _tty[PATH_MAX + UT_LINESIZE];
#else
  char _tty[512 + UT_LINESIZE];
#endif
  char *tty = _tty;
.....
found_tty = tty_name (STDIN_FILENO, &tty, sizeof (_tty));

看出, 有两个变量指向那块空间. 还是数组, 安全得很呢.

因此, 如果**作为参数时:
1. 如果在函数内, 改变了**指向的内存, 这时, **不是指向通过malloc得到的内存是安全的
2. 否则, 请确保还有其它变量引用之

如:

int len = 100;
char *name = (char *)malloc( len );
char * other = name;
tty_name (1, &name, len)

或者是:

char name[100];
char *new = name;
tty_name (1, &new, sizeof( name ));

这种做法:
char name[100];

char *new = name;

可以像malloc一样得到内存, 但是它比较安全: 会自动根据变量的作用范围释放使用的内存.

这种细节, 处理又麻烦, 不处理又不对. 纠结.... 需要长期的code能力.

Wednesday, January 20, 2010

man手册里的section 2和section 3

一直对section 2 和 section 3两个段的API分得不是很开.

section 2和3的说明:
       2   System calls (functions provided by the kernel)
       3   Library calls (functions within program libraries)

当然, 本人是分得到system calls和library calls的.

只是这两者的API有部分是一样的.

可以man 2 setgid和man 3 setgid. 可以发现, 差不多是一个屁样了.

3的存在, 是因为glibc需要有posix标准的一套API. 如glibc的setgid函数其实就是调用了system call的setgid API.

往往对section 2和3的API出现混淆. 举个例子吧:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>


int main()
{
  int fd = open("/tmp", O_DIRECTORY|O_RDONLY);
  if(fd >=0)
    printf("sucess\n");
  else
    printf("no sucess\n");
}
编译时使用: gcc  /tmp/c2.c  -D _GNU_SOURCE -o /tmp/a.out
这时的open是使用system call的open, 可以打开一个文件夹的, 手册是man 2 open里说的内容.

如果不用_GNU_SOURCE这个宏的话, 就是man 3 open里说的内容

对特同名这种函数, 使用了宏去控制


取大文件最后N行数据

取大文件最后N行数据, 这个问题, 如果是我想的话, 就直接往'\n'字符里去想了:
1. 使用seek, 从后面读一块数据出.
2. 数'\n'的个数.

看到人家这样做:

import os

def tail (name, n_line):
    lines = []
    f = file (name)
    n = 1
    while len(lines) <= n_line:
        try:
            // -n_line * 80 是一个大概猜测的一块数据长度

            f.seek (-n_line * 80 * n, os.SEEK_END)

            // 当 len( lines ) > n_line 才跳出循环, 其中, lines[0]行的数据不一定是完整的
            // 这代码的好处就是当最小条件跳出循环时, len( lines ) ==4 . 就算lines[0]是不完整的数据也没有关系. 因为只想取len(lines) - 1 行的数据.

            lines = f.readlines()


            n = n + 1
        except:
            lines = f.seek (0, os.SEEK_SET)
            lines = f.readlines ()
            break
    return lines[-n_line:]

[] 操作符用于函数参数

glibc/locale/setlocale.c文件里

113 /* Construct a new composite name.  */
114 static char *
115 new_composite_name (int category, const char *newnames[__LC_LAST])

这种代码奶奶的还真没有看过. 一般都是直接使用指针做参数的. 这里使用了 数组的形式 做参数. 有什么好处呢?

朋友给出了一个简单的例子说明了道理:
#include <stdio.h>
void test(char *p[2])
{
   puts(p[1]);
}

int main()
{
   char *p[2] = {"hello\0","world\0"};
   test(p);
}
输出为:
world
 
之所以使用数组的形式传参, 主要是为了方便地址偏移方便. 如果不是这样的话, 偏移时需要计算一下, 自己的例子说明:


void test(char **p)
{
   puts(*(p + 1));
}


int main()
{
   char *p[2] = {"hello\0","world\0"};
   test(p);
}

重点被颜色的文字标出


顺便来一个整数偏移:

void test(char **p)
{
   // convert pointer to int
   int number = (int )p;
   // convert int to pointer
   // 可以看出, 指针变量加1是偏移4bytes, 如果使用整数的话, 还需要自己计算
   p = (char **) (number + 4);
   puts(*(p));
}


int main()
{
   char *p[2] = {"hello\0","world\0"};
   test(p);
}


**作为C函数参数

C的指针一旦多层. 读代码是相当难理解.

glibc/string/argz-ctsep.c里:

 26 error_t
 27 __argz_create_sep (const char *string, int delim, char **argz, size_t *len)

这个参数有一个char **argz, 很难理解是什么意思.

后面想通了这种做法意义.

1. 指针:  期待函数可以对指向的内存进行修改, 从而得到返回值. (除指针使用const修饰)
2. 指针的指针: 期待函数返回指向一块内存的地址.

举个例子
指针的情况:
extern int change( char *buffer);
char *buffer = (char *)malloc(100);
change(buffer);
* change函数会修改buffer内存

指针的指针的情况:
extern int change( char **buffer);

char *buffer = NULL;

change( &buffer );
* change函数会返回一块已经被修改的内存.

再看一组函数:
1.  char *ttyname(int fd);
2.  int ttyname_r(int fd, char *buf, size_t buflen);

参数是char *buf。 可以猜测。 ttyname_r会对一块已经申请的内存进行修改。因为内存在函数体里无法知道长度, 所以就有size_t buflen参数。因此使用时是这样的:

size_t buflen = 100;
char *buffer = (char *) malloc( buflen ); #这是一定事先申请的
if ( ttyname_r ( 1, buffer, buflen ) ){
    ...do_you_want....
}

如果 glibc是提供了一个这样的函数: int ttyname_r(int fd, char **buf);

那么使用时就可以:
char  *buffer=NULL;
if ( ttyname_r ( 1,  &buffer ) ){

    ...do_you_want....

}

这样感觉传少了参数, 还少打代码.

但是用法没有绝对的! 这样总结只是为了看代码是容易有方法

Sunday, January 17, 2010

string.h里的函数的maxlen限制

maxlen是指最大长度。 第一次注意到maxlen这个参数是在:
size_t __strnlen (const char *str, size_t maxlen)

C里, 对内存的操作常常是一个循环, 针对于字符串的话, 循环的退出条件常常是: 一个特定的字符

1. 如'\0'
2. strchr的第二个参数

这样, 在历遍这块内存时, 需要程序员明白一点: 自己是否确认1和2的字符串在你操作的内存中?

否则, 这将是很危险的. 举个例子:

void main(int argc, char *argv){
  char a[10], b[10];
  memset(a, 'b', 10);
  a[5] = '\0';
  //这里证明a的址址比b的高
  printf("a - b = %d\n", (int)(a - b));
  //b长度就会有问题
  printf("strlen(a): %d, strlen(b): %d\n", strlen(a), strlen(b));
  //应该使用strnlen函数!, strlen和strnlen的区别就在于maxlen
  printf("strnlen(a): %d, strnlen(b): %d\n", strnlen(a, sizeof(a)), strnlen(b, sizeof(b)));
}
输出:
a - b = 10
strlen(a): 5, strlen(b): 15
strnlen(a): 5, strnlen(b): 10

如果在使用strlen的同时, 使用memset函数, 将会是灾难性的

按照这一种考虑, 把glibc/string下的函数分成如下


no maxlen
optimizemaxlen
optimizeinformation
copy






strcpy
No
strncpy
Yes
strncpy(s1, s2, maxlen)
如果maxlen > strlen(s2), 那么
剩下的都用'\0'填充. 使用strcpy最后一个也为'\0'



memset
Yes




memcpy
Yes




memccpy
No
memccpy(dest, src, '\0', maxlen)与strncp差不多,但是最后只有一个'\0'



mempcpy
Yes
与memcpy相似
duplicate






strdup
Yes
strndup

两者的区别是
调用strlen或者
调用strnlen
最后都调用
memcpy
最后byte为'\0'
find






strchr
Yes
memchr
Yes


strrchr
Yes
memrchr
Yes
strrchr调用strchr

strchrnull
Yes




strstr
Yes
memmem
Yes
using a linear algorithm with a smaller coefficient.

strcasestrYes



length






strlen
Yes
strnlen
Yes

compare






strcmp
No
strncmp
Yes




memcmpYes


strcasecmpNo
strncaseNo







concatenate






strcat
No
strncat
Yes

move








memmove


set






strspn




strcspn



other






strfry







strxfrm


* optimize的意思是被多个字节一起操作, 或者可以被compiler优化

PS:: 为了安全, 还是有mexlen参数的函数靠谱, 再者, 有maxlen的函数会快




Friday, January 15, 2010

NULL, 0x00, '\0'怎么看都是一个东西

void main(int argc, char *argv){
  char a = '\0';
  char b = (char ) 0x00;
  char c = (char )NULL;
  if ( a == b) printf("OK\n");
  if ( a == c) printf("OK\n");
}

输出結果为:
OK
OK

只是NULL对compiler来说, 会测试变量的类型, 如:
char c = NULL;
会报: warning: initialization makes integer from pointer without a cast

如果不信息(char )NULL和 NULL是相同的数值的话, 可以使用:
grep -r "define NULL"
可以看到:
define NULL (0)
define NULL 0
define NULL ((void*)0)


数组的长度

用高级语言用惯了, 想到得长度时, 一般有函数可以得到。对于C的数组, 我也是这样想的。至少还有一个sizeof

晚上和网友聊C的时候, 他贴了不少自己的笔记, 其中有这样一段:
 * 在函数参数中,array和*是一回事

从文字中, 我很明白它说的是什么意思. 但是很深的一层, 我还不了解数组, 连sizeof都不了解.

后面. 我在分析glibc/string/strlen.c的代码时, 这个问题让我重新深刻了解

在使用malloc分配空间时, 程序员是应该确切了解空间长度的. 否则后果严重.

那么, 数组的长度呢? 如:
void main(int argc, char *argv[])
{
  char s[10];
  printf("%d\n", sizeof(s));
}
输出为10

这个sizeof就让本人感觉到: 数组的长度是随时都可以得到的. 真是这样吗?

strlen.c的代码就让我提出了这个疑問:
1. 在strlen.c的函数出, 根本就没有sizeof的出现. 它就一个死循环, 去测试第一个'\0'.
2. 如果在某种情况下, 不知道参数的长度, 只有一种結果: 非法引用. 关于Segmentation, 可见: http://en.wikipedia.org/wiki/Segmentation_fault
* 某种情况是指刚刚好一直找不到'\0'

在使用strlen.c得到Segmentation fault, 是相当crazy的事!

数组参数也一样有这个问题, 这不是strlen.c里不使用sizeof的问题. 是因为sizeof也无法知道! 来个例子:

void print_len(char *p){
  printf("%d\n", sizeof(s));

}

void main(int argc, char *argv[])
{
  char s[10];
  print_len(s);
}

gcc直接就报错. 因为sizeof不能这样用. 可以大概猜出, sizeof的結果是编译器在编译时得到的值. 不是run time得到的.

可以猜出, 数组与malloc得到的连续空间的比较:
1. char a[10] 等于 char * const a = malloc(10);
2. 数组多一个在编译时得到长度的可行性
* char * const 和const char * 可是不同的哦


PS:
怪不得go lang的数组参数在调用时, 被要求测试数组的长度. 原来不是为了安全.
看来go lang像C一样强大, 又要比C安全, 是有道理的.

数另一种计算方法

朋友说, 这种技术可以从这书中得到: www.hackersdelight.org

只是没有必要去看. 了解这种用法, 只是为了看懂glibc罢了. 没有它意.

得到负数的方法


C语言里的“位与”, “位或“, ”取反“, 在数的计算中, 相当活用。

void main(int argc, char *argv[])
{
  printf("%x\n", 2);
  printf("%x\n", -2);
  printf("%x\n", ~2);
  printf("%x\n", ~2 + 1);
}
输出結果:
2
fffffffe
fffffffd
fffffffe

可以得到 -2 == (~2 + 1)

实际上就是补码: http://baike.baidu.com/view/377340.htm

divmod的算术

在glibc/string/strncmp.c中, 可以看到divmod的方法:

size_t n4 = n >> 2; n4就是商

n &= 3; 就是除以4后的余数. 因为最后两位情况是0到3, 不可能被4除.

">>"和"<<"只合适乘以2^n和除以2^n

用"&"求余又要方便, 只合适求除以2^n后的余数.

余数的求法

和前面的divmod有关系, 都是余数的问题, 只是不够深刻, 来一个深刻一些的例子:

代码来自于glibc/string/strnlen.c

 /* Handle the first few characters by reading one character at a time.
     Do this until CHAR_PTR is aligned on a longword boundary.  */
  for (char_ptr = str; ((unsigned long int) char_ptr
      & (sizeof (longword) - 1)) != 0
;
       ++char_ptr)
    if (*char_ptr == '\0')
      {
  if (char_ptr > end_ptr)
    char_ptr = end_ptr;
  return char_ptr - str;
      }
longword其实就是long int , 等于本人的电脑等于4
上面蓝色的代码就是求除以 longword 后, 余数是否为0



Thursday, January 7, 2010

import只是一次

下面是测试过程:

In [7]: import sys

In [8]: os
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)

/home/jessinio/data/workspace/project/<ipython console> in <module>()

NameError: name 'os' is not defined

In [9]: sys.getrefcount( sys.modules['os'] )
Out[9]: 58

In [10]: import os

In [11]: sys.getrefcount( sys.modules['os'] )
Out[11]: 59

In [12]: id( sys.modules['os'] )
Out[12]: 3078271308L

In [13]: id( os )
Out[13]: 3078271308L

golang中的迭代

python中的这种功能:

for key, value in a.items(): print key, value

go language也有这种功能:

func main() {
    var infomation string = "中国比美国好多了.";
    fmt.Println( infomation );
    //是unicode吗? 答案:否
    fmt.Println( len( infomation ) );
    fmt.Printf("%c\n", infomation[0]);
    for index, value := range infomation {
        //%c与C中的%c不同      the character represented by the corresponding Unicode code point
        fmt.Printf("%d:%c ", index, value);
    }
}
输出結果:
中国比美国好多了.
25
ä
0:中 3:国 6:比 9:美 12:国 15:好 18:多 21:了 24:.

range一个string时, 为unicode code point, 而不是byte

golang中的字符串

golang的String type与python的basestring实例差不多.

immutable

python里str的情况:
In [3]: name = str("abcde")
In [4]: name[1]
Out[4]: 'b'
In [5]: name[1] = "3"
TypeError: 'str' object does not support item assignment

python里unicode的情况:
In [6]: name = unicode("abcde")
In [7]: name[2]
Out[7]: u'c'
In [8]: name[2] = u'f'
TypeError: 'unicode' object does not support item assignment

golang中的string类型也是immutable的:

func main() {

    var name string = "abcd";
    fmt.Printf("%c\n", name[2]);
    //这种修改将会产出错
    name[1] = 'c';
    //只能得到string变量的开始地址.
    fmt.Printf("%p\n", &name);
    //下面会报错, 因为不能求元素的地址!
    fmt.Prinft("%p\n", &name[2]);

}

string literals

在字符串上, golang使用三种符号:
1. 单引号: 与C一样, 表示是byte,
2. 双引号: 与C一样, 表示N种byte,  只是golang中不可以修改和求元素的地址.
3. 反引号: 不解释字符串的转义符.

python中也有这三种用法, 只是使用的表达方式:
1. 单(双)引号: 表示N种chr实例的集合
2. u开头的unicode string literals, 还没有发现golang有unicode string literals
3. r开头的string literals:

In [10]: print   r'a\nb'
a\nb

unicode问题

双引号得到的是不可修改的byte集. 不是unicode类型!

下面做一个测试, 先得到一个汉字的外码和unicode码:
In [14]: u'中'
Out[14]: u'\xe4\xb8\xad'
In [15]: unicode('中', "utf-8")
Out[15]: u'\u4e2d'

再看golang的代码:

func main() {
    var infomation string = "\u4e2d"; //使用unicode的标志法, 也无法改变string就是byte集的现实!
    fmt.Println( infomation );
    //是unicode吗? 答案:否
    fmt.Println( len( infomation ) );
    fmt.Printf("%x", infomation);
}

输出的結果是:

3
e4b8ad

golang中怎么样python一样, 有unicode string literals呢? 答案: 没有!

只有使用函数得到。


golang中的slice

slice 数据类型大概原理是这样的:

type Slice struct {
  base *elem_type;
  len int;
  cap int;
}

最容易和数组乱的就是下面的这样声明:
  var a []int;

空长度表示变量是一个slice type, 不是空数组。

下面的使用方式:
var slice = []int{ 1,2,3,4,5 }; //这是产生一个数组, 然后产生一个slice
* 在这种背后创建了数组的情况, golang提供了reslice和cap函数功能

与下面的是不同的:
var ar = [...]int{ 1,2,3,4,5 };

golang中的slice与python中的list的slice是不同的.
1. [:8] 没有隐含的开始方式, 也没有隐含的结束方式
2. [1:999999999] 超出不等于到最后, 报错!
3. 没有负数的end, 报错.

func main() {
    //创建一个slice, 它是引用[8]int的
    ar := []int {0, 1, 2, 3, 4, 5, 6, 7};
    //slice类型支持reslice, 調整它的引用情况
    ar = ar[2:4];
    fmt.Printf("length: %d\n", len(ar));
    fmt.Println(ar[0]);
   
    //怎么重新引用[8]int的完整长度呢? 答案是否定的
    //提供了cap()函数
    ar = ar[0:cap(ar)];
    fmt.Printf("length: %d\n", len(ar));
    fmt.Println(ar[0]);
}

输出結果是:
length: 2
2
length: 6
2

reslice无法修改slice的起始地址. 只能修改结束地址. capacity的长度会保存不变(除非引用了其它数组)

一步产生数组和slice变量:

var a = &[100]int{};

但是这样是不行的:

var a = &[100]int;

还可以使用make()函数:
a := make([]int, 100);

make([]int) 是等于[]int , 为一个slice对象

new([]int) 返回 *[]int , 为指向slice的指针变量.

Wednesday, January 6, 2010

golang中的指针数组

在golang的文档里没有看到怎么声明指针数组的例子(不知道是不是自己看漏了)

文档里本人只看到“指向数组的指针”的声明。如:
  var p2array *[3]int;

不知道什么时候突然一个想法就出来了:
  var pointers [3]*int;
这就是指针数组的声明。和文档里说的指向数组的指针的声明是不同的。

试了一下, 还真的可以的, 牛B!!

import "fmt"
func main() {

    var ar [3]*int;
    //这是真的吗?
    fmt.Println(len(ar));
   
    value := new(int);
    *value = 3;
    //存放指针
    ar[0] = value;
    fmt.Println(*(ar[0]));

}


Tuesday, January 5, 2010

amule下载完的文件名乱码问题

最近使用amule的次数比较好, 发现amule像一个金矿, 里面太奶奶的多好货了.

当然, 也不是白取的, 从我notebook上upload的数据都有33G多了. 下载点也是正常的.

不过, 奶奶的, 今天在Incoming里找文件, 无法忍受amule下载的文件名是乱码一事~ 决定清理!

In [76]: f = os.listdir('.')

In [77]: for i in range(len(f)):
                   if f[i].startswith("LINUX"):
                           print i
Out[77]: 8
找出自己要试验的一条文件名

乱码, 一般都是gbk编码吧, 于是:
In [81]: print f[8].decode("gbk")
-------> print(f[8].decode("gbk"))
LINUX莽鲁禄莽禄聼莽庐隆莽聬聠忙聤聙忙聹炉忙聣聥氓聠聦茂录聢莽卢卢2莽聣聢茂录聣.pdf


顶~~~~~~ 屁都不是. 又试了几种编码都不行~~~~ 试多了几次, 发现一个现象:

In [87]: f[8].decode("utf-8")
Out[87]: u'LINUX\xe7\xb3\xbb\xe7\xbb\x9f\xe7\xae\xa1\xe7\x90\x86\xe6\x8a\x80\xe6\x9c\xaf\xe6\x89\x8b\xe5\x86\x8c\xef\xbc\x88\xe7\xac\xac2\xe7\x89\x88\xef\xbc\x89.pdf'

In [88]: print "\xe7\xb3\xbb"
-------> print("\xe7\xb3\xbb")


* 从上面取出的一段外码, 是可以正确显示的. 看来amule是把unicode的文件名直接给str()成raw string串了. 乱码就是这样产生的:

In [108]: c = u'梁'

In [109]: c
Out[109]: u'\xe6\xa2\x81'

In [110]: str(c)
Out[110]: '\xc3\xa6\xc2\xa2\xc2\x81'

In [111]: print str(c)
--------> print(str(c))
æ¢

现在的问题是怎么把上面一串为unicode的外码整成raw string呢?

现在的目标是把u'\xe7'转成'\xe7', 如下方法:
In [153]: chr(ord(u'\xe7'))
Out[153]: '\xe7'


OK, 妈的。 一个for处理完你~~~ 狗日的乱码

#!/usr/bin/env python
# coding: utf-8
import os

DIR="/home/jessinio/.aMule/Incoming"

for f in os.listdir( DIR ):
    print f
    retval = []
    for i in f.decode("utf-8"):
        # 文件名本身没有乱码的情况时, 会是unicode, 这时ord的值为unicode的内码, 超过256
        if ord(i) > 256:
            retval.append(i.encode("utf-8"))
        else:
            retval.append(chr(ord(i)))
    print ''.join(retval)
    os.rename( os.path.join(DIR, f), os.path.join( DIR, ''.join(retval) ) )


golang中的指针

总结一下golang中的指针与C语言的指针的用法.

总体一致:
C的代码:
    int *number;
    number = (int *) malloc(sizeof(int));
    *number = 3;
    printf("%d\n", *number);

golang的代码:
    var pointer *int;
    pointer = new(int);
    *pointer = 3;
    fmt.Println(*pointer);

多重指针情况:
C的代码:
    int **outer;
    int *inter;
    inter = (int *) malloc(sizeof(int));

    *inter = 3;
    outer = &inter;
    //地址一样
    printf("%p\n", inter);
    printf("%p\n", *outer);
    //值一样
    printf("%d\n", *inter);
    printf("%d\n", **outer);

golang的代码:
    var outer **int;
    var inter *int;
    inter = new(int);
    *inter = 3;
    outer = &inter;
    //地址一样
    fmt.Println(inter);
    fmt.Println(*outer);
    //值一样
    fmt.Println(*inter);
    fmt.Println(**outer);

C语言的下面这种方式在golang里实现:
    int **outer;
    int *inter;
    inter = (int *) malloc(sizeof(int));
    outer = (int **) malloc(sizeof(int));
   
    *inter = 3;
    *outer = inter;
    //地址一样
    printf("%p\n", inter);
    printf("%p\n", *outer);
    //值一样
    printf("%d\n", *inter);
    printf("%d\n", **outer);

在golang中:
    var inter *int;
    var outer **int;
    inter = new(int);
    *inter = 3;
    outer = new(*int);
    *outer = inter;
    //地址一样
    fmt.Println(inter);
    fmt.Println(*outer);
    //值一样
    fmt.Println(*inter);
    fmt.Println(**outer);

上面都是在玩指针, 下面看看基本的数据结构

基本的数据结构有: 数组与结构体 (map和树之类的不在討論范围)

golang中的数组与C中的数组有很大的差别

golang中的数组是这样说的: Arrays are values, not implicit pointers as in C.

0. 数组做参数时, 需要被检查长度.
1. 变量名不等于数组开始指针!
2. 不支持C中的*(ar + sizeof(int))方式的指针移动. 需要使用到unsafe包
3. 如果p2array为指向数组的指针, *p2array不等于p2array[0]

例子0 数组做参数时, 需要被检查长度.

func use_array( args [4]int ){
    args[1] = 100;
}

func main() {
    var args = [5]int{1, 2, 3, 4, 5};
    use_array(args);
    fmt.Println(args);
}

编译出错: cannot use args (type [5]int) as type [4]int in function argument, 需要有长度上的检查

例子1 变量名不等于数组开始指针!

func use_array( args [4]int ){
    args[1] = 100;
}

func main() {
    var args = [5]int{1, 2, 3, 4, 5};
    use_array(args);
    fmt.Println(args);
}

输出結果是 [1 2 3 4], 没有保存結果, 数组名的用法与C的不一样. 在golang里是这样的:

// 又长度检查, 也为地址传参
func use_array( args *[4]int ){
    args[1] = 100;  //但是使用还是和C一致, 不需要别加"*"操作符
}
            
func main() {        
    var args = [4]int{1, 2, 3, 4};
    use_array(&args); //数组名已经不是表示地址了, 需要使用"&"得到地址
    fmt.Println(args);
}

例子2 如果p2array为指向数组的指针, *p2array不等于p2array[0]
对比一下C和golang在这方面的差别:
void main(int argc, char *argv[]){
    int *p2array;
    p2array = (int *) malloc(sizeof(int) * 3);
    //等于p2array[0]
    *p2array = 0;
    printf("%d\n", *p2array + 1);
}
* 输出为1

func main() {
    var p2array *[3]int ;
    p2array = new([3]int);
    fmt.Printf("%x\n", *p2array + 1); //不管p2array是指针变量还是数组变量, 都只能使用"[]"方式使用
}
* 报错.

golang中的结构体也与C中的有差别


下面的方式是相当一致的:
C版本的:
    typedef struct
    {  
        int x;
        int y;
    } Point;

    Point p;
    p.x = 10;
    p.y = 20;
   
    //开始地址
    printf("%p\n", &p);
    //某元素地址
    printf("%p\n", &(p.x));

golang版本的:
    type Point struct{
        x int;
        y int;
    };
   
    var p Point;
    p.x = 10;
    p.y = 20;
    fmt.Printf("%p\n", &p);
    fmt.Printf("%p\n", &(p.x));

使用allocate的方式:
C代码:
    typedef struct
    {  
        int x;
        int y;
    } Point;

    Point *p;
    p = (Point *) malloc(sizeof(Point));
    p->x = 10;
    p->y = 20;
   
    //开始地址
    printf("%p\n", p); //地址
    //某元素地址
    printf("%p\n", &(p->x));

golang代码:
    type Point struct{
        x int;
        y int;
    };
   
    var p *Point;
    p = new(Point);
    p.x = 10;
    p.y = 20;
    fmt.Printf("%p\n", p); //地址
    fmt.Printf("%p\n", &(p.x));

也可以说是一样的, 只不过在使用结构中的元素时没有了"->"操作符:
There is no -> notation for structure pointers. Go provides the indirection for you.  

结构体的地址传参与数组的方式一样, 当然, 和C的风格也是一模一样的. 如下例子:

C代码:
#include <stdio.h>
#include <stdlib.h>
typedef struct
    {
        int x;
        int y;
    } Point;
    
void use_struct(Point *arg){
    arg->x = 100;
}

void main(int argc, char *argv[]){
    
    Point *p;
    p = (Point *) malloc(sizeof(Point));
    p->x = 10;
    p->y = 20;
    
    use_struct(p);
    printf("%d\n", p->x);

}

golang代码:
import "fmt"

type Point struct{
    x int;
    y int;
};

func use_sturct( p *Point ){
    p.x = 100;
}

func main() {

    var p *Point;
    p = new(Point);
    p.x = 10;
    p.y = 20;
    use_sturct(p);
    fmt.Printf("%d\n", p.x);
}

总的来说......

在传参方面, 大体上看与C的相同:

  f(ar);    // passes a copy of ar

  fp(&ar);  // passes a pointer to ar
* 只是变量名是不是表示首个地址 有区别


"&" 与C语言一样是得到变量的指针. 与C有点不同, 取golang中指针指针的内容的值是不需要使用"*"操作符的, 但是指针的指针(或者继续多层时)需要显式使用"*"符号.
在http://golang.org/doc/go_spec.html#Selectors 一节中有描述。

* 三层以上的指针使用是比较少的. 两层还是常见, 如main函数的参数char * argv[]



Monday, January 4, 2010

man命令出现 ESC[ 控制符

安装gentoo时, 出现使用man查看手册时出现一堆 ESC[ 之类的字符。
这些字符是consolse下的颜色控制。

了解了一下。 原来man命令也是调用外部程序来显示的。三个选项:
-P, -B, -H

然后对比一下ubuntu下的man和gentoo下的man, 发现不同的man手册这方面已经变化。

下面是gentoo的(man version 1.6f)
       -P  pager
              Specify which pager to use.  This option  overrides  the  MANPAGER  environment  variable,
              which in turn overrides the PAGER variable.  By default, man uses /usr/bin/less -is.

下面是ubuntu9.10的(man 2.5.6)
       -P pager, --pager=pager
              Specify which output pager to use.  By default, man uses pager -s.  This option overrides the $MAN‐
              PAGER environment variable, which in turn overrides the $PAGER environment  variable.   It  is  not
              used in conjunction with -f or -k.

调用的程序已经不同.

在不安装pager程序下, 可以使用less程序的-R参数

       -R or --RAW-CONTROL-CHARS
              Like  -r, but only ANSI "color" escape sequences are output in "raw" form.  Unlike -r, the
              screen appearance is maintained correctly in most cases.  ANSI  "color"  escape  sequences
              are sequences of the form:

所以, 让gentoo下的man不出现一堆 ESC[ , 可以:
export PAGER='less -R'