שאלה לגבי מצביעים

fireballfxp

New member
שאלה לגבי מצביעים

היי,

במסגרת דוגמה שהראו לנו בשיעור של מצביעים (קורס C) לא הבנתי את כל הקטע של הצהרת והעברת מצביעים, מתי מעבירים עם * ומתי עם *** ובפונקציה שמקבלת מתי משתמשים ב-* וב** וב*** וב &.

ובהקצאה הדינאמית של זכרון כדוגמת :
קוד:
*res = (char**)realloc(*res, *current_capacity*sizeof(char*));

או

res[curr_token_idx] = (char*)malloc((strlen(p) + 1)*sizeof(char));
 

fireballfxp

New member
רצ"ב קטע הקוד המלא

קוד:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAXLENGTH 200
#define WHITESPACE " \n\t"
#define MALLOC_ERROR_CODE -1
#define _CRT_SECURE_NO_WARNINGS
#define strdup _strdup

int tokenize_v1(char *str, char ***res);
int tokenize_v2(char *str, char ***res);
int count_tokens(char *str, char *delimiters);
void allocate_token_array(int num_of_tokens, char ***res);
void fill_token_array(char *str, char *delimiters, char **res);
void double_array_size(char ***res, int *current_capacity);


void main(){

	char **tokens=NULL;
	char str[] = "abc     def    xxx    yyy     1111      555555    ajhjsd  dkhfdkf   dfjdkf   dfkjdkfk   dfkj  dkfjdkf";
	int num_of_tokens;

	num_of_tokens = tokenize_v1(str, &tokens);

	printf("================================================ \n");

	num_of_tokens = tokenize_v2(str, &tokens);

}

int tokenize_v1(char *str, char ***res){

	int num_of_tokens;
	char *copy, *p;

	copy = strdup(str);

	if (!copy){

		fprintf(stderr, "allocation error!\n");
		exit(MALLOC_ERROR_CODE);
	}

	num_of_tokens = count_tokens(copy, WHITESPACE);
	allocate_token_array(num_of_tokens, res);
	strcpy(copy, str);
	fill_token_array(copy, WHITESPACE, *res);
	free(copy);
	return (num_of_tokens);
}

int count_tokens(char *str, char *delimiters){

	int num_of_tokens = 0;
	char *p;

	p = strtok(str, WHITESPACE);

	while (p){

		num_of_tokens++;
		p = strtok(NULL, WHITESPACE);
	}

	return num_of_tokens;
}

void allocate_token_array(int num_of_tokens, char ***res){

	*res = (char**)malloc(num_of_tokens*sizeof(char*));
	if (!res){

		fprintf(stderr, "allocation error!\n");
		exit(MALLOC_ERROR_CODE);
	}
}

void fill_token_array(char *str, char *delimiters, char **res){

	int curr_token_idx = 0;
	char *p;

	p = strtok(str, delimiters);

	while (p){

		res[curr_token_idx] = (char*)malloc((strlen(p) + 1)*sizeof(char));

		if (!res[curr_token_idx]){

			fprintf(stderr, "allocation error!\n");
			exit(MALLOC_ERROR_CODE);

		}

		strcpy(res[curr_token_idx], p);
		curr_token_idx++;
		p = strtok(NULL, delimiters);
	}
}

int tokenize_v2(char *str, char ***res){

	int num_of_tokens, current_capacity;
	char *copy, *p;

	*res = NULL;

	copy = strdup(str);

	if (!copy){

		fprintf(stderr, "allocation error!\n");
		exit(MALLOC_ERROR_CODE);
	}

	current_capacity = num_of_tokens = 0;
	p = strtok(copy, WHITESPACE);
	while (p){

		if (num_of_tokens == current_capacity)

			double_array_size(res, &current_capacity);

			(*res)[num_of_tokens] = strdup(p);
			p = strtok(NULL, WHITESPACE);
	}
	
	*res = (char**)realloc(*res, num_of_tokens*sizeof(char*));
	free(copy);
	return(num_of_tokens);
}

void double_array_size(char ***res, int *current_capacity){

	*current_capacity = (*current_capacity) * 2 + 1;
	printf("resizing\n");
	*res = (char**)realloc(*res, *current_capacity*sizeof(char*));
	
	if (!*(res)){

		fprintf(stderr, "allocation error!\n");
		exit(MALLOC_ERROR_CODE);
	}

}
 

BravoMan

Active member
האם הבנת בכלל מה הוא מצביע?

