Професионална програма
Loading...
agogo avatar agogo 12 Точки

C programming: Възможно ли е?

Здравейте!

Искам да ви запитам дали следното присвояване е коректно и кога би предизвикало вероятна грешка:

// Това е една от примерните задачи в курса:

// Да се съединят всички низове в масива, разделени чрез даден символ.

int main()

{

             char *delim = ", ";

             char *to[10] = { "Plovdiv","Sofia" ..., " "};

             char *result = join(to,length,delim);

             .......

}

char * join(char *to[], site_t size,char *delim)

{

             char *p = strcat(to[0],delim);

             ........

            return p;

}                                        

// Идеята ми тук, е да се избегне предварително използване на malloc или calloc, а директно да се използва първия низ от масива.

// В случая примерът сработва!

// Според описанието на strcat, се връща указател към низ. Но дали по посочения начин се работи с копие на този низ (понеже се предава // на функция ) или  не! Искам да попитам това не е ли явно задаване на низ от тип char *p = string; (може би без '\0')

Благодаря!

1
C Programming 26/01/2016 16:22:02
fristgerb avatar fristgerb 50 Точки

Здрасти! :)

кога би предизвикало вероятна грешка

Винаги. Опита ли се поне веднъж да пуснеш кода, който си написал? :)

Програма ти се разваля при strcat (to[0], delim); понеже се опитваш да модифицираш низа "Plovdiv", но той не бива да се променя. Такива низове операционната система ги слага в памет, която е read-only за твоята програма. Когато се опитваш да пишеш по тази read-only памет, получава "Segmentation Fault" и процесът ти бива терминиран.

Паметта на стартираната програма изглежда така:

 

   Process Memory
+-------------------+
|stack:             |
|  char *to[2] = {  |
|    0x01, >--------+-----+
|    0x02, >--------+---+ |
|  };               |   | |
|                   |   | |
+-------------------+   | |
|heap:              |   | |
|                   |   | |
+-------------------+   | |
|read-only:         |   | |
|  "Plovdiv\0" <----+---+-+
|  "Sofia\0" <------+---+
|                   |
+-------------------+
5
26/01/2016 14:02:27
agogo avatar agogo 12 Точки

Извинявам се!

Неправилно съм показал примера, сега съм го редактирал!

Според http://www.delorie.com/djgpp/doc/libc-2.02/libc_640.html strcat връща указател.

Дали този указател не може да се използва по посочения начин.

Посочения пример работи, но дали е коректно и дали да се избягва?

1
26/01/2016 15:10:20
denka avatar denka 52 Точки

Здравей,

Указателя, който връща strcat сочи към първия елемент на първия параметър на strcat. Т.е.

char * ptr = strcat(str1, str2);

Тук ptr и str сочат на едно и също място в паметта. - Функцията не събира str1 и str2  в ptr, а добавя str2 в края на str1.

Дали може да го върнеш като резултат от функция или не зависи от това къде е паметта на str1 - ако е в heap-a няма проблем, но ако е в стека ще се замаже при излизане от функцията:

char * test1() {

char * str1 = malloc(200);

......

return strcat(str1, str2); } е ОК,

но

char * test1() {

char str1[200];

......

return strcat(str1, str2); } не е ОК

Иначе за самия код който си написал, наистина няма как да работи, защото както ти е казал колегата, to[0]  е в read only паметта, а strcat се опитва да го модифицира.

Също така имай впредвид, че в str1 трябва да има достатъчно памет за конкатинацията. Тоест ако в примера който съм написала се опитам да конкатинирам стрингове с размери 100 и 150 ще има проблем, защото паметта на str1 е само 200.

В някоя от лекциите бяха казали за valgrind програмката. Много е полезна и ти препоръчвам винаги да се тестваш кода с нея.

 

 

2
agogo avatar agogo 12 Точки

Здравейте и благодаря!

Ето целия код, където използвам въпросното присвояване http://tjelev.free.bg/work/zd6.c и макар той да сработва в DJGPP(FREEDOS) и GCC(LINUX), когато проверих с Valgrind се появиха грешките за които писахте.

Явно не може да се избегне malloc и calloc.

Искам да попитам има ли подобна програма на Valgrind и за FREEDOS - не успях да открия в Интернет!

Лека и спокойна!

 

1
26/01/2016 18:48:29
fristgerb avatar fristgerb 50 Точки

Неправилно съм показал примера, сега съм го редактирал!

На практика е същото като предишния код. Проблемът е същия.

Кодът http://tjelev.free.bg/work/zd6.c е доста труден за четене, но изглежда при него се ползва calloc за заделяне на място за съединените низовете и го няма проблема с read-only паметта.

когато проверих с Valgrind се появиха грешките

Може Valgrind да има добра причина да се оплаква при този код, но може причината и да не е добра. Макар и някои хора да биха оспорили, част от грешките, които Valgrind дава не са истински грешки. Може би е полезен в някои ситуации, но не може да се разчита сляпо на него, без да се осмисли какво всъщност се случва.

Примерно, Valgrind ще се оплаче, ако не направим free() на всичко преди спирането на програмата. Да не се прави <code>free()</code> в края на програмта не е грешка, а по-скоро обратното е грешка. Тази грешка е допусната и в примера, който си дал:

  ...
  for(i = 0; i < n; i++)
     free(np[i]);
  free(result);

  return 0;

Това са абсолютно излишни процесорни операции, които само бавят завършването на програмата и усложняват кода.

0
27/01/2016 00:59:42
agogo avatar agogo 12 Точки

Здравей!

Ако е възможно да ми напишеш, защо кодът е труден за четене !

Това е важно за мен! Би ли нахвърлял някои препоръки?

Благодаря, предварително!

1
fristgerb avatar fristgerb 50 Точки

Труден ми е да го разбера понеже не се разбира по имената на променливите и на функциите за какво служат те.

Примерно, ако погледна main функцията, не знам за какво са buffer, str, token, np, i, нито send(). За да разбера е нужно да проследя цялата логика на кода, влючително това, което се случва и в другите функции.

Нека погледнем реда char *str = getstr (...);. Името "str" не ми дава никаква информация. Аз знам, че тези променлива е низ, понеже е char *. Много по-полезно щеше да е, ако се казва примерно user_input или още по-добре city_names_input, ако целта на програмата е да прави нещо конкретно. Но аз и не знам каква е целта на програмата.

"np" дори и като съкращение не знам какво означава, а "size_t i" е като абсолютно произволна буква без значение.

Добре е да се сложи поне едно изречение относно каква е целта на програмата, понеже без някакъв контекст е много пъти по-трудно да се разбере какво се случва в кода. Може да го сложиш най-отгоре във файла. Примерно:

/* Gets a comma separated list of cities and prints them on separate
 * lines.
 */
0
27/01/2016 13:22:04
agogo avatar agogo 12 Точки

Да, наистина е така!

Прав си във всяко едно отношение, ще имам предвид всичко, което си написал!

Благодаря за отделеното време!

1