为什么这个指针是fu-segfault?

时间:2013-08-27 16:55:01

标签: c pointers gsoap

我似乎已经达到了我的Pointer-Fu的极限,并且正在寻求帮助(或某种脑部医学)。

该项目的大致概述:运行Linux的嵌入式ARM视频编码器板,使用制造商提供的记录不良的不良支持SDK。其广泛的代码中有一大堆是由gSoap从某些WSDL生成的,这就是导致头痛的原因。

在gSoap自动生成的巨大数据结构的一部分中,我们有一个写一些数据的地方(或者,一个地方写一个指向我们编写一些数据的地方的指针):

 struct tt__IPAddress
 {
    enum tt__IPType Type;   /* required element of type tt:IPType */
    char *IPv4Address;  /* optional element of type tt:IPv4Address */
    char *IPv6Address;  /* optional element of type tt:IPv6Address */
 };

然后我们有这个代码,简而言之,应该是将字符串写入IPv4Address:

DNSInformation->DNSManual = ((struct tt__IPAddress *)soap_malloc(soap, sizeof(struct tt__IPAddress)));
DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *));
DNSInformation->DNSManual->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH);
// Code crashes at this next line:
strncpy(*DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1);

dns_string是你所期望的 - 类似于“192.168.2.254”。它正确地以null结尾,LARGE_INFO_LENGTH的值很大(比如1024),因此字符串有足够的空间。为安全起见,我从strcpy()更改为strncpy()。

我的背景是较小的嵌入式东西(没有操作系统,没有使用malloc())所以我在说服自己理解这段代码的作用时遇到了一些麻烦。代码是自动生成的/ SDK的一部分,因此它不是我的创建,也没有记录/评论。

以下是我认为它正在做的事情:

DNSInformation->DNSManual = ((struct tt__IPAddress *)soap_malloc(soap, sizeof(struct tt__IPAddress)));

分配一块RAM,由DNSManual指向,其中tt__IPAddress结构将存在。

DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *));

正在分配由IPv4Address指向的一块RAM,其中将写入指向包含该地址的字符串的指针。

DNSInformation->DNSManual->IPv4Address[0] = (char *)soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH);

现在这个让我有点兴奋,看起来它正在尝试分配RAM来保存IPv4Address [0]将指向的字符串,除了看起来像他们正试图写一个(32- bit)指向char的指针,可能。

此代码以前有效,但是在其他地方进行了一些更改后,它现在是段错误,总是在strncpy()处或在strncpy()期间。

我的问题有两个:

  1. 有人可以帮我正确理解mallocs / pointer-fu发生了什么吗?
  2. 关于如何跟踪/调试此问题的任何指导?
  3. 遗憾的是,我们没有这种设置的GDB设施 - 是的我确信它可以设置它,但是现在让我们假设这对于许多蹩脚和乏味的原因是不实际的。

    目前我调试printf散布在代码中,事实上在这个小片段的每一行上,它总是在strncpy()行停止使用SIGSEGV。


    修改以关闭,因为WhozCraig找到答案:

    由于其自身最为人所知的原因,gSoap已经改变了struct tt__IPAddress,也许它已经用完了星号,但它在以前的版本中是什么,它应该是什么,是这样的:

    struct tt__IPAddress
     {
        enum tt__IPType Type; 
        char **IPv4Address;  /* note ptr to ptr */
        char **IPv6Address;
     };
    

4 个答案:

答案 0 :(得分:2)

代码不遵循结构布局。布局是:

 struct tt__IPAddress
 {
    enum tt__IPType Type;   /* required element of type tt:IPType */
    char *IPv4Address;  /* optional element of type tt:IPv4Address */
    char *IPv6Address;  /* optional element of type tt:IPv6Address */
 };

含义:IPv4Addresschar指针。但是这个:

DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap, sizeof(char *));

正在为其分配char **演员。但类型仍然是char *所以这个:

strncpy(*DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1);

取消引用指向单个char的指针,我可以向您保证,您与平台上的char *不兼容(可能还有其他任何内容)。

至少应该有关于此编译的警告,如果你的编译器有任何大脑,那么就会出现完全错误。这个看起来就像它本来是这样的:

 struct tt__IPAddress
 {
    enum tt__IPType Type; 
    char **IPv4Address;  /* note ptr to ptr */
    char **IPv6Address;
 };

用于具有动态指针数组,每个指针是为单个IP地址动态分配的内存。如果它是这样的话,它会更有意义。也就是说,如果您只打算每个结构只有单个 IPv4地址,那么应该更改:

DNSInformation->DNSManual = soap_malloc(soap, sizeof(struct tt__IPAddress)));
if (DNSInformation->DNSManual)
{
    DNSInformation->DNSManual->IPv4Address = soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH);
    if (DNSInformation->DNSManual->IPv4Address)
    {
        strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1);
        DNSInformation->DNSManual->IPv4Address[LARGE_INFO_LENGTH-1] = 0;
    }
}

或类似的东西。

答案 1 :(得分:0)

我觉得它看起来很破碎。

此:

char *IPv4Address;  /* optional element of type tt:IPv4Address */

IPv4Address是指向字符数据的单个指针,即字符串。

但是它的使用方式如下:

DNSInformation->DNSManual->IPv4Address = (char **)soap_malloc(soap,
                                                              sizeof(char *));

这是错的。假设soap_malloc()(即void *符合malloc())的合理返回值,则不需要演员,但演员不同实际类型信号出现某种错误。

它将IPv4Address struct字段视为指针指针,但显然不是。

答案 2 :(得分:0)

我确信它应该与此相似:

DNSInformation->DNSManual = soap_malloc(soap, sizeof(struct tt__IPAddress)));
DNSInformation->DNSManual->IPv4Address = soap_malloc(soap, sizeof(char) * LARGE_INFO_LENGTH);

strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH-1);

你的struct包含指向字符串的指针,但首先是分配一个指针数组(char **),然后为这个数组中的第一个指针分配内存。

并且在使用strncpy()之后不要忘记设置二进制零,因为它本身没有设置它。

//编辑:第一部分错了,抱歉

答案 3 :(得分:0)

Here是一个有效的解决方案(我使用malloc代替soap_malloc等。)

#include <stdio.h>
#include <stdlib.h>

#define LARGE_INFO_LENGTH 1024

enum tt__IPType { tt__IPv4, tt__IPv6 };

struct tt__IPAddress
{
  enum tt__IPType Type;   /* required element of type tt:IPType */
  char *IPv4Address;  /* optional element of type tt:IPv4Address */
  char *IPv6Address;  /* optional element of type tt:IPv6Address */
};

struct tt__DNSInformation
{
  struct tt__IPAddress* DNSManual;
};

int main()
{
  struct tt__DNSInformation* DNSInformation;
  char dns_string[] = "192.168.2.254";

  DNSInformation = malloc(sizeof(struct tt__DNSInformation));
  DNSInformation->DNSManual = malloc(sizeof(struct tt__IPAddress));
  DNSInformation->DNSManual->IPv4Address = malloc(sizeof(char) * LARGE_INFO_LENGTH);
  strncpy(DNSInformation->DNSManual->IPv4Address, dns_string, LARGE_INFO_LENGTH - 1);

  printf("%s\n", DNSInformation->DNSManual->IPv4Address);
  return 0;
}