字符指针:
c代码:
char* p = "test";
反汇编代码:
mov dword ptr [p],offset string
"test" (41573Ch)
通过查看汇编,可以清楚的看到字符指针实际上是将常量字符串的地址赋值给了变量p。
字符数组:
c代码:
char p[] = "test";
反汇编代码:
mov eax,dword ptr [string
"test" (41573Ch)]
mov dword ptr [ebp-10h],eax
mov cl,byte ptr ds:[415740h]
mov byte ptr [ebp-0Ch],cl
而字符数组实际上是将常量字符串通过mov指令拷贝到了栈区,由于存在‘\0’字符的存在,在该反汇编代码中进行了两次拷贝,第一次拷贝了4个字符,第二次拷贝了1个字符。
......
字符指针:
c代码:
char* p = "test";
反汇编代码:
mov dword ptr [p],offset string
"test" (41573Ch)
通过查看汇编,可以清楚的看到字符指针实际上是将常量字符串的地址赋值给了变量p。
字符数组:
c代码:
char p[] = "test";
反汇编代码:
mov eax,dword ptr [string
"test" (41573Ch)]
mov dword ptr [ebp-10h],eax
mov cl,byte ptr ds:[415740h]
mov byte ptr [ebp-0Ch],cl
而字符数组实际上是将常量字符串通过mov指令拷贝到了栈区,由于存在‘\0’字符的存在,在该反汇编代码中进行了两次拷贝,第一次拷贝了4个字符,第二次拷贝了1个字符。
......
C语言的初学者常犯的一个失误是调用系统或第三方API时忘了在函数声明中标注WINAPI调用方式,而菜鸟C++程序员也每每困惑为什么成员函数必须指示为CALLBACK再添加static关键字才能作为回调函数。在x86系统中,存在多种函数调用约定。如果调用者与被调用者采用不同的调用约定,很可能导致堆栈破坏、非法访问等致命错误。
也许您会得出一个结论,除非借助汇编指令,否则调用约定之间的转换是不可能的。不过很多事情都不是绝对的。我们先看下面的例子:
#include
int __cdecl CDeclFunction()
{
printf("from CDecl function\n");
return 1;
}
int main()
{
printf("Begin call STDCall function\n");
typedef int(__stdcall * STDCALLFUNCTION)();
STDCALLFUNCTION pfn =(STDCALLFUNCTION)CDeclFunction;
int i=pfn();
printf("End call STDCall function\n");
return i;
}
这是一个简单的函数调用的例子,唯一特别的地方是函数定义为__cdecl,而调用时采用__stdcall方式。我们把这段代码编译后执行。嗬,一切正常。如果您不服气,再加一个for循环看看J
我们再来看另一个更特别的例子:
#include
int __stdcall StdCallFunction (int i, ......
C语言的初学者常犯的一个失误是调用系统或第三方API时忘了在函数声明中标注WINAPI调用方式,而菜鸟C++程序员也每每困惑为什么成员函数必须指示为CALLBACK再添加static关键字才能作为回调函数。在x86系统中,存在多种函数调用约定。如果调用者与被调用者采用不同的调用约定,很可能导致堆栈破坏、非法访问等致命错误。
也许您会得出一个结论,除非借助汇编指令,否则调用约定之间的转换是不可能的。不过很多事情都不是绝对的。我们先看下面的例子:
#include
int __cdecl CDeclFunction()
{
printf("from CDecl function\n");
return 1;
}
int main()
{
printf("Begin call STDCall function\n");
typedef int(__stdcall * STDCALLFUNCTION)();
STDCALLFUNCTION pfn =(STDCALLFUNCTION)CDeclFunction;
int i=pfn();
printf("End call STDCall function\n");
return i;
}
这是一个简单的函数调用的例子,唯一特别的地方是函数定义为__cdecl,而调用时采用__stdcall方式。我们把这段代码编译后执行。嗬,一切正常。如果您不服气,再加一个for循环看看J
我们再来看另一个更特别的例子:
#include
int __stdcall StdCallFunction (int i, ......
1.大尾(big_endian)小尾(little_endian)的问题
基于Web的测试软件是由C++数据采集服务器程序和客户端Java显示程序两部分构成,前者用C++,后者Java语言,存在数据移植问题。因为
在计算机系统中,当包含数字的二进制文件从一个结构移到另一结构时,就出现大尾小尾问题。不同CPU在多字节数(如四字节int)存储时有两种方法,一种
方法叫小尾(little_endian),数据的低字节被放置在连续存储区的首位,另一种方法叫大尾(big_endian),数据的高字节被放置在连
续存储区的首位。Intel 80×86家族处理器是最后一个仍然坚持小尾的主要结构。所有其他的CPU结构(Motorola
680×0和所有RISC芯片)或者是纯粹的大尾或者是既适应大尾也适应小尾,大尾被认为是更符合逻辑的方法)。当数字由小尾处理器写入文件然后又由大尾
处理器读取(或者倒过来)时,数字就会被搞乱(除了0和-1)。
目前在笔者参与的项目中平台中心的GM
Server是C语言实现的,而我们这边的GM
client为Java实现的,自然需要考虑这个通信时的大小尾转换,主要涉及short,int,long类型,String,byte类型不需要转
换.
2.类型字节大小的问题
C语言中的基本类型如下:
类型
定义
说明 ......
1.大尾(big_endian)小尾(little_endian)的问题
基于Web的测试软件是由C++数据采集服务器程序和客户端Java显示程序两部分构成,前者用C++,后者Java语言,存在数据移植问题。因为
在计算机系统中,当包含数字的二进制文件从一个结构移到另一结构时,就出现大尾小尾问题。不同CPU在多字节数(如四字节int)存储时有两种方法,一种
方法叫小尾(little_endian),数据的低字节被放置在连续存储区的首位,另一种方法叫大尾(big_endian),数据的高字节被放置在连
续存储区的首位。Intel 80×86家族处理器是最后一个仍然坚持小尾的主要结构。所有其他的CPU结构(Motorola
680×0和所有RISC芯片)或者是纯粹的大尾或者是既适应大尾也适应小尾,大尾被认为是更符合逻辑的方法)。当数字由小尾处理器写入文件然后又由大尾
处理器读取(或者倒过来)时,数字就会被搞乱(除了0和-1)。
目前在笔者参与的项目中平台中心的GM
Server是C语言实现的,而我们这边的GM
client为Java实现的,自然需要考虑这个通信时的大小尾转换,主要涉及short,int,long类型,String,byte类型不需要转
换.
2.类型字节大小的问题
C语言中的基本类型如下:
类型
定义
说明 ......
1.大尾(big_endian)小尾(little_endian)的问题
基于Web的测试软件是由C++数据采集服务器程序和客户端Java显示程序两部分构成,前者用C++,后者Java语言,存在数据移植问题。因为
在计算机系统中,当包含数字的二进制文件从一个结构移到另一结构时,就出现大尾小尾问题。不同CPU在多字节数(如四字节int)存储时有两种方法,一种
方法叫小尾(little_endian),数据的低字节被放置在连续存储区的首位,另一种方法叫大尾(big_endian),数据的高字节被放置在连
续存储区的首位。Intel 80×86家族处理器是最后一个仍然坚持小尾的主要结构。所有其他的CPU结构(Motorola
680×0和所有RISC芯片)或者是纯粹的大尾或者是既适应大尾也适应小尾,大尾被认为是更符合逻辑的方法)。当数字由小尾处理器写入文件然后又由大尾
处理器读取(或者倒过来)时,数字就会被搞乱(除了0和-1)。
目前在笔者参与的项目中平台中心的GM
Server是C语言实现的,而我们这边的GM
client为Java实现的,自然需要考虑这个通信时的大小尾转换,主要涉及short,int,long类型,String,byte类型不需要转
换.
2.类型字节大小的问题
C语言中的基本类型如下:
类型
定义
说明 ......
1.大尾(big_endian)小尾(little_endian)的问题
基于Web的测试软件是由C++数据采集服务器程序和客户端Java显示程序两部分构成,前者用C++,后者Java语言,存在数据移植问题。因为
在计算机系统中,当包含数字的二进制文件从一个结构移到另一结构时,就出现大尾小尾问题。不同CPU在多字节数(如四字节int)存储时有两种方法,一种
方法叫小尾(little_endian),数据的低字节被放置在连续存储区的首位,另一种方法叫大尾(big_endian),数据的高字节被放置在连
续存储区的首位。Intel 80×86家族处理器是最后一个仍然坚持小尾的主要结构。所有其他的CPU结构(Motorola
680×0和所有RISC芯片)或者是纯粹的大尾或者是既适应大尾也适应小尾,大尾被认为是更符合逻辑的方法)。当数字由小尾处理器写入文件然后又由大尾
处理器读取(或者倒过来)时,数字就会被搞乱(除了0和-1)。
目前在笔者参与的项目中平台中心的GM
Server是C语言实现的,而我们这边的GM
client为Java实现的,自然需要考虑这个通信时的大小尾转换,主要涉及short,int,long类型,String,byte类型不需要转
换.
2.类型字节大小的问题
C语言中的基本类型如下:
类型
定义
说明 ......
1 选择new->project->win32 console project;
在这一步的选框上有一个选项是 create dictionary for solution, 我不知道这个具体是什么作用,选了之后会项目出现两层文件夹,比如你创建一个命名为test的项目,会生成test文件夹,test中包含另一个test文件夹,你的项目实际上放在了内层test文件夹中。选与不选对于后续的创建没有影响。这个的具体作用等我查明白了再补上!
2 输入你的文件名字->按下next->选择DLL->选择OK;
3 在工程中添加一个与工程名字同名的test.h文件,在文件中写入代码: extern "C" __declspec(dllexport) void testexam(char *input); (文件名字是需要导出的文件名);
4 在cpp文件的头上其他include语句后面,添加#include "test.h",在其中写入你的函数void testexam(char *input);
5 在build->batch build 中将两个选项都选中,然后选择这个选项框中的编译指令
OK, now you can get your dll and lib file in the release and debug file folder! Congratulations !
......
1 选择new->project->win32 console project;
在这一步的选框上有一个选项是 create dictionary for solution, 我不知道这个具体是什么作用,选了之后会项目出现两层文件夹,比如你创建一个命名为test的项目,会生成test文件夹,test中包含另一个test文件夹,你的项目实际上放在了内层test文件夹中。选与不选对于后续的创建没有影响。这个的具体作用等我查明白了再补上!
2 输入你的文件名字->按下next->选择DLL->选择OK;
3 在工程中添加一个与工程名字同名的test.h文件,在文件中写入代码: extern "C" __declspec(dllexport) void testexam(char *input); (文件名字是需要导出的文件名);
4 在cpp文件的头上其他include语句后面,添加#include "test.h",在其中写入你的函数void testexam(char *input);
5 在build->batch build 中将两个选项都选中,然后选择这个选项框中的编译指令
OK, now you can get your dll and lib file in the release and debug file folder! Congratulations !
......
1,栈区(stack)— 由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈。 例如,声明在函数中的一个局部变量int b;系统自动在栈中为b开辟空间。只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。比如:
char* AllocStrfromStack()
{
char pstr[100];
return pstr; //用数组模拟栈
}
对于该函数,那块pstr的内存在函数返回时就被系统释放了,只在函数内部调用时才有,其实返回的char*什么也没有。
为此做过一个实验:
#include "stdafx.h"
#include "string.h"
char * AddReturn()
{
char s[]="abcdef";
unsigned char n=sizeof(s)/sizeof(char);
printf("%d\n",n); //7
printf("%d\n",strlen(s)); //6
for(unsigned char i=0;i<n;i++)
......