Невозможность работать при сравнении строк с использованием указателя функции в C

Есть две версии функции compare_first_character, и обе они передаются в другую функцию. Однако первый даст правильные совпадения слов, а второй не вернет ничего найденного, несмотря на то, что он есть.

Версия 1: [Версия 1 сравнения строк]

int compare_first_characters(const void *p, const void *q) {
    return **(char **)p - **(char **)q;
}

Версия 2 (сбой): [Версия 2 сравнения строк]

int compare_first_characters(const void *p, const void *q) {
    return strcmp(*(char **)p, *(char **)q);
}

Предполагаемая функция, при которой возникают проблемы:

    const void *found = bsearch(key, words, n, sizeof(words[0]), compare_first_characters);

Полная программа:

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

int compare_first_characters(const void *p, const void *q) {
    return strcmp(*(char **)p, *(char **)q);
}

int main(int argc, char *argv[]) {
    char *words[] = {"red", "blue", "green", "yellow"};
    int n = sizeof(words) / sizeof(*words);
    const char *key = "g";

    qsort(words, n, sizeof(words[0]), compare_first_characters);
    printf("Sorted by first letter: \n");
    for (int i = 0; i < n; i++) {
        printf("%s ", words[i]);
    } 
    printf("\n");
    printf("Looking for a word that starts with '%s': ", key);
    const void *found = bsearch(key, words, n, sizeof(words[0]), compare_first_characters);
    printf("found '%s'\n", found ? *(const char **)found : "none");
    return 0;
}

Приветствую вас, но фотографии исходного кода не являются. Пожалуйста, опубликуйте весь код в виде текста. А еще лучше, опубликуйте простой минимально воспроизводимый пример обоих кодов, который читатели могут проверить. В комплекте с #include и main() и т. д.

Weather Vane 01.08.2023 20:53

Понятно. Я внес изменения. Спасибо, что указали.

Hank Tang 01.08.2023 20:54

@HankTang Ожидаете ли вы, что strcmp("green", "g") вернет значение, одинаковое или разное?

chux - Reinstate Monica 02.08.2023 05:29

Привет, я ожидаю, что они будут другими.

Hank Tang 02.08.2023 17:49
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
59
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

int compare_first_characters(const void *p, const void *q) {
    return strcmp(*(char **)p, *(char **)q);
}
  1. это полная ерунда и вызывает неопределенное поведение. *(char **)p не является эквивалентом (char *)p
  2. strcmp сравнивает целые строки, а не первые символы.

если вы хотите сравнить строки

int compare_first_characters(const void *p, const void *q) {
    return !strcmp(p, q);
}

если только первые символы

int compare_first_characters(const void *p, const void *q) {
    const char *a = p, *b = q;
    return *a == *b;
}

https://godbolt.org/z/MMEra35vY

Спасибо. Но после внесения изменений я все еще не могу найти совпадения.

Hank Tang 01.08.2023 21:29

Понял, спасибо!

Hank Tang 02.08.2023 17:59
Ответ принят как подходящий

Обе ваши функции сравнения, вызванные с помощью bsearch, недействительны.

Функция bsearch объявляется следующим образом

void *bsearch(const void *key, const void *base,
              size_t nmemb, size_t size,
              int (*compar)(const void *, const void *));

То есть key нужно передавать по ссылке через указатель на него.

Однако вы вызываете функцию, например

const void *found = bsearch(key, words, n, sizeof(words[0]), compare_first_characters);
                            ^^^  

Итак, в первой функции сравнения это двойное разыменование

int compare_first_characters(const void *p, const void *q) {
    return **(char **)p - **(char **)q;
}

указателя, соответствующего аргументу key, который фактически имеет тип const char * в соответствии с его объявлением

const char *key = "g";

вызывает неопределенное поведение. Вы ошибаетесь, говоря, что при использовании первой функции сравнения программа работает корректно. Попробуйте, например, использовать другую строку в качестве ключа, например, "y".

Вы должны написать вызов функции, например

const void *found = bsearch( &key, words, n, sizeof( words[0] ), compare_first_characters );
                             ^^^^

Обратите внимание, что функция strcmp сравнивает строки целиком. Поэтому для использования второй функции сравнения необходимо указать строковый литерал, равный одной из строк массива. В этом случае обе функции сравнения будут работать корректно.

И относительно первой функции сравнения вы должны привести указатели void к беззнаковому типу символов const unsigned char **.

Вот ваша обновленная программа.

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

int compare_first_characters1( const void *p, const void *q ) {
    return **( const unsigned char ** )p - **( const unsigned char ** )q;
}

int compare_first_characters2( const void *p, const void *q ) {
    return strcmp( *( char ** )p, *( char ** )q );
}

int main( void )
{
    const char *words[] = { "red", "blue", "green", "yellow" };
    int n = sizeof( words ) / sizeof( *words );
    const char *key1 = "y";

    qsort( words, n, sizeof( words[0] ), compare_first_characters1 );
    printf( "Sorted by first letter: \n" );
    for (int i = 0; i < n; i++)
    {
        printf( "%s ", words[i] );
    }
    printf( "\n" );
    printf( "Looking for a word that starts with '%s': ", key1 );
    const void *found = bsearch( &key1, words, n, sizeof( words[0] ), compare_first_characters1 );
    printf( "found '%s'\n", found ? *( const char ** )found : "none" );

    putchar( '\n' );

    const char *key2 = "yellow";
    qsort( words, n, sizeof( words[0] ), compare_first_characters2 );
    printf( "Sorted by first letter: \n" );
    for (int i = 0; i < n; i++)
    {
        printf( "%s ", words[i] );
    }
    printf( "\n" );

    printf( "Looking for a word that starts with '%s': ", key2 );
    found = bsearch( &key2, words, n, sizeof( words[0] ), compare_first_characters2 );
    printf( "found '%s'\n", found ? *( const char ** )found : "none" );
}

Вывод программы

Sorted by first letter:
blue green red yellow
Looking for a word that starts with 'y': found 'yellow'

Sorted by first letter:
blue green red yellow
Looking for a word that starts with 'yellow': found 'yellow'

Кстати, поскольку все строки в массиве отличаются по первому символу, то вы можете отсортировать массив с помощью первой функции сравнения, а затем искать целевую строку, например, "yellow" или "green", используя вторую функцию сравнения при вызове bsearch.

Здесь обновлена ​​предыдущая программа.

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

int compare_first_characters1( const void *p, const void *q ) {
    return **( const unsigned char ** )p - **( const unsigned char ** )q;
}

int compare_first_characters2( const void *p, const void *q ) {
    return strcmp( *( char ** )p, *( char ** )q );
}

int main( void )
{
    const char *words[] = { "red", "blue", "green", "yellow" };
    int n = sizeof( words ) / sizeof( *words );
    const char *key = "green";

    qsort( words, n, sizeof( words[0] ), compare_first_characters1 );
    printf( "Sorted by first letter: \n" );
    for (int i = 0; i < n; i++)
    {
        printf( "%s ", words[i] );
    }
    printf( "\n" );
    printf( "Looking for a word that starts with '%s': ", key );
    const void *found = bsearch( &key, words, n, sizeof( words[0] ), compare_first_characters1 );
    printf( "found '%s'\n", found ? *( const char ** )found : "none" );
}

Его вывод

Sorted by first letter:
blue green red yellow
Looking for a word that starts with 'green': found 'green'

Понял, спасибо! Просто деталь, что bsearch должен использовать ту же функцию сравнения, что и qsort, верно? Я думаю, что это написано в руководстве bsearch.

Hank Tang 02.08.2023 18:15

Другие вопросы по теме