LINUX.ORG.RU

Как в C красиво заменить NULL-pointer

 


1

2

Я написал вот такой код:

printf("My string is %s", get_my_string() || "unknown");
подразумевая что get_my_string возвращает строку или NULL. Компилятор такое не съел. Можно ли как-то красиво получить нужную мне функциональность?

★★★★★
printf("My string is %s", first_non_null(get_my_string(), "unknown"));
crowbar ★
()
static inline const char *
fb(const char *ptr, const char *fallback)
{
    return ptr ? ptr : fallback;
}


printf("My string is %s", fb(get_my_string(), "unknown"));
i-rinat ★★★★★
()

Забудь про псевдокрасивости. Важна только читабельность кода, поэтому не выпендривийся и пиши прямо.

const char* str = get_my_string();
if (str == NULL)
  str = "unknown";

printf("My string is %s", str);

или

const char* string_or(const char* string, const char* or) {
  if (string)
    return string;
  return or;
}
...
printf("My string is %s", string_or(get_my_string(), "unknown"));
В самом крайнем случае через тернарный оператор.

slovazap ★★★★★
()
Последнее исправление: slovazap (всего исправлений: 2)
Ответ на: комментарий от slovazap

Инкрементирую этого адеквата. Все, кто пишут однострочники — пидо^Wплохо относятся к людям, которые будут читать их код.

Deleted
()
Ответ на: комментарий от i-rinat

Ты всё правильно пишешь, осталось сделать последний шаг:

printf("My string is %s", get_my_string() ?: "unknown");

d ★★★★★
()

Ну, раз вы тут наркоманите:

#include <stdio.h>

char *
get_my_string()
{
        return NULL;
}

int
main()
{
        char *s;
        printf("My string is %s\n", (s = get_my_string(), s ? s : "unknown"));
        return 0;
}

UPD:

printf("My string is %s\n", get_my_string() ?: "unknown");
Вообще сказка! ;) Правда по поводу портабельности у меня сомнения... thx, d!

beastie ★★★★★
()
Последнее исправление: beastie (всего исправлений: 2)
Ответ на: комментарий от morse

Смешно звучит от чувака с лямбдой на аватарке.

Я же не фанатик, а самый таки обычный программист за еду.

Deleted
()
Ответ на: комментарий от beastie

Касательно ?: — А, вообще, если хорошо подумать — это не очень хорошее gnu расширение:

A GNU extension to C allows omitting the second operand, and using implicitly the first operand as the second also:

a = x ? : y;
The expression is equivalent to
a = x ? x : y;
except that if x is an expression, it is evaluated only once. The difference is significant if evaluating the expression has side effects.

ref: http://en.wikipedia.org/wiki/?:

beastie ★★★★★
()

?: (ровно как и ? x :) на каждом шагу использовать не стоит, слишком там уж много условностей. Лучше уж-таки простым условием, а если свербит, то прописать макрос для препроцессора.

Bfgeshka ★★★★★
()
Ответ на: комментарий от beastie

x 1 раз упомянут, 1 раз вычислен. Логично. Не очень хорошо было бы обратное.

wakuwaku ★★★★
()
Ответ на: комментарий от slovazap

И чем же твоё говно читабельней? Тем, что вместо 100строк я получу овер1к? Отлично - это же так удобно, когда вместо взгляда на полный код - я скролю как мразь.

Эти нулёвые if(str == NULL), это неосиляторство ?: и пацан рождает говняную лапшу. Хорошо хоть додумался спастить функцию у пацанов.

anonymous
()
Ответ на: комментарий от beastie

Интересно, а в реальном мире вообще есть код типа

int a = (expression-with-side-effects) ? (expression-with-side-effects) : something;

Просто подобная хрень, имхо, похожа на баг.

Stil ★★★★★
()

а если сделать get_my_string() возвращать строку или 0?

anonymous
()

за вас все давно уже сделано

anonymous
()
Ответ на: комментарий от slovazap

Забудь про псевдокрасивости. Важна только читабельность кода, поэтому не выпендривийся и пиши прямо.

Как же ты прав, дорогой человек. К слову, мне безумно нравится в Go, что там просто нельзя сделать иначе.

mix_mix ★★★★★
()

Почему множественные глобальные неинициализированные переменные с одинаковыми именами и разными типами не приводят к ошибке компиляции?

$ cat run.sh
#!/bin/bash

ls

cat >a.c <<EOF
char global;
int get_a(void) { return global; }
void set_a(int value) { global = value; }
EOF

cat >b.c <<EOF
int global;
int get_b(void) { return global; }
void set_b(int value) { global = value; }
EOF

cat >c.c <<EOF
float global;
int get_c(void) { return global; }
void set_c(int value) { global = value; }
EOF

cat >main.c <<EOF
#include <stdio.h>
int get_a(void); void set_a(int);
int get_b(void); void set_b(int);
int get_c(void); void set_c(int);
void p(void) {
  printf("%i %i %i\n",
    get_a(), get_b(), get_c()
  );
}
int main() {
  set_a(1); p();
  set_b(2); p();
  set_c(3); p();
  set_a(4); p();
  set_b(5); p();
  set_c(6); p();
  return 0;
}
EOF

gcc -Wall -Wextra -pedantic -ansi *.c -o main
./main
$ ./run.sh
run.sh
1 1 0
2 2 0
0 1077936128 3
4 1077936132 3
5 5 0
0 1086324736 6

anonymous
()
Ответ на: комментарий от slovazap

Ты чертовски прав!

Вообще, возврат некоторой функции вместо реального значения некоторого NULL по смыслу может быть по трем причинам:

1) Функция должна сигнализировать о том, что что-то пошло не так. Тогда она должна возвращать NULL, и результат должен явно проверяться if (result==null)...

2) Функция возвращает NULL, но это нормально, это не сигнал о том, что что-то пошло не так. Тогда функция не должна возвращать NULL, и проверку надо перенести в саму функцию return (result==NULL) ? «unknown» : result

3) Функция возвращает NULL, и в одних случаях это сигнал о том, что что-то пошло не так, а в других случаях это нормально - например в случае печати. Тогда функция должна возвращать NULL и он должен в одних местах обрабатываться явно if (result==NULL), а в других местах, например для печати, мне кажется выгоднее завести функцию const char *printable(const char *) приводящую все возможные результаты исходной функции к печатному виду.

anonymous
()
Ответ на: комментарий от anonymous

Если выражаться более формально: есть функция f возвращающая результат из домена Ef, и функция g, принимающая параметр из домена Dg. Предположим, Ef не совпадает с Dg, а мы хотим использовать функции так g(f()). Тогда нам нужно написать функцию h, переводящую Ef в Dg, и использовать их так g(h(f())).

Рассмотрим теперь домен X=Ef\Dg. Если в X нет значений, которые мы хотим обрабатывать особым образом (то есть любым образом, кроме передачи в g), то мы можем не определять отдельно f, а определить сразу h*f. Или, если это возможно, сразу определить g*h. Если не возможно ни то, ни другое, то придётся определить отдельно f и g, и в этом случае, мне кажется, намерения будут в коде видны яснее, если явно определить h.

Конечно, иногда для одной f много разных g1...gN, с параметрами из разных доменов. Выгодно или нет определять функции перевода для всех этих доменов, - это решит ситуация.

anonymous
()
Ответ на: комментарий от anonymous

В рассматриваемом случае f() это get_string(), Ef это множество всех действительных строк и NULL, g(s) это printf(«My string is %s\n», s), Dg это множество всех действительных строк, f(s) это printable(s) сопоставляющая значению NULL действительную строку «unknown», h*f это то же самое, что перенести проверку на NULL в функцию get_string(), g*h это то же самое, что и включить в printf тернарный оператор или выполнить проверку на NULL перед printf. Как бы то ни было, если вы будете держать такой взгляд на происходящее в уме, у вас появится возможность осознанно выбирать между вариантами, а не зацикливаться на одном.

anonymous
()
Ответ на: комментарий от anonymous

Вообще, возврат некоторой функции вместо реального значения некоторого NULL по смыслу может быть по трем причинам

Плюсую, это все излишние навороты. Классика жанра:

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

int
get_my_string( char * out, int len )
{
    if ( len >= 4 )
      {
        strcpy( out, "good" );
        return 1;
      }

    /* error */
    return 0;
}

int main ( void )
{
    int length = 100;
    char * out = malloc( length * sizeof(char) );
    printf( "My string is %s\n",
            get_my_string( out, length ) ? out : "unknown" );
    free( out );

    return 0;
}

gh0stwizard ★★★★★
()
Ответ на: комментарий от anonymous

3) Функция возвращает NULL, и в одних случаях это сигнал о том, что что-то пошло не так, а в других случаях это нормально

бьём этого погромиста (например) «совершенным кодом» по голове до тех пор пока он не перестанет так делать

anonymous
()

Компилятор такое не съел.

что сказал?

emulek ★
()
Ответ на: комментарий от anonymous

1) Функция должна сигнализировать о том, что что-то пошло не так. Тогда она должна возвращать NULL, и результат должен явно проверяться if (result==null)...

Голуб смотрит на тебя с презрением.

emulek ★
()

лови:

   const char *t = get_my_string();
   printf("%s\n", t? t: "unknown");
вариант ?: не нужен, ибо непонятный.

emulek ★
()
Ответ на: комментарий от anonymous

emulek пророкъ его!

К тому же, многие программисты избегают условной операции (?:) просто потому, что она им кажется непонятной. Тем не менее, это условное выражение может существенно упростить ваш код и, следственно, сделать его лучше читаемым. Я думаю, что:

printf("%s", str ? str : "<пусто>");  
гораздо элегантнее, чем:
if ( str == NULL )  
 printf( "<пусто>" );  
else  
 printf( "%s", str );  
Вы к тому же экономите на двух вызовах printf(). Мне также часто приходится видеть неправильное использование операций ++ и --. Весь смысл автоинкремента или автодекремента заключается в соединении этих операций с другими. Вместо:
while ( *p )  
{ 
 putchar ( *p );  
++p;  
}  
или:
for ( ; *p ; ++p ) 
 putchar ( *p );  
используйте:
while ( *p ) 
 putchar ( *p++ );  
Этот код вполне читаем для компетентного программиста на языке С, даже если ему нет эквивалентной операции в ФОРТРАНе или Паскале.

emulek ★
()
Ответ на: комментарий от gh0stwizard

а ничего, что твоя get_my_string() дважды вызывается?

emulek ★
()
Ответ на: комментарий от Stil

Интересно, а в реальном мире вообще есть код типа

а как же!

Просто подобная хрень, имхо, похожа на баг.

конечно это баг: строка же откуда-то _берётся_, сначала одну возьмёт и проверит, потом _вторую_ использует.

emulek ★
()
Ответ на: комментарий от gh0stwizard
    if ( len >= 4 )
      {
        strcpy( out, "good" );

Хе-хе, классика сипроблем: дотационный нуль-терминатор.

anonymous
()
Вы не можете добавлять комментарии в эту тему: топик перемещен в архив.