🚙

💨 💨 💨

×

  • Categories

  • Archives

  • Tags

  • About

Unity中C#调用C++写的DLL之Swig篇

Posted on 09-13-2015 | In Misc

近来要用Unity打包到安卓上玩, Unity那边需要用到服务器中用C++写的库,
对比了 P/Invoke 和 C++/CLI 两种方式, 都不够省心省力, 决定使用 Swig来撸.

教程基本上按照这篇文章就可以, 文章写得非常详尽,

但文中关于设置 swiglib.i 自定义生成工具的命令行的时候,

他文中的下面一段要注意 :

在常规中选择命令行并且写入:

echo on
$(SolutionDir)/../../thirdpart/swigwin-3.0.12/swig.exe -c++ -csharp -outdir “$(SolutionDir)/../../../UnityProj/UnityCppLearn/Assets/SwigTools/Interface” “%(FullPath)”
echo off

应改成 :

我们在自己填的时候要记得改成自己项目中的路径, 以及把上面这段命令中的中文引号改成英文引号.

hash索引btree索引聚簇索引非聚簇索引

Posted on 09-12-2015 | In DB

索引概要

索引是帮助mysql获取数据的数据结构。最常见的索引是

  • Btree索引
  • Hash索引

不同的引擎对于索引有不同的支持:

  • Innodb和MyISAM默认的索引是Btree索引;
  • Mermory默认的索引是Hash索引。

Hash索引

Mermory默认的索引是Hash索引。

所谓Hash索引,当我们要给某张表某列增加索引时,将这张表的这一列进行哈希算法计算,得到哈希值,

排序在哈希数组上。所以Hash索引可以一次定位,其效率很高,而Btree索引需要经过多次的磁盘IO,
但是innodb和myisam之所以没有采用它,是因为它存在着好多缺点.

Hash索引的缺点

  • Hash 索引仅仅能满足”=”,”IN”和”<=>”查询,不能使用范围查询。
    由于 Hash 索引比较的是进行 Hash 运算之后的 Hash 值,所以它只能用于等值的过滤,不能用于基于范围的过滤,因为经过相应的 Hash 算法处理之后的 Hash 值的大小关系,并不能保证和Hash运算前完全一样。

  • Hash 索引无法被用来避免数据的排序操作。
    由于 Hash 索引中存放的是经过 Hash 计算之后的 Hash 值,而且Hash值的大小关系并不一定和 Hash 运算前的键值完全一样,所以数据库无法利用索引的数据来避免任何排序运算;

  • Hash 索引不能利用部分索引键查询。
    对于组合索引,Hash 索引在计算 Hash 值的时候是组合索引键合并后再一起计算 Hash 值,而不是单独计算 Hash 值,所以通过组合索引的前面一个或几个索引键进行查询的时候,Hash 索引也无法被利用。

  • Hash 索引在任何时候都不能避免表扫描。
    前面已经知道,Hash 索引是将索引键通过 Hash 运算之后,将 Hash运算结果的 Hash 值和所对应的行指针信息存放于一个 Hash 表中,由于不同索引键存在相同 Hash 值,所以即使取满足某个 Hash 键值的数据的记录条数,也无法从 Hash 索引中直接完成查询,还是要通过访问表中的实际数据进行相应的比较,并得到相应的结果。

  • Hash 索引遇到大量Hash值相等的情况后性能并不一定就会比B-Tree索引高。
    对于选择性比较低的索引键,如果创建 Hash 索引,那么将会存在大量记录指针信息存于同一个 Hash 值相关联。这样要定位某一条记录时就会非常麻烦,会浪费多次表数据的访问,而造成整体性能低下

Btree索引

Innodb和MyISAM默认的索引是Btree索引;
至于Btree索引,它是以B+树为存储结构实现的。
但是Btree索引的存储结构在Innodb和MyISAM中有很大区别。

btree索引在MyISAM中的实现

在MyISAM中,我们如果要对某张表的某列建立Btree索引的话,如图:

所以我们经常会说MyISAM中数据文件和索引文件是分开的。
因此MyISAM的索引方式也称为非聚集,
至于辅助索引,类似于主索引,唯一区别就是主索引上的值不能重复,而辅助索引可以重复。

因此当我们根据Btree索引去搜索的时候,若key存在,在data域找到其地址,然后根据地址去表中查找数据记录。

btree索引在Innodb中的实现

至于Innodb它跟上面又有很大不同,它的叶子节点存储的并不是表的地址,而是数据

我们可以看到这里并没有将地址放入叶子节点,而是直接放入了对应的数据,

这也就是我们平常说到的,Innodb的索引文件就是数据文件,

那么对于Innodb的辅助索引结构跟主索引也相差很多,如图:

我们可以发现,这里叶子节点存储的是主键的信息,

所以我们在利用辅助索引的时候,检索到主键信息,

然后再通过主键去主索引中定位表中的数据,这就可以说明Innodb中主键之所以不宜用过长的字段,由于所有的辅助索引都包含主索引,

所以很容易让辅助索引变得庞大。

我们还可以发现:在Innodb中尽量使用自增的主键,

这样每次增加数据时只需要在后面添加即可,

非单调的主键在插入时会需要维持B+tree特性而进行分裂调整,十分低效。

Btree索引中的最左匹配原则:

Btree是按照从左到右的顺序来建立搜索树的。

比如索引是(name,age,sex),

会先检查name字段,如果name字段相同再去检查后两个字段。

所以当传进来的是后两个字段的数据(age,sex),

因为建立搜索树的时候是按照第一个字段建立的,所以必须根据name字段才能知道下一个字段去哪里查询。

所以传进来的是(name,sex)时,首先会根据name指定搜索方向,但是第二个字段缺失,所以将name字段正确的都找到后,然后才会去匹配sex的数据。

建立索引的规则:

  • 利用最左前缀:
    Mysql会一直向右查找直到遇到范围操作(>,<,like、between)就停止匹配。
    比如a=1 and b=2 and c>3 and d=6;此时如果建立了(a, b, c, d)索引,那么后面的d索引是完全没有用到,当换成了(a, b, d, c)就可以用到。

  • 不能过度索引:
    在修改表内容的时候,索引必须更新或者重构,所以索引过多时,会消耗更多的时间。

  • 尽量扩展索引而不要新建索引

  • 最适合的索引的列是出现在where子句中的列或连接子句中指定的列。

  • 不同值较少的列不必要建立索引(性别)。

练习题

  1. 数据索引的正确是(正确答案A, D)

    A、一个表只能有一个聚族索引,多个非聚族索引
    B、字符串模糊查询不适合索引
    C、哈希 索引有利于查询字段用于大小范围的比较查询
    D、多余的索引字段会降低性能

  2. Select A,B from Table1 where A between 60 and 100 order by B,下面哪些优化sql性能(正确答案B)

    A、字段A 建立hash索引,字段 B不建立索引
    B、字段 A 建立btree索引,字段 B不建立索引
    C、字段A 不建立 索引,字段 B建立btree索引

GDB多进程多线程调试实战

Posted on 08-31-2015 | In Linux

GDB多进程/多线程调试实战例子

在gcc编译的时候,记得附加 -lpthread参数, 否则会出现 undefined reference to ‘pthread_create’ 的错误.
(因为在链接的时候,无法找到phread库中哥函数的入口地址,于是链接会失败。)

例程

#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

void processA();
void processB();
void *processAworker(void *arg);

int main(int argc, const char *argv[])
{
int pid;

pid = fork();

if (pid != 0)
processA();
else
processB();

return 0;
}

void processA()
{
pid_t pid = getpid();
char prefix[] = "ProcessA: ";
char tprefix[] = "thread ";
int tstatus;
pthread_t pt;

printf("%s%lu %s\n", prefix, pid, "step1");

tstatus = pthread_create(&pt, NULL, processAworker, NULL);
if (tstatus != 0)
{
printf("ProcessA: Can not create new thread.");
}

processAworker(NULL);
sleep(1);
}

