
פורסם: 09/10/2008 - 17:47
נושא ההודעה: כיצד ניתן להמיר char ל-char* ב-C?
|
אני מנסה להבין את הנושא של עיבוד טקסט והקצאות זיכרון דינאמיות ב-C, אבל נתקלתי בבעיה של המרה בין טיפוסי נתונים. למשל הקוד הבא:
| קוד: |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld = '9';
char num[2];
strcpy(num, ld);
printf("%s\n", num);
}
|
המשתנה ld הוא char והפונקציה strcpy מצפה למחרוזת. לא הצלחתי למצוא דרך לעשות את ההמרה הזו למרות שבתחילה חשבתי שמדובר במשהו פשוט.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:25
נושא ההודעה: Re: כיצד ניתן להמיר char ל-char* ב-C?
|
| Anonymous : | אני מנסה להבין את הנושא של עיבוד טקסט והקצאות זיכרון דינאמיות ב-C, אבל נתקלתי בבעיה של המרה בין טיפוסי נתונים. למשל הקוד הבא:
| קוד: |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld = '9';
char num[2];
strcpy(num, ld);
printf("%s\n", num);
}
|
המשתנה ld הוא char והפונקציה strcpy מצפה למחרוזת. לא הצלחתי למצוא דרך לעשות את ההמרה הזו למרות שבתחילה חשבתי שמדובר במשהו פשוט. |
אני לא מאמין שאני עונה כאן.
| קוד: |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld = '9';
char *num = malloc(2);
strncpy(num, ld, 2);
printf("%s\n", num);
}
|
על הנייר זה אמור לעבוד (לא ניסיתי).
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:30
נושא ההודעה:
|
כן, זה לא עובד וניסיתי גם דברים כאלה  .
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:37
נושא ההודעה: Re: כיצד ניתן להמיר char ל-char* ב-C?
|
| קוד: |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld = '9';
char num[2];
strncpy(num, &ld, 1);
printf("%s\n", num);
}
|
זה האק סביר בשביל שזה יעבוד. כאמור (על ידך) הפוקנציה strcpy מצפה למחרוזת, שזה בעצם מערך של char שבתא האחרון יש null. בגלל שאתה מייצג את ld כ char, אתה צריך להעביר את המצביע לתא הראשון במחרוזת (יש רק תא אחד, אז זה בעצם המצביע לchar) ובגלל שאין לך null בסוף המערך, אתה צריך להשתמש ב strncpy בשביל להגביל להעתקה של char אחד, כי אין לו null שיגיד לו מתי להפסיק.
הצעה הרבה יותר חכמה זה לשנות את הקוד לכך:
| קוד: |
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld[2] = "9";
char num[2];
strcpy(num, ld);
printf("%s\n", num);
}
|
אבל בעקרון תמיד כדאי להשתמש ב strncpy בשביל שלא יהיו טעויות שיגמרו ל bufferoverflow  יום טוב.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:40
נושא ההודעה:
|
מאוד פשוט: מחרוזת היא מערך של תווים שהתו האחרון שלו הוא 0. במקרה שלך:
| קוד: | #include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
char ld = '9';
char num[2];
num[0]=ld;
num[1]=0
printf("%s\n", num);
} |
או שיטה כללית יותר להמרה של כל דבר למחרוזת
| קוד: |
char ld='9';
char str[2];
snprintf(str,sizeof(str),"%c",ld)); |
זכרון. עבודה עם מחרוזות snprintf הוא ידידך הטוב ביותר
עידו... הקוד שלך שגוי strcpy מקבל מחרוזת. תו איננו כזה. חוץ מזה, מה עם שחרור זכרון???
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:41
נושא ההודעה:
|
אתה חייב לשים null בסוף המחרוזת אחרת הוא ידפיס לך ג'בריש אחרי התו.
משהו כזה:
חוץ מזה למה לך להשתמש ב- strcpy פשוט תעשה num[0]=ld
וזהו.
אסף.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:46
נושא ההודעה:
|
|
תודה רבה לכולם במיוחד לך ארתיום, snprintf גורם לי לסלוד מניהול מחרוזות ב-C קצת פחות.
למי שתהה אני צריך את האפשרות הזו למשהו שונה בתכלית מהדוגמא בקוד, זה סתם הייתה הדרך שלי להבהיר למה אני מתכוון.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 18:52
נושא ההודעה:
|
| Anonymous : | תודה רבה לכולם במיוחד לך ארתיום, snprintf גורם לי לסלוד מניהול מחרוזות ב-C קצת פחות.
למי שתהה אני צריך את האפשרות הזו למשהו שונה בתכלית מהדוגמא בקוד, זה סתם הייתה הדרך שלי להבהיר למה אני מתכוון. |
תשמע, ניהול מחרוזות ב־C הוא לא מסובך אם אתה יכול להניח מגבלות על אורך המחרוזות. לדוגמה, אורך שם הקובץ עם 255 תווים (זו ד"א מגבלה אמתית).
להניח ששם של מישהו לא ארוך מ־100 תווים ועוד.
אז אתה עובד עם מחרוזות באורכים קבועים ואז אתה פתור מטיפול בשחרור והקצאה של זכרון.
אבל **זכור** להשתמש ב־snprint במקום sprintf, להשתמש ב־strncpy במקום strcpy... כדי להבטיח שהקוד שלך יהיה בטוח מ־buffer overflow.
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 19:21
נושא ההודעה:
|
ואם כבר פתחתי נושא. שאלה אחרת שיש לי על ניהול זיכרון. לא מצאתי דוגמאות קוד טובות לנושא של הקצאה דינאמית ולכן כתבתי את הקוד הבא שמקצה זיכרון לקובץ:
| קוד: |
char *file_read(const char *name)
{
FILE *fp = fopen(name, "r");
char *pa = (char *) malloc(1);
char line[RSIZE-1];
int count = 0;
while (fgets(line, RSIZE, fp)) {
pa = realloc(pa, count += strlen(line));
strcat(pa, line);
}
return pa;
}
|
אני חושב שזה מכוער ובטח יש שיטה הרבה יותר טובה וסטנדרטית לעשות את זה.
אם אפשר דוגמאות זה יהיה מעולה  .
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 19:24
נושא ההודעה:
|
|
ואני לא חושב שיש סיכוי לגלישת חוצץ בדוגמא הזו עם strcat לכן זה אני חושב שזה בטוח.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 19:44
נושא ההודעה:
|
| Anonymous : | | ואני לא חושב שיש סיכוי לגלישת חוצץ בדוגמא הזו עם strcat לכן זה אני חושב שזה בטוח. |
נכון, בדומה ספציפית לא... שוב, כשאתה יודע מה אתה עושה, לך על זה.
לגבי הדוגמה שלך. נראה שזה די בסדר (במבט ראשון). מה שכן, לא חייבים להשתמש ב־gets אלא fread ועוד.
בכל אופן... אני הייתי כותב את זה כך:
| קוד: | char *file_read(const char *name)
{
char *ptr=NULL;
FILE *f=fopen(name,"r");
if(!f) return NULL;
fseek(f,SEEK_END,0);
size=ftell(f);
fseek(f,SEEK_SET,0);
if((ptr=calloc(size+1))==NULL)
goto error_exit;
if(fread(ptr,1,size,f)!=size)
goto error_exit;
fclose(f);
return ptr;
error_exit:
fclose(f);
free(ptr);
return NULL;
} |
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 19:51
נושא ההודעה:
|
|
fseek יותר מהיר מ-fstat...?
|
|
|
| חזרה לתוכן הדיון |

פורסם: 09/10/2008 - 20:00
נושא ההודעה:
|
| ארתיום : | | Anonymous : | | ואני לא חושב שיש סיכוי לגלישת חוצץ בדוגמא הזו עם strcat לכן זה אני חושב שזה בטוח. |
נכון, בדומה ספציפית לא... שוב, כשאתה יודע מה אתה עושה, לך על זה.
לגבי הדוגמה שלך. נראה שזה די בסדר (במבט ראשון). מה שכן, לא חייבים להשתמש ב־gets אלא fread ועוד.
בכל אופן... אני הייתי כותב את זה כך:
| קוד: | char *file_read(const char *name)
{
char *ptr=NULL;
FILE *f=fopen(name,"r");
if(!f) return NULL;
fseek(f,SEEK_END,0);
size=ftell(f);
fseek(f,SEEK_SET,0);
if((ptr=calloc(size+1))==NULL)
goto error_exit;
if(fread(ptr,1,size,f)!=size)
goto error_exit;
fclose(f);
return ptr;
error_exit:
fclose(f);
free(ptr);
return NULL;
} |
|
כן הגיוני, אני לא רגיל לחשוב על הטיפול בכל הדברים הללו. תודה רבה על כל העזרה =).
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 20:58
נושא ההודעה:
|
| ארתיום : | | Anonymous : | | ואני לא חושב שיש סיכוי לגלישת חוצץ בדוגמא הזו עם strcat לכן זה אני חושב שזה בטוח. |
נכון, בדומה ספציפית לא... שוב, כשאתה יודע מה אתה עושה, לך על זה.
לגבי הדוגמה שלך. נראה שזה די בסדר (במבט ראשון). מה שכן, לא חייבים להשתמש ב־gets אלא fread ועוד.
בכל אופן... אני הייתי כותב את זה כך:
| קוד: | char *file_read(const char *name)
{
char *ptr=NULL;
FILE *f=fopen(name,"r");
if(!f) return NULL;
fseek(f,SEEK_END,0);
size=ftell(f);
fseek(f,SEEK_SET,0);
if((ptr=calloc(size+1))==NULL)
goto error_exit;
if(fread(ptr,1,size,f)!=size)
goto error_exit;
fclose(f);
return ptr;
error_exit:
fclose(f);
free(ptr);
return NULL;
} |
|
זה כבר לא קונצנזוס ש goto זה קקה?
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 21:16
נושא ההודעה:
|
|
לא בצורה גורפת. ובטח שלא בדוגמא הזו, שהופכת את הקוד להרבה יותר קריא.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 21:20
נושא ההודעה:
|
| ציטוט: | | זה כבר לא קונצנזוס ש goto זה קקה? |
ממש לא. goto משמש אותך מצויין בכל הנושא של מימוש RAII. ואני אסביר בקצרה.
תסתכל בקוד הזה:
| קוד: | int copy_file(char *in,char *out)
{
/* Init resource handlers */
void *buffer=NULL;
FILE *f1=NULL,*f2=NULL;
int result=0; /* Error */
/* Start Processing */
if((f1=fopen(in,"r"))==NULL)
goto exit;
if((f2=fopen(out,"w"))==NULL)
goto exit;
if((buffer=malloc(BUFFER_SIZE))==NULL)
goto exit;
int n;
while((n=fread(buffer,1,BUFFER_SIZE,f1))>0)
if(fwrite(buffer,1,n,f2)!=n)
goto exit;
result = 1; /* OK */
exit:
/* Free resources */
if(f1) fclose(f1);
if(f2) fclose(f2);
free(buffer);
return result;
} |
הוא עובד 100% בכל מקרה של שגיאה אפשרית. אם יש בעיה כלשהי, הוא יוצא. ומשחרר כל משאב שהוקצה, זכרון, קובץ ועוד.
זאת השיטה המקובלת ב־C לעבוד עם משאבים. היא מבטיחה שחרור מרוכז של כל המשאבים האפשריים. זאת שיטה מסודרת ומגנה בפני memory leaks, זאת שיטה שמחליפה try/finally או ctor/dtor בשפות אחרות.
זאת ההצדקה היחידה והחשובה שקיום goto. כמובן אסור לעשות איתו לולאות ושאר דברים מוזרים. אבל הוא תחליף מצויין ל־try/throw/catch/finally בשפה ללא תמיכה מובנית בזה. בפועל, throw זה אותו goto רק שלפעמים הרבה יותר גרוע.
אגב, את השיטה הזו למדתי מ־coding style של linux kernel (אגב, מסמך מאוד מעניין ודי מצחיק)
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 21:26
נושא ההודעה:
|
| ארתיום : | | ציטוט: | | זה כבר לא קונצנזוס ש goto זה קקה? |
ממש לא. goto משמש אותך מצויין בכל הנושא של מימוש RAII. ואני אסביר בקצרה.
תסתכל בקוד הזה:
| קוד: | int copy_file(char *in,char *out)
{
/* Init resource handlers */
void *buffer=NULL;
FILE *f1=NULL,*f2=NULL;
int result=0; /* Error */
/* Start Processing */
if((f1=fopen(in,"r"))==NULL)
goto exit;
if((f2=fopen(out,"w"))==NULL)
goto exit;
if((buffer=malloc(BUFFER_SIZE))==NULL)
goto exit;
int n;
while((n=fread(buffer,1,BUFFER_SIZE,f1))>0)
if(fwrite(buffer,1,n,f2)!=n)
goto exit;
result = 1; /* OK */
exit:
/* Free resources */
if(f1) fclose(f1);
if(f2) fclose(f2);
free(buffer);
return result;
} |
הוא עובד 100% בכל מקרה של שגיאה אפשרית. אם יש בעיה כלשהי, הוא יוצא. ומשחרר כל משאב שהוקצה, זכרון, קובץ ועוד.
זאת השיטה המקובלת ב־C לעבוד עם משאבים. היא מבטיחה שחרור מרוכז של כל המשאבים האפשריים. זאת שיטה מסודרת ומגנה בפני memory leaks, זאת שיטה שמחליפה try/finally או ctor/dtor בשפות אחרות.
זאת ההצדקה היחידה והחשובה שקיום goto. כמובן אסור לעשות איתו לולאות ושאר דברים מוזרים. אבל הוא תחליף מצויין ל־try/throw/catch/finally בשפה ללא תמיכה מובנית בזה. בפועל, throw זה אותו goto רק שלפעמים הרבה יותר גרוע.
אגב, את השיטה הזו למדתי מ־coding style של linux kernel (אגב, מסמך מאוד מעניין ודי מצחיק) |
גם אני חושב ש goto מעולה למקרים כאלה, וגם אני ראיתי דברים דומים בקרנל, אבל אני לא מכיר את המסמך שאתה מדבר עליו וחיפוש מהיר בגוגל הראה שיש כמה תוצאות אפשריות, או בקיצור, אפשר לינק? 
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 21:41
נושא ההודעה:
|
בבקשה: http://www.kernel.org/doc/Documentation/CodingStyle
אגב, קח את מה שלינוס כותב בקלות... לא חייבים להסכים עם כל מה שהוא אומר.
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 21:50
נושא ההודעה:
|
אל תדאג, אני יודע הוא אחלה גבר, אבל לא אלוהים
בכל מקרה, הוא כן מתחזק של פורייקט גדול ויש לו הרבה ניסיון בנושא, כאמור, הוא לא אלוהים, אבל בהחלט יש לקחת מה שהוא אומר ברצינות.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 10/10/2008 - 23:16
נושא ההודעה:
|
strncpy היא פונקציה מסוכנת כי במקרה של הגעה לגבול היא מעתיקה עד התווית האחרונה והמחרוזת שלך נשארת בלי NULL בסוף. snprintf עדיפה, לפיכך, והעובדה שמקרה כזה היא מחזירה את מספר התוויות *שהיו מועתקות* אילו לא היתה מגיעה לגבול, היא לפעמים שימושית.
באשר לgoto, דווקא יש לזה תחליף סביר ב-C, הנה הוא :
| קוד: |
int copy_file(char *in,char *out)
{
/* Init resource handlers */
void *buffer=NULL;
FILE *f1=NULL,*f2=NULL;
int result=0; /* Error */
do {
/* Start Processing */
if((f1=fopen(in,"r"))==NULL)
break;
if((f2=fopen(out,"w"))==NULL)
break;
if((buffer=malloc(BUFFER_SIZE))==NULL)
break;
int n;
while((n=fread(buffer,1,BUFFER_SIZE,f1))>0)
if(fwrite(buffer,1,n,f2)!=n)
break ;
result = 1; /* OK */
} while (0) ;
/* Free resources */
if(f1) fclose(f1);
if(f2) fclose(f2);
free(buffer);
return result;
}
|
יש הרואים בזה סגנון רע אבל זה בהחלט עושה את העבודה.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 00:16
נושא ההודעה:
|
|
אני לא חושב שזה יותר טוב מה-goto. שם זה גם מאוד ברור.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 07:43
נושא ההודעה:
|
|
ותיקון קטן: כדאי גם לבדוק את ערכו של buffer לפני שמשחררים אותו.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 07:50
נושא ההודעה:
|
| צפריר : | | ותיקון קטן: כדאי גם לבדוק את ערכו של buffer לפני שמשחררים אותו. |
ציטוט מ־man 3 free
| ציטוט: | | If ptr is NULL, no operation is performed |
לא צריך לבדוק את זה.
_________________ קזית 3 - קנופיקס עברי.
BiDiTeX - תמיכה בכיווניות ל-LaTeX.
CppCMS - פיתוח ל־web ב־++C.
גם לי יש בלוג
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 11:53
נושא ההודעה:
|
| ארתיום : | | צפריר : | | ותיקון קטן: כדאי גם לבדוק את ערכו של buffer לפני שמשחררים אותו. |
ציטוט מ־man 3 free
| ציטוט: | | If ptr is NULL, no operation is performed |
לא צריך לבדוק את זה. |
לדעתי הוא התכוון לאם הפוינטר לא null אלא מצביע למקום שכבר שוחרר, אבל אני לא מבין בדיוק איך הוא יכול לבדוק את הערך.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 12:05
נושא ההודעה:
|
|
לא. התכוונתי בדיוק למה שארתיום דיבר עליו. גרסאות ישנות יותר של התקן של C לא הבטיחו את זה ואני לא רגיל לסמוך על זה.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 12:33
נושא ההודעה:
|
| פינגווין אדום : | strncpy היא פונקציה מסוכנת כי במקרה של הגעה לגבול היא מעתיקה עד התווית האחרונה והמחרוזת שלך נשארת בלי NULL בסוף. snprintf עדיפה, לפיכך, והעובדה שמקרה כזה היא מחזירה את מספר התוויות *שהיו מועתקות* אילו לא היתה מגיעה לגבול, היא לפעמים שימושית.
באשר לgoto, דווקא יש לזה תחליף סביר ב-C, הנה הוא :
| קוד: |
int copy_file(char *in,char *out)
{
/* Init resource handlers */
void *buffer=NULL;
FILE *f1=NULL,*f2=NULL;
int result=0; /* Error */
do {
/* Start Processing */
if((f1=fopen(in,"r"))==NULL)
break;
if((f2=fopen(out,"w"))==NULL)
break;
if((buffer=malloc(BUFFER_SIZE))==NULL)
break;
int n;
while((n=fread(buffer,1,BUFFER_SIZE,f1))>0)
if(fwrite(buffer,1,n,f2)!=n)
break ;
result = 1; /* OK */
} while (0) ;
/* Free resources */
if(f1) fclose(f1);
if(f2) fclose(f2);
free(buffer);
return result;
}
|
יש הרואים בזה סגנון רע אבל זה בהחלט עושה את העבודה. |
חביב ביותר בתור תחליף למילה הנשכחת goto שרק האקרים מדופלמים עדיין רואים צורך להשתמש בה.
אסף.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 13:48
נושא ההודעה:
|
|
האם זה יותר קריא? האם זה פחות מבלבל?
לא. זה אפילו גרם לבאג: מהי התוצאה המוחזרת אם לא התלחנו לקרוא את כל התווים הנדרשים?
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 13:50
נושא ההודעה: עוד שיטה
|
|
אגב, עוד שיטה שאני מכיר (ואולי יהיו כאלה שיאמרו שהיא לא כ"כ מתאימה פה, וזה יכול להיות -
אבל היא טובה להרבה מיקרים) - היא לרכז מצביעים לכל המשאבים לפונקציה ב - structure.
אחרי כל בדיקת הקצאה אפשר לקרוא לפונקציה copy_file_free ולאחריה ל - return.
copy_file_free תקבל כארגומנט מצביע לאותו structure מדובר, ובפונקציה אתה משחרר
את כל המשאבים וזהו.
אם פעם נוספים לך משאבים כלשהם אתה צריך רק לעדכן את ה - structure ולהוסיף (בדר"כ
שורה או שתיים) בפונקציה copy_file_free.
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 13:51
נושא ההודעה:
|
דרך אגב (אני זה שכתב את התגובה האחרונה).
גם שמי אסף, אבל כפי הנראה הצטרף אלינו עוד אסף לאחרונה (יש לך אחלה שם  )
אז אני חושב שאני פשוט ארשם (הגיע הזמן אחרי איזה 3 שנים) לפורום...
אסף 
|
|
|
| חזרה לתוכן הדיון |

פורסם: 11/10/2008 - 14:40
נושא ההודעה:
|
|
|