מחשב שומר מידע בזיכרון שמחולק לתאים.
לכל תא יש מספר, כמו שלדירה בה אתה גר יש מספר.
תא 1, תא 2, תא 3 וכו'.
&nbsp
"מצביע" הוא משתנה שמכיל מספר של תא בזיכרון, שבו שמור ערך כלשהו.
סוג המצביע, עוזר לך לדעת את סוג הערך, למשל int * ptr אומר שבמשתנה prt יש מספר תא ובתא הזה שמור מידע מסוג int.
&nbsp
עד כאן מובן?
&nbsp
עכשיו, מה הקטע של ריבוי כוכביות?
אובכן, אפשר לשחק עם מצביעים "עקוב אחרי". כמו שאתה מחייג למספר מסוים, והוא מעביר אותך למספר אחר, וכך הלאה.
&nbsp
כלומר, משתנה מסוג מצביע יכול להכיל כתובת של משתנה אחר שגם הוא מצביע:
int ** ptr_to_ptr זה אומר שמשתנה ptr_to_ptr מחזיק בתוכו כתובת של תא בזיכרון, שבה מצויה גם כתובת של תא אחר בזיכרון, ובתא האחר הזה יש מספר מסוג int.
&nbsp
אם תוסיף כוכבית שלישית, תוסיף עוד שלב בשרשרת כתובת של תא -> כתובת של תא -> כתובת של תא שמכיל מספר.
&nbsp
ואז יש כמובן את ה-& שהוא הפעולה ההפוכה מכוכבית.
אם יש לך משתנה למשל כך int number, ואתה רוצה לדעת איפה באמת בזיכרון יושב הערך שלו, כלומר את מספר התא, אתה כותב number& וכך הקומפיילר יודע שאתה לא רוצה את הערך, אלא את הכתובת שלו.
&nbsp
ולהפך - אם יש לך משתנה כתובת, אבל אתה רוצה את הערך שבכתובת הזו, אתה מוסיף כוכביות לפני שם המשתנה.
&nbsp
ועכשיו לשאלת מליון הדולר - למה זה טוב?
התשובה פשוטה: העברת מידע ממקום למקום.
&nbsp
כפי שאתה וודאי כבר למדת, פונקציה ב-C יכולה להחזיר רק ערך אחד בודד.
והיא לא יכולה לשנות את הפרמטרים שלה.
&nbsp
למשל, בקוד שהבאת, tokenize_v1 לא יכולה לשנות את הערך של res.
אבל, תודות לכך ש-res מכיל כתובת של כתובת של כתובת, אז אין צורך לשנות אותו.
מספיק לקחת את הכתובת שבו, ולשנות את הזיכרון בכתובת הזו.
&nbsp
אז זה לפונקציה מותר לעשות, וכך היא יכולה להוציא החוצה מידע בלי לעשות return.
אם קיבלת פעם מחברת הביטוח או איזה מוסד מכתב, עם מעטפה בפנים כדי שתחזיר להם טופס, זה אותו הרעיון - הפונקציה מקבלת כתובת בזיכרון לשם היא צריכה להחזיר תשובה.
&nbsp
מקווה שזה מבהיר את העניין.
 

פרסאוס

New member
מצביע הוא משתנה שמכיל בתוכו כתובת של משתנה אחר.

המשתנה הזה יכול להיות מערך, מבנה או משתנה בודד.
* הוא הסימון למצביע. אז מה זה **? כאן יש לנו מצביע למצביע.
ו***? מצביע למצביע למצביע. למה כל זה? כדי לנהל זיכרון.
עכשיו נלך אחורה. מצביע יכול להצביע על מערך, מה שאומר שמצביע למצביע יכול להצביע על מערך מצביעים.
ועכשיו דוגמה מעשית:
מחרוזת היא מערך תווים. לכן אפשר להציב מצביע למחרוזת.
מה קורה אם יש לנו מחרוזות רבות? נניח של יישום מסויים?
יש לנו ** מצביע למערך של מצביעים שכל אחד מהם מצביע על מחרוזת.
ומה אם יש לנו את אותן מחרוזות בכמה שפות?
אז יש מצביע אחד למערך השפות, ממנו למערך המחרוזות וממנו למחרוזת הספציפית - ***.
אז מה זה &? זהו אופרטור שמבקש את כתובתו של משנה ובעצם מעביר מצביע למשתנה הזה. למה זה טוב? כדי לשנות ערך במשתנה שנכנס לפונקציה.
 
למעלה