void *processAworker(void *arg)
{
pid_t pid = getpid();
pthread_t tid = pthread_self();
char prefix[] = "ProcessA: ";
char tprefix[] = "thread ";

printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step2");
printf("%s%lu %s%lu %s\n", prefix, pid, tprefix, tid, "step3");

return NULL;
}

void processB()
{
pid_t pid = getpid();
char prefix[] = "ProcessB: ";
printf("%s%lu %s\n", prefix, pid, "step1");
printf("%s%lu %s\n", prefix, pid, "step2");
printf("%s%lu %s\n", prefix, pid, "step3");
}

输出

[cnwuwil@centos c-lab]$ ./test
ProcessA: 802 step1
ProcessB: 803 step1
ProcessB: 803 step2
ProcessB: 803 step3
ProcessA: 802 thread 3077555904 step2
ProcessA: 802 thread 3077555904 step3
ProcessA: 802 thread 3077553008 step2
ProcessA: 802 thread 3077553008 step3

GDB多进程调试命令

  • set follow-fork-mode [parent|child] set detach-on-fork [on|off]
follow-fork-mode detach-on-fork 说明
parent on 只调试主进程(GDB默认)
child on 只调试子进程
parent off 同时调试两个进程,GDB跟主进程,子进程block在fork位置
child off 同时调试两个进程,GDB跟子进程,主进程block在fork位置
  • 查询正在调试的进程:info inferiors
  • 切换调试的进程: inferior +inferior number
  • catch fork命令可以捕获进程的创建
  • attach + pid , 可以附到一个正在运行的进程上进行调试

. . .

Putty配置

Posted on 08-23-2015 | In Misc

平时工作学习必须要使用Windows,在SSH远程连接软件里Putty算是用得比较顺手的,而且很小巧。

113555117.png


但是每次输入密码很麻烦,还容易输错,OpenSSH可以利用密钥来自动登陆,如此一来方便了不少。配置过程分为三步:

1、生成公钥和私钥

先要下载一个叫puttygen的软件(下载见附件),在Windows端生成公钥和私钥。

点击Generate开始生成

113555442.png

在生成过程中用鼠标在进度条下面的空白处乱晃几下,产生随机性:

113555424.png

生成完毕,将私钥保存起来:

131658317.png

然后将公钥全选复制。

131248930.png


2、远程主机配置

我这里使用的是CentOS6.4,已经安装了OpenSSH,如果远程主机没有安装的,先要安装。

先连接上远程主机,然后输入命令

mkdir .ssh
chmod 700 .ssh
vim ~/.ssh/authorized_keys

按“i”键进入编辑模式(用过vi/vim的都应该知道吧),然后点鼠标右键将刚才复制的公钥粘贴进去,然后按“Esc”,输入wq<Enter>保存。

131401631.png

安全起见,设置验证文件为只读:


chmod 400 ~/.ssh/authorized_keys


3、Putty端配置


先到Connection-Data项设置自己的登陆用户名,如图(我的是root):

131535572.png


再点SSH项下面的Auth,添加第一步保存的私钥

131534643.png


然后很重要的是要回去Session项里保存!!!不然下次又得重新添加一遍

132022649.png


然后再双击Default Settings里保存的任务,就直接登陆进去了:

wKiom1MP7Wfh2XdgAABhqqyu7f4525.png

是不是很棒~


最后再优化一下显示设置(转过来的):

字体大小设置
Window->Appearance->Font settings—>Change按钮设置(我的设置为12)
字体颜色设置
Window->Colours->Default Foreground->Modify设置(我喜欢绿色设置:R:0 G:255 B:64)
此外在默认的黑色背景下 蓝色看不太清楚,可以把Window->Colours->ANSI Blue 更改一下设置(我设置为R:255 G:0 B:128)
全屏/退出全屏的快捷键设置
Window->Behaviour最下面有个Full screen on Alt-Enter 勾上就可以了。

putty配置导出的方法

