代码之家  ›  专栏  ›  技术社区  ›  Tyler Treat

检查C中类似元素的数据数组

  •  1
  • Tyler Treat  · 技术社区  · 15 年前

    我创建了一个“地址”结构。每个地址(xx.yy.zz.mm)由一个xx、yy、zz和mm元素组成,这些元素都是int。每个地址也有一个与之相关联的“name”元素。

    我有一个最多100个称为“网络”的地址数组。以下是网络中一些元素的示例:

    186.88.1.21泰勒
    18687.911鲍勃
    101.21.0.13汤姆
    111.11.3.89夹头
    101.21.5.99卢克

    我需要检查每个地址,看看是否有其他地址来自同一个位置。如果元素xx和yy相同,则两个地址来自同一位置。如果同一位置有一个或多个地址,我需要输出此信息。

    下面是我为尝试执行此操作而编写的一些代码:

    char temp[11];
    int nameCount;
    for (i = 0; i < count; i++)
    {
        char names[100][10] = {};
        strcpy(temp, network[i].name);
        temp[11] = '\0';
        nameCount = 0;
        for (j = i + 1; j < count; j++)
        {
            if (network[i].xx == network[j].xx && network[i].yy == network[j].yy)
            {
                strcpy(names[nameCount], network[j].name);
                nameCount++;
            } 
        }
        if (nameCount == 0)
            printf("No matches for %s.\n", temp);
        else
        {
            printf("%s ", temp);
            for (j = 0; j < nameCount; j++)
                printf("and %s ", names[i]);
            printf("are from the same location.\n\n");
        }
    }
    

    此代码适用于数组中来自同一位置的前两个地址,但对其余地址无效(尽管它看起来几乎可以——它打印空白而不是名称,但它有正确的空白数)。上面列出的地址的输出是(抱歉格式不正确):

    Tyler  
     and Bob  
     are from the same location.  
    
    No matches for Bob  
    .  
    Tom  
     and [space] and [space] are from the same location.  
    
    No matches for Chuck  
    .  
    Luke  
     and [space] are from the same location.  
    
    No matches for Nick  
    .
    

    似乎在每个名称的末尾都添加了一个换行符。

    6 回复  |  直到 12 年前
        1
  •  1
  •   Aidan Cully    15 年前

    这里至少有几个问题。

    0: temp[11] 是char数组中定义为11个元素长的第12个元素。这是缓冲区溢出。

    1.本公司: names[100][10] 应该是 names[100][11] ,以便每个元素都足够大,可以存储 temp .

    2:您使用strcpy(),然后插入一个终止字符,大概是为了防止您从strcpy()复制了超过10个字符。在这种情况下,数据溢出。您要使用strncpy(),并且 然后 终止字符串。

    strcpy(temp, network[i].name);
    temp[11] = '\0';
    

    具有

    strncpy(temp, network[i].name, sizeof(temp) - 1);
    temp[sizeof(temp) - 1] = '\0';
    

    替代

            strcpy(names[nameCount], network[j].name);
            nameCount++;
    

    具有

            strncpy(names[nameCount], network[j].name, sizeof(names[nameCount] - 1);
            names[nameCount][sizeof(nameCount) - 1] = '\0';
            nameCount++;
    

    3:打印“和%s”列表的循环正在使用错误的变量取消对数组的引用。您正在使用“j”进行迭代,但将“i”元素拉出。

    4:就换行而言,network[i].name(对于任何i)很可能包含要复制的换行符。

    5:如果你在同一个地方有三样东西,你可能会以你不想要的方式列出它们。

    1.1.1.1 chuck
    1.1.2.2 larry
    1.1.3.3 biff
    

    可能会输出(其他错误修复后)

    chuck and larry and biff are from the same location
    larry and biff are from the same location
    No matches for biff.
    

    解决这个问题剩下的只是一个练习。

        2
  •  1
  •   Jerry Coffin    15 年前

    我会把这个改一改。我首先根据xx和yy值对地址/名称数组进行排序。然后你可以穿过阵列,所有来自同一地点的人都会紧挨着彼此……

        3
  •  1
  •   pmg    15 年前

    似乎在每个名称的末尾都添加了一个换行符。

    显然,你用 fgets() 从文件中读取数据。 FGSE() 保留最后一条新线。您可以使用以下方法删除它:

    fgets(buf, sizeof buf, file);
    if (buf[0] != '\0') buf[strlen(buf) - 1] = '\0';
    

    你的其他问题是错误的索引

        for (j = 0; j < nameCount; j++)
            printf("and %s ", names[i]);
        /*                         ^^^ should be j */
    
        4
  •  0
  •   bta    15 年前

    避免使用 strcpy 使用 strncpy 相反。这将防止缓冲区溢出问题,我认为这就是这里发生的问题。

    数组 temp 大小为11,将10个字符串复制到其中并添加一个尾随字符 '\0' (正确)。元素 names[100][] 只有10个字符长,因此当将10个字符的字符串写入其中时,将在下一个数组元素的第一个字符中写入一个空字符。当您稍后尝试读取此元素时,它将显示为空(这将解释您所看到的空白名称)。

    关于额外的新行,我将重新检查您在数据中的阅读方式。如果是从文本文件中读取,则可能是在文件每行末尾的换行符中读取。解决这个问题的方法是用一个空值(因为这通常是字符串的结尾)替换换行符,比如

    char* pEndl = strchr(input_string,'\0');
    if (pEndl != NULL)
      *pEndl = '\0';
    
        5
  •  0
  •   Bill Lynch    15 年前

    下面是我在修改代码时采取的一些不同的迭代步骤。我没有运行任何一个函数,但是我希望它基本上是正确的(除了最后一个函数,我很久没有接触过c qsort()函数)。前两个具有复杂性O(n^2),而后一个具有复杂性O(n*log(n))。这在“大型”网络上很重要。

    除非你有一个特别的需要去做所有这些副本,否则你真的应该远离它。

    下面代码的最后一个版本还修改了数组的顺序。(对它排序)。


    for (int i = 0; i < count; i++) { 
        bool any_matches = false;
    
        for (int j = i + 1; j < count; j++) {
            if (network[i].xx == network[j].xx && network[i].yy == network[j].yy) {
                if (!any_matches) {
                     printf("%s ", network[i].name);               
                     any_matches = true;
                }
    
                printf("and %s ", network[j].name);
            }
        }
    
        if (any_matches == false)
            printf("No matches for %s.\n", network[i].name);
        else
            printf("are from the same location.\n\n");
    }
    

    for (int i = 0; i < count; i++) { 
        bool any_matches = false;
    
        for (int j = i + 1; j < count; j++) {
            printf("%s matches: ", network[i].name);               
    
            if (network[i].xx == network[j].xx && network[i].yy == network[j].yy)
                printf("%s, ", network[j].name);
        }
    }
    

    int compare_networks(struct Network *left, struct Network *right) {
        if (left->xx < right->xx)
            return -1;
        if (left->xx > right->xx)
            return 1;
        if (left->yy < right->yy)
            return -1;
        if (left->yy > right->yy)
            return 1;
        return 0;
    }
    
    // Sort the list
    qsort(network, count, sizeof(network), compare_networks);
    
    printf("%s matches: ", network[0].name);
    for (int i=1; i<count; ++i) {
        if (network[i-1].xx == network[i].xx && network[i-1].yy == network[i].yy)
            printf("%s, ", network[i].name);
        else
            printf("\n%s matches: ", network[i].name);
    }
    
        6
  •  0
  •   Norman Ramsey    15 年前

    您可能想尝试使用更多的抽象。你的问题本质上与我在 introductory homework assignment . 在同一个网站上 a solution 使用Dave Hanson的 C Interfaces and Implementations 图书馆。

    最基本的想法是使用 Table 以位置(XX和YY元素)为键,以该键为值的地址列表。然后,Fingerprint Groups程序会准确地告诉您多个地址何时具有相同的位置。如果您愿意,下载解决方案并对其进行调整。