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
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.