PuTTY 是一款小巧的开源 Telnet/SSH 客户端,但是它不提供设置的导入导出工具,PuTTY 将设置都保存在注册表中,所以要备份主要就是要备份注册表里的资料。

下面是备份步骤,实质上就是导出相应的注册表键值:

  1. 开始->运行(Win+R)->regedit
  2. 找到 HKEY_CURRENT_USER\Software\SimonTatham
  3. 在 SimonTatham 这个节点上点击右键,选择导出,保存。

如果你想恢复配置信息,只需要双击保存的这个文件,导入注册表信息即可。

说明:注册表PuTTY下的Sessions中保存设置连接的项目和设定值,SshHostKeys保存设置过的Remote Host Public Key。

KeepAlive

很多远程主机当你一段时间没有输入, 他就会把你踢下线, 所以需要 KeepAlive 功能,

KeepAlive

如果填写 0 , 就表示不需要 KeepAlive 功能,
填写大于 0 的数, 比如 4, 就意味着每 4 秒就会发送一个空包到远程主机来 KeepAlive .
所以建议填写8秒左右的数.

SSH 证书登陆配置

sudo vi /etc/ssh/sshd_config

取消注释 : #AuthorizedKeysFile .ssh/authorized_keys
禁止密码登录 : 修改yes->no : PasswordAuthentication no

然后重启ssh : sudo service sshd restart

Putty server refused our key的三种原因和解决方法

server refused our key 是非常容易遇到的错误

1、.ssh文件夹权限错或authorized_keys权限错

.ssh 以及其父文件夹(root为/root,普通用户为Home目录)都应该设置为只有该用户可写(比如700)。
且 设置 authorized_keys 的权限为 400

chmod 700 .ssh
chmod 400 ~/.ssh/authorized_keys

以下为原因:
ssh服务器的key方式登录对权限要求严格。

  • 对于客户端: 私钥必须为600权限或者更严格权限(400), 一旦其他用户可读, 私钥就不起作用(如640), 表现为系统认为不存在私钥
  • 对于服务器端: 要求必须公钥其他用户不可写, 一旦其他用户可写(如660), 就无法用key登录, 表现为:Permission denied (publickey).

同时要求.ssh目录其他用户不可写,一旦其他用户可写(如770), 就无法使用key登录, 表现为:Permission denied (publickey).

2、SElinux导致

密钥文件不能通过SElinux认证,解决方法如下:

# restorecon -R -v /home #root用户为/root

我遇到的就是这种情况,找了好久还找到是这个原因,因为是新装的虚拟机,SElinux还没关闭。
这篇博文详细得说明了原因:http://www.toxingwang.com/linux-unix/linux-basic/846.html

3、sshd配置不正确

正确配置方法如下:
/etc/ssh/sshd_config

1、找到 #StrictModes yes 改成 StrictModes no (去掉注释后改成 no)

2、找到 #PubkeyAuthentication yes 改成 PubkeyAuthentication yes (去掉注释)

3、找到 #AuthorizedKeysFile .ssh/authorized_keys 改成 AuthorizedKeysFile .ssh/authorized_keys (去掉注释)

4、保存 5、/etc/rc.d/init.d/sshd reload 重新加载

Linux 常用SIG信号及其键值

Posted on 08-04-2015 | In Linux
  • 01 SIGHUP 挂起(hangup)
  • 02 SIGINT 中断,当用户从键盘按^c键或^break键时
  • 03 SIGQUIT 退出,当用户从键盘按quit键时
  • 04 SIGILL 非法指令
  • 05 SIGTRAP 跟踪陷阱(trace trap),启动进程,跟踪代码的执行
  • 06 SIGIOT IOT指令
  • 07 SIGEMT EMT指令
  • 08 SIGFPE 浮点运算溢出
  • 09 SIGKILL 杀死、终止进程
  • 10 SIGBUS 总线错误
  • 11 SIGSEGV 段违例(segmentation violation),进程试图去访问其虚地址空间以外的位置
  • 12 SIGSYS 系统调用中参数错,如系统调用号非法
  • 13 SIGPIPE 向某个非读管道中写入数据
  • 14 SIGALRM 闹钟。当某进程希望在某时间后接收信号时发此信号
  • 15 SIGTERM 软件终止(software termination)
  • 16 SIGUSR1 用户自定义信号1
  • 17 SIGUSR2 用户自定义信号2
  • 18 SIGCLD 某个子进程死
  • 19 SIGPWR 电源故障

