sprintf не может изменить статическую переменную внутри вызова printf - PullRequest
0 голосов
/ 19 декабря 2018

Кажется, я не понимаю, что именно здесь происходит

#include <stdio.h>

const char* mes(int a)
{
    static char mess[100];
    sprintf(mess, "%d", a);
    return mess;
}
const int* hes(int a)
{
    static int arr[100];
    arr[0] = a;
    return arr;
}

int main()
{
    printf("%s %s\n", mes(55), mes(25)); //55 55
    printf("%s\n", mes(55)); //55
    printf("%s\n", mes(25)); //25
    printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

В первом printf вторая функция, кажется, игнорируется, и вывод более раннего ввода печатается снова.
Сначала я предположил, что это проблема статических переменных, поэтому я попытался распечатать их по отдельности, а затем они, кажется, работают нормально.
Затем я предположил, что это проблема printf, поэтому я попытался смоделировать то же поведение с целочисленным массивом, итам тоже все работало нормально.
Я пару раз запускал эту программу с различными входами, исключая возможность использования UB.
Итак, что именно мне здесь не хватает?

РЕДАКТИРОВАТЬ:Я столкнулся с этой проблемой где-то еще и не мог понять, что происходит.Таким образом, я воспроизвел проблему в коротком примере кода.Но мой вопрос стоит, (как многие уже упоминали) все параметры оцениваются перед печатью?Если это так, в обоих случаях должна быть перезапись (массив int и char) независимо от порядка вычисления.

Ответы [ 5 ]

0 голосов
/ 19 декабря 2018

Почему код неправильный, хорошо объясняется другими.

Альтернативой static char mess[100] является использование составного литерала в качестве аргумента.Тогда возвращаемое значение из mes(), hes() будет действительным до конца блока кода - намного позже printf().

#include <stdio.h>

const char* mes_helper(char mess[100], int a) {
  sprintf(mess, "%d", a);
  return mess;
}
const int* hes_helper(int arr[100], int a) {
  arr[0] = a;
  return arr;
}

// compound literal        v-------------v
#define mes(a) mes_helper( (char [100]){0}, (a))
#define hes(a) hes_helper( (int  [100]){0}, (a))

// No changes to `main() code
int main(void) {
  printf("%s %s\n", mes(55), mes(25)); //55 55
  printf("%s\n", mes(55)); //55
  printf("%s\n", mes(25)); //25
  printf("%d %d\n", hes(55)[0], hes(25)[0]); //55 25
}

Выход

55 25
55
25
55 25
0 голосов
/ 19 декабря 2018

Возвращение адреса статической переменной почти всегда является плохим шаблоном, как вы уже нашли.Для всей программы существует только одно такое статическое распределение, поэтому, например, двойной вызов mes приводит к тому, что второй вызов перезаписывает результаты первого.

Более разумный вариант - заставить вызывающего предоставить буфер.Это также необходимо для безопасности потока:

#include <stdio.h>

const char* mes(char *buf, int a)
{
    sprintf(buf, "%d", a);
    return buf;
}

const int* hes(int *arr, char *buf, int a)
{
    arr[0] = a;
    return arr;
}

int main()
{
    char buf1[100], buf2[100];
    int arr[100];
    printf("%s %s\n", mes(buf1, 55), mes(buf2, 25)); //55 55
    printf("%s\n", mes(buf1, 55)); //55
    printf("%s\n", mes(buf1, 25)); //25
    printf("%d %d\n", hes(arr, 55)[0], hes(arr, 25)[0]); //55 25
}
0 голосов
/ 19 декабря 2018

Порядок оценки параметров функции не определен, что означает, что теоретически вы можете увидеть 25 25 вместо этого.Это первое.Во-вторых, когда вызывается printf, обе функции уже были оценены, и указатель с одинаковым передается как первая и вторая строка (потому что это статическое расположение), что является результатом последнегооценка (55 в вашем случае).Так идентичный текст печатается.

Это в значительной степени эквивалентно следующему:

char* a = mes(25);
char* b = mes(55);

// Note, the above can swap depending on the order of evaluation

printf("%s %s\n", a, b);

Но здесь a равно b, поскольку оба указывают на статический массив.

Что касаетсяво втором примере этого не происходит, поскольку это будет эквивалентно следующему (вплоть до порядка оценки):

int *ap = hes(55);
int a = ap[0];

int *bp = hes(25);
int b = bp[0];

printf("%d %d\n", a, b);

Обратите внимание, что здесь указанные значения передаются, а несам указатель.Таким образом, хотя ap равно bp, a не совпадает с b.

0 голосов
/ 19 декабря 2018

При вызове:

printf("%s %s\n", mes(55), mes(25));

mes(55) и mes(25) оцениваются до того, как их результат будет введен в строку.И поскольку вы указываете на одну и ту же статическую память, когда пришло время заполнить строку, вы получите то же значение.

0 голосов
/ 19 декабря 2018

Существует только одна переменная mess.Когда вы звоните:

printf("%s %s\n", mes(55), mes(25));

Вы заполняете эту одну переменную два раза, один раз с "25" и один раз с "55" (перезаписывая "25").В результате, когда printf переходит к форматированию с %s %s, он дважды находит одну и ту же строку, "55" и снова "55", поскольку "25" уже перезаписан.

Вам необходимоПомните, что все параметры функции оцениваются до ее вызова.Порядок оценки параметров не определен, но часто справа налево.Разбивая это printf на маленькие шаги:

  1. Оцените mes(25);теперь беспорядок статических символов теперь "25".
  2. Оцените mes(55), теперь беспорядок статических символов перезаписан в "55".
  3. Оцените параметр "%s %s\n" (нетЗдесь многое можно оценить. Это просто строка)
  4. Вызов printf с параметрами: "%s %s\n", "55" и "55"
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...