Znak '\0' na końcu stringa

0

Witam
piszę program który ma drukować następujący wzorzec:
1
22
333
4444
55555
666666
itd w zależności od n podanego jako argument funkcji

char* pattern(const int n)
{
	char *result = (char*)malloc(sizeof(char)*(n*(n + 1) / 2) + 1 + n);
	char *temp = (char*)malloc(sizeof(char) * 3);
	int i, j;
	memset(result, '\0', (n*(n + 1) / 2) + 1 + n);
	if (n<1)
		return "";
	for (i = 1; i <= n; i++)
	{
		for (j = 1; j <= i; j++)
		{
			sprintf(temp, "%d", i);
			strcat(result, temp);
		}
		if (i == n&&j==n+1)
			break;
		strcat(result, "\n");
	}
	strcat(result, "\0");
	return result;
}

W jaki sposób dodać na końcu stringa znak '\0', zamiast "\n" tak żebym mógł pozbyć się instrukcji if i break w zagnieżdżonej pętli for

1

Jak program ma to drukować to po co te wszystkie alokacje?

0

Źle się wyraziłem. Program ma to zapisywać do stringa
po każdym wykonaniu wewnętrznej pętli zapisywany jest znak "\n" na koniec wiersza. Znak ten będzie też zapisany na końcu całego ciągu. I teraz chciałbym zamienić ostatni znak "\n", przez znak '\0'. Nie podoba mi się to jak to zrobiłem bo jest instrukcja if break w pętli. Gdybym na końcu stringa znak "\n" mógł zastąpić znakiem '\0' program byłby bardziej przejrzysty i zwarty

3

Zapisuj z \n, a na samym końcu nadpisz ostatni znak zerem.

0

Czy można to zrobić tak:

result[strlen(result)-1]='\0'
0

No można, ale lepiej zawsze pamiętać gdzie się kończy string, inaczej czas wykonania algorytmu staje się kwadratowy

0
//Classic example how do algorithm type N make to n^2
#include <cstdlib>
#include <string.h>
#include <stdio.h>

using namespace std;

int main(int argc, char** argv) {
    
    /* N^2 */
    char *waste = "I love waste time!";
    char *temp = waste;
    for(int i = 0; i < strlen(waste); i++){
        printf("%c", *temp);
        temp += 1;
    }
    
    printf("\n");
    
    /* N */
    char *noWaste = "Don't waste my time!";
    temp = noWaste;
    int length = strlen(noWaste);
    for(int i = 0; i < length; i++){
        printf("%c", *temp);
        temp++;
    }
    
    /*
     Conclusion 
     *  If you want have a clean and fast code.
     *  No call function i definition loop and functions having loop into code.
     *  
     */
    
    return 0;
}


0

Mam jeszcze jeden problem. Program przechodzi testy bazowe ale nie przechodzi testów randomowych. I nie wiem gdzie jest błąd. Może chodzi o alokację pamięci.

0

Ten program nie przechodzi mi testów randomowych. Wszystkie podstawowe są ok. Myślę że zwalnianie pamięci nie ma znaczenia bo wszystko jest robione po stronie programu testowego.

char* pattern(const int n)
{
	char *result=(char*)malloc(sizeof(char)*n*n*n);
	char *temp = (char*)malloc(sizeof(char) * 4);
	memset(result, '\0', n*n*n);
        memset(temp,'\0',4);
	if (n<1)
		return "";
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			sprintf(temp, "%d", i);
                        strcat(result,temp);
		}
		strcat(result, "\n");
	}
	result[strlen(result) - 1] = '\0';
	return result;
}

Dodanie znaku '\0' działa prawidłowo

0
gonskabalbinka napisał(a):

Ten program nie przechodzi mi testów randomowych. Wszystkie podstawowe są ok. Myślę że zwalnianie pamięci nie ma znaczenia bo wszystko jest robione po stronie programu testowego.

char* pattern(const int n)
{
	char *result=(char*)malloc(sizeof(char)*n*n*n);
	char *temp = (char*)malloc(sizeof(char) * 4);
	memset(result, '\0', n*n*n);
        memset(temp,'\0',4);
	if (n<1)
		return "";
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= i; j++)
		{
			sprintf(temp, "%d", i);
                        strcat(result,temp);
		}
		strcat(result, "\n");
	}
	result[strlen(result) - 1] = '\0';
	return result;
}