C++链接性之extern和static和const的用法

Posted on 07-19-2015 | In Misc

基本解释

基本解释:extern可以置于变量或者函数前,以标示变量或者函数的定义在别的文件中,提示编译器遇到此变量和函数时在其他模块中寻找其定义。此外extern也可用来进行链接指定。

也就是说extern有两个作用:

  • 当它与”C”一起连用时,如: extern “C” void fun(int a, int b);则告诉编译器在编译fun这个函数名时按着C的规则去翻译相应的函数名而不是C++的,C++的规则在翻译这个函数名时会把fun这个名字变得面目全非,可能是fun@aBc_int_int#%$也可能是别的,这要看编译器的”脾气”了(不同的编译器采用的方法不一样),为什么这么做呢,因为C++支持函数的重载啊,在这里不去过多的论述这个问题,如果你有兴趣可以去网上搜索,相信你可以得到满意的解释!

  • 当extern不与”C”在一起修饰变量或函数时,如在头文件中: extern int g_Int; 它的作用就是声明函数或全局变量的作用范围的关键字,其声明的函数和变量可以在本模块或其他模块中使用,记住它是一个声明不是定义!也就是说B模块(编译单元)要是引用模块(编译单元)A中定义的全局变量或函数时,它只要包含A模块的头文件即可,在编译阶段,模块B虽然找不到该函数或变量,但它不会报错,它会在链接时从模块A生成的目标代码中找到此函数。

extern 变量

在一个源文件里定义了一个数组:char a[6];
在另外一个文件里用下列语句进行了声明:extern char *a;
请问,这样可以吗?
答案与分析:

  • 不可以,程序运行时会告诉你非法访问。原因在于,指向类型T的指针并不等价于类型T的数组。extern char *a声明的是一个指针变量而不是字符数组,因此与实际的定义不同,从而造成运行时非法访问。应该将声明改为extern char a[ ]。
  • 例子分析如下,如果a[] = “abcd”,则外部变量a=0x61626364 (abcd的ASCII码值),*a显然没有意义显然a指向的空间(0x61626364)没有意义,易出现非法内存访问。
  • 这提示我们,在使用extern时候要严格对应声明时的格式,在实际编程中,这样的错误屡见不鲜。
  • extern用在变量声明中常常有这样一个作用,你在.c文件中声明了一个全局的变量,这个全局的变量如果要被引用,就放在.h中并用extern来声明。

extern “c”

在C++环境下使用C函数的时候,常常会出现编译器无法找到obj模块中的C函数定义,从而导致链接失败的情况,应该如何解决这种情况呢
答案与分析:

C++语言在编译的时候为了解决函数的多态问题,会将函数名和参数联合起来生成一个中间的函数名称,而C语言则不会,因此会造成链接时找不到对应函数的情况,此时C函数就需要用extern “C”进行链接指定,这告诉编译器,请保持我的名称,不要给我生成用于链接的中间函数名。
下面是一个标准的写法:

//在.h文件的头上
#ifdef __cplusplus
#if __cplusplus
extern "C"{
 #endif
 #endif /* __cplusplus */
 …
 …
 //.h文件结束的地方
 #ifdef __cplusplus
 #if __cplusplus
}
#endif
#endif /* __cplusplus */

定义放在头文件还是源文件中?

常常见extern放在函数的前面成为函数声明的一部分,那么,C语言的关键字extern在函数的声明中起什么作用?
答案与分析:

如果函数的声明中带有关键字extern,仅仅是暗示这个函数可能在别的源文件里定义,没有其它作用。即下述两个函数声明没有明显的区别:
extern int f(); 和int f();
当然,这样的用处还是有的,就是在程序中取代include “*.h”来声明函数,在一些复杂的项目中,我比较习惯在所有的函数声明前添加extern修饰。关于这样做的原因和利弊可见下面的这个例子:“用extern修饰的全局变量”

