来源:http://bianbian.org/technology/295.html
用PHP生成结构体:
http://bbs.chinaunix.net/viewthread.php?tid=1034276
尽管php是用C语言开发的,不过令我不解的是php没有提供对结构体struct的直接支持。
不过php提供了pack和unpack函数,用来进行二进制数据(binary data)和php内部数据的互转:
- string pack ( string $format [, mixed $args [, mixed $...]] )
- //Pack given arguments into binary string according to format.
- array unpack ( string $format, string $data )
- //Unpacks from a binary string into an array according to the given format.
其中,$format跟perl里的pack格式类似,有如下一些(中文是我加的,有不准确的欢迎提出):
a NUL-padded string,即“\0”作为“空字符”的表示形式
A SPACE-padded string,空格作为“空字符”的表示形式
h Hex string, low nibble first,升序位顺序
H Hex string, high nibble first,降序位顺序
c signed char,有符号单字节
C unsigned char,无符号单字节
s signed short (always 16 bit, machine byte order)
S unsigned short (always 16 bit, machine byte order)
n unsigned short (always 16 bit, big endian byte order)
v unsigned short (always 16 bit, little endian byte order)
i signed integer (machine dependent size and byte order)
I unsigned integer (machine dependent size and byte order)
l signed long (always 32 bit, machine byte order)
L unsigned long (always 32 bit, machine byte order)
N unsigned long (always 32 bit, big endian byte order)
V unsigned long (always 32 bit, little endian byte order)
f float (machine dependent size and representation)
d double (machine dependent size and representation)
x NUL byte,实际使用的时候作为跳过多少字节用,很有用
X Back up one byte,后退1字节
@ NUL-fill to absolute position,实际使用的时候作为从开头跳到某字节用,很有用
实际使用发现:C里的“\0”(即字符串终止符)在php里并不是终止符,而是作为了字符串的一部分。因此,必须对“\0”进行特殊处理,才能进行struct和php内部数据的完美互转。比如 char name[10]; 如果实际数据是“62 69 61 6E 00 62 69 61 6E 00”,在C语言里第5个位置有终止符,name应该是“bian”;而用了unpack转换以后在php里的name却是“bian\0bian\0”。
一开始我用了strpos函数找到“\0”的位置,然后进行substr截取:
- $name = substr($name, 0, strpos($name, "\0"));
不过很Faint的事情发生了,不知道是strpos的bug还是substr的bug(其实测试一下就知道,懒得试),有些字符串没问题,有些字符串却只能得到空值(即$name == ”)。很是郁闷,后来找了个strtok函数,这下没有问题了:
- $name = strtok($name, "\0");
难为大家看了那么多,下面写个完整的php读取二进制数据流(C语言结构体struct数据)文件的示例代码:
首先是C的struct定义示例,为了演示,我就写个简单点的,实际对照上面那个$format格式表应该没有问题:
- struct BIANBIAN {
- char name[10];
- char pass[33];
- int age;
- unsigned char flag;
- };
比如有个“bianbian.org”文件,内容就是上面的N个BIANBIAN结构体构成的。读取的php代码:
- //下面根据struct确定$format,注意int类型跟机器环境有关,我的32位Linux是4个长度
- $format = 'a10name/a33pass/iage/Cflag';
- //确定一个struct占用多少长度字节,如果只是读取单个结构体这是不需要的
- $length = 10 + 33 + 4 + 1;
- //也可以用fopen + fread + fclose,不过file_get_contents因为可以mmap,效率更高
- $data = file_get_contents('bianbian.org', 'r');
- for ($i = 0, $c = strlen($data); $i < $c; $i += $length) {
- $bianbian = unpack("@$i/$format", $data);
- //reference传递是php 5才支持的,如果用php4,得用其他办法
- foreach ($bianbian as &$value) {
- if (is_string($value)) {
- $value = strtok($value, "\0");
- }
- }
- print_r($bianbian);
- }
- //输出为array,即类似:
- Array
- (
- [name] => 'bianbian'
- [pass] => 'bianbian.org'
- [age] => 100
- [flag] => 0
- )
- ...
pack应该跟unpack相反
==================================================
实际使用发现:C里的“\0”(即字符串终止符)在php里并不是终止符,而是作为了字符串的一部分。因此,必须对“\0”进行特殊处理,才能进行 struct和php内部数据的完美互转。比如 char name[10]; 如果实际数据是“62 69 61 6E 00 62 69 61 6E 00”,在C语言里第5个位置有终止符,name应该是“bian”;而用了unpack转换以后在php里的name却是“bian\0bian \0”。
一开始我用了strpos函数找到“\0”的位置,然后进行substr截取.
不过很Faint的事情发生了,不知道是strpos的bug还是substr的bug(其实测试一下就知道,懒得试),有些字符串没问题,有些字符串却只能得到空值(即$name == ”)。很是郁闷,后来找了个strtok函数,这下没有问题了.
难为大家看了那么多,下面写个完整的php读取二进制数据流(C语言结构体struct数据)文件的示例代码:
首先是C的struct定义示例,为了演示,我就写个简单点的,实际对照上面那个$format格式表应该没有问题:
struct TEST{
char name[10];
char pass[33];
int age;
unsigned char flag;
};
比如有个“file.dat”文件,内容就是上面的N个BIANBIAN结构体构成的。读取的php代码:
//下面根据struct确定$format,注意int类型跟机器环境有关,我的32位Linux是4个长度
$format = 'a10name/a33pass/iage/Cflag';
//确定一个struct占用多少长度字节,如果只是读取单个结构体这是不需要的
$length = 10 + 33 + 4 + 1;
//也可以用fopen + fread + fclose,不过file_get_contents因为可以mmap,效率更高
$data = file_get_contents('file.dat', 'r');
for ($i = 0, $c = strlen($data); $i < $c; $i += $length) {
$bianbian = unpack("$format", $data);
//reference传递是php 5才支持的,如果用php4,得用其他办法
foreach ($bianbian as &$value) {
if (is_string($value)) {
$value = strtok($value, "\0");
}
}
print_r($bianbian);
}
?>
pack应该跟unpack相反。
顺便附上生成结构体文件的C语言代码:
#include
#include
struct example
{
char name[10];
char pass[33];
int age;
unsigned char flag;
};
int main()
{
example test;
example read;
FILE *fp;
test.age = 111;
test.flag = 10;
strcpy(test.name, "Hello World!");
strcpy(test.pass, "zbl110119");
fp = fopen("file.dat", "w+");
if (!fp)
{
printf("open file error!");
return -1;
}
rewind(fp);
fwrite(&test, sizeof(example), 1, fp);
rewind(fp);
fread(&read, sizeof(example), 1, fp);
printf("%d, %s\n", read.age, read.name);
fclose(fp);
return 0;
}
配置TomCat环境
1、到我的机器(IP为192.1.168.7,用户为Guest,口令没有)上的"共享"目录中,拷贝j2sdk-1_4_2_02-windows-i586-p.exe、jakarta-tomcat-5.0.14.zip、jakarta-struts-1.1.zip三个压包文件。
2、先双击j2sdk-1_4_2_02-windows-i586-p.exe安装jdk,最好安装到一个根目录下。在此假设安装在D:j2sdk1.4.2_02下。
3、安装TOMCAT。TOMCAT不用安装,只用直接解压jakarta-tomcat-5.0.14.zip到硬盘上。假设解压到D:TOMCAT下。
4、配置环境变量。右键点击"我的电脑"图标,选择"高级"子项,点击"环境变量"按扭。在"系统变量"中新建如下几个环境变量:
名称 键值 说明
JAVA_HOME D:j2sdk1.4.2_02 就是你的JDK的安装目录
CLASSPATH .;%JAVA_HOME%LIB
5、解压jakarta-struts-1.1.zip到任一目录,将其中jakarta-struts-1.1webapp目录下的struts-example.war文件拷贝到TOMCAT下的 omcatwebapps中
6、进入 omcatin目录,点击startup.bat以启动TomCat。(TomCat启动后点击shutdown.bat可关闭)
7、让TomCat在后台运行,打开ie,在地址栏中输入http://localhost:8080/可进入TomCat的主页,输入http://localhost:8080/struts-example可进入struts的一个例子。
祝大家安装顺利 :)
然后,我们来看一下STRUTS例子中的最重要的两个文件,这两个都是配置文件,
struts-config.xml(具体配置的情况我作了注释)
<?xml version="1.0" encoding="ISO-8859-1" ?>
<!DOCTYPE struts-config PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 1.1//EN"
"http://jakarta.apache.org/struts/dtds/struts-config_1_1.dtd">
<!--
This is the Struts configuration file for the example application,
using the proposed new syntax.
-->
<struts-config>
<!-- ========== Form Bean Definitions =================================== -->
<!-- FormBean是struts的一个概念,本质是JavaBean,用来自动存储页面表单中各个域的值,并在适当的时候回填表单域,不需要象传统那样request.getParameter("fieldName");,常被action-mappings中的action 使用 -->
<form-beans>
<!-- 稍后我们会新增一个GetparameterForm类,用来存储用户信息 -->
<form-bean name="GetParameterForm" type="beans.GetParameterForm"/>
</form-beans>
<!-- ========== Global Forward Definitions ============================== -->
<!--这里存放整个系统都可以使用的全局转向中转(Forward)地址 -->
<!-- 一般情况下,一个Action处理完毕后,会转发到一个JSP页面进行显示。这也是JSP中的MVC的实现的要点。-->
<global-forwards>
<!--failed.do和success.do将被当成servlet请求,到action-mappings中寻找对应的action处理。-->
<forward name="success" path="/success.do"/>
<forward name="fail" path="/fail.do"/>
</global-forwards>
<!-- ========== Action Mapping Definitions ============================== -->
<!--web.xml中后缀为.do的请求被转到这里处理。这里相当于struts的Model部分,Model部分是struts中比较灵活的地方。-->
<action-mappings>
<!--处理showinput.do的请求,使用的FormBean是GetparameterForm,既beans.GetParameterForm类,当处理过程发生错误时将返回index.jsp-->
<action path="/showinput" type="beans.ShowAction" name="GetParameterForm" scope="request" input="/index.jsp" />
<action path="/success" forward="/success.jsp"/>
<action path="/fail" forward="/error.jsp" />
</action-mappings>
</struts-config>
web.xml(具体配置的情况我作了注释)
<!-- Action Servlet Mapping -->
<!--该系统的servlet可以映射成.do为后缀的文件,后缀名可以改成任何名称.-->
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- The Welcome File List -->
<!--该系统的默认首页是index.jsp,可以有多个,系统按次序找,类似IIS-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- Application Tag Library Descriptor -->
<!-- 定义标签库 -->
<!--
<taglib>
<taglib-uri>/WEB-INF/app.tld</taglib-uri>
<taglib-location>/WEB-INF/app.tld</taglib-location>
</taglib>
-->
</web-app>
这两个文件是我们用JDK+TOMCAT+STRUTS的基本配置,注意:在更改了这个配置后需要对TOMCAT进行重新启动!
在你的目录中存放你的.JSP文件,他们的编写你可以使用HTML或FRONGPAGE等工具编写,对于大家这个是比较简单的,在此我就不再觜书,代码如下:
INDEX.JSP
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.*,java.sql.*,java.text.*,java.io.*"%>
<form name="form1" method="post" action="showinput.do">
输入success将返回到"success"页面,否则返回到"fail"页面<br><br>
input:<input type="text" name="valu"> <input type="submit" value="submit">
</form>
<br>
<a href="success.do">success</a><br>
<a href="fail.do">fail</a>
SUCCESS.JSP
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.*,java.sql.*,java.text.*,java.io.*"%>
success!
ERROR.JSP
<%@ page contentType="text/html;charset=gb2312"%>
<%@ page import="java.util.*,java.sql.*,java.text.*,java.io.*"%>
error page!
再在你的文件夹WEB-INFclasseseans中我们编写需要处理消息请求的类。具体代码如下:
GetParameterForm.java
package beans;
import org.apache.struts.action.ActionForm;
public class GetParameterForm extends ActionForm
{
private String valu="null";
public GetParameterForm() {
}
public void setValu(String s) {
valu = s;
}
public String getValu() {
return valu;
}
}
ShowAction.java
package beans;
import java.lang.reflect.InvocationTargetException;
import java.util.Locale;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.servlet.http.HttpServletResponse;
import org.apache.struts.action.*;
import org.apache.struts.util.*;
public final class ShowAction extends Action
{
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {
Locale locale = getLocale(request);
MessageResources messages = getResources(request);
HttpSession session = request.getSession();
GetParameterForm userform = (GetParameterForm) form;
if(userform.getValu().equals("success")) {
return(mapping.findForward("success"));
}
else {
System.out.println(userform.getValu());
return(mapping.findForward("fail"));
}
}
}
最后你可以运行了。
在TOMCAT中地址中填写http://localhost:8080/你的文件夹/
这是一个简单的例子,希望大家能喜欢,不知道我的讲解是否正确,请指教!
class A
{
public:
A(int i)
{
m_A = i;
}
~A()
{
}
static void print()
{
std::cout<< "A" <<std::endl;
}
friend class B;
protected:
int m_A;
private:
};
class B
{
public:
B(int i)
{
m_B = i;
}
static void print()
{
std::cout<< "B" << std::endl;
}
void show(B b)
{
b.a->m_A = 3;
b.m_B = 2;
}
protected:
private:
A* a;
int m_B;
};
template<class T1, class T2>
class CTestTemplate
{
public:
CTestTemplate(T1 t)
{
m_number = t;
}
void print()
{
T2::print();
std::cout<< m_number<< std::endl;
}
protected:
private:
T1 m_number;
};
int main(int argc, char* argv[])
{
CTestTemplate<int, B> testtem(3);
testtem.print();
}
chrome里读取:
console.log(document.cookie)
language1=zh; uin=7srsOd27A3NlR3MU5LVzWg%3D%3D
chrome里设置:
document.cookie="name=xiangdong"
"name=xiangdong"
console.log(document.cookie)
name=xiangdong; language1=zh; uin=7srsOd27A3NlR3MU5LVzWg%3D%3D
===============================================
<script langrage=javascript>
// writeCookie("myCookie", "my name", 24);
// Stores the string "my name" in the cookie "myCookie" which expires after 24 hours.
// The hours parameter is optional; if hours is left out, the cookie value expires at the end of the visitor's browser session.
function writeCookie(name, value, hours)
{
var expire = "";
if(hours != null)
{
expire = new Date((new Date()).getTime() + hours * 3600000);
expire = "; expires=" + expire.toGMTString();
}
document.cookie = name + "=" + escape(value) + expire;
}
// Example:
// alert( readCookie("myCookie") );
function readCookie(name)
{
var cookieValue = "";
var search = name + "=";
if(document.cookie.length > 0)
{
offset = document.cookie.indexOf(search);
if (offset != -1)
{
offset += search.length;
end = document.cookie.indexOf(";", offset);
if (end == -1) end = document.cookie.length;
cookieValue = unescape(document.cookie.substring(offset, end))
}
}
return cookieValue;
}
</script>
<body>
<script>
writeCookie("name","xiangdong2",24);
alert( readCookie("name") );
</script>
</body>
var a = [];
for(var k in o){
a.push(k + "=" + o[k]);
}
return a.join("\n");
}
alert(forIn(document.del.elements));
alert(document.del.action);
{
alert("小组名前后不能有空格字符.");
return false;
}
C语言指针传递详解:http://www.cnblogs.com/archimedes/p/c-transfer-point.html
从函数返回对象经常使用以下两种技术:
使用malloc在函数内部分配内存并返回其地址,调用者负责释放返回的内存
传递一个对象给函数,让函数修改它,这样分配和释放对象的内存都是调用者的责任
先讲难度大一点的,指针的地址传入函数里,传递指针的指针-将指针传递给函数的时候,传递的是值,如果希望修改原指针而不是指针的副本,就需要传递指情况一,针的指针,如下,allocateArray.c :
gcc -g -o allocateArray allocateArray.c
[root@test pointerparam]# ./allocateArray
45
45
45
45
45
用gdb看一下其指针地址,为何这样就能实现在函数里malloc后,在外面依然能够获取到malloc里的值呢?是因为传入的指针的地址,相当于这个malloc的内存根本没有退出函数后销毁,而且传入的地址是指针的地址,不是指针,这两点保证了其地址是同一个地址,不是副本,所以,函数退出后还在,需要外面作free(;vector),vector=NULL:
(gdb) b 4
Breakpoint 1 at 0x40053a: file allocateArray.c, line 4.
(gdb) b 18
Breakpoint 2 at 0x4005ad: file allocateArray.c, line 18.
(gdb) r
Breakpoint 2, main () at allocateArray.c:18
18 allocateArray(&vector,5,45);
(gdb) p &vector
$1 = (int **) 0x7fff3cb989f0 //函数外的地址: 0x7fff3cb989f0
(gdb) c
Continuing.
Breakpoint 1, allocateArray (arr=0x7fff3cb989f0, size=5, value=45) at allocateArray.c:5
5 *arr = (int*)malloc(size*sizeof(int));
(gdb) p arr
$2 = (int **) 0x7fff3cb989f0 //这个地址在函数里也一直是传入的地址: 0x7fff3cb989f0
(gdb) n
6 if(*arr != NULL){
(gdb) p arr
$4 = (int **) 0x7fff3cb989f0
(gdb) n
7 if(*arr != NULL){
(gdb) n
8 for(i=0;i<size;i++){
(gdb) n
9 *(*arr+i) = value;
(gdb) n
main () at allocateArray.c:19
............next............
19 for(i = 0; i < 5; i++) {
(gdb) n
20 printf("%d\n", vector[i]);
(gdb) p &vector
$6 = (int **) 0x7fff3cb989f0 //发现没到函数里转一圈后,这个地址一直是: 0x7fff3cb989f0
(gdb) n
45
19 for(i = 0; i < 5; i++) {//打印出函数里赋的值是没有问题的..正常运行结束。
情况二:下面这个版本的allocateArray函数传递了一个数组指针、数组的长度和用来初始化数组元素的值,返回指针只是为了方便
[root@test pointerparam]# gcc allocateArray3.c
[root@test pointerparam]# ./a.out
45
45
45
45
45
这个值是变了,但指针是没有变的,也就是说指针做了它该做的工作,并没有像第一种那样: allocateArray(&vector,5,45);实现对指针本身地址通过函数进行了修改,这儿只是对指针里的值做了修改,是不一样的。
用gdb来跟踪一下这个指针的传指针值的操作:
[root@test pointerparam]# gdb allocateArray3
(gdb) b 15
Breakpoint 1 at 0x400571: file allocateArray3.c, line 15.
(gdb) b 5
Breakpoint 2 at 0x400536: file allocateArray3.c, line 5.
(gdb) r
Starting program: /tmp/pointerparam/allocateArray3
Breakpoint 1, main () at allocateArray3.c:15
15 int* vector = (int*)malloc(5 * sizeof(int));
(gdb) p vector
$1 = (int *) 0x0 //此时是光一个指针还没有分配指针指和的malloc真实数据地址
(gdb) n
16 allocateArray(vector, 5, 45);
(gdb) p vector
$2 = (int *) 0xb447010 //分配地址:0xb447010 这个值会带入进去,并在里面对其作赋值操作,出函数后这个值还在,因为在传入前就已经分配好空间,在函数里只是赋值,把东西放进这个空间,函数返回后空间并没有销毁,因为是在函数外。
(gdb) c
Continuing.
Breakpoint 2, allocateArray (arr=0xb447010, size=5, value=45) at allocateArray3.c:6
6 if(arr != NULL) {
(gdb) c
Continuing.
45
45
45
45
45
情况三,指针值传入函数里面,这样会出现一个segment default的,情况三作一下修改:
allocateArray2.c
gcc -g -o allocateArray2 allocateArray2.c
[root@test pointerparam]# ./allocateArray2
Segmentation fault
如这样写呢? *arr = (int*)malloc(size*sizeof(int));
如果想这样,编译都会报错,如下:
porinter.c:6:10: 警告:赋值时将指针赋给整数,未作类型转换 [默认启用]
因为文件名传入的是一个指针,而*arr是一个值,不再是一个指针了。
而传入的是指针的指针就不一样了,如前面的例子1:void allocateArray(int ** arr,int size,int value){
那么因为函数int ** arr是指针的指针,也就是这个指针是可以修改的,这个*arr它不是一个值了,而是一个指针,所以,它可以给它malloc一个空间,并把这个空间给到*arr这个脂针地址里去,这块一定要区分开来,否则指向指针这一块的概念就没彻底理解清楚。
再理解前面引用的Url里的这一段话,加强深入理解传入指向指针的指针是可以修改指针的地址,指针也理一个数据,和int char是一样的东西,它也是能被修改的,而一维指针可以修改int char,int这样的值,但指针地址不变,而指针的指针它的地址都能被修改,这就是指针的精华人所在,摘录如下:
传递指针可以让多个函数访问指针所引用的对象,而不用把对象声明为全局可访问,要在某个函数中修改数据,需要用指针传递数据,当数据是需要修改的指针的时候,就要传递指针的指针,传递参数(包括指针)的时候,传递的是它们的值,也就是说,传递给函数的是参数值的一个副本,也就是说这儿数据是需要修改的指针的时候,就要传递指针的指针,而咱们只传了指针,修改后退出函数这个指针并没有变,销毁函数后,没有对指针作出修改,等于空做了一次运算,像做梦一样,函数并没改变什么,所以出现错误。
为何会出现segment default的情况?是因为指针传入函数后进行了malloc,而这个指针是在函数里经过malloc时给修改了指向地址,而这个malloc的空间因为函数退出而回收了指针传值地址,也就是其并没有记得下来,其malloc出来的存放整数的空间还在,但退出函数时就丢了,为此,在外面打印一个不存在的地址,当然会出现了segment default的情况,gdb分析如下:
(gdb) b 18
Breakpoint 1 at 0x40059a: file allocateArray2.c, line 18.
(gdb) b 19
Breakpoint 2 at 0x4005ad: file allocateArray2.c, line 19.
(gdb) b 4
Breakpoint 3 at 0x40053a: file allocateArray2.c, line 4.
(gdb) r
Starting program: /tmp/pointerparam/allocateArray2
Breakpoint 1, main () at allocateArray2.c:18
18 allocateArray(vector,5,45);
(gdb) n
Breakpoint 3, allocateArray (arr=0x0, size=5, value=45) at allocateArray2.c:5 //arr传入是0x0,也就是副本vector的地址。
5 arr = (int*)malloc(size*sizeof(int));
(gdb) n
6 if(arr != NULL){
(gdb) p arr
$1 = (int *) 0x14c3c010 //经malloc后是0x14c3c010
(gdb) c
Continuing.
Breakpoint 2, main () at allocateArray2.c:19
19 for(i = 0; i < 5; i++) {
(gdb) p vector //返回main函数后,这个vector还是0x0,并没有经过函数而改变,当然,下面对其进行打印会Segmentation fault.
$2 = (int *) 0x0
(gdb) n
20 printf("%d\n", vector[i]);
(gdb) n
Program received signal SIGSEGV, Segmentation fault.
0x00000000004005c3 in main () at allocateArray2.c:20
20 printf("%d\n", vector[i]);
回顾总结:
allocateArray.c allocateArray2.c
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
void allocateArray(int *arr,int size,int value){ void allocateArray(int ** arr,int size,int value){
int i; int i;
arr = (int*)malloc(size*sizeof(int)); *arr = (int*)malloc(size*sizeof(int));
if(arr != NULL){ if(*arr != NULL){
if(arr != NULL){ if(*arr != NULL){
for(i=0;i<size;i++){ for(i=0;i<size;i++){
arr[i] = value; *(*arr+i) = value;
} }
} }
} }
} }
int main(){ int main(){
int i; int i;
int *vector = NULL; int *vector = NULL;
allocateArray(vector,5,45); allocateArray(&vector,5,45);
for(i = 0; i < 5; i++) { for(i = 0; i < 5; i++) {
printf("%d\n", vector[i]); printf("%d\n", vector[i]);
} }
free(vector); free(vector);
} }
结论是malloc在函数里都得销毁掉,详细如下所述:
C语言是传值调用的,左边的你传递进去vector的地址,右边的是你传递了vector这个指针的地址值,所以左边的allocateArray函数内对arr的修改不会对函数外vector的值有什么影响,但是右边就不一样了,对arr的修改就是对vector变量所在的空间中的值的修改,也就是修改了vector的值,所以左边的allocateArray函数内对arr的修改不会对函数外vector的值有什么影响,那这个左边的malloc有没有运行完后还在呢? 右边这个如果不free会有内存泄漏的呀。
两边的都要销毁,只是左边的你在函数外没办法销毁。
【活跃】回忆未来-向东-Jàck 15/3/18 星期三 11:13:40
哈哈,了解了,多谢兄弟啊,相当感谢,我都说这malloc这玩意用时要注意才是。搞不好就泄漏了呢。
11:13:53
【潜水】Now&Fight 15/3/18 星期三 11:13:53
嗯嗯

鼠标右键(注意是右不是左)点桌面图标后不放,拖动到下面(在这之前就要留好任务栏左边的空隙)操作如下:在下面的左侧有一个快说启动的(有时因为锁定了任务栏会变得得窄看不到),得在任务栏里取消锁定任务栏,再拉长一点儿,这样就能看到桌面图标了。
有人把“显示桌面”按钮删除了,不知道怎么恢复,可以采用如下方法:
打开“记事本”,在里面输入下面内容:
[Shell]
Command=2
IconFile=explorer.exe,3
[Taskbar]
Command=ToggleDesktop
把这个文件保存为:“显示桌面.scf”.随便放到一个地方(比如桌面).
然后把它拖到"快速启动"栏里即可。
在运行里面输入“regsvr32 /n /i:u shell32”回车,在弹出框点击“确定”。
当系统提示“shell32中的dllinstall成功”时,再次点击“确定”。
不知道什么时候,在快速查看有关“启动”的文章">启动栏的显示桌面的快捷方式不见了。
没有了他很不方便。
先新建一个txt文件,用记事本打开,填入如下内容
[Shell]
Command=2
IconFile=explorer.exe,3
[Taskbar]
Command=ToggleDesktop
保存为“显示桌面.scf”,然后把他拖到快速启动栏,就能用了。
但这仅仅是能用,显示桌面.scf的存放位置放哪,万一哪天被删除了,不就又没了。
我们要找到快速启动栏的文件目录位置
Documents and Settings\用户名\Application Data\Microsoft\Internet Explorer\Quick Launch
存放在这里就安全了。
http://www.google.cn/search?hl=zh-CN&newwindow=1&client=firefox-a&rls=org.mozilla%3Azh-CN%3Aofficial&q=%E9%82%AE%E4%BB%B6%E4%BC%A0%E8%BE%93%E6%B5%81%E7%A8%8B+blog&btnG=Google+%E6%90%9C%E7%B4%A2&meta=&aq=f&oq=
"set nocompatible
set nocp
"不备份文件
set nobackup
" use mouse everywhere
set mouse=a (再用secure时后要是选择了拖动自动复制,最好不要改选项)
" wrap lines
set wrap
"显示行号
"set nu
"设置匹配模式,类似当输入一个左括号时会匹配相应的那个右括号
set showmatch
"去除vim的GUI版本中的toolbar
set guioptions-=T
"VIM的状态栏标尺
"set ruler
set ru
"检测文件的类型
filetype on
"记录历史的行数
set history=1000
"背景使用黑色
set background=dark
"查找时高亮显示关键字
set hls
"查找时自动找到第一个单词
set incsearch
#设置配色方案
colorscheme murphy
"设置TAB的宽度为4个空格
"set ts=4
set tabstop=4
"行间交错的宽度为4个空格
set shiftwidth=4
"语法高亮度显示
syntax on
"使用自动对齐
"set autoindent
set ai
"智能的选择对齐方式
"set smartindent
set si
"which tags files CTRL-] will search
set tags=./tags,./../tags,./../../tags,./**/tags
"支持C/C++的缩进
set cin
"方便使用taglist
nmap <F8> : Tlist <CR>
"方便使用A.vim插件
nmap <F7> :A<CR>
"方便执行python程序
map <F12> :!python.exe %<CR>
"把tab展开为空格
set expandtab
if(has("gui_running"))
" 图形界面下的设置
else
" 字符界面下的设置
endif
#include "ldap.h"
#include "stdio.h"
int main()
{
LDAP *ld;
LDAPMessage *res,*e;
int i,version;
char *server;
int port;
char *dn;
char *a;
BerElement *ptr;
char **vals;
char **ppValue = NULL;
char *sdn;
server="10.210.72.141";
port = 389;
//联接服务器
printf("hello the world");
if( (ld = ldap_open(server, port )) == NULL )
{
printf("NO CONNECT");
exit( 1 );
}else{
printf("ldap_open successful\n");
}
version = LDAP_VERSION2;
ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION,&version);
if(ldap_simple_bind_s(ld,"cn=Directory Manager","1234qwer")!=LDAP_SUCCESS)
{
ldap_perror( ld, "ldap_simple_bind_s by xiangdong2" );
exit( 1 );
}else{
printf("Ldap_bind succesful\n");
}
sdn="dc=sina,dc=net";
if (ldap_search_s(ld,sdn,LDAP_SCOPE_SUBTREE,"(entDefaultDomain=testquanjun1.sina.net)",NULL,0,&res)
!= LDAP_SUCCESS)
{
ldap_perror(ld,"ldap_search_s");
exit(1);
}else{
printf("ldap_search_s is successful\n");
}
for(e=ldap_first_entry(ld,res);e!=NULL;e=ldap_next_entry(ld,res))
{
//取出DN
dn=ldap_get_dn(ld,e);
printf("dn: %s \n",dn);
ldap_memfree( dn );
//对条目的属性进行逐条分析
for ( a = ldap_first_attribute( ld, e, &ptr );a != NULL;a = ldap_next_attribute( ld, e, ptr ) )
{
printf( "%s:",a );
vals = ldap_get_values( ld, e, a );
for ( i = 0; vals[i] != NULL; i++ )
{
printf("%s",vals[i]);
}
printf("\n");
ldap_value_free( vals );
}
printf("\n");
}
ldap_msgfree(res);
ldap_unbind(ld);
}
1 LDAP_OPERATIONS_ERROR Operations error
2 LDAP_PROTOCOL_ERROR Protocol error
3 LDAP_TIMELIMIT_EXCEEDED Timelimit exceeded
4 LDAP_SIZELIMIT_EXCEEDED Sizelimit exceeded
5 LDAP_COMPARE_FALSE Compare false
6 LDAP_COMPARE_TRUE Compare true
7 LDAP_STRONG_AUTH_NOT_SUPPORTED Strong authentication not supported
8 LDAP_STRONG_AUTH_REQUIRED Strong authentication required
9 LDAP_PARTIAL_RESULTS Partial results
16 LDAP_NO_SUCH_ATTRIBUTE No such attribute
17 LDAP_UNDEFINED_TYPE Undefined attribute type
18 LDAP_INAPPROPRIATE_MATCHING Inappropriate matching
19 LDAP_CONSTRAINT_VIOLATION Constraint violation
20 LDAP_TYPE_OR_VALUE_EXISTS Type or value exists
21 LDAP_INVALID_SYNTAX Invalid syntax
32 LDAP_NO_SUCH_OBJECT No such object
33 LDAP_ALIAS_PROBLEM Alias problem
34 LDAP_INVALID_DN_SYNTAX Invalid DN syntax
35 LDAP_IS_LEAF Object is a leaf
36 LDAP_ALIAS_DEREF_PROBLEM Alias dereferencing problem
48 LDAP_INAPPROPRIATE_AUTH Inappropriate authentication
49 LDAP_INVALID_CREDENTIALS Invalid credentials
50 LDAP_INSUFFICIENT_ACCESS Insufficient access
51 LDAP_BUSY DSA is busy
52 LDAP_UNAVAILABLE DSA is unavailable
53 LDAP_UNWILLING_TO_PERFORM DSA is unwilling to perform
54 LDAP_LOOP_DETECT Loop detected
64 LDAP_NAMING_VIOLATION Naming violation
65 LDAP_OBJECT_CLASS_VIOLATION Object class violation
66 LDAP_NOT_ALLOWED_ON_NONLEAF Operation not allowed on nonleaf
67 LDAP_NOT_ALLOWED_ON_RDN Operation not allowed on RDN
68 LDAP_ALREADY_EXISTS Already exists
69 LDAP_NO_OBJECT_CLASS_MODS Cannot modify object class
70 LDAP_RESULTS_TOO_LARGE Results too large
80 LDAP_OTHER Unknown error
81 LDAP_SERVER_DOWN Can't contact LDAP server
82 LDAP_LOCAL_ERROR Local error
83 LDAP_ENCODING_ERROR Encoding error
84 LDAP_DECODING_ERROR Decoding error
85 LDAP_TIMEOUT Timed out
86 LDAP_AUTH_UNKNOWN Unknown authentication method
87 LDAP_FILTER_ERROR Bad search filter
88 LDAP_USER_CANCELLED User cancelled operation
89 LDAP_PARAM_ERROR Bad parameter to an ldap routine
90 LDAP_NO_MEMORY Out of memory
我们通常在做一个有数据库后台的网站的时候,都会考虑到程序需要适用于不同的应用环境。和其他编程语言有所不同的是,在PHP中,操作数据库的是一系列的具体功能函数(如果你不使用ODBC接口的话)。这样做虽然效率很高,但是封装却不够。如果有一个统一的数据库接口,那么我们就可以不对程序做任何修改而适用于多种数据库,从而使程序的移植性和跨平台能力都大大提高。
在PHP中要完成OOP,需要进行对象封装,也就是编写类。我们可以通过生成一个新的SQL类实现对数据库的简单封装。例如:
< ?
class SQL
{
var $Driver; //实际操作的数据库驱动子类
var $connection; //共用的数据库连接变量
function DriverRegister($d)
{
if($d!="")
{
$include_path = ini_get("include_path");
$DriverFile = $include_path."/".$d.".php";
//驱动的存放路径必须在PHP.ini文件中设定的INCLUDE_PATH下
if( file_exists( $DriverFile)) //查找驱动是否存在
{
include($DriverFile);
$this->Driver = new $d();
// 根据驱动名称生成相应的数据库驱动类
return true;
}
}
return false; //注册驱动失败
}
function Connect($host,$user,$passwd,$database)//连接数据库的函数
{
$this->Driver->host=$host;
$this->Driver->user=$user;
$this->Driver->passwd=$pas
swd;
$this->Driver->database=$d
atabase;
$this->connection = $this->Driver->Connect();
}
function Close()//关闭数据库函数
{
$this->Driver->close($this->connection);
}
function Query($queryStr)//数据库字符串查询函数
{
return $this->Driver->query($queryStr,$this->connection);
}
function getRows($res)//查找行
{
return $this->Driver->getRows($res);
}
function getRowsNum($res)//取得行号
{
return $this->Driver-> getRowsNum ($res);
}
}
? >
我们以操作MySQL数据库为例。我们写一个数据库驱动类MySQL,在该类中,我们把有关MySQL数据库操作的函数都做进一步的封装。把包含该类,文件名为MySQL.php的文件放在PHP的系统 include_path下,就可以正常地使用了。注意编写数据库驱动文件时,文件名应和类名保持一致。
< ?
Class MySQL
{
var $host;
var $user;
var $passwd;
var $database;
function MySQL() //利用构造函数实现变量初始化
{
$host = "";
$user = "";
$passwd = "";
$database = "";
}
function Connect()
{
$conn = MySQL_connect($this->host, $this->user,$this->passwd) or
die("Could not connect to $this->host");
MySQL_select_db($this->database,$conn) or
die("Could not switch to database $this->database;");
return $conn;
}
function Close($conn)
{
MySQL_close($conn);
}
function Query($queryStr, $conn)
{
$res =MySQL_query($queryStr, $conn) or
die("Could not query database");
return $res;
}
function getRows($res)
{
$rowno = 0;
$rowno = MySQL_num_rows($res);
if($rowno>0)
{
for($row=0;$row<$rowno;$row++)
{
$rows[$row]=MySQL_fetch_row($res);
}
return $rows;
}
}
function getRowsNum($res)
{
$rowno = 0;
$rowno = mysql_num_rows($res);
return $rowno;
}
}
? >
同样我们要封装其他的“数据库驱动”到我们的SQL类中,只需要建立相应的类,并以同名命名驱动文件,放到PHP的include目录就可以了。
完成封装以后,就可以在PHP中按照OOP的思想来实现对数据库的编程了。
< ?
Include(“SQL.php”);
$sql = new SQL; //生成新的Sql对象
if($sql-> DriverRegister(“MySQL”)) //注册数据库驱动
{
$sql->Connect(“localhost”,”root”,””,”test”);
$res=$sql->query(“select * from test”); //返回查询记录集
$rowsnum = $sql->getRowsNum($res);
if($rowsnum > 0)
{
$rows = $sql->getRows($res);
foreach($rows as $row) //循环取出记录集内容
{
foreach($row as $field){
print $field;}
}
}
$sql->Close();
}
? >
在实际应用中,我们还可以根据实际需求对各种对象类做进一步扩展。在PHP中,还提供了一系列复杂的OOP方法,例如继承,重载,引用,串行化等等。充分调动各种方法并灵活运用,就能够使你的网站更合理和结构化,开发和维护也更容易。
var autoclick_ok=false;
var cncodenumber = 1
function mClk()
{
if(!autoclick_ok && cncodenumber==1)
{
var source=event.srcElement;source.click();
cncodenumber+=1;
}
}
</script>
<font onmouseover=mClk()>
<a href="http://www.cncode.com"
target="_blank"><font color=darkorchid><b>移到这里,就会自动点击!</b></font></a></font>
如果是图片,就用下面的
<script language="JavaScript" type="text/JavaScript">
var autoclick_ok=false;
var cncodenumber = 1
function mClk()
{
if(!autoclick_ok && cncodenumber==1)
{
var source=event.srcElement;source.click();
cncodenumber+=1;
}
}
</script>
<a href="http://www.cncode.com/" onmouseover=mClk()><img src="http://www.cncode.com/logo/cncodelogo.gif"></a>
-------------
(1) 创建CVSROOT根目录
编辑有关的环境变量,加入CVSROOT的定义(比如在 /etc/bashrc 文件中加入下面两行):
CVSROOT=/usr/local/cvsroot
eXPort CVSROOT
然后在相应位置开始创建CVSROOT
$cd /usr/local/
$mkdir cvsroot
$cvs –d /usr/local/cvsroot init
这时就会产生/usr/local/cvsroot/CVSROOT 目录,这下面放着有关CVS的配置文件。同时/usr/local/cvsroot/也作为文件仓库存放所有的文件。
(2) 创建开发项目
如果从头开始一个新的项目,就需要创建一个单独的目录,并把所有要使用的文件做一个有效的组织。而如果在开始使用源文件的目录之前就有了,则只需进入该目录就行了。
$cd /work/tang
$ls cvstest
. .. c/
$cd cvstest
然后,就可以输入源文件目录:
$cvs import –m “Create Source Dir” cvstest/c tang cvstest
这样会生成 $CVSROOT/cvstest/c 目录。 其中 -m 用来指定注释信息,如果后面在命令行不指定注释信息,则会启动缺省编辑器(vi)要求输入注释信息。 tang, cvstest分别标识了厂商和发行标识。
注意,使用import命令会把当前目录下的所有文件和目录(包括子目录)引入到文件仓库中指定模块(目录)下。
Cvs命令行小结
一个项目的首次导入
cvs import -m "write some comments here" project_name vendor_tag release_tag
执行后:会将所有源文件及目录导入到/path/to/cvsroot/project_name目录下
vender_tag: 开发商标记
release_tag: 版本发布标记
项目导出:将代码从CVS库里导出
cvs checkout project_name
cvs 将创建project_name目录,并将最新版本的源代码导出到相应目录中。这个checkout和Virvual SourceSafe中的check out不是一个概念,相对于Virvual SourceSafe的check out是cvs update, check in是cvs commit。
CVS的日常使用
=============
注意:第一次导出以后,就不是通过cvs checkout来同步文件了,而是要进入刚才cvs checkout project_name导出的project_name目录下进行具体文件的版本同步(添加,修改,删除)操作。
将文件同步到最新的版本
cvs update
不制定文件名,cvs将同步所有子目录下的文件,也可以制定某个文件名/目录进行同步
cvs update file_name
最好每天开始工作前或将自己的工作导入到CVS库里前都要做一次,并养成"先同步 后修改"的习惯,和Virvual SourceSafe不同,CVS里没有文件锁定的概念,所有的冲突是在commit之前解决,如果你修改过程中,有其他人修改并commit到了CVS库中,CVS会通知你文件冲突,并自动将冲突部分用
>>>>>> content on cvs server
<<<<<<
content in your file
>>>>>> 标记出来,由你确认冲突内容的取舍。
版本冲突一般是在多个人修改一个文件造成的,但这种项目管理上的问题不应该指望由CVS来解决。
确认修改写入到CVS库里
cvs commit -m "write some comments here" file_name
注意:CVS的很多动作都是通过cvs commit进行最后确认并修改的,最好每次只修改一个文件。在确认的前,还需要用户填写修改注释,以帮助其他开发人员了解修改的原因。如果不用写-m "comments"而直接确认`cvs commit file_name` 的话,cvs会自动调用系统缺省的文字编辑器(一般是vi)要求你写入注释。
注释的质量很重要:所以不仅必须要写,而且必须写一些比较有意义的内容:以方便其他开发人员能够很好的理解
不好的注释,很难让其他的开发人员快速的理解:比如: -m "bug fixed" 甚至 -m ""
好的注释,甚至可以用中文: -m "在用户注册过程中加入了Email地址校验"
修改某个版本注释:每次只确认一个文件到CVS库里是一个很好的习惯,但难免有时候忘了指定文件名,把多个文件以同样注释commit到CVS库里了,以下命令可以允许你修改某个文件某个版本的注释:
cvs admin -m 1.3:"write some comments here" file_name
添加文件
创建好新文件后,比如:touch new_file
cvs add new_file
注意:对于图片,Word文档等非纯文本的项目,需要使用cvs add -b选项按2进制文件方式导入,否则有可能出现文件被破坏的情况
比如:
cvs add -b new_file.gif
cvs add -b readme.doc
然后确认修改并注释
cvs ci -m "write some comments here"
删除文件
将某个源文件物理删除后,比如:rm file_name
cvs rm file_name
然后确认修改并注释
cvs ci -m "write some comments here"
以上面前2步合并的方法为:
cvs rm -f file_name
cvs ci -m "why delete file"
注意:很多cvs命令都有缩写形式:commit=>ci; update=>up; checkout=>co/get; remove=>rm;
添加目录
cvs add dir_name
查看修改历史
cvs log file_name
cvs history file_name
查看当前文件不同版本的区别
cvs diff -r1.3 -r1.5 file_name
查看当前文件(可能已经修改了)和库中相应文件的区别
cvs diff file_name
cvs的web界面提供了更方便的定位文件修改和比较版本区别的方法,具体安装设置请看后面的cvsweb使用
正确的通过CVS恢复旧版本的方法:
如果用cvs update -r1.2 file.name
这个命令是给file.name加一个STICK TAG: "1.2" ,虽然你的本意只是想将它恢复到1.2版本
正确的恢复版本的方法是:cvs update -p -r1.2 file_name >file_name
如果不小心已经加成STICK TAG的话:用cvs update -A 解决
移动文件/文件重命名
cvs里没有cvs move或cvs rename,因为这两个操作是可以由先cvs remove old_file_name,然后cvs add new_file_name实现的。
删除/移动目录
最方便的方法是让管理员直接移动,删除CVSROOT里相应目录(因为CVS一个项目下的子目录都是独立的,移动到$CVSROOT目录下都可以作为新的独立项目:好比一颗树,其实砍下任意一枝都能独立存活),对目录进行了修改后,要求其开发人员重新导出项目cvs checkout project_name 或者用cvs update -dP同步。
项目发布导出不带CVS目录的源文件
做开发的时候你可能注意到了,每个开发目录下,CVS都创建了一个CVS/目录。里面有文件用于记录当前目录和CVS库之间的对应信息。但项目发布的时候你一般不希望把文件目录还带着含有CVS信息的CVS目录吧,这个一次性的导出过程使用cvs export命令,不过export只能针对一个TAG或者日期导出,比如:
cvs export -r release1 project_name
cvs export -D 20021023 project_name
cvs export -D now project_name
CVS Branch:项目多分支同步开发
=============================
确认版本里程碑:多个文件各自版本号不一样,项目到一定阶段,可以给所有文件统一指定一个阶段里程碑版本号,方便以后按照这个阶段里程碑版本号导出项目,同时也是项目的多个分支开发的基础。
cvs tag release_1_0
开始一个新的里程碑:
cvs commit -r 2 标记所有文件开始进入2.x的开发
注意:CVS里的revsion和软件包的发布版本可以没有直接的关系。但所有文件使用和发布版本一致的版本号比较有助于维护。
版本分支的建立
在开发项目的2.x版本的时候发现1.x有问题,但2.x又不敢用,则从先前标记的里程碑:release_1_0导出一个分支release_1_0_patch
cvs rtag -b -r release_1_0 release_1_0_patch proj_dir
一些人先在另外一个目录下导出release_1_0_patch这个分支:解决1.0中的紧急问题,
cvs checkout -r release_1_0_patch
而其他人员仍旧在项目的主干分支2.x上开发
在release_1_0_patch上修正错误后,标记一个1.0的错误修正版本号
cvs tag release_1_0_patch_1
如果2.0认为这些错误修改在2.0里也需要,也可以在2.0的开发目录下合并release_1_0_patch_1中的修改到当前代码中:
cvs update -j release_1_0_patch_1