Dodanie znaku '\0' działa prawidłowo

Kod wywołujący zobowiązany jest zwrócić to, co jest u niego wynikiem 'result'. To częsty problem w C, i kontrakt na to wynika jedynie z dokumentacji (tj. nie ma w języku formalnych środków ku temu). Nie wiem co twoje testy o tym "wiedzą".

Zwolnienie temp w tym kodzie, to twój obowiązek. W ogóle temp u ciebie jest małej długości, nie ma sensu go alokować, zrobić zwykłą lokalną tablicę.

0

Spróbuję tak jak mówisz zamiast alokować pamięć dla temp zrobić z niego tablicę i zobaczymy co z tego wyjdzie.

Dalej to samo przechodzę testy bazowe ale nie przechodzę randomowych

1

char *result=(char*)malloc(sizeof(char)*n*n*n); wcześniej narzekałem na kwadratową złożoność result[strlen(result) - 1] = '\0' i strcat, więc widzę, że zdecydowałeś się pójść za ciosem i wprowadzić sześcienną złożoność pamięciową? WTF.

Zastanów się ile pamięci potrzebuje ten program dla n = 2000. Bo powinien około 2MB.

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

/*
1
22
333
4444
55555
666666
 */
int countDigit(int n) {
    int count = 0;
    while (n != 0) {
        n = n / 10;
        ++count;
    }
    return count;
}

char * getDigitFromInt(int number) {
    int digits = countDigit(number);
    char * result = (char*) malloc(digits * sizeof (char));
    int i = digits - 1;
    while (number > 0) {
        result[i] = '0' + (number % 10);
        number = number / 10;
        i--;
    }
    /*
    for(int i = 0; i < digits; i++){
      printf("%c", result[i]);
    }
     */
    return result;
}

char *** pattern3d(const int n) {
    char ***result = (char***) malloc(n * sizeof (char**));
    for (int row = 0; row < n; row++) {
        result[row] = (char**) malloc((row + 1) * sizeof (char*));
        for (int column = 0; column <= row; column++) {
            result[row][column] = getDigitFromInt(row + 1);
        }
        printf("\n");
    }
    return result;
}

void pattern3dPrinter(char *** arr, int n) {
    for (int row = 0; row < n; row++) {
        for (int column = 0; column <= row; column++) {
            printf("%s", arr[row][column]);
        }
        printf("\n");
    }
}

int main() {
    char ***result = pattern3d(12);
    pattern3dPrinter(result, 12);
    return 0;
}

Rozwiązałem to zadanie w ten sposób i faktycznie potrzebna jest tablica 3d, bo mam tak wiersze * kolumny * łańcuchy znaków, o ile samo wydrukowanie tego na konsoli to proste zadanie to upakowanie tego do tablicy 3d na gwiazdeczkach to już trzeba pomyśleć.

Zapraszam do testowania.
https://code.sololearn.com/cjt2HFLwPvu2/#c

Ups valgrind znalazł namnie 78 cosiów XD.

==8432== Invalid read of size 1
==8432==    at 0x4C32D04: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8432==    by 0x4E994D2: vfprintf (vfprintf.c:1643)
==8432==    by 0x4EA0F25: printf (printf.c:33)
==8432==    by 0x1088DD: pattern3dPrinter(char***, int) (main.cpp:68)
==8432==    by 0x10891D: main (main.cpp:76)
==8432==  Address 0x522d131 is 0 bytes after a block of size 1 alloc'd
==8432==    at 0x4C2FB0F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==8432==    by 0x108738: getDigitFromInt(int) (main.cpp:23)
==8432==    by 0x10884E: pattern3d(int) (main.cpp:57)
==8432==    by 0x108908: main (main.cpp:75)
==8432== 
1
22
333
4444
55555
666666
7777777
88888888
999999999
10101010101010101010
1111111111111111111111
121212121212121212121212
==8432== 
==8432== HEAP SUMMARY:
==8432==     in use at exit: 831 bytes in 91 blocks
==8432==   total heap usage: 92 allocs, 1 frees, 1,855 bytes allocated
==8432== 
==8432== LEAK SUMMARY:
==8432==    definitely lost: 96 bytes in 1 blocks
==8432==    indirectly lost: 735 bytes in 90 blocks
==8432==      possibly lost: 0 bytes in 0 blocks
==8432==    still reachable: 0 bytes in 0 blocks
==8432==         suppressed: 0 bytes in 0 blocks
==8432== Rerun with --leak-check=full to see details of leaked memory
==8432== 
==8432== For counts of detected and suppressed errors, rerun with: -v
==8432== ERROR SUMMARY: 78 errors from 1 contexts (suppressed: 0 from 0)