(1)在test1.h中有下列声明 :

#ifndef TEST1H
#define TEST1H
extern char g_str[]; // 声明全局变量g_str
void fun1();
#endif

(2)在test1.cpp中 :

#include "test1.h"
char g_str[] = "123456"; // 定义全局变量g_str
void fun1() { cout << g_str << endl; }

(3)以上是test1模块, 它的编译和链接都可以通过,如果我们还有test2模块也想使用g_str,只需要在原文件中引用就可以了 :

#include "test1.h"
void fun2() { cout << g_str << endl; }

以上test1和test2可以同时编译链接通过,如果你感兴趣的话可以用ultraEdit打开test1.obj,你可以在里面找到”123456”这个字符串,但是你却不能在test2.obj里面找到,这是因为g_str是整个工程的全局变量,在内存中只存在一份,test2.obj这个编译单元不需要再有一份了,不然会在链接时报告重复定义这个错误!

(4) 有些人喜欢把全局变量的声明和定义放在一起,这样可以防止忘记了定义,如把上面test1.h改为

extern char g_str[] = "123456"; // 这个时候相当于没有extern

然后把test1.cpp中的g_str的定义去掉,这个时候再编译链接test1和test2两个模块时,会报链接错误,这是因为你把全局变量g_str的定义放在了头文件之后,

test1.cpp这个模块包含了test1.h所以定义了一次g_str,而test2.cpp也包含了test1.h所以再一次定义了g_str,这个时候链接器在链接test1和test2时发现两个g_str。如果你非要把g_str的定义放在test1.h中的话,那么就把test2的代码中#include “test1.h”去掉 换成:

extern char g_str[];
void fun2() { cout << g_str << endl; }

这个时候编译器就知道g_str是引自于外部的一个编译模块了,不会在本模块中再重复定义一个出来,但是我想说这样做非常糟糕,因为你由于无法在test2.cpp中使用#include “test1.h”,那么test1.h中声明的其他函数你也无法使用了,除非也用都用extern修饰,这样的话你光声明的函数就要一大串,而且头文件的作用就是要给外部提供接口使用的,所以 请记住, 只在头文件中做声明,真理总是这么简单。

extern 和 static

  • extern 表明该变量在别的地方已经定义过了,在这里要使用那个变量.
  • static 表示静态的变量,分配内存的时候, 存储在静态区,不存储在栈上面.

static 作用范围是内部链接的关系, 和extern有点相反.它和对象本身是分开存储的,extern也是分开存储的,但是extern可以被其他的对象用extern 引用,而static 不可以,只允许对象本身用它. 具体差别首先,static与extern是一对“水火不容”的家伙,也就是说extern和static不能同时修饰一个变量;其次,static修饰的全局变量声明与定义同时进行,也就是说当你在头文件中使用static声明了全局变量后,它也同时被定义了;最后,static修饰全局变量的作用域只能是本身的编译单元,也就是说它的“全局”只对本编译单元有效,其他编译单元则看不到它,如:

(1)test1.h :

#ifndef TEST1H
#define TEST1H
static char g_str[] = "123456";
void fun1();
#endif

(2)test1.cpp :

#include "test1.h"
void fun1() { cout << g_str << endl; }

(3)test2.cpp :

#include "test1.h"
void fun2() { cout << g_str << endl; }

