C语言中一个关于指针传递的问题

  1. 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
  2. 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
  3. 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。

C语言中一个关于指针传递的问题

李云

UTStarcom通讯有限公司 E-Box Team

2005-06-22

摘要

指针在C语言中扮演着极为重要的角色,它的存在为C语言提供了极大的灵活性,当然,不少问题也是由指针所引起的(双刃剑)。本文通过分析一个由指针传递所引起的错误,从而使得我们更加重视指针在编程中的传递问题。

关键词

C语言指针传递

缩略语

Significant

Byte 最低有效字节

Least

LSB

MCI Management & Control Interface 管理控制接口

Byte 最高有效字节

MSB Most

Significant

1 问题的提出

指针因为灵活使得我们在编程时有意识的利用这一特性,从而使得我们的设计也更加的灵活,如函数指针等等。在很多情况下,我们需要从被调用函数返回结果。这可以通过两种方法来实现,一是通过函数的返回值,二是通过将指针作为参数传递给被调用函数。

图 1.1就是一个例子。

00001:S32 mci_module_id_from_name(S8* name, U16* module_id)

00002:{

00003:mci_module_t *module;

00004:U16 index = 0;

00005:

00006:if(name == NULL || module_id == NULL)

00007:return ERR_MCI_INV_PRARAM;

00008:

00009:for(;index <= g_mci_last_module_id; index ++)

00010:{

00011:module = g_mci_module_array[index];

00012:

00013:if(module == NULL)

00014:continue;

00015:

00016:if(strcmp(module->name, name) == 0)

00017:{

00018:*module_id = index;

00019:return 0;

00020:}

00021:}

00022:

00023:return ERR_MCI_MOD_NOT_EXIST;

00024:}

图 1.1 采用指针传递获取返回结果的示例函数

在图 1.1中需要关心的是第18行,这一行将找到的MCI模块的ID通过指针传递的方法,将其返回给调用者。图 1.2是使用图 1.1的mci_module_id_from_name函数的一个例子程序。

00026:int foo(char* name)

00027:{

00028:U32 module_id = 0;

00029:

00030:if (mci_module_id_from_name(name, &module_id) < 0)

00031:return FAILURE;

00032:

00033:...

00034:

00035:return SUCCESS;

00036:}

图 1.2 使用mci_module_id_from_name函数的一个例子

foo函数中对于mci_module_id_from_name函数的调用永远能得到正确的结果吗?答应案是:否。如果在x86处理器上运行这一程序,则总是能得到正确的结果,但在我们熟悉的PowerPC处理器上运行这一程序则总是很难得到正确的结果(只有当module_id_from_name函数中返回的module_id恰好为0时结果才正确)。这是为什么呢?产生这一问题的关键是:mci_module_id_from_name函数需要的是一个U16(我们可以理解为32位处理器上的unsigned short int)的指针,但foo函数在调用mci_module_id_from_name时,所给的指针是U32(我们可以理解为32位处理器上的unsigned int)。这一程序如果在Visual C++中进行编译,则会出现编译错误(指出指针类型不匹配),为了能在Visual C++中编译通过,则需要做一个将U32*强制转换成U16*的转换(如(U16*)&module_id)。但在我们所使用的VxWorks编译环境中,是能正常编译并且不会出现任何的告警信息的(见注)。

注:这一问题的出现是在VxWorks中采用-ansi编译选项,且在foo函数所在的文件中没有声名mci_module_id_from_name的原型的情况下出现的。为了让编译器能检查出这一函数指错不匹配的问题可以采用-std=c9x等编译选项进行编译。当采用-std=c9x编译选项进行编译时,如果在函数调用处,没有找到被调用函数的原型声名,则会报错。

相同的程序,为什么在x86和PowerPC处理器上却会产生截然不同的结果呢?对于这一问题,两种处理器的字节顺序问题是其根源,即x86采用的little-endian,而PowerPC 采用的big-endian。

2 little-endian和big-endian

little-endian采用低位字节放在低地址内存,而高位字节放在高地址内存的方法。反之,big-endian是采用高位字节放在低地址内存,低位字节放在高地址内存的方法。以图 1.2

的foo函数中的module_id变量为例(由于是局部变量,所在这一变量位于堆栈内存中),由于module_id被定义为U32,因此,它将占用4个字节的内存,假设module_id位于

0x10000地址开始的内存中,则在little-endian的CPU上,其字节在内存中的存放顺序如

图 2.1所示,而在big-endian的CPU上其字节在内存中的存放顺序如图 2.2所示。

U32 module_id = 0

0x10000

0x10001

0x10002

0x10003

图 2.1 module_id在little-endian下的字节存放顺序

U32 module_id = 0

0x10000

0x10001

0x10002

0x10003

图 2.2 module_id在big-endian下的字节存放顺序

思考

TCP/IP协议采用的是big-endian模式,因此,在进行网络套接字(socket)编程时,我们需要用到htonl、ntohl等函数,为什么?请仔细分析在不同endian模式的两台主机之

间进行通讯时,所需发送的数据在发送主机上的字序存储方式、数据在网络上的传输顺序、以及数据在接收主机上的字序存储方式。

相关文档
最新文档