0
invisiblehat napisał(a):

Już sprawdziłem ;p jakieś wskazówki jak to załatać nie wiem jak się do tego zabrać w ogóle??

Każdy malloc musi mieć do pary free.
Dwie twoje funkcje alokują i to alokowane zwracają - za zwolnienie odpowiada kod wywołujący.

Musisz to mieć w głowie, zanim chwycisz za analizę narzędziem.

...  i faktycznie potrzebna jest tablica 3d, bo mam tak wiersze * kolumny * łańcuchy znaków, o ile samo wydrukowanie tego na konsoli to proste zadanie to upakowanie tego do tablicy 3d na gwiazdeczkach to już trzeba pomyśleć.

Bywałeś w wątku obok, o potrójnych gwiazdkach. To patologia.

O ile pozostajemy w C, klarownym rozwiązaniem jest tablica struktur, która to struktura jest zasobnikiem na "rzeczy" *) niższego rzędu. Kod się robi 100x jaśniejszy

*) by nie powiedzieć obiekty

0

W miarę sensowne rozwiązanie znajduje się poniżej. Należy jednak zwrócić uwagę na to, że precyzja obliczeń zmiennoprzecinkowych może okazać się za mała dla większych wartości n (lub nawet dla odpowiednich mniejszych), oraz że nie ma żadnej weryfikacji błędów. Niemniej jednak, koncept jest bez zmian:

static size_t count_digits_log(int n)
{
    size_t digits = 1 + floor(log(n)/log(10));
    size_t min = pow(10, digits-1);
    size_t smaller = n < 10 ? 0 : count_digits_log(min - 1);
    return digits * (n - min + 1) * (n + min) / 2 + smaller;
}

char* gen_strings(int n)
{
    size_t alloc_size = n + count_digits_log(n);
    char* buf = (char*)malloc(alloc_size);
    char* cursor = buf;
    buf[0] = '\0';

    for(int i = 1; i <= n; i++) {
        char current[10];
        int len = sprintf(current, "%d", i);
        for(int j = 0; j < i; j++) {
            strcat(cursor, current);
            cursor += len;
        }
        *cursor++ = '\n';
    }

    cursor[-1] = '\0';

    return buf;
}

https://wandbox.org/permlink/zfxZni435pJVWwAe
W przykładzie nie zwalniam pamięci.

Dodałem funkcję którą bym umieścił na produkcji (tylko skomentowałbym magiczne stałe...). Na mojej maszynie dla n=1000 funkcja z log zwraca błędne dane (czyli wychodzi niedokładność wartości zmiennoprzecinkowych):

static size_t count_digits_ifs(int n)
{
    assert(n < 91055);
    constexpr static auto count_in_range = [](size_t min, size_t max) {
        return (max - min + 1) * (min + max) / 2;
    };

    if(n < 10)
        return count_in_range(1, n);
    if(n < 100)
        return count_in_range(10, n) * 2 + 45;
    if(n < 1000)
        return count_in_range(100, n) * 3 + 9855;
    if(n < 10000)
        return count_in_range(1000, n) * 4 + 1493505;
    if(n < 100000)
        return count_in_range(10000, n) * 5 + 199475505;
}
0

Poniżej przykładowe rozwiązanie:

char *pattern(const int n)
{
    int pos = 0;
    static char str[10240];

    str[0] = '\0';
    for (int i = 1 ; i <= n ; ++i)
    {
        for (int j = 0 ; j < i ; ++j)
        {
            str[pos++] = '0' + i;
        }
        str[pos++] = i == n ? '\0' : '\n';
    }

    return str;
}

Dlaczego tablica wynikowa ma rozmiar 10240 i dlaczego kolejny znak to '0'+'i';

0

Bo niedoprecyzowałeś warunków zadania. Powyżej 9 nadal ma być jeden znak, i najwyraźniej drukujemy kolejne znaki jakie mamy w tablicy znaków.

1 użytkowników online, w tym zalogowanych: 0, gości: 1