以上两个编译单元可以链接成功, 当你打开test1.obj时,你可以在它里面找到字符串”123456”,同时你也可以在test2.obj中找到它们,它们之所以可以链接成功而没有报重复定义的错误是因为虽然它们有相同的内容,但是存储的物理地址并不一样,就像是两个不同变量赋了相同的值一样,而这两个变量分别作用于它们各自的编译单元。 也许你比较较真,自己偷偷的跟踪调试上面的代码,结果你发现两个编译单元(test1,test2)的g_str的内存地址相同,于是你下结论static修饰的变量也可以作用于其他模块,但是我要告诉你,那是你的编译器在欺骗你,大多数编译器都对代码都有优化功能,以达到生成的目标程序更节省内存,执行效率更高,当编译器在链接各个编译单元的时候,它会把相同内容的内存只拷贝一份,比如上面的”123456”, 位于两个编译单元中的变量都是同样的内容,那么在链接的时候它在内存中就只会存在一份了,如果你把上面的代码改成下面的样子,你马上就可以拆穿编译器的谎言:

(1)test1.cpp:

#include "test1.h"
void fun1()
{
g_str[0] = 'a';
cout << g_str << endl;
}

(2)test2.cpp :

#include "test1.h"
void fun2() { cout << g_str << endl; }

(3) :

void main()     {
fun1(); // a23456
fun2(); // 123456
}

这个时候你在跟踪代码时,就会发现两个编译单元中的g_str地址并不相同,因为你在一处修改了它,所以编译器被强行的恢复内存的原貌,在内存中存在了两份拷贝给两个模块中的变量使用。正是因为static有以上的特性,所以一般定义static全局变量时,都把它放在原文件中而不是头文件,这样就不会给其他模块造成不必要的信息污染,同样记住这个原则吧!

extern 和const

C++中const修饰的全局常量据有跟static相同的特性,即它们只能作用于本编译模块中,但是const可以与extern连用来声明该常量可以作用于其他编译模块中, 如extern const char g_str[];
然后在原文件中别忘了定义: const char g_str[] = “123456”;

所以当const单独使用时它就与static相同,而当与extern一起合作的时候,它的特性就跟extern的一样了!所以对const我没有什么可以过多的描述,我只是想提醒你,const char* g_str = "123456" 与 const char g_str[] ="123465"是不同的, 前面那个const 修饰的是

char *

而不是g_str,它的g_str并不是常量,它被看做是一个定义了的全局变量(可以被其他编译单元使用), 所以如果你像让char*g_str遵守const的全局常量的规则,最好这么定义const char* const g_str="123456".

PHP的超级全局变量小结

Posted on 07-13-2015 | In Misc

PHP 超级全局变量概绍

PHP中预定义了几个超级全局变量(superglobals) ,

这意味着它们在一个脚本的全部作用域中都可用。

你不需要特别说明,就可以在函数及类中使用。

PHP 超级全局变量列表:

  • $GLOBALS
  • $_SERVER
  • $_REQUEST
  • $_POST
  • $_GET
  • $_FILES
  • $_ENV
  • $_COOKIE
  • $_SESSION

$GLOBALS

$GLOBALS 是PHP的一个超级全局变量组,

在一个PHP脚本的全部作用域中都可以访问。

$GLOBALS 是一个包含了全部变量的全局组合数组。变量的名字就是数组的键。

以下实例介绍了如何使用超级全局变量 $GLOBALS:

<?php 
$x = 75;
$y = 25;

function addition()
{
$GLOBALS['z'] = $GLOBALS['x'] + $GLOBALS['y'];
}

addition();
echo $z;
?>

以上实例中 z 是一个$GLOBALS数组中的超级全局变量,
该变量同样可以在函数外访问。

$_SERVER

$_SERVER 是一个包含了诸如头信息(header)、路径(path)、以及脚本位置(script locations)等等信息的数组。

这个数组中的项目由 Web 服务器创建。

不能保证每个服务器都提供全部项目;服务器可能会忽略一些,或

者提供一些没有在这里列举出来的项目。

以下实例中展示了如何使用$_SERVER中的元素:

<?php 
echo $_SERVER['PHP_SELF'];
echo "<br>";
echo $_SERVER['SERVER_NAME'];
echo "<br>";
echo $_SERVER['HTTP_HOST'];
echo "<br>";
echo $_SERVER['HTTP_REFERER'];
echo "<br>";
echo $_SERVER['HTTP_USER_AGENT'];
echo "<br>";
echo $_SERVER['SCRIPT_NAME'];
?>

$_REQUEST

PHP $_REQUEST 用于收集HTML表单提交的数据。

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。

当用户通过点击 “Submit” 按钮提交表单数据时,

表单数据将发送至

标签中 action 属性中指定的脚本文件。

在这个实例中,我们指定文件来处理表单数据。

如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。

然后,我们可以使用超级全局变量 $_REQUEST 来收集表单中的 input 字段数据:

 <html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php
$name = $_REQUEST['fname'];
echo $name;
?>

</body>
</html>

$_POST

PHP $_POST 被广泛应用于收集表单数据,

在HTML form标签的指定该属性:”method=”post”。

以下实例显示了一个输入字段(input)及提交按钮(submit)的表单(form)。

当用户通过点击 “Submit” 按钮提交表单数据时,

表单数据将发送至

标签中 action 属性中指定的脚本文件。

在这个实例中,我们指定文件来处理表单数据。

如果你希望其他的PHP文件来处理该数据,你可以修改该指定的脚本文件名。

然后,我们可以使用超级全局变量 $_POST 来收集表单中的 input 字段数据:

<html>
<body>

<form method="post" action="<?php echo $_SERVER['PHP_SELF'];?>">
Name: <input type="text" name="fname">
<input type="submit">
</form>

<?php
$name = $_POST['fname'];
echo $name;
?>

</body>
</html>

$_GET

PHP $_GET 同样被广泛应用于收集表单数据,

在HTML form标签的指定该属性:”method=”get”。

$_GET 也可以收集URL中发送的数据。

假定我们有一个包含参数的超链接HTML页面:

<html>
<body>

<a href="test_get.php?subject=PHP&web=runoob.com">Test $GET</a>

</body>
</html>

当用户点击链接 “Test $GET”, 参数 “subject” 和 “web” 将发送至”test_get.php”,

你可以在 “test_get.php” 文件中使用 $_GET 变量来获取这些数据。

以下实例显示了 “test_get.php” 文件的代码:

<html>
<body>

<?php
echo "Study " . $_GET['subject'] . " at " . $_GET['web'];
?>

</body>
</html>

$_REQUEST、$_POST、$_GET的区别和联系小结

1. $_REQUEST

php中$_REQUEST可以获取以POST方法和GET方法提交的数据,但是速度比较慢

2. $_GET

用来获取由浏览器通过GET方法提交的数据。GET方法他是通过把参数数据加在提交表单的action属性所指的URL中,值和表单内每个字段一一对应,然后在URL中可以看到,但是有如下缺点:

    1. 安全性不好,在URL中可以看得到
    1. 传送数据量较小,不能大于2KB。

3. $_POST

用来获取由浏览器通过POST方法提交的数据。POST方法他是通过HTTP POST机制,将表单的各个字段放置在HTTP HEADER内一起传送到action属性所指的URL地址中,用户看不到这个过程。他提交的大小一般来说不受限制,但是具体根据服务器的不同,还是略有不同。相对于_GET方式安全性略高

4. $_REQUEST、$_POST、$_GET 的区别和联系

$_REQUEST[“参数”]具用$_POST[“参数”] $_GET[“参数”]的功能,但是$_REQUEST[“参数”]比较慢。通过post和get方法提交的所有数据都可以通过$_REQUEST数组[“参数”]获得

redis和hiredis安装教程

Posted on 07-11-2015 | In DB

在ubuntu上

redis安装 : sudo apt-get install redis-server

hiredis安装 : 先到 https://github.com/redis/hiredis 下载 hiredis , 然后

sudo make
sudo make install
sudo ldconfig
1…192021222324252627282930313233343536
Mike

Mike

🚙 🚗 💨 💨 If you want to create a blog like this, just follow my open-source project, "hexo-theme-neo", click the GitHub button below and check it out ^_^ . It is recommended to use Chrome, Safari, or Edge to read this blog since this blog was developed on Edge (Chromium kernel version) and tested on Safari.

11 categories
287 posts
110 tags
about
GitHub Spotify
© 2013 - 